1 /*-------------------------------------------------------------------------
4 * Support routines for scanning RangeFunctions (functions in rangetable).
6 * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.27 2004/09/22 17:41:51 tgl Exp $
13 *-------------------------------------------------------------------------
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
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 #include "utils/typcache.h"
38 static TupleTableSlot *FunctionNext(FunctionScanState *node);
39 static bool tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
41 /* ----------------------------------------------------------------
43 * ----------------------------------------------------------------
45 /* ----------------------------------------------------------------
48 * This is a workhorse for ExecFunctionScan
49 * ----------------------------------------------------------------
51 static TupleTableSlot *
52 FunctionNext(FunctionScanState *node)
56 ScanDirection direction;
57 Tuplestorestate *tuplestorestate;
62 * get information from the estate and scan state
64 estate = node->ss.ps.state;
65 direction = estate->es_direction;
67 tuplestorestate = node->tuplestorestate;
70 * If first time through, read all tuples from function and put them
71 * in a tuplestore. Subsequent calls just fetch tuples from
74 if (tuplestorestate == NULL)
76 ExprContext *econtext = node->ss.ps.ps_ExprContext;
77 TupleDesc funcTupdesc;
79 node->tuplestorestate = tuplestorestate =
80 ExecMakeTableFunctionResult(node->funcexpr,
86 * If function provided a tupdesc, cross-check it. We only really
87 * need to do this for functions returning RECORD, but might as
90 if (funcTupdesc && !tupledesc_match(node->tupdesc, funcTupdesc))
92 (errcode(ERRCODE_DATATYPE_MISMATCH),
93 errmsg("query-specified return row and actual function return row do not match")));
97 * Get the next tuple from tuplestore. Return NULL if no more tuples.
99 heapTuple = tuplestore_getheaptuple(tuplestorestate,
100 ScanDirectionIsForward(direction),
102 slot = node->ss.ss_ScanTupleSlot;
103 return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
106 /* ----------------------------------------------------------------
107 * ExecFunctionScan(node)
109 * Scans the function sequentially and returns the next qualifying
111 * It calls the ExecScan() routine and passes it the access method
112 * which retrieves tuples sequentially.
117 ExecFunctionScan(FunctionScanState *node)
120 * use FunctionNext as access method
122 return ExecScan(&node->ss, (ExecScanAccessMtd) FunctionNext);
125 /* ----------------------------------------------------------------
126 * ExecInitFunctionScan
127 * ----------------------------------------------------------------
130 ExecInitFunctionScan(FunctionScan *node, EState *estate)
132 FunctionScanState *scanstate;
136 TupleDesc tupdesc = NULL;
139 * FunctionScan should not have any children.
141 Assert(outerPlan(node) == NULL);
142 Assert(innerPlan(node) == NULL);
145 * create new ScanState for node
147 scanstate = makeNode(FunctionScanState);
148 scanstate->ss.ps.plan = (Plan *) node;
149 scanstate->ss.ps.state = estate;
152 * Miscellaneous initialization
154 * create expression context for node
156 ExecAssignExprContext(estate, &scanstate->ss.ps);
158 #define FUNCTIONSCAN_NSLOTS 2
161 * tuple table initialization
163 ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
164 ExecInitScanTupleSlot(estate, &scanstate->ss);
167 * initialize child expressions
169 scanstate->ss.ps.targetlist = (List *)
170 ExecInitExpr((Expr *) node->scan.plan.targetlist,
171 (PlanState *) scanstate);
172 scanstate->ss.ps.qual = (List *)
173 ExecInitExpr((Expr *) node->scan.plan.qual,
174 (PlanState *) scanstate);
177 * get info about function
179 rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
180 Assert(rte->rtekind == RTE_FUNCTION);
181 funcrettype = exprType(rte->funcexpr);
184 * Now determine if the function returns a simple or composite type,
185 * and build an appropriate tupdesc.
187 functyptype = get_typtype(funcrettype);
189 if (functyptype == 'c')
191 /* Composite data type, e.g. a table's row type */
192 tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(funcrettype, -1));
194 else if (functyptype == 'b' || functyptype == 'd')
196 /* Must be a base data type, i.e. scalar */
197 char *attname = strVal(linitial(rte->eref->colnames));
199 tupdesc = CreateTemplateTupleDesc(1, false);
200 TupleDescInitEntry(tupdesc,
207 else if (funcrettype == RECORDOID)
209 /* Must be a pseudo type, i.e. record */
210 tupdesc = BuildDescForRelation(rte->coldeflist);
214 /* crummy error message, but parser should have caught this */
215 elog(ERROR, "function in FROM has unsupported return type");
219 * For RECORD results, make sure a typmod has been assigned. (The
220 * function should do this for itself, but let's cover things in case
223 if (tupdesc->tdtypeid == RECORDOID && tupdesc->tdtypmod < 0)
224 assign_record_type_typmod(tupdesc);
226 scanstate->tupdesc = tupdesc;
227 ExecSetSlotDescriptor(scanstate->ss.ss_ScanTupleSlot,
231 * Other node-specific setup
233 scanstate->tuplestorestate = NULL;
234 scanstate->funcexpr = ExecInitExpr((Expr *) rte->funcexpr,
235 (PlanState *) scanstate);
237 scanstate->ss.ps.ps_TupFromTlist = false;
240 * Initialize result tuple type and projection info.
242 ExecAssignResultTypeFromTL(&scanstate->ss.ps);
243 ExecAssignProjectionInfo(&scanstate->ss.ps);
249 ExecCountSlotsFunctionScan(FunctionScan *node)
251 return ExecCountSlotsNode(outerPlan(node)) +
252 ExecCountSlotsNode(innerPlan(node)) +
256 /* ----------------------------------------------------------------
257 * ExecEndFunctionScan
259 * frees any storage allocated through C routines.
260 * ----------------------------------------------------------------
263 ExecEndFunctionScan(FunctionScanState *node)
266 * Free the exprcontext
268 ExecFreeExprContext(&node->ss.ps);
271 * clean out the tuple table
273 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
274 ExecClearTuple(node->ss.ss_ScanTupleSlot);
277 * Release tuplestore resources
279 if (node->tuplestorestate != NULL)
280 tuplestore_end(node->tuplestorestate);
281 node->tuplestorestate = NULL;
284 /* ----------------------------------------------------------------
285 * ExecFunctionMarkPos
287 * Calls tuplestore to save the current position in the stored file.
288 * ----------------------------------------------------------------
291 ExecFunctionMarkPos(FunctionScanState *node)
294 * if we haven't materialized yet, just return.
296 if (!node->tuplestorestate)
299 tuplestore_markpos(node->tuplestorestate);
302 /* ----------------------------------------------------------------
303 * ExecFunctionRestrPos
305 * Calls tuplestore to restore the last saved file position.
306 * ----------------------------------------------------------------
309 ExecFunctionRestrPos(FunctionScanState *node)
312 * if we haven't materialized yet, just return.
314 if (!node->tuplestorestate)
317 tuplestore_restorepos(node->tuplestorestate);
320 /* ----------------------------------------------------------------
323 * Rescans the relation.
324 * ----------------------------------------------------------------
327 ExecFunctionReScan(FunctionScanState *node, ExprContext *exprCtxt)
329 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
332 * If we haven't materialized yet, just return.
334 if (!node->tuplestorestate)
338 * Here we have a choice whether to drop the tuplestore (and recompute
339 * the function outputs) or just rescan it. This should depend on
340 * whether the function expression contains parameters and/or is
341 * marked volatile. FIXME soon.
343 if (node->ss.ps.chgParam != NULL)
345 tuplestore_end(node->tuplestorestate);
346 node->tuplestorestate = NULL;
349 tuplestore_rescan(node->tuplestorestate);
353 * Check that function result tuple type (src_tupdesc) matches or can be
354 * considered to match what the query expects (dst_tupdesc).
356 * We really only care about number of attributes and data type.
357 * Also, we can ignore type mismatch on columns that are dropped in the
358 * destination type, so long as the physical storage matches. This is
359 * helpful in some cases involving out-of-date cached plans.
362 tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
366 if (dst_tupdesc->natts != src_tupdesc->natts)
369 for (i = 0; i < dst_tupdesc->natts; i++)
371 Form_pg_attribute dattr = dst_tupdesc->attrs[i];
372 Form_pg_attribute sattr = src_tupdesc->attrs[i];
374 if (dattr->atttypid == sattr->atttypid)
375 continue; /* no worries */
376 if (!dattr->attisdropped)
378 if (dattr->attlen != sattr->attlen ||
379 dattr->attalign != sattr->attalign)