]> granicus.if.org Git - postgresql/blob - src/backend/executor/execScan.c
Revise TupleTableSlot code to avoid unnecessary construction and disassembly
[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-2005, 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.35 2005/03/16 21:38:06 tgl 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 ExecClearTuple(projInfo->pi_slot);
110                         else
111                                 return slot;
112                 }
113
114                 /*
115                  * place the current tuple into the expr context
116                  */
117                 econtext->ecxt_scantuple = slot;
118
119                 /*
120                  * check that the current tuple satisfies the qual-clause
121                  *
122                  * check for non-nil qual here to avoid a function call to ExecQual()
123                  * when the qual is nil ... saves only a few cycles, but they add
124                  * up ...
125                  */
126                 if (!qual || ExecQual(qual, econtext, false))
127                 {
128                         /*
129                          * Found a satisfactory scan tuple.
130                          */
131                         if (projInfo)
132                         {
133                                 /*
134                                  * Form a projection tuple, store it in the result tuple
135                                  * slot and return it --- unless we find we can project no
136                                  * tuples from this scan tuple, in which case continue
137                                  * scan.
138                                  */
139                                 resultSlot = ExecProject(projInfo, &isDone);
140                                 if (isDone != ExprEndResult)
141                                 {
142                                         node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
143                                         return resultSlot;
144                                 }
145                         }
146                         else
147                         {
148                                 /*
149                                  * Here, we aren't projecting, so just return scan tuple.
150                                  */
151                                 return slot;
152                         }
153                 }
154
155                 /*
156                  * Tuple fails qual, so free per-tuple memory and try again.
157                  */
158                 ResetExprContext(econtext);
159         }
160 }
161
162 /*
163  * ExecAssignScanProjectionInfo
164  *              Set up projection info for a scan node, if necessary.
165  *
166  * We can avoid a projection step if the requested tlist exactly matches
167  * the underlying tuple type.  If so, we just set ps_ProjInfo to NULL.
168  * Note that this case occurs not only for simple "SELECT * FROM ...", but
169  * also in most cases where there are joins or other processing nodes above
170  * the scan node, because the planner will preferentially generate a matching
171  * tlist.
172  *
173  * ExecAssignScanType must have been called already.
174  */
175 void
176 ExecAssignScanProjectionInfo(ScanState *node)
177 {
178         Scan       *scan = (Scan *) node->ps.plan;
179
180         if (tlist_matches_tupdesc(&node->ps,
181                                                           scan->plan.targetlist,
182                                                           scan->scanrelid,
183                                                           node->ss_ScanTupleSlot->tts_tupleDescriptor))
184                 node->ps.ps_ProjInfo = NULL;
185         else
186                 ExecAssignProjectionInfo(&node->ps);
187 }
188
189 static bool
190 tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc)
191 {
192         int                     numattrs = tupdesc->natts;
193         int                     attrno;
194         bool            hasoid;
195         ListCell   *tlist_item = list_head(tlist);
196
197         /* Check the tlist attributes */
198         for (attrno = 1; attrno <= numattrs; attrno++)
199         {
200                 Form_pg_attribute att_tup = tupdesc->attrs[attrno - 1];
201                 Var                *var;
202
203                 if (tlist_item == NULL)
204                         return false;           /* tlist too short */
205                 var = (Var *) ((TargetEntry *) lfirst(tlist_item))->expr;
206                 if (!var || !IsA(var, Var))
207                         return false;           /* tlist item not a Var */
208                 Assert(var->varno == varno);
209                 Assert(var->varlevelsup == 0);
210                 if (var->varattno != attrno)
211                         return false;           /* out of order */
212                 if (att_tup->attisdropped)
213                         return false;           /* table contains dropped columns */
214                 Assert(var->vartype == att_tup->atttypid);
215                 Assert(var->vartypmod == att_tup->atttypmod);
216
217                 tlist_item = lnext(tlist_item);
218         }
219
220         if (tlist_item)
221                 return false;                   /* tlist too long */
222
223         /*
224          * If the plan context requires a particular hasoid setting, then that
225          * has to match, too.
226          */
227         if (ExecContextForcesOids(ps, &hasoid) &&
228                 hasoid != tupdesc->tdhasoid)
229                 return false;
230
231         return true;
232 }