]> granicus.if.org Git - postgresql/blob - src/backend/executor/execScan.c
Update copyright to 2004.
[postgresql] / src / backend / executor / execScan.c
1 /*-------------------------------------------------------------------------
2  *
3  * execScan.c
4  *        This code provides support for generalized relation scans. ExecScan
5  *        is passed a node and a pointer to a function to "do the right thing"
6  *        and return a tuple from the relation. ExecScan then does the tedious
7  *        stuff - checking the qualification and projecting the tuple
8  *        appropriately.
9  *
10  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
11  * Portions Copyright (c) 1994, Regents of the University of California
12  *
13  *
14  * IDENTIFICATION
15  *        $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.32 2004/08/29 04:12:31 momjian Exp $
16  *
17  *-------------------------------------------------------------------------
18  */
19 #include "postgres.h"
20
21 #include "executor/executor.h"
22 #include "miscadmin.h"
23 #include "utils/memutils.h"
24
25
26 static bool tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc);
27
28
29 /* ----------------------------------------------------------------
30  *              ExecScan
31  *
32  *              Scans the relation using the 'access method' indicated and
33  *              returns the next qualifying tuple in the direction specified
34  *              in the global variable ExecDirection.
35  *              The access method returns the next tuple and execScan() is
36  *              responsible for checking the tuple returned against the qual-clause.
37  *
38  *              Conditions:
39  *                -- the "cursor" maintained by the AMI is positioned at the tuple
40  *                       returned previously.
41  *
42  *              Initial States:
43  *                -- the relation indicated is opened for scanning so that the
44  *                       "cursor" is positioned before the first qualifying tuple.
45  * ----------------------------------------------------------------
46  */
47 TupleTableSlot *
48 ExecScan(ScanState *node,
49                  ExecScanAccessMtd accessMtd)   /* function returning a tuple */
50 {
51         EState     *estate;
52         ExprContext *econtext;
53         List       *qual;
54         ProjectionInfo *projInfo;
55         ExprDoneCond isDone;
56         TupleTableSlot *resultSlot;
57
58         /*
59          * Fetch data from node
60          */
61         estate = node->ps.state;
62         econtext = node->ps.ps_ExprContext;
63         qual = node->ps.qual;
64         projInfo = node->ps.ps_ProjInfo;
65
66         /*
67          * Check to see if we're still projecting out tuples from a previous
68          * scan tuple (because there is a function-returning-set in the
69          * projection expressions).  If so, try to project another one.
70          */
71         if (node->ps.ps_TupFromTlist)
72         {
73                 Assert(projInfo);               /* can't get here if not projecting */
74                 resultSlot = ExecProject(projInfo, &isDone);
75                 if (isDone == ExprMultipleResult)
76                         return resultSlot;
77                 /* Done with that source tuple... */
78                 node->ps.ps_TupFromTlist = false;
79         }
80
81         /*
82          * Reset per-tuple memory context to free any expression evaluation
83          * storage allocated in the previous tuple cycle.  Note this can't
84          * happen until we're done projecting out tuples from a scan tuple.
85          */
86         ResetExprContext(econtext);
87
88         /*
89          * get a tuple from the access method loop until we obtain a tuple
90          * which passes the qualification.
91          */
92         for (;;)
93         {
94                 TupleTableSlot *slot;
95
96                 CHECK_FOR_INTERRUPTS();
97
98                 slot = (*accessMtd) (node);
99
100                 /*
101                  * if the slot returned by the accessMtd contains NULL, then it
102                  * means there is nothing more to scan so we just return an empty
103                  * slot, being careful to use the projection result slot so it has
104                  * correct tupleDesc.
105                  */
106                 if (TupIsNull(slot))
107                 {
108                         if (projInfo)
109                                 return ExecStoreTuple(NULL,
110                                                                           projInfo->pi_slot,
111                                                                           InvalidBuffer,
112                                                                           true);
113                         else
114                                 return slot;
115                 }
116
117                 /*
118                  * place the current tuple into the expr context
119                  */
120                 econtext->ecxt_scantuple = slot;
121
122                 /*
123                  * check that the current tuple satisfies the qual-clause
124                  *
125                  * check for non-nil qual here to avoid a function call to ExecQual()
126                  * when the qual is nil ... saves only a few cycles, but they add
127                  * up ...
128                  */
129                 if (!qual || ExecQual(qual, econtext, false))
130                 {
131                         /*
132                          * Found a satisfactory scan tuple.
133                          */
134                         if (projInfo)
135                         {
136                                 /*
137                                  * Form a projection tuple, store it in the result tuple
138                                  * slot and return it --- unless we find we can project no
139                                  * tuples from this scan tuple, in which case continue
140                                  * scan.
141                                  */
142                                 resultSlot = ExecProject(projInfo, &isDone);
143                                 if (isDone != ExprEndResult)
144                                 {
145                                         node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
146                                         return resultSlot;
147                                 }
148                         }
149                         else
150                         {
151                                 /*
152                                  * Here, we aren't projecting, so just return scan tuple.
153                                  */
154                                 return slot;
155                         }
156                 }
157
158                 /*
159                  * Tuple fails qual, so free per-tuple memory and try again.
160                  */
161                 ResetExprContext(econtext);
162         }
163 }
164
165 /*
166  * ExecAssignScanProjectionInfo
167  *              Set up projection info for a scan node, if necessary.
168  *
169  * We can avoid a projection step if the requested tlist exactly matches
170  * the underlying tuple type.  If so, we just set ps_ProjInfo to NULL.
171  * Note that this case occurs not only for simple "SELECT * FROM ...", but
172  * also in most cases where there are joins or other processing nodes above
173  * the scan node, because the planner will preferentially generate a matching
174  * tlist.
175  *
176  * ExecAssignScanType must have been called already.
177  */
178 void
179 ExecAssignScanProjectionInfo(ScanState *node)
180 {
181         Scan       *scan = (Scan *) node->ps.plan;
182
183         if (tlist_matches_tupdesc(&node->ps,
184                                                           scan->plan.targetlist,
185                                                           scan->scanrelid,
186                                                         node->ss_ScanTupleSlot->ttc_tupleDescriptor))
187                 node->ps.ps_ProjInfo = NULL;
188         else
189                 ExecAssignProjectionInfo(&node->ps);
190 }
191
192 static bool
193 tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc)
194 {
195         int                     numattrs = tupdesc->natts;
196         int                     attrno;
197         bool            hasoid;
198         ListCell   *tlist_item = list_head(tlist);
199
200         /* Check the tlist attributes */
201         for (attrno = 1; attrno <= numattrs; attrno++)
202         {
203                 Form_pg_attribute att_tup = tupdesc->attrs[attrno - 1];
204                 Var                *var;
205
206                 if (tlist_item == NULL)
207                         return false;           /* tlist too short */
208                 var = (Var *) ((TargetEntry *) lfirst(tlist_item))->expr;
209                 if (!var || !IsA(var, Var))
210                         return false;           /* tlist item not a Var */
211                 Assert(var->varno == varno);
212                 Assert(var->varlevelsup == 0);
213                 if (var->varattno != attrno)
214                         return false;           /* out of order */
215                 if (att_tup->attisdropped)
216                         return false;           /* table contains dropped columns */
217                 Assert(var->vartype == att_tup->atttypid);
218                 Assert(var->vartypmod == att_tup->atttypmod);
219
220                 tlist_item = lnext(tlist_item);
221         }
222
223         if (tlist_item)
224                 return false;                   /* tlist too long */
225
226         /*
227          * If the plan context requires a particular hasoid setting, then
228          * that has to match, too.
229          */
230         if (ExecContextForcesOids(ps, &hasoid) &&
231                 hasoid != tupdesc->tdhasoid)
232                 return false;
233
234         return true;
235 }