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