- /* Composite data type, e.g. a table's row type */
- tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(funcrettype, -1));
- }
- else if (functypclass == TYPEFUNC_SCALAR)
- {
- /* Base data type, i.e. scalar */
- char *attname = strVal(linitial(rte->eref->colnames));
-
- tupdesc = CreateTemplateTupleDesc(1, false);
- TupleDescInitEntry(tupdesc,
- (AttrNumber) 1,
- attname,
- funcrettype,
- -1,
- 0);
+ RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
+ Node *funcexpr = rtfunc->funcexpr;
+ int colcount = rtfunc->funccolcount;
+ FunctionScanPerFuncState *fs = &scanstate->funcstates[i];
+ TypeFuncClass functypclass;
+ Oid funcrettype;
+ TupleDesc tupdesc;
+
+ fs->funcexpr = ExecInitExpr((Expr *) funcexpr, (PlanState *) scanstate);
+
+ /*
+ * Don't allocate the tuplestores; the actual calls to the functions
+ * do that. NULL means that we have not called the function yet (or
+ * need to call it again after a rescan).
+ */
+ fs->tstore = NULL;
+ fs->rowcount = -1;
+
+ /*
+ * Now determine if the function returns a simple or composite type,
+ * and build an appropriate tupdesc. Note that in the composite case,
+ * the function may now return more columns than it did when the plan
+ * was made; we have to ignore any columns beyond "colcount".
+ */
+ functypclass = get_expr_result_type(funcexpr,
+ &funcrettype,
+ &tupdesc);
+
+ if (functypclass == TYPEFUNC_COMPOSITE)
+ {
+ /* Composite data type, e.g. a table's row type */
+ Assert(tupdesc);
+ Assert(tupdesc->natts >= colcount);
+ /* Must copy it out of typcache for safety */
+ tupdesc = CreateTupleDescCopy(tupdesc);
+ }
+ else if (functypclass == TYPEFUNC_SCALAR)
+ {
+ /* Base data type, i.e. scalar */
+ tupdesc = CreateTemplateTupleDesc(1, false);
+ TupleDescInitEntry(tupdesc,
+ (AttrNumber) 1,
+ NULL, /* don't care about the name here */
+ funcrettype,
+ -1,
+ 0);
+ TupleDescInitEntryCollation(tupdesc,
+ (AttrNumber) 1,
+ exprCollation(funcexpr));
+ }
+ else if (functypclass == TYPEFUNC_RECORD)
+ {
+ tupdesc = BuildDescFromLists(rtfunc->funccolnames,
+ rtfunc->funccoltypes,
+ rtfunc->funccoltypmods,
+ rtfunc->funccolcollations);
+
+ /*
+ * For RECORD results, make sure a typmod has been assigned. (The
+ * function should do this for itself, but let's cover things in
+ * case it doesn't.)
+ */
+ BlessTupleDesc(tupdesc);
+ }
+ else
+ {
+ /* crummy error message, but parser should have caught this */
+ elog(ERROR, "function in FROM has unsupported return type");
+ }
+
+ fs->tupdesc = tupdesc;
+ fs->colcount = colcount;
+
+ /*
+ * We only need separate slots for the function results if we are
+ * doing ordinality or multiple functions; otherwise, we'll fetch
+ * function results directly into the scan slot.
+ */
+ if (!scanstate->simple)
+ {
+ fs->func_slot = ExecInitExtraTupleSlot(estate);
+ ExecSetSlotDescriptor(fs->func_slot, fs->tupdesc);
+ }
+ else
+ fs->func_slot = NULL;
+
+ natts += colcount;
+ i++;