]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeFunctionscan.c
Make the world safe (more or less) for dropped columns in plpgsql rowtypes.
[postgresql] / src / backend / executor / nodeFunctionscan.c
1 /*-------------------------------------------------------------------------
2  *
3  * nodeFunctionscan.c
4  *        Support routines for scanning RangeFunctions (functions in rangetable).
5  *
6  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.22 2003/09/25 23:02:12 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 /*
16  * INTERFACE ROUTINES
17  *              ExecFunctionScan                scans a function.
18  *              ExecFunctionNext                retrieve next tuple in sequential order.
19  *              ExecInitFunctionScan    creates and initializes a functionscan node.
20  *              ExecEndFunctionScan             releases any storage allocated.
21  *              ExecFunctionReScan              rescans the function
22  */
23 #include "postgres.h"
24
25 #include "access/heapam.h"
26 #include "catalog/pg_type.h"
27 #include "executor/execdebug.h"
28 #include "executor/execdefs.h"
29 #include "executor/execdesc.h"
30 #include "executor/nodeFunctionscan.h"
31 #include "parser/parsetree.h"
32 #include "parser/parse_expr.h"
33 #include "parser/parse_type.h"
34 #include "utils/lsyscache.h"
35
36
37 static TupleTableSlot *FunctionNext(FunctionScanState *node);
38 static bool tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
39
40 /* ----------------------------------------------------------------
41  *                                              Scan Support
42  * ----------------------------------------------------------------
43  */
44 /* ----------------------------------------------------------------
45  *              FunctionNext
46  *
47  *              This is a workhorse for ExecFunctionScan
48  * ----------------------------------------------------------------
49  */
50 static TupleTableSlot *
51 FunctionNext(FunctionScanState *node)
52 {
53         TupleTableSlot *slot;
54         EState     *estate;
55         ScanDirection direction;
56         Tuplestorestate *tuplestorestate;
57         bool            should_free;
58         HeapTuple       heapTuple;
59
60         /*
61          * get information from the estate and scan state
62          */
63         estate = node->ss.ps.state;
64         direction = estate->es_direction;
65
66         tuplestorestate = node->tuplestorestate;
67
68         /*
69          * If first time through, read all tuples from function and put them
70          * in a tuplestore. Subsequent calls just fetch tuples from
71          * tuplestore.
72          */
73         if (tuplestorestate == NULL)
74         {
75                 ExprContext *econtext = node->ss.ps.ps_ExprContext;
76                 TupleDesc       funcTupdesc;
77
78                 node->tuplestorestate = tuplestorestate =
79                         ExecMakeTableFunctionResult(node->funcexpr,
80                                                                                 econtext,
81                                                                                 node->tupdesc,
82                                                                                 &funcTupdesc);
83
84                 /*
85                  * If function provided a tupdesc, cross-check it.      We only really
86                  * need to do this for functions returning RECORD, but might as
87                  * well do it always.
88                  */
89                 if (funcTupdesc && !tupledesc_match(node->tupdesc, funcTupdesc))
90                         ereport(ERROR,
91                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
92                                          errmsg("query-specified return row and actual function return row do not match")));
93         }
94
95         /*
96          * Get the next tuple from tuplestore. Return NULL if no more tuples.
97          */
98         slot = node->ss.ss_ScanTupleSlot;
99         if (tuplestorestate)
100                 heapTuple = tuplestore_getheaptuple(tuplestorestate,
101                                                                            ScanDirectionIsForward(direction),
102                                                                                         &should_free);
103         else
104         {
105                 heapTuple = NULL;
106                 should_free = false;
107         }
108
109         return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
110 }
111
112 /* ----------------------------------------------------------------
113  *              ExecFunctionScan(node)
114  *
115  *              Scans the function sequentially and returns the next qualifying
116  *              tuple.
117  *              It calls the ExecScan() routine and passes it the access method
118  *              which retrieves tuples sequentially.
119  *
120  */
121
122 TupleTableSlot *
123 ExecFunctionScan(FunctionScanState *node)
124 {
125         /*
126          * use FunctionNext as access method
127          */
128         return ExecScan(&node->ss, (ExecScanAccessMtd) FunctionNext);
129 }
130
131 /* ----------------------------------------------------------------
132  *              ExecInitFunctionScan
133  * ----------------------------------------------------------------
134  */
135 FunctionScanState *
136 ExecInitFunctionScan(FunctionScan *node, EState *estate)
137 {
138         FunctionScanState *scanstate;
139         RangeTblEntry *rte;
140         Oid                     funcrettype;
141         char            functyptype;
142         TupleDesc       tupdesc = NULL;
143
144         /*
145          * FunctionScan should not have any children.
146          */
147         Assert(outerPlan(node) == NULL);
148         Assert(innerPlan(node) == NULL);
149
150         /*
151          * create new ScanState for node
152          */
153         scanstate = makeNode(FunctionScanState);
154         scanstate->ss.ps.plan = (Plan *) node;
155         scanstate->ss.ps.state = estate;
156
157         /*
158          * Miscellaneous initialization
159          *
160          * create expression context for node
161          */
162         ExecAssignExprContext(estate, &scanstate->ss.ps);
163
164 #define FUNCTIONSCAN_NSLOTS 2
165
166         /*
167          * tuple table initialization
168          */
169         ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
170         ExecInitScanTupleSlot(estate, &scanstate->ss);
171
172         /*
173          * initialize child expressions
174          */
175         scanstate->ss.ps.targetlist = (List *)
176                 ExecInitExpr((Expr *) node->scan.plan.targetlist,
177                                          (PlanState *) scanstate);
178         scanstate->ss.ps.qual = (List *)
179                 ExecInitExpr((Expr *) node->scan.plan.qual,
180                                          (PlanState *) scanstate);
181
182         /*
183          * get info about function
184          */
185         rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
186         Assert(rte->rtekind == RTE_FUNCTION);
187         funcrettype = exprType(rte->funcexpr);
188
189         /*
190          * Now determine if the function returns a simple or composite type,
191          * and build an appropriate tupdesc.
192          */
193         functyptype = get_typtype(funcrettype);
194
195         if (functyptype == 'c')
196         {
197                 /*
198                  * Composite data type, i.e. a table's row type
199                  */
200                 Oid                     funcrelid;
201                 Relation        rel;
202
203                 funcrelid = typeidTypeRelid(funcrettype);
204                 if (!OidIsValid(funcrelid))
205                         elog(ERROR, "invalid typrelid for complex type %u",
206                                  funcrettype);
207                 rel = relation_open(funcrelid, AccessShareLock);
208                 tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
209                 relation_close(rel, AccessShareLock);
210         }
211         else if (functyptype == 'b' || functyptype == 'd')
212         {
213                 /*
214                  * Must be a base data type, i.e. scalar
215                  */
216                 char       *attname = strVal(lfirst(rte->eref->colnames));
217
218                 tupdesc = CreateTemplateTupleDesc(1, false);
219                 TupleDescInitEntry(tupdesc,
220                                                    (AttrNumber) 1,
221                                                    attname,
222                                                    funcrettype,
223                                                    -1,
224                                                    0,
225                                                    false);
226         }
227         else if (functyptype == 'p' && funcrettype == RECORDOID)
228         {
229                 /*
230                  * Must be a pseudo type, i.e. record
231                  */
232                 tupdesc = BuildDescForRelation(rte->coldeflist);
233         }
234         else
235         {
236                 /* crummy error message, but parser should have caught this */
237                 elog(ERROR, "function in FROM has unsupported return type");
238         }
239
240         scanstate->tupdesc = tupdesc;
241         ExecSetSlotDescriptor(scanstate->ss.ss_ScanTupleSlot,
242                                                   tupdesc, false);
243
244         /*
245          * Other node-specific setup
246          */
247         scanstate->tuplestorestate = NULL;
248         scanstate->funcexpr = ExecInitExpr((Expr *) rte->funcexpr,
249                                                                            (PlanState *) scanstate);
250
251         scanstate->ss.ps.ps_TupFromTlist = false;
252
253         /*
254          * Initialize result tuple type and projection info.
255          */
256         ExecAssignResultTypeFromTL(&scanstate->ss.ps);
257         ExecAssignProjectionInfo(&scanstate->ss.ps);
258
259         return scanstate;
260 }
261
262 int
263 ExecCountSlotsFunctionScan(FunctionScan *node)
264 {
265         return ExecCountSlotsNode(outerPlan(node)) +
266                 ExecCountSlotsNode(innerPlan(node)) +
267                 FUNCTIONSCAN_NSLOTS;
268 }
269
270 /* ----------------------------------------------------------------
271  *              ExecEndFunctionScan
272  *
273  *              frees any storage allocated through C routines.
274  * ----------------------------------------------------------------
275  */
276 void
277 ExecEndFunctionScan(FunctionScanState *node)
278 {
279         /*
280          * Free the exprcontext
281          */
282         ExecFreeExprContext(&node->ss.ps);
283
284         /*
285          * clean out the tuple table
286          */
287         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
288         ExecClearTuple(node->ss.ss_ScanTupleSlot);
289
290         /*
291          * Release tuplestore resources
292          */
293         if (node->tuplestorestate != NULL)
294                 tuplestore_end(node->tuplestorestate);
295         node->tuplestorestate = NULL;
296 }
297
298 /* ----------------------------------------------------------------
299  *              ExecFunctionMarkPos
300  *
301  *              Calls tuplestore to save the current position in the stored file.
302  * ----------------------------------------------------------------
303  */
304 void
305 ExecFunctionMarkPos(FunctionScanState *node)
306 {
307         /*
308          * if we haven't materialized yet, just return.
309          */
310         if (!node->tuplestorestate)
311                 return;
312
313         tuplestore_markpos(node->tuplestorestate);
314 }
315
316 /* ----------------------------------------------------------------
317  *              ExecFunctionRestrPos
318  *
319  *              Calls tuplestore to restore the last saved file position.
320  * ----------------------------------------------------------------
321  */
322 void
323 ExecFunctionRestrPos(FunctionScanState *node)
324 {
325         /*
326          * if we haven't materialized yet, just return.
327          */
328         if (!node->tuplestorestate)
329                 return;
330
331         tuplestore_restorepos(node->tuplestorestate);
332 }
333
334 /* ----------------------------------------------------------------
335  *              ExecFunctionReScan
336  *
337  *              Rescans the relation.
338  * ----------------------------------------------------------------
339  */
340 void
341 ExecFunctionReScan(FunctionScanState *node, ExprContext *exprCtxt)
342 {
343         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
344
345         /*
346          * If we haven't materialized yet, just return.
347          */
348         if (!node->tuplestorestate)
349                 return;
350
351         /*
352          * Here we have a choice whether to drop the tuplestore (and recompute
353          * the function outputs) or just rescan it.  This should depend on
354          * whether the function expression contains parameters and/or is
355          * marked volatile.  FIXME soon.
356          */
357         if (node->ss.ps.chgParam != NULL)
358         {
359                 tuplestore_end(node->tuplestorestate);
360                 node->tuplestorestate = NULL;
361         }
362         else
363                 tuplestore_rescan(node->tuplestorestate);
364 }
365
366 /*
367  * Check that function result tuple type (src_tupdesc) matches or can be
368  * considered to match what the query expects (dst_tupdesc).
369  *
370  * We really only care about number of attributes and data type.
371  * Also, we can ignore type mismatch on columns that are dropped in the
372  * destination type, so long as the physical storage matches.  This is
373  * helpful in some cases involving out-of-date cached plans.
374  */
375 static bool
376 tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
377 {
378         int                     i;
379
380         if (dst_tupdesc->natts != src_tupdesc->natts)
381                 return false;
382
383         for (i = 0; i < dst_tupdesc->natts; i++)
384         {
385                 Form_pg_attribute dattr = dst_tupdesc->attrs[i];
386                 Form_pg_attribute sattr = src_tupdesc->attrs[i];
387
388                 if (dattr->atttypid == sattr->atttypid)
389                         continue;                       /* no worries */
390                 if (!dattr->attisdropped)
391                         return false;
392                 if (dattr->attlen != sattr->attlen ||
393                         dattr->attalign != sattr->attalign)
394                         return false;
395         }
396
397         return true;
398 }