*** a/src/backend/access/common/tupdesc.c --- b/src/backend/access/common/tupdesc.c *************** *** 192,197 **** CreateTupleDescCopyExtend(TupleDesc tupdesc, int moreatts) --- 192,234 ---- } /* + * CreateTupleDescCopyMany + * This function creates a new TupleDesc by copying from a set of + * existing TupleDescs. The new tupdesc is an anonymous record type + * (and never has oids) + * + * !!! Constraints and defaults are not copied !!! + */ + TupleDesc + CreateTupleDescCopyMany(TupleDesc *tupdescs, int numtupdescs) + { + TupleDesc desc; + int i,j; + int src_natts = 0; + int att = 0; + + for (i = 0; i < numtupdescs; ++i) + src_natts += tupdescs[i]->natts; + + desc = CreateTemplateTupleDesc(src_natts, false); + + for (i = 0; i < numtupdescs; ++i) + { + int natts = tupdescs[i]->natts; + for (j = 0; j < natts; j++) + { + memcpy(desc->attrs[att], tupdescs[i]->attrs[j], ATTRIBUTE_FIXED_PART_SIZE); + desc->attrs[att]->attnum = att + 1; + desc->attrs[att]->attnotnull = false; + desc->attrs[att]->atthasdef = false; + ++att; + } + } + + return desc; + } + + /* * CreateTupleDescCopyConstr * This function creates a new TupleDesc by copying from an existing * TupleDesc (including its constraints and defaults). *** a/src/backend/commands/explain.c --- b/src/backend/commands/explain.c *************** *** 1259,1265 **** ExplainNode(PlanState *planstate, List *ancestors, break; case T_FunctionScan: if (es->verbose) ! show_expression(((FunctionScan *) plan)->funcexpr, "Function Call", planstate, ancestors, es->verbose, es); show_scan_qual(plan->qual, "Filter", planstate, ancestors, es); --- 1259,1265 ---- break; case T_FunctionScan: if (es->verbose) ! show_expression((Node *) ((FunctionScan *) plan)->funcexprs, "Function Call", planstate, ancestors, es->verbose, es); show_scan_qual(plan->qual, "Filter", planstate, ancestors, es); *************** *** 1984,1990 **** ExplainTargetRel(Plan *plan, Index rti, ExplainState *es) break; case T_FunctionScan: { ! Node *funcexpr; /* Assert it's on a RangeFunction */ Assert(rte->rtekind == RTE_FUNCTION); --- 1984,1990 ---- break; case T_FunctionScan: { ! List *funcexprs; /* Assert it's on a RangeFunction */ Assert(rte->rtekind == RTE_FUNCTION); *************** *** 1995,2004 **** ExplainTargetRel(Plan *plan, Index rti, ExplainState *es) * happen if the optimizer simplified away the function call, * for example). */ ! funcexpr = ((FunctionScan *) plan)->funcexpr; ! if (funcexpr && IsA(funcexpr, FuncExpr)) { ! Oid funcid = ((FuncExpr *) funcexpr)->funcid; objectname = get_func_name(funcid); if (es->verbose) --- 1995,2004 ---- * happen if the optimizer simplified away the function call, * for example). */ ! funcexprs = ((FunctionScan *) plan)->funcexprs; ! if (funcexprs && list_length(funcexprs) == 1 && IsA(linitial(funcexprs), FuncExpr)) { ! Oid funcid = ((FuncExpr *) linitial(funcexprs))->funcid; objectname = get_func_name(funcid); if (es->verbose) *** a/src/backend/executor/nodeFunctionscan.c --- b/src/backend/executor/nodeFunctionscan.c *************** *** 44,57 **** FunctionNext(FunctionScanState *node) { EState *estate; ScanDirection direction; ! Tuplestorestate *tuplestorestate; TupleTableSlot *scanslot; ! TupleTableSlot *funcslot; ! ! if (node->func_slot) { /* ! * ORDINALITY case: * * We fetch the function result into FUNCSLOT (which matches the * function return type), and then copy the values to SCANSLOT --- 44,63 ---- { EState *estate; ScanDirection direction; ! Tuplestorestate **tuplestorestatep = node->tuplestorestates; TupleTableSlot *scanslot; ! TupleTableSlot **funcslotp; ! TupleDesc *func_tupdescp = node->func_tupdescs; ! ListCell *lc; ! int att = 0; ! bool alldone = true; ! int64 *rowcountp = node->rowcounts; ! int64 oldpos; ! ! if (node->func_slots) { /* ! * ORDINALITY or multiple functions case: * * We fetch the function result into FUNCSLOT (which matches the * function return type), and then copy the values to SCANSLOT *************** *** 59,76 **** FunctionNext(FunctionScanState *node) * column in the process. */ ! funcslot = node->func_slot; scanslot = node->ss.ss_ScanTupleSlot; } else { /* ! * non-ORDINALITY case: the function return type and scan result * type are the same, so we fetch the function result straight * into the scan result slot. */ ! funcslot = node->ss.ss_ScanTupleSlot; scanslot = NULL; } --- 65,83 ---- * column in the process. */ ! funcslotp = node->func_slots; scanslot = node->ss.ss_ScanTupleSlot; + ExecClearTuple(scanslot); } else { /* ! * trivial case: the function return type and scan result * type are the same, so we fetch the function result straight * into the scan result slot. */ ! funcslotp = &node->ss.ss_ScanTupleSlot; scanslot = NULL; } *************** *** 80,120 **** FunctionNext(FunctionScanState *node) estate = node->ss.ps.state; direction = estate->es_direction; - tuplestorestate = node->tuplestorestate; - - /* - * If first time through, read all tuples from function and put them in a - * tuplestore. Subsequent calls just fetch tuples from tuplestore. - */ - if (tuplestorestate == NULL) - { - node->tuplestorestate = tuplestorestate = - ExecMakeTableFunctionResult(node->funcexpr, - node->ss.ps.ps_ExprContext, - node->func_tupdesc, - node->eflags & EXEC_FLAG_BACKWARD); - } - - /* - * Get the next tuple from tuplestore. Return NULL if no more tuples. - */ - (void) tuplestore_gettupleslot(tuplestorestate, - ScanDirectionIsForward(direction), - false, - funcslot); - - if (!scanslot) - return funcslot; - - /* - * we're doing ordinality, so we copy the values from the function return - * slot to the (distinct) scan slot. We can do this because the lifetimes - * of the values in each slot are the same; until we reset the scan or - * fetch the next tuple, both will be valid. - */ - - ExecClearTuple(scanslot); - /* * increment or decrement before checking for end-of-data, so that we can * move off either end of the result by 1 (and no more than 1) without --- 87,92 ---- *************** *** 123,151 **** FunctionNext(FunctionScanState *node) */ if (ScanDirectionIsForward(direction)) ! node->ordinal++; else ! node->ordinal--; ! if (!TupIsNull(funcslot)) { ! int natts = funcslot->tts_tupleDescriptor->natts; ! int i; ! slot_getallattrs(funcslot); ! for (i = 0; i < natts; ++i) { ! scanslot->tts_values[i] = funcslot->tts_values[i]; ! scanslot->tts_isnull[i] = funcslot->tts_isnull[i]; } ! scanslot->tts_values[natts] = Int64GetDatumFast(node->ordinal); ! scanslot->tts_isnull[natts] = false; ! ExecStoreVirtualTuple(scanslot); } return scanslot; } --- 95,187 ---- */ if (ScanDirectionIsForward(direction)) ! oldpos = node->ordinal++; else ! oldpos = node->ordinal--; ! foreach(lc, node->funcexprs) { ! TupleDesc tupdesc = *func_tupdescp++; ! TupleTableSlot *slot = *funcslotp++; ! int i, natts; ! ! /* ! * If first time through, read all tuples from function and put them in a ! * tuplestore. Subsequent calls just fetch tuples from tuplestore. ! */ ! if (*tuplestorestatep == NULL) ! { ! *tuplestorestatep = ! ExecMakeTableFunctionResult(lfirst(lc), ! node->ss.ps.ps_ExprContext, ! tupdesc, ! node->eflags & EXEC_FLAG_BACKWARD); ! } ! ! /* ! * Get the next tuple from tuplestore. Return NULL if no more tuples. ! */ ! if (!rowcountp || *rowcountp == -1 || *rowcountp >= oldpos) ! (void) tuplestore_gettupleslot(*tuplestorestatep, ! ScanDirectionIsForward(direction), ! false, ! slot); ! else ! ExecClearTuple(slot); ! ! /* bail on the simple case now */ ! if (!scanslot) ! return slot; ! natts = tupdesc->natts; ! ! /* ! * If we ran out of data for this function in the forward direction ! * then we now know how many rows it returned. We need to know this ! * in order to handle backwards scans. The row count we store is ! * actually 1+ the actual number, because we have to position the ! * tuplestore 1 off its end sometimes. ! */ ! Assert(rowcountp); ! ! if (TupIsNull(slot)) { ! if (ScanDirectionIsForward(direction) && *rowcountp == -1) ! *rowcountp = node->ordinal; ! ! for (i = 0; i < natts; ++i, ++att) ! { ! scanslot->tts_values[att] = (Datum) 0; ! scanslot->tts_isnull[att] = true; ! } } + else + { + slot_getallattrs(slot); ! for (i = 0; i < natts; ++i, ++att) ! { ! scanslot->tts_values[att] = slot->tts_values[i]; ! scanslot->tts_isnull[att] = slot->tts_isnull[i]; ! } ! alldone = false; ! } ! ! ++rowcountp; ! ++tuplestorestatep; } + if (node->ordinality) + { + scanslot->tts_values[att] = Int64GetDatumFast(node->ordinal); + scanslot->tts_isnull[att] = false; + } + + if (!alldone) + ExecStoreVirtualTuple(scanslot); + return scanslot; } *************** *** 186,193 **** ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) FunctionScanState *scanstate; Oid funcrettype; TypeFuncClass functypclass; ! TupleDesc func_tupdesc = NULL; TupleDesc scan_tupdesc = NULL; /* check for unsupported flags */ Assert(!(eflags & EXEC_FLAG_MARK)); --- 222,233 ---- FunctionScanState *scanstate; Oid funcrettype; TypeFuncClass functypclass; ! TupleDesc *func_tupdescs = NULL; TupleDesc scan_tupdesc = NULL; + int nfuncs = list_length(node->funcexprs); + bool ordinality = node->funcordinality; + int i, atts_done; + ListCell *lc; /* check for unsupported flags */ Assert(!(eflags & EXEC_FLAG_MARK)); *************** *** 206,211 **** ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) --- 246,253 ---- scanstate->ss.ps.state = estate; scanstate->eflags = eflags; + scanstate->ordinality = ordinality; + /* * Miscellaneous initialization * *************** *** 220,233 **** ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) ExecInitScanTupleSlot(estate, &scanstate->ss); /* ! * We only need a separate slot for the function result if we are doing ! * ordinality; otherwise, we fetch function results directly into the ! * scan slot. */ ! if (node->funcordinality) ! scanstate->func_slot = ExecInitExtraTupleSlot(estate); else ! scanstate->func_slot = NULL; /* * initialize child expressions --- 262,287 ---- ExecInitScanTupleSlot(estate, &scanstate->ss); /* ! * We only need separate slots for the function results if we are doing ! * ordinality or multiple functions; otherwise, we fetch function ! * results directly into the scan slot. Same for rowcounts. */ ! if (ordinality || nfuncs > 1) ! { ! scanstate->func_slots = palloc(nfuncs * sizeof(TupleTableSlot *)); ! scanstate->rowcounts = palloc(nfuncs * sizeof(int64)); ! ! for (i = 0; i < nfuncs; ++i) ! { ! scanstate->func_slots[i] = ExecInitExtraTupleSlot(estate); ! scanstate->rowcounts[i] = -1; ! } ! } else ! { ! scanstate->func_slots = NULL; ! scanstate->rowcounts = NULL; ! } /* * initialize child expressions *************** *** 239,349 **** ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) ExecInitExpr((Expr *) node->scan.plan.qual, (PlanState *) scanstate); ! /* ! * Now determine if the function returns a simple or composite ! * type, and build an appropriate tupdesc. This tupdesc ! * (func_tupdesc) is the one that matches the shape of the ! * function result, no extra columns. ! */ ! functypclass = get_expr_result_type(node->funcexpr, ! &funcrettype, ! &func_tupdesc); ! if (functypclass == TYPEFUNC_COMPOSITE) { ! /* Composite data type, e.g. a table's row type */ ! Assert(func_tupdesc); /* ! * XXX ! * Existing behaviour is a bit inconsistent with regard to aliases and ! * whole-row Vars of the function result. If the function returns a ! * composite type, then the whole-row Var will refer to this tupdesc, ! * which has the type's own column names rather than the alias column ! * names given in the query. This affects the output of constructs like ! * row_to_json which read the column names from the passed-in values. */ ! /* Must copy it out of typcache for safety */ ! func_tupdesc = CreateTupleDescCopy(func_tupdesc); ! } ! else if (functypclass == TYPEFUNC_SCALAR) ! { ! /* Base data type, i.e. scalar */ ! char *attname = strVal(linitial(node->funccolnames)); ! ! func_tupdesc = CreateTemplateTupleDesc(1, false); ! TupleDescInitEntry(func_tupdesc, ! (AttrNumber) 1, ! attname, ! funcrettype, ! -1, ! 0); ! TupleDescInitEntryCollation(func_tupdesc, ! (AttrNumber) 1, ! exprCollation(node->funcexpr)); ! } ! else if (functypclass == TYPEFUNC_RECORD) ! { ! func_tupdesc = BuildDescFromLists(node->funccolnames, ! node->funccoltypes, ! node->funccoltypmods, ! node->funccolcollations); ! } ! else ! { ! /* crummy error message, but parser should have caught this */ ! elog(ERROR, "function in FROM has unsupported return type"); ! } ! /* ! * 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(func_tupdesc); /* * If doing ordinality, we need a new tupdesc with one additional column * tacked on, always of type "bigint". The name to use has already been * recorded by the parser as the last element of funccolnames. * ! * Without ordinality, the scan result tupdesc is the same as the ! * function result tupdesc. (No need to make a copy.) */ ! if (node->funcordinality) { ! int natts = func_tupdesc->natts; ! scan_tupdesc = CreateTupleDescCopyExtend(func_tupdesc, 1); ! TupleDescInitEntry(scan_tupdesc, ! natts + 1, ! strVal(llast(node->funccolnames)), ! INT8OID, ! -1, ! 0); BlessTupleDesc(scan_tupdesc); } else ! scan_tupdesc = func_tupdesc; scanstate->scan_tupdesc = scan_tupdesc; ! scanstate->func_tupdesc = func_tupdesc; ExecAssignScanType(&scanstate->ss, scan_tupdesc); ! if (scanstate->func_slot) ! ExecSetSlotDescriptor(scanstate->func_slot, func_tupdesc); /* * Other node-specific setup */ scanstate->ordinal = 0; - scanstate->tuplestorestate = NULL; ! scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr, ! (PlanState *) scanstate); scanstate->ss.ps.ps_TupFromTlist = false; --- 293,434 ---- ExecInitExpr((Expr *) node->scan.plan.qual, (PlanState *) scanstate); ! scanstate->func_tupdescs ! = func_tupdescs ! = palloc((ordinality ? (nfuncs + 1) : nfuncs) * sizeof(TupleDesc)); ! i = 0; ! atts_done = 0; ! foreach(lc, node->funcexprs) { ! TupleDesc tupdesc; /* ! * Now determine if the function returns a simple or composite ! * type, and build an appropriate tupdesc. This tupdesc ! * (func_tupdesc) is the one that matches the shape of the ! * function result, no extra columns. */ + functypclass = get_expr_result_type(lfirst(lc), + &funcrettype, + &tupdesc); ! if (functypclass == TYPEFUNC_COMPOSITE) ! { ! /* Composite data type, e.g. a table's row type */ ! Assert(tupdesc); ! ! /* ! * XXX ! * Existing behaviour is a bit inconsistent with regard to aliases and ! * whole-row Vars of the function result. If the function returns a ! * composite type, then the whole-row Var will refer to this tupdesc, ! * which has the type's own column names rather than the alias column ! * names given in the query. This affects the output of constructs like ! * row_to_json which read the column names from the passed-in values. ! */ ! ! /* Must copy it out of typcache for safety */ ! tupdesc = CreateTupleDescCopy(tupdesc); ! ! atts_done += tupdesc->natts; ! } ! else if (functypclass == TYPEFUNC_SCALAR) ! { ! /* Base data type, i.e. scalar */ ! char *attname = strVal(list_nth(node->funccolnames, atts_done)); ! ! tupdesc = CreateTemplateTupleDesc(1, false); ! TupleDescInitEntry(tupdesc, ! (AttrNumber) 1, ! attname, ! funcrettype, ! -1, ! 0); ! TupleDescInitEntryCollation(tupdesc, ! (AttrNumber) 1, ! exprCollation(lfirst(lc))); ! ! ++atts_done; ! } ! else if (functypclass == TYPEFUNC_RECORD) ! { ! Assert(atts_done == 0); ! Assert(lnext(lc) == NULL); ! tupdesc = BuildDescFromLists(node->funccolnames, ! node->funccoltypes, ! node->funccoltypmods, ! node->funccolcollations); ! } ! else ! { ! /* crummy error message, but parser should have caught this */ ! elog(ERROR, "function in FROM has unsupported return type"); ! } ! ! /* ! * 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); ! ! func_tupdescs[i++] = tupdesc; ! } /* * If doing ordinality, we need a new tupdesc with one additional column * tacked on, always of type "bigint". The name to use has already been * recorded by the parser as the last element of funccolnames. * ! * Without ordinality or multiple functions, the scan result tupdesc is ! * the same as the function result tupdesc. (No need to make a copy.) */ ! if (ordinality || nfuncs > 1) { ! int ntupdescs = nfuncs; ! ! if (ordinality) ! { ! TupleDesc tupdesc = CreateTemplateTupleDesc(1, false); ! TupleDescInitEntry(tupdesc, ! 1, ! strVal(llast(node->funccolnames)), ! INT8OID, ! -1, ! 0); ! ! func_tupdescs[ntupdescs++] = tupdesc; ! } ! scan_tupdesc = CreateTupleDescCopyMany(func_tupdescs, ntupdescs); BlessTupleDesc(scan_tupdesc); } else ! scan_tupdesc = func_tupdescs[0]; scanstate->scan_tupdesc = scan_tupdesc; ! ExecAssignScanType(&scanstate->ss, scan_tupdesc); ! if (scanstate->func_slots) ! for (i = 0; i < nfuncs; ++i) ! ExecSetSlotDescriptor(scanstate->func_slots[i], func_tupdescs[i]); /* * Other node-specific setup */ scanstate->ordinal = 0; ! scanstate->tuplestorestates = palloc(nfuncs * sizeof(Tuplestorestate *)); ! for (i = 0; i < nfuncs; ++i) ! scanstate->tuplestorestates[i] = NULL; ! ! scanstate->funcexprs = (List *) ExecInitExpr((Expr *) node->funcexprs, ! (PlanState *) scanstate); scanstate->ss.ps.ps_TupFromTlist = false; *************** *** 365,370 **** ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) --- 450,458 ---- void ExecEndFunctionScan(FunctionScanState *node) { + int i; + int nfuncs = list_length(node->funcexprs); + /* * Free the exprcontext */ *************** *** 375,389 **** ExecEndFunctionScan(FunctionScanState *node) */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); ! if (node->func_slot) ! ExecClearTuple(node->func_slot); /* * Release tuplestore resources */ ! if (node->tuplestorestate != NULL) ! tuplestore_end(node->tuplestorestate); ! node->tuplestorestate = NULL; } /* ---------------------------------------------------------------- --- 463,482 ---- */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); ! ! if (node->func_slots) ! for (i = 0; i < nfuncs; ++i) ! ExecClearTuple(node->func_slots[i]); /* * Release tuplestore resources */ ! for (i = 0; i < nfuncs; ++i) ! { ! if (node->tuplestorestates[i] != NULL) ! tuplestore_end(node->tuplestorestates[i]); ! node->tuplestorestates[i] = NULL; ! } } /* ---------------------------------------------------------------- *************** *** 395,403 **** ExecEndFunctionScan(FunctionScanState *node) void ExecReScanFunctionScan(FunctionScanState *node) { ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ! if (node->func_slot) ! ExecClearTuple(node->func_slot); ExecScanReScan(&node->ss); --- 488,500 ---- void ExecReScanFunctionScan(FunctionScanState *node) { + int i; + int nfuncs = list_length(node->funcexprs); + ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ! if (node->func_slots) ! for (i = 0; i < nfuncs; ++i) ! ExecClearTuple(node->func_slots[i]); ExecScanReScan(&node->ss); *************** *** 406,425 **** ExecReScanFunctionScan(FunctionScanState *node) /* * If we haven't materialized yet, just return. */ ! if (!node->tuplestorestate) return; /* ! * Here we have a choice whether to drop the tuplestore (and recompute the ! * function outputs) or just rescan it. We must recompute if the * expression contains parameters, else we rescan. XXX maybe we should ! * recompute if the function is volatile? */ ! if (node->ss.ps.chgParam != NULL) { ! tuplestore_end(node->tuplestorestate); ! node->tuplestorestate = NULL; } - else - tuplestore_rescan(node->tuplestorestate); } --- 503,529 ---- /* * If we haven't materialized yet, just return. */ ! if (!node->tuplestorestates[0]) return; /* ! * Here we have a choice whether to drop the tuplestores (and recompute the ! * function outputs) or just rescan them. We must recompute if the * expression contains parameters, else we rescan. XXX maybe we should ! * recompute if the function is volatile? Work out what params belong ! * to what functions? */ ! for (i = 0; i < nfuncs; ++i) { ! if (node->ss.ps.chgParam != NULL) ! { ! if (node->tuplestorestates[i] != NULL) ! tuplestore_end(node->tuplestorestates[i]); ! node->tuplestorestates[i] = NULL; ! if (node->rowcounts) ! node->rowcounts[i] = -1; ! } ! else ! tuplestore_rescan(node->tuplestorestates[i]); } } *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 504,510 **** _copyFunctionScan(const FunctionScan *from) /* * copy remainder of node */ ! COPY_NODE_FIELD(funcexpr); COPY_NODE_FIELD(funccolnames); COPY_NODE_FIELD(funccoltypes); COPY_NODE_FIELD(funccoltypmods); --- 504,510 ---- /* * copy remainder of node */ ! COPY_NODE_FIELD(funcexprs); COPY_NODE_FIELD(funccolnames); COPY_NODE_FIELD(funccoltypes); COPY_NODE_FIELD(funccoltypmods); *************** *** 1980,1986 **** _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); COPY_NODE_FIELD(joinaliasvars); ! COPY_NODE_FIELD(funcexpr); COPY_NODE_FIELD(funccoltypes); COPY_NODE_FIELD(funccoltypmods); COPY_NODE_FIELD(funccolcollations); --- 1980,1986 ---- COPY_SCALAR_FIELD(security_barrier); COPY_SCALAR_FIELD(jointype); COPY_NODE_FIELD(joinaliasvars); ! COPY_NODE_FIELD(funcexprs); COPY_NODE_FIELD(funccoltypes); COPY_NODE_FIELD(funccoltypmods); COPY_NODE_FIELD(funccolcollations); *************** *** 2300,2306 **** _copyRangeFunction(const RangeFunction *from) COPY_SCALAR_FIELD(ordinality); COPY_SCALAR_FIELD(lateral); ! COPY_NODE_FIELD(funccallnode); COPY_NODE_FIELD(alias); COPY_NODE_FIELD(coldeflist); --- 2300,2306 ---- COPY_SCALAR_FIELD(ordinality); COPY_SCALAR_FIELD(lateral); ! COPY_NODE_FIELD(funccallnodes); COPY_NODE_FIELD(alias); COPY_NODE_FIELD(coldeflist); *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 2128,2134 **** _equalRangeFunction(const RangeFunction *a, const RangeFunction *b) { COMPARE_SCALAR_FIELD(ordinality); COMPARE_SCALAR_FIELD(lateral); ! COMPARE_NODE_FIELD(funccallnode); COMPARE_NODE_FIELD(alias); COMPARE_NODE_FIELD(coldeflist); --- 2128,2134 ---- { COMPARE_SCALAR_FIELD(ordinality); COMPARE_SCALAR_FIELD(lateral); ! COMPARE_NODE_FIELD(funccallnodes); COMPARE_NODE_FIELD(alias); COMPARE_NODE_FIELD(coldeflist); *************** *** 2231,2237 **** _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); COMPARE_NODE_FIELD(joinaliasvars); ! COMPARE_NODE_FIELD(funcexpr); COMPARE_NODE_FIELD(funccoltypes); COMPARE_NODE_FIELD(funccoltypmods); COMPARE_NODE_FIELD(funccolcollations); --- 2231,2237 ---- COMPARE_SCALAR_FIELD(security_barrier); COMPARE_SCALAR_FIELD(jointype); COMPARE_NODE_FIELD(joinaliasvars); ! COMPARE_NODE_FIELD(funcexprs); COMPARE_NODE_FIELD(funccoltypes); COMPARE_NODE_FIELD(funccoltypmods); COMPARE_NODE_FIELD(funccolcollations); *** a/src/backend/nodes/makefuncs.c --- b/src/backend/nodes/makefuncs.c *************** *** 164,177 **** makeWholeRowVar(RangeTblEntry *rte, * If ordinality is set, we return a composite var even if * the function is a scalar. This var is always of RECORD type. * * If ordinality is not set but the function returns a row, * we keep the function's return type. * * If the function is a scalar, we do what allowScalar requests. */ ! toid = exprType(rte->funcexpr); ! if (rte->funcordinality) { /* ORDINALITY always produces an anonymous RECORD result */ result = makeVar(varno, --- 164,180 ---- * If ordinality is set, we return a composite var even if * the function is a scalar. This var is always of RECORD type. * + * If the RTE has more than one function, we return a composite + * var of record type. + * * If ordinality is not set but the function returns a row, * we keep the function's return type. * * If the function is a scalar, we do what allowScalar requests. */ ! toid = exprType(linitial(rte->funcexprs)); ! if (rte->funcordinality || list_length(rte->funcexprs) > 1) { /* ORDINALITY always produces an anonymous RECORD result */ result = makeVar(varno, *************** *** 198,204 **** makeWholeRowVar(RangeTblEntry *rte, 1, toid, -1, ! exprCollation(rte->funcexpr), varlevelsup); } else --- 201,207 ---- 1, toid, -1, ! exprCollation(linitial(rte->funcexprs)), varlevelsup); } else *** a/src/backend/nodes/nodeFuncs.c --- b/src/backend/nodes/nodeFuncs.c *************** *** 2000,2006 **** range_table_walker(List *rtable, return true; break; case RTE_FUNCTION: ! if (walker(rte->funcexpr, context)) return true; break; case RTE_VALUES: --- 2000,2006 ---- return true; break; case RTE_FUNCTION: ! if (walker(rte->funcexprs, context)) return true; break; case RTE_VALUES: *************** *** 2725,2731 **** range_table_mutator(List *rtable, } break; case RTE_FUNCTION: ! MUTATE(newrte->funcexpr, rte->funcexpr, Node *); break; case RTE_VALUES: MUTATE(newrte->values_lists, rte->values_lists, List *); --- 2725,2731 ---- } break; case RTE_FUNCTION: ! MUTATE(newrte->funcexprs, rte->funcexprs, List *); break; case RTE_VALUES: MUTATE(newrte->values_lists, rte->values_lists, List *); *************** *** 3113,3119 **** raw_expression_tree_walker(Node *node, { RangeFunction *rf = (RangeFunction *) node; ! if (walker(rf->funccallnode, context)) return true; if (walker(rf->alias, context)) return true; --- 3113,3119 ---- { RangeFunction *rf = (RangeFunction *) node; ! if (walker(rf->funccallnodes, context)) return true; if (walker(rf->alias, context)) return true; *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** *** 516,522 **** _outFunctionScan(StringInfo str, const FunctionScan *node) _outScanInfo(str, (const Scan *) node); ! WRITE_NODE_FIELD(funcexpr); WRITE_NODE_FIELD(funccolnames); WRITE_NODE_FIELD(funccoltypes); WRITE_NODE_FIELD(funccoltypmods); --- 516,522 ---- _outScanInfo(str, (const Scan *) node); ! WRITE_NODE_FIELD(funcexprs); WRITE_NODE_FIELD(funccolnames); WRITE_NODE_FIELD(funccoltypes); WRITE_NODE_FIELD(funccoltypmods); *************** *** 2379,2385 **** _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_NODE_FIELD(joinaliasvars); break; case RTE_FUNCTION: ! WRITE_NODE_FIELD(funcexpr); WRITE_NODE_FIELD(funccoltypes); WRITE_NODE_FIELD(funccoltypmods); WRITE_NODE_FIELD(funccolcollations); --- 2379,2385 ---- WRITE_NODE_FIELD(joinaliasvars); break; case RTE_FUNCTION: ! WRITE_NODE_FIELD(funcexprs); WRITE_NODE_FIELD(funccoltypes); WRITE_NODE_FIELD(funccoltypmods); WRITE_NODE_FIELD(funccolcollations); *************** *** 2618,2624 **** _outRangeFunction(StringInfo str, const RangeFunction *node) WRITE_BOOL_FIELD(ordinality); WRITE_BOOL_FIELD(lateral); ! WRITE_NODE_FIELD(funccallnode); WRITE_NODE_FIELD(alias); WRITE_NODE_FIELD(coldeflist); } --- 2618,2624 ---- WRITE_BOOL_FIELD(ordinality); WRITE_BOOL_FIELD(lateral); ! WRITE_NODE_FIELD(funccallnodes); WRITE_NODE_FIELD(alias); WRITE_NODE_FIELD(coldeflist); } *** a/src/backend/nodes/readfuncs.c --- b/src/backend/nodes/readfuncs.c *************** *** 1219,1225 **** _readRangeTblEntry(void) READ_NODE_FIELD(joinaliasvars); break; case RTE_FUNCTION: ! READ_NODE_FIELD(funcexpr); READ_NODE_FIELD(funccoltypes); READ_NODE_FIELD(funccoltypmods); READ_NODE_FIELD(funccolcollations); --- 1219,1225 ---- READ_NODE_FIELD(joinaliasvars); break; case RTE_FUNCTION: ! READ_NODE_FIELD(funcexprs); READ_NODE_FIELD(funccoltypes); READ_NODE_FIELD(funccoltypmods); READ_NODE_FIELD(funccolcollations); *** a/src/backend/optimizer/path/allpaths.c --- b/src/backend/optimizer/path/allpaths.c *************** *** 37,42 **** --- 37,43 ---- #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" + #include "catalog/pg_opfamily.h" /* These parameters are set by GUC */ *************** *** 1259,1264 **** static void --- 1260,1266 ---- set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { Relids required_outer; + List *pathkeys = NIL; /* * We don't support pushing join clauses into the quals of a function *************** *** 1267,1274 **** set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) */ required_outer = rel->lateral_relids; /* Generate appropriate path */ ! add_path(rel, create_functionscan_path(root, rel, required_outer)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); --- 1269,1310 ---- */ required_outer = rel->lateral_relids; + /* + * The result is treated as unordered unless ORDINALITY was used, in which + * case it is ordered by the ordinal column (last). + */ + if (rte->funcordinality) + { + ListCell *lc; + Var *var = NULL; + AttrNumber ordattno = list_length(rte->eref->colnames); + + foreach(lc, rel->reltargetlist) + { + Var *node = lfirst(lc); + + if (IsA(node,Var) + && node->varno == rel->relid + && node->varattno == ordattno + && node->varlevelsup == 0) + { + var = node; + break; + } + } + + if (var) + { + Oid operator = get_opfamily_member(INTEGER_BTREE_FAM_OID, + var->vartype, var->vartype, + BTLessStrategyNumber); + + pathkeys = build_expression_pathkey(root, rel, (Expr*) var, operator, false); + } + } + /* Generate appropriate path */ ! add_path(rel, create_functionscan_path(root, rel, pathkeys, required_outer)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); *** a/src/backend/optimizer/path/costsize.c --- b/src/backend/optimizer/path/costsize.c *************** *** 1042,1048 **** cost_functionscan(Path *path, PlannerInfo *root, * estimates for functions tend to be, there's not a lot of point in that * refinement right now. */ ! cost_qual_eval_node(&exprcost, rte->funcexpr, root); startup_cost += exprcost.startup + exprcost.per_tuple; --- 1042,1048 ---- * estimates for functions tend to be, there's not a lot of point in that * refinement right now. */ ! cost_qual_eval_node(&exprcost, (Node *) rte->funcexprs, root); startup_cost += exprcost.startup + exprcost.per_tuple; *************** *** 3799,3812 **** void set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel) { RangeTblEntry *rte; /* Should only be applied to base relations that are functions */ Assert(rel->relid > 0); rte = planner_rt_fetch(rel->relid, root); Assert(rte->rtekind == RTE_FUNCTION); /* Estimate number of rows the function itself will return */ ! rel->tuples = expression_returns_set_rows(rte->funcexpr); /* Now estimate number of output rows, etc */ set_baserel_size_estimates(root, rel); --- 3799,3820 ---- set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel) { RangeTblEntry *rte; + ListCell *lc; /* Should only be applied to base relations that are functions */ Assert(rel->relid > 0); rte = planner_rt_fetch(rel->relid, root); Assert(rte->rtekind == RTE_FUNCTION); + rel->tuples = 0; + /* Estimate number of rows the function itself will return */ ! foreach(lc, rte->funcexprs) ! { ! double ntup = expression_returns_set_rows(lfirst(lc)); ! if (ntup > rel->tuples) ! rel->tuples = ntup; ! } /* Now estimate number of output rows, etc */ set_baserel_size_estimates(root, rel); *** a/src/backend/optimizer/path/pathkeys.c --- b/src/backend/optimizer/path/pathkeys.c *************** *** 491,496 **** build_index_pathkeys(PlannerInfo *root, --- 491,546 ---- return retval; } + + /* + * build_expression_pathkey + * Build a pathkeys list (empty or 1 element) that describes an ordering + * of a single expression using a given operator + * + * The result is empty if the expression isn't already in some equivalence + * class. + * + * The main use for this is in declaring the ordering of ordinality + * columns in FunctionScan, but it's written this way to avoid making any + * assumptions about type. + */ + + List * + build_expression_pathkey(PlannerInfo *root, + RelOptInfo *rel, + Expr *expr, + Oid operator, + bool nulls_first) + { + List *pathkeys = NIL; + Oid opfamily, + opcintype; + int16 strategy; + PathKey *cpathkey; + + /* Find the operator in pg_amop --- failure shouldn't happen */ + if (!get_ordering_op_properties(operator, + &opfamily, &opcintype, &strategy)) + elog(ERROR, "operator %u is not a valid ordering operator", + operator); + + cpathkey = make_pathkey_from_sortinfo(root, + expr, + opfamily, + opcintype, + exprCollation((Node*) expr), + (strategy == BTGreaterStrategyNumber), + nulls_first, + 0, + rel->relids, + false); + + if (cpathkey) + pathkeys = lappend(pathkeys, cpathkey); + + return pathkeys; + } + /* * convert_subquery_pathkeys * Build a pathkeys list that describes the ordering of a subquery's *** a/src/backend/optimizer/plan/createplan.c --- b/src/backend/optimizer/plan/createplan.c *************** *** 115,121 **** static BitmapHeapScan *make_bitmap_heapscan(List *qptlist, static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, List *tidquals); static FunctionScan *make_functionscan(List *qptlist, List *qpqual, ! Index scanrelid, Node *funcexpr, bool ordinality, List *funccolnames, List *funccoltypes, List *funccoltypmods, List *funccolcollations); static ValuesScan *make_valuesscan(List *qptlist, List *qpqual, --- 115,121 ---- static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, List *tidquals); static FunctionScan *make_functionscan(List *qptlist, List *qpqual, ! Index scanrelid, List *funcexprs, bool ordinality, List *funccolnames, List *funccoltypes, List *funccoltypmods, List *funccolcollations); static ValuesScan *make_valuesscan(List *qptlist, List *qpqual, *************** *** 1708,1720 **** create_functionscan_plan(PlannerInfo *root, Path *best_path, FunctionScan *scan_plan; Index scan_relid = best_path->parent->relid; RangeTblEntry *rte; ! Node *funcexpr; /* it should be a function base rel... */ Assert(scan_relid > 0); rte = planner_rt_fetch(scan_relid, root); Assert(rte->rtekind == RTE_FUNCTION); ! funcexpr = rte->funcexpr; /* Sort clauses into best execution order */ scan_clauses = order_qual_clauses(root, scan_clauses); --- 1708,1720 ---- FunctionScan *scan_plan; Index scan_relid = best_path->parent->relid; RangeTblEntry *rte; ! List *funcexprs; /* it should be a function base rel... */ Assert(scan_relid > 0); rte = planner_rt_fetch(scan_relid, root); Assert(rte->rtekind == RTE_FUNCTION); ! funcexprs = rte->funcexprs; /* Sort clauses into best execution order */ scan_clauses = order_qual_clauses(root, scan_clauses); *************** *** 1728,1738 **** create_functionscan_plan(PlannerInfo *root, Path *best_path, scan_clauses = (List *) replace_nestloop_params(root, (Node *) scan_clauses); /* The func expression itself could contain nestloop params, too */ ! funcexpr = replace_nestloop_params(root, funcexpr); } scan_plan = make_functionscan(tlist, scan_clauses, scan_relid, ! funcexpr, rte->funcordinality, rte->eref->colnames, rte->funccoltypes, --- 1728,1738 ---- scan_clauses = (List *) replace_nestloop_params(root, (Node *) scan_clauses); /* The func expression itself could contain nestloop params, too */ ! funcexprs = (List *) replace_nestloop_params(root, (Node *) funcexprs); } scan_plan = make_functionscan(tlist, scan_clauses, scan_relid, ! funcexprs, rte->funcordinality, rte->eref->colnames, rte->funccoltypes, *************** *** 3366,3372 **** static FunctionScan * make_functionscan(List *qptlist, List *qpqual, Index scanrelid, ! Node *funcexpr, bool ordinality, List *funccolnames, List *funccoltypes, --- 3366,3372 ---- make_functionscan(List *qptlist, List *qpqual, Index scanrelid, ! List *funcexprs, bool ordinality, List *funccolnames, List *funccoltypes, *************** *** 3382,3388 **** make_functionscan(List *qptlist, plan->lefttree = NULL; plan->righttree = NULL; node->scan.scanrelid = scanrelid; ! node->funcexpr = funcexpr; node->funcordinality = ordinality; node->funccolnames = funccolnames; node->funccoltypes = funccoltypes; --- 3382,3388 ---- plan->lefttree = NULL; plan->righttree = NULL; node->scan.scanrelid = scanrelid; ! node->funcexprs = funcexprs; node->funcordinality = ordinality; node->funccolnames = funccolnames; node->funccoltypes = funccoltypes; *** a/src/backend/optimizer/plan/initsplan.c --- b/src/backend/optimizer/plan/initsplan.c *************** *** 300,306 **** extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) if (rte->rtekind == RTE_SUBQUERY) vars = pull_vars_of_level((Node *) rte->subquery, 1); else if (rte->rtekind == RTE_FUNCTION) ! vars = pull_vars_of_level(rte->funcexpr, 0); else if (rte->rtekind == RTE_VALUES) vars = pull_vars_of_level((Node *) rte->values_lists, 0); else --- 300,306 ---- if (rte->rtekind == RTE_SUBQUERY) vars = pull_vars_of_level((Node *) rte->subquery, 1); else if (rte->rtekind == RTE_FUNCTION) ! vars = pull_vars_of_level((Node *) rte->funcexprs, 0); else if (rte->rtekind == RTE_VALUES) vars = pull_vars_of_level((Node *) rte->values_lists, 0); else *** a/src/backend/optimizer/plan/planner.c --- b/src/backend/optimizer/plan/planner.c *************** *** 487,493 **** subquery_planner(PlannerGlobal *glob, Query *parse, { /* Preprocess the function expression fully */ kind = rte->lateral ? EXPRKIND_RTFUNC_LATERAL : EXPRKIND_RTFUNC; ! rte->funcexpr = preprocess_expression(root, rte->funcexpr, kind); } else if (rte->rtekind == RTE_VALUES) { --- 487,493 ---- { /* Preprocess the function expression fully */ kind = rte->lateral ? EXPRKIND_RTFUNC_LATERAL : EXPRKIND_RTFUNC; ! rte->funcexprs = (List *) preprocess_expression(root, (Node *) rte->funcexprs, kind); } else if (rte->rtekind == RTE_VALUES) { *** a/src/backend/optimizer/plan/setrefs.c --- b/src/backend/optimizer/plan/setrefs.c *************** *** 381,387 **** add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) /* zap unneeded sub-structure */ newrte->subquery = NULL; newrte->joinaliasvars = NIL; ! newrte->funcexpr = NULL; newrte->funccoltypes = NIL; newrte->funccoltypmods = NIL; newrte->funccolcollations = NIL; --- 381,387 ---- /* zap unneeded sub-structure */ newrte->subquery = NULL; newrte->joinaliasvars = NIL; ! newrte->funcexprs = NULL; newrte->funccoltypes = NIL; newrte->funccoltypmods = NIL; newrte->funccolcollations = NIL; *************** *** 525,532 **** set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) fix_scan_list(root, splan->scan.plan.targetlist, rtoffset); splan->scan.plan.qual = fix_scan_list(root, splan->scan.plan.qual, rtoffset); ! splan->funcexpr = ! fix_scan_expr(root, splan->funcexpr, rtoffset); } break; case T_ValuesScan: --- 525,532 ---- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset); splan->scan.plan.qual = fix_scan_list(root, splan->scan.plan.qual, rtoffset); ! splan->funcexprs = ! fix_scan_list(root, splan->funcexprs, rtoffset); } break; case T_ValuesScan: *** a/src/backend/optimizer/plan/subselect.c --- b/src/backend/optimizer/plan/subselect.c *************** *** 2136,2142 **** finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_FunctionScan: ! finalize_primnode(((FunctionScan *) plan)->funcexpr, &context); context.paramids = bms_add_members(context.paramids, scan_params); break; --- 2136,2142 ---- break; case T_FunctionScan: ! finalize_primnode((Node *) ((FunctionScan *) plan)->funcexprs, &context); context.paramids = bms_add_members(context.paramids, scan_params); break; *** a/src/backend/optimizer/prep/prepjointree.c --- b/src/backend/optimizer/prep/prepjointree.c *************** *** 576,582 **** inline_set_returning_functions(PlannerInfo *root) /* Successful expansion, replace the rtable entry */ rte->rtekind = RTE_SUBQUERY; rte->subquery = funcquery; ! rte->funcexpr = NULL; rte->funccoltypes = NIL; rte->funccoltypmods = NIL; rte->funccolcollations = NIL; --- 576,582 ---- /* Successful expansion, replace the rtable entry */ rte->rtekind = RTE_SUBQUERY; rte->subquery = funcquery; ! rte->funcexprs = NULL; rte->funccoltypes = NIL; rte->funccoltypmods = NIL; rte->funccolcollations = NIL; *************** *** 1502,1509 **** replace_vars_in_jointree(Node *jtnode, context); break; case RTE_FUNCTION: ! rte->funcexpr = ! pullup_replace_vars(rte->funcexpr, context); break; case RTE_VALUES: --- 1502,1509 ---- context); break; case RTE_FUNCTION: ! rte->funcexprs = (List *) ! pullup_replace_vars((Node *) rte->funcexprs, context); break; case RTE_VALUES: *** a/src/backend/optimizer/util/clauses.c --- b/src/backend/optimizer/util/clauses.c *************** *** 4457,4466 **** inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) return NULL; /* Fail if FROM item isn't a simple FuncExpr */ ! fexpr = (FuncExpr *) rte->funcexpr; ! if (fexpr == NULL || !IsA(fexpr, FuncExpr)) return NULL; func_oid = fexpr->funcid; /* --- 4457,4468 ---- return NULL; /* Fail if FROM item isn't a simple FuncExpr */ ! if (list_length(rte->funcexprs) != 1 ! || !IsA(linitial(rte->funcexprs), FuncExpr)) return NULL; + fexpr = (FuncExpr *) linitial(rte->funcexprs); + func_oid = fexpr->funcid; /* *** a/src/backend/optimizer/util/pathnode.c --- b/src/backend/optimizer/util/pathnode.c *************** *** 1623,1628 **** create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, --- 1623,1629 ---- */ Path * create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, + List *pathkeys, Relids required_outer) { Path *pathnode = makeNode(Path); *************** *** 1631,1637 **** create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, pathnode->parent = rel; pathnode->param_info = get_baserel_parampathinfo(root, rel, required_outer); ! pathnode->pathkeys = NIL; /* for now, assume unordered result */ cost_functionscan(pathnode, root, rel, pathnode->param_info); --- 1632,1638 ---- pathnode->parent = rel; pathnode->param_info = get_baserel_parampathinfo(root, rel, required_outer); ! pathnode->pathkeys = pathkeys; cost_functionscan(pathnode, root, rel, pathnode->param_info); *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 402,409 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type def_elem reloption_elem old_aggr_elem %type def_arg columnElem where_clause where_or_current_clause a_expr b_expr c_expr AexprConst indirection_el ! columnref in_expr having_clause func_table array_expr ExclusionWhereClause %type ExclusionConstraintList ExclusionConstraintElem %type func_arg_list %type func_arg_expr --- 402,410 ---- %type def_elem reloption_elem old_aggr_elem %type def_arg columnElem where_clause where_or_current_clause a_expr b_expr c_expr AexprConst indirection_el ! columnref in_expr having_clause array_expr ExclusionWhereClause + %type func_table func_table_list func_table_single %type ExclusionConstraintList ExclusionConstraintElem %type func_arg_list %type func_arg_expr *************** *** 9588,9594 **** table_ref: relation_expr opt_alias_clause RangeFunction *n = makeNode(RangeFunction); n->lateral = false; n->ordinality = false; ! n->funccallnode = $1; n->alias = linitial($2); n->coldeflist = lsecond($2); $$ = (Node *) n; --- 9589,9595 ---- RangeFunction *n = makeNode(RangeFunction); n->lateral = false; n->ordinality = false; ! n->funccallnodes = $1; n->alias = linitial($2); n->coldeflist = lsecond($2); $$ = (Node *) n; *************** *** 9598,9604 **** table_ref: relation_expr opt_alias_clause RangeFunction *n = makeNode(RangeFunction); n->lateral = false; n->ordinality = true; ! n->funccallnode = $1; n->alias = linitial($3); n->coldeflist = lsecond($3); $$ = (Node *) n; --- 9599,9605 ---- RangeFunction *n = makeNode(RangeFunction); n->lateral = false; n->ordinality = true; ! n->funccallnodes = $1; n->alias = linitial($3); n->coldeflist = lsecond($3); $$ = (Node *) n; *************** *** 9608,9614 **** table_ref: relation_expr opt_alias_clause RangeFunction *n = makeNode(RangeFunction); n->lateral = true; n->ordinality = false; ! n->funccallnode = $2; n->alias = linitial($3); n->coldeflist = lsecond($3); $$ = (Node *) n; --- 9609,9615 ---- RangeFunction *n = makeNode(RangeFunction); n->lateral = true; n->ordinality = false; ! n->funccallnodes = $2; n->alias = linitial($3); n->coldeflist = lsecond($3); $$ = (Node *) n; *************** *** 9618,9624 **** table_ref: relation_expr opt_alias_clause RangeFunction *n = makeNode(RangeFunction); n->lateral = true; n->ordinality = true; ! n->funccallnode = $2; n->alias = linitial($4); n->coldeflist = lsecond($4); $$ = (Node *) n; --- 9619,9625 ---- RangeFunction *n = makeNode(RangeFunction); n->lateral = true; n->ordinality = true; ! n->funccallnodes = $2; n->alias = linitial($4); n->coldeflist = lsecond($4); $$ = (Node *) n; *************** *** 9933,9941 **** relation_expr_opt_alias: relation_expr %prec UMINUS } ; ! func_table: func_expr_windowless { $$ = $1; } ; where_clause: WHERE a_expr { $$ = $2; } --- 9934,9975 ---- } ; ! func_table: func_table_single { $$ = $1; } ! | TABLE '(' func_table_list ')' { $$ = $3; } ; + func_table_list: func_table_single { $$ = $1; } + | func_table_list ',' func_table_single { $$ = list_concat($1,$3); } + + func_table_single: func_expr_windowless + { + FuncCall *n = (FuncCall *) $1; + List *res = NIL; + + if (list_length(n->funcname) == 1 + && pg_strcasecmp(strVal(linitial(n->funcname)),"unnest") == 0) + { + ListCell *lc; + + if (n->agg_order != NIL || n->func_variadic || n->agg_star || n->agg_distinct) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid call to unnest"), + parser_errposition(@1))); + + foreach(lc, n->args) + { + res = lappend(res, makeFuncCall(SystemFuncName("unnest"), + list_make1(lfirst(lc)), + n->location)); + } + } + else + res = list_make1(n); + + $$ = res; + } + ; where_clause: WHERE a_expr { $$ = $2; } *** a/src/backend/parser/parse_clause.c --- b/src/backend/parser/parse_clause.c *************** *** 515,524 **** transformRangeSubselect(ParseState *pstate, RangeSubselect *r) static RangeTblEntry * transformRangeFunction(ParseState *pstate, RangeFunction *r) { ! Node *funcexpr; char *funcname; bool is_lateral; RangeTblEntry *rte; /* * Get function name for possible use as alias. We use the same --- 515,525 ---- static RangeTblEntry * transformRangeFunction(ParseState *pstate, RangeFunction *r) { ! List *funcexprs = NIL; char *funcname; bool is_lateral; RangeTblEntry *rte; + ListCell *lc; /* * Get function name for possible use as alias. We use the same *************** *** 526,532 **** transformRangeFunction(ParseState *pstate, RangeFunction *r) * node, the result will be the function name, but it is possible for the * grammar to hand back other node types. */ ! funcname = FigureColname(r->funccallnode); /* * We make lateral_only names of this level visible, whether or not the --- 527,536 ---- * node, the result will be the function name, but it is possible for the * grammar to hand back other node types. */ ! if (list_length(r->funccallnodes) == 1) ! funcname = FigureColname(linitial(r->funccallnodes)); ! else ! funcname = "table"; /* * We make lateral_only names of this level visible, whether or not the *************** *** 541,567 **** transformRangeFunction(ParseState *pstate, RangeFunction *r) pstate->p_lateral_active = true; /* ! * Transform the raw expression. */ ! funcexpr = transformExpr(pstate, r->funccallnode, EXPR_KIND_FROM_FUNCTION); pstate->p_lateral_active = false; /* * We must assign collations now so that we can fill funccolcollations. */ ! assign_expr_collations(pstate, funcexpr); /* * Mark the RTE as LATERAL if the user said LATERAL explicitly, or if * there are any lateral cross-references in it. */ ! is_lateral = r->lateral || contain_vars_of_level(funcexpr, 0); /* * OK, build an RTE for the function. */ ! rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr, r, is_lateral, true); /* --- 545,575 ---- pstate->p_lateral_active = true; /* ! * Transform the raw expressions. */ ! foreach(lc, r->funccallnodes) ! { ! funcexprs = lappend(funcexprs, ! transformExpr(pstate, lfirst(lc), EXPR_KIND_FROM_FUNCTION)); ! } pstate->p_lateral_active = false; /* * We must assign collations now so that we can fill funccolcollations. */ ! assign_list_collations(pstate, funcexprs); /* * Mark the RTE as LATERAL if the user said LATERAL explicitly, or if * there are any lateral cross-references in it. */ ! is_lateral = r->lateral || contain_vars_of_level((Node *) funcexprs, 0); /* * OK, build an RTE for the function. */ ! rte = addRangeTableEntryForFunction(pstate, funcname, funcexprs, r, is_lateral, true); /* *** a/src/backend/parser/parse_relation.c --- b/src/backend/parser/parse_relation.c *************** *** 27,32 **** --- 27,33 ---- #include "parser/parsetree.h" #include "parser/parse_relation.h" #include "parser/parse_type.h" + #include "parser/parse_target.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/rel.h" *************** *** 43,49 **** static void expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars); ! static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars); --- 44,50 ---- int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars); ! static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, int atts_done, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars); *************** *** 1216,1222 **** addRangeTableEntryForSubquery(ParseState *pstate, RangeTblEntry * addRangeTableEntryForFunction(ParseState *pstate, char *funcname, ! Node *funcexpr, RangeFunction *rangefunc, bool lateral, bool inFromCl) --- 1217,1223 ---- RangeTblEntry * addRangeTableEntryForFunction(ParseState *pstate, char *funcname, ! List *funcexprs, RangeFunction *rangefunc, bool lateral, bool inFromCl) *************** *** 1228,1238 **** addRangeTableEntryForFunction(ParseState *pstate, Alias *alias = rangefunc->alias; List *coldeflist = rangefunc->coldeflist; Alias *eref; rte->rtekind = RTE_FUNCTION; rte->relid = InvalidOid; rte->subquery = NULL; ! rte->funcexpr = funcexpr; rte->funccoltypes = NIL; rte->funccoltypmods = NIL; rte->funccolcollations = NIL; --- 1229,1244 ---- Alias *alias = rangefunc->alias; List *coldeflist = rangefunc->coldeflist; Alias *eref; + Oid *funcrettypes = NULL; + TupleDesc *functupdescs = NULL; + int nfuncs = list_length(funcexprs); + ListCell *lc; + int i; rte->rtekind = RTE_FUNCTION; rte->relid = InvalidOid; rte->subquery = NULL; ! rte->funcexprs = funcexprs; rte->funccoltypes = NIL; rte->funccoltypmods = NIL; rte->funccolcollations = NIL; *************** *** 1243,1252 **** addRangeTableEntryForFunction(ParseState *pstate, /* * Now determine if the function returns a simple or composite type. */ ! functypclass = get_expr_result_type(funcexpr, ! &funcrettype, ! &tupdesc); /* * A coldeflist is required if the function returns RECORD and hasn't got --- 1249,1313 ---- /* * Now determine if the function returns a simple or composite type. + * + * If there's more than one function, the result must be composite, + * and none of the functions must return RECORD. */ ! if (nfuncs == 1) ! { ! functypclass = get_expr_result_type(linitial(funcexprs), ! &funcrettype, ! &tupdesc); ! } ! else ! { ! /* ! * Produce a flattened TupleDesc with all the constituent ! * columns from all functions. ! * ! * This would be less painful if there was a reasonable way ! * to insert dropped columns into a tupdesc. ! */ ! ! funcrettypes = palloc(nfuncs * sizeof(Oid)); ! functupdescs = palloc(nfuncs * sizeof(TupleDesc)); ! ! i = 0; ! foreach(lc, funcexprs) ! { ! functypclass = get_expr_result_type(lfirst(lc), ! &funcrettypes[i], ! &functupdescs[i]); ! ! switch (functypclass) ! { ! case TYPEFUNC_RECORD: ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("functions returning \"record\" may not be combined in TABLE()"), ! parser_errposition(pstate, exprLocation(lfirst(lc))))); ! ! case TYPEFUNC_SCALAR: ! functupdescs[i] = CreateTemplateTupleDesc(1, false); ! TupleDescInitEntry(functupdescs[i], ! (AttrNumber) 1, ! FigureColname(lfirst(lc)), ! funcrettypes[i], ! -1, ! 0); ! break; ! ! default: ! break; ! } ! ! ++i; ! } ! ! functypclass = TYPEFUNC_COMPOSITE; ! funcrettype = RECORDOID; ! tupdesc = CreateTupleDescCopyMany(functupdescs, nfuncs); ! } /* * A coldeflist is required if the function returns RECORD and hasn't got *************** *** 1258,1264 **** addRangeTableEntryForFunction(ParseState *pstate, ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("a column definition list is only allowed for functions returning \"record\""), ! parser_errposition(pstate, exprLocation(funcexpr)))); } else { --- 1319,1325 ---- ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("a column definition list is only allowed for functions returning \"record\""), ! parser_errposition(pstate, exprLocation(linitial(funcexprs))))); } else { *************** *** 1266,1272 **** addRangeTableEntryForFunction(ParseState *pstate, ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("a column definition list is required for functions returning \"record\""), ! parser_errposition(pstate, exprLocation(funcexpr)))); } if (functypclass == TYPEFUNC_COMPOSITE) --- 1327,1333 ---- ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("a column definition list is required for functions returning \"record\""), ! parser_errposition(pstate, exprLocation(linitial(funcexprs))))); } if (functypclass == TYPEFUNC_COMPOSITE) *************** *** 1279,1285 **** addRangeTableEntryForFunction(ParseState *pstate, else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ ! buildScalarFunctionAlias(funcexpr, funcname, alias, eref, rangefunc->ordinality); } else if (functypclass == TYPEFUNC_RECORD) { --- 1340,1346 ---- else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ ! buildScalarFunctionAlias(linitial(funcexprs), funcname, alias, eref, rangefunc->ordinality); } else if (functypclass == TYPEFUNC_RECORD) { *************** *** 1289,1295 **** addRangeTableEntryForFunction(ParseState *pstate, ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("WITH ORDINALITY is not supported for functions returning \"record\""), ! parser_errposition(pstate, exprLocation(funcexpr)))); /* * Use the column definition list to form the alias list and --- 1350,1356 ---- ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("WITH ORDINALITY is not supported for functions returning \"record\""), ! parser_errposition(pstate, exprLocation(linitial(funcexprs))))); /* * Use the column definition list to form the alias list and *************** *** 1324,1330 **** addRangeTableEntryForFunction(ParseState *pstate, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function \"%s\" in FROM has unsupported return type %s", funcname, format_type_be(funcrettype)), ! parser_errposition(pstate, exprLocation(funcexpr)))); /* * Set flags and access permissions. --- 1385,1391 ---- (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function \"%s\" in FROM has unsupported return type %s", funcname, format_type_be(funcrettype)), ! parser_errposition(pstate, exprLocation(linitial(funcexprs))))); /* * Set flags and access permissions. *************** *** 1762,1853 **** expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, TypeFuncClass functypclass; Oid funcrettype; TupleDesc tupdesc; ! int ordinality_attno = 0; ! functypclass = get_expr_result_type(rte->funcexpr, ! &funcrettype, ! &tupdesc); ! if (functypclass == TYPEFUNC_COMPOSITE) { ! /* Composite data type, e.g. a table's row type */ ! Assert(tupdesc); ! ! /* ! * we rely here on the fact that expandTupleDesc doesn't ! * care about being passed more aliases than it needs. ! */ ! expandTupleDesc(tupdesc, rte->eref, ! rtindex, sublevels_up, location, ! include_dropped, colnames, colvars); ! ! ordinality_attno = tupdesc->natts + 1; ! } ! else if (functypclass == TYPEFUNC_SCALAR) ! { ! /* Base data type, i.e. scalar */ ! if (colnames) ! *colnames = lappend(*colnames, ! linitial(rte->eref->colnames)); ! ! if (colvars) { ! Var *varnode; ! ! varnode = makeVar(rtindex, 1, ! funcrettype, -1, ! exprCollation(rte->funcexpr), ! sublevels_up); ! varnode->location = location; ! ! *colvars = lappend(*colvars, varnode); } ! ! ordinality_attno = 2; ! } ! else if (functypclass == TYPEFUNC_RECORD) ! { ! if (colnames) ! *colnames = copyObject(rte->eref->colnames); ! if (colvars) { ! ListCell *l1; ! ListCell *l2; ! ListCell *l3; ! int attnum = 0; ! ! forthree(l1, rte->funccoltypes, ! l2, rte->funccoltypmods, ! l3, rte->funccolcollations) { - Oid attrtype = lfirst_oid(l1); - int32 attrtypmod = lfirst_int(l2); - Oid attrcollation = lfirst_oid(l3); Var *varnode; ! attnum++; ! varnode = makeVar(rtindex, ! attnum, ! attrtype, ! attrtypmod, ! attrcollation, sublevels_up); varnode->location = location; *colvars = lappend(*colvars, varnode); } } ! /* note, ordinality is not allowed in this case */ ! } ! else ! { ! /* addRangeTableEntryForFunction should've caught this */ ! elog(ERROR, "function in FROM has unsupported return type"); } /* tack on the extra ordinality column if present */ if (rte->funcordinality) { ! Assert(ordinality_attno > 0); if (colnames) *colnames = lappend(*colnames, llast(rte->eref->colnames)); --- 1823,1922 ---- TypeFuncClass functypclass; Oid funcrettype; TupleDesc tupdesc; ! int atts_done = 0; ! ListCell *lc; ! foreach(lc, rte->funcexprs) { ! functypclass = get_expr_result_type(lfirst(lc), ! &funcrettype, ! &tupdesc); ! if (functypclass == TYPEFUNC_COMPOSITE) { ! /* Composite data type, e.g. a table's row type */ ! Assert(tupdesc); ! ! /* ! * we rely here on the fact that expandTupleDesc doesn't ! * care about being passed more aliases than it needs. ! */ ! expandTupleDesc(tupdesc, rte->eref, atts_done, ! rtindex, sublevels_up, location, ! include_dropped, colnames, colvars); ! ! atts_done += tupdesc->natts; } ! else if (functypclass == TYPEFUNC_SCALAR) { ! /* Base data type, i.e. scalar */ ! if (colnames) ! *colnames = lappend(*colnames, ! list_nth(rte->eref->colnames, atts_done)); ! ! if (colvars) { Var *varnode; ! varnode = makeVar(rtindex, atts_done + 1, ! funcrettype, -1, ! exprCollation(lfirst(lc)), sublevels_up); varnode->location = location; + *colvars = lappend(*colvars, varnode); } + + ++atts_done; } + else if (functypclass == TYPEFUNC_RECORD) + { + Assert(atts_done == 0); + Assert(lnext(lc) == NULL); ! if (colnames) ! *colnames = copyObject(rte->eref->colnames); ! ! if (colvars) ! { ! ListCell *l1; ! ListCell *l2; ! ListCell *l3; ! int attnum = 0; ! ! forthree(l1, rte->funccoltypes, ! l2, rte->funccoltypmods, ! l3, rte->funccolcollations) ! { ! Oid attrtype = lfirst_oid(l1); ! int32 attrtypmod = lfirst_int(l2); ! Oid attrcollation = lfirst_oid(l3); ! Var *varnode; ! ! attnum++; ! varnode = makeVar(rtindex, ! attnum, ! attrtype, ! attrtypmod, ! attrcollation, ! sublevels_up); ! varnode->location = location; ! *colvars = lappend(*colvars, varnode); ! } ! } ! ! /* note, ordinality is not allowed in this case */ ! } ! else ! { ! /* addRangeTableEntryForFunction should've caught this */ ! elog(ERROR, "function in FROM has unsupported return type"); ! } } /* tack on the extra ordinality column if present */ if (rte->funcordinality) { ! Assert(atts_done > 0); if (colnames) *colnames = lappend(*colnames, llast(rte->eref->colnames)); *************** *** 1855,1861 **** expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, if (colvars) { Var *varnode = makeVar(rtindex, ! ordinality_attno, INT8OID, -1, InvalidOid, --- 1924,1930 ---- if (colvars) { Var *varnode = makeVar(rtindex, ! atts_done + 1, INT8OID, -1, InvalidOid, *************** *** 2030,2036 **** expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, /* Get the tupledesc and turn it over to expandTupleDesc */ rel = relation_open(relid, AccessShareLock); ! expandTupleDesc(rel->rd_att, eref, rtindex, sublevels_up, location, include_dropped, colnames, colvars); relation_close(rel, AccessShareLock); --- 2099,2105 ---- /* Get the tupledesc and turn it over to expandTupleDesc */ rel = relation_open(relid, AccessShareLock); ! expandTupleDesc(rel->rd_att, eref, 0, rtindex, sublevels_up, location, include_dropped, colnames, colvars); relation_close(rel, AccessShareLock); *************** *** 2043,2055 **** expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, * it is not an error to supply too many. (ordinality depends on this) */ static void ! expandTupleDesc(TupleDesc tupdesc, Alias *eref, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars) { int maxattrs = tupdesc->natts; ! int numaliases = list_length(eref->colnames); int varattno; for (varattno = 0; varattno < maxattrs; varattno++) --- 2112,2124 ---- * it is not an error to supply too many. (ordinality depends on this) */ static void ! expandTupleDesc(TupleDesc tupdesc, Alias *eref, int atts_done, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars) { int maxattrs = tupdesc->natts; ! int numaliases = list_length(eref->colnames) - atts_done; int varattno; for (varattno = 0; varattno < maxattrs; varattno++) *************** *** 2080,2086 **** expandTupleDesc(TupleDesc tupdesc, Alias *eref, char *label; if (varattno < numaliases) ! label = strVal(list_nth(eref->colnames, varattno)); else label = NameStr(attr->attname); *colnames = lappend(*colnames, makeString(pstrdup(label))); --- 2149,2155 ---- char *label; if (varattno < numaliases) ! label = strVal(list_nth(eref->colnames, varattno + atts_done)); else label = NameStr(attr->attname); *colnames = lappend(*colnames, makeString(pstrdup(label))); *************** *** 2090,2096 **** expandTupleDesc(TupleDesc tupdesc, Alias *eref, { Var *varnode; ! varnode = makeVar(rtindex, attr->attnum, attr->atttypid, attr->atttypmod, attr->attcollation, sublevels_up); --- 2159,2165 ---- { Var *varnode; ! varnode = makeVar(rtindex, attr->attnum + atts_done, attr->atttypid, attr->atttypmod, attr->attcollation, sublevels_up); *************** *** 2260,2265 **** get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, --- 2329,2336 ---- TypeFuncClass functypclass; Oid funcrettype; TupleDesc tupdesc; + ListCell *lc; + int atts_done = 0; /* * if ordinality, then a reference to the last column *************** *** 2275,2335 **** get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, break; } ! functypclass = get_expr_result_type(rte->funcexpr, ! &funcrettype, ! &tupdesc); ! ! if (functypclass == TYPEFUNC_COMPOSITE) { ! /* Composite data type, e.g. a table's row type */ ! Form_pg_attribute att_tup; ! Assert(tupdesc); ! /* this is probably a can't-happen case */ ! if (attnum < 1 || attnum > tupdesc->natts) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_COLUMN), ! errmsg("column %d of relation \"%s\" does not exist", ! attnum, ! rte->eref->aliasname))); ! att_tup = tupdesc->attrs[attnum - 1]; ! /* ! * If dropped column, pretend it ain't there. See notes ! * in scanRTEForColumn. ! */ ! if (att_tup->attisdropped) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_COLUMN), ! errmsg("column \"%s\" of relation \"%s\" does not exist", ! NameStr(att_tup->attname), ! rte->eref->aliasname))); ! *vartype = att_tup->atttypid; ! *vartypmod = att_tup->atttypmod; ! *varcollid = att_tup->attcollation; ! } ! else if (functypclass == TYPEFUNC_SCALAR) ! { ! Assert(attnum == 1); ! /* Base data type, i.e. scalar */ ! *vartype = funcrettype; ! *vartypmod = -1; ! *varcollid = exprCollation(rte->funcexpr); ! } ! else if (functypclass == TYPEFUNC_RECORD) ! { ! *vartype = list_nth_oid(rte->funccoltypes, attnum - 1); ! *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1); ! *varcollid = list_nth_oid(rte->funccolcollations, attnum - 1); ! } ! else ! { ! /* addRangeTableEntryForFunction should've caught this */ ! elog(ERROR, "function in FROM has unsupported return type"); } } break; case RTE_VALUES: --- 2346,2423 ---- break; } ! foreach(lc, rte->funcexprs) { ! functypclass = get_expr_result_type(lfirst(lc), ! &funcrettype, ! &tupdesc); ! if (functypclass == TYPEFUNC_COMPOSITE) ! { ! /* Composite data type, e.g. a table's row type */ ! Form_pg_attribute att_tup; ! Assert(tupdesc); ! if (attnum > atts_done ! && attnum <= atts_done + tupdesc->natts) ! { ! att_tup = tupdesc->attrs[attnum - atts_done - 1]; ! ! /* ! * If dropped column, pretend it ain't there. See notes ! * in scanRTEForColumn. ! */ ! if (att_tup->attisdropped) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_COLUMN), ! errmsg("column \"%s\" of relation \"%s\" does not exist", ! NameStr(att_tup->attname), ! rte->eref->aliasname))); ! *vartype = att_tup->atttypid; ! *vartypmod = att_tup->atttypmod; ! *varcollid = att_tup->attcollation; ! return; ! } ! atts_done += tupdesc->natts; ! } ! else if (functypclass == TYPEFUNC_SCALAR) ! { ! if (attnum == atts_done + 1) ! { ! /* Base data type, i.e. scalar */ ! *vartype = funcrettype; ! *vartypmod = -1; ! *varcollid = exprCollation(lfirst(lc)); ! return; ! } ! ++atts_done; ! } ! else if (functypclass == TYPEFUNC_RECORD) ! { ! Assert(atts_done == 0); ! Assert(lnext(lc) == NULL); ! ! *vartype = list_nth_oid(rte->funccoltypes, attnum - 1); ! *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1); ! *varcollid = list_nth_oid(rte->funccolcollations, attnum - 1); ! return; ! } ! else ! { ! /* addRangeTableEntryForFunction should've caught this */ ! elog(ERROR, "function in FROM has unsupported return type"); ! } } + + /* this is probably a can't-happen case */ + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %d of relation \"%s\" does not exist", + attnum, + rte->eref->aliasname))); } break; case RTE_VALUES: *************** *** 2435,2442 **** get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) case RTE_FUNCTION: { /* Function RTE */ ! Oid funcrettype = exprType(rte->funcexpr); ! Oid funcrelid = typeidTypeRelid(funcrettype); /* * if ordinality, then a reference to the last column --- 2523,2535 ---- case RTE_FUNCTION: { /* Function RTE */ ! TypeFuncClass functypclass; ! Oid funcrettype; ! TupleDesc tupdesc; ! ListCell *lc; ! int atts_done = 0; ! ! result = false; /* * if ordinality, then a reference to the last column *************** *** 2445,2479 **** get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) */ if (rte->funcordinality && attnum == list_length(rte->eref->colnames)) { ! result = false; ! } ! else if (OidIsValid(funcrelid)) ! { ! /* ! * Composite data type, i.e. a table's row type ! * ! * Same as ordinary relation RTE ! */ ! HeapTuple tp; ! Form_pg_attribute att_tup; ! ! tp = SearchSysCache2(ATTNUM, ! ObjectIdGetDatum(funcrelid), ! Int16GetDatum(attnum)); ! if (!HeapTupleIsValid(tp)) /* shouldn't happen */ ! elog(ERROR, "cache lookup failed for attribute %d of relation %u", ! attnum, funcrelid); ! att_tup = (Form_pg_attribute) GETSTRUCT(tp); ! result = att_tup->attisdropped; ! ReleaseSysCache(tp); ! } ! else ! { ! /* ! * Must be a base data type, i.e. scalar ! */ ! result = false; } } break; --- 2538,2593 ---- */ if (rte->funcordinality && attnum == list_length(rte->eref->colnames)) + break; + + foreach(lc, rte->funcexprs) { ! functypclass = get_expr_result_type(lfirst(lc), ! &funcrettype, ! &tupdesc); ! ! if (functypclass == TYPEFUNC_COMPOSITE) ! { ! /* Composite data type, e.g. a table's row type */ ! Form_pg_attribute att_tup; ! ! Assert(tupdesc); ! ! if (attnum > atts_done ! && attnum <= atts_done + tupdesc->natts) ! { ! att_tup = tupdesc->attrs[attnum - atts_done - 1]; ! ! result = att_tup->attisdropped; ! break; ! } ! ! atts_done += tupdesc->natts; ! } ! else if (functypclass == TYPEFUNC_SCALAR) ! { ! if (attnum == atts_done + 1) ! { ! /* Base data type, i.e. scalar */ ! result = false; ! break; ! } ! ! ++atts_done; ! } ! else if (functypclass == TYPEFUNC_RECORD) ! { ! Assert(atts_done == 0); ! Assert(lnext(lc) == NULL); ! ! result = false; ! break; ! } ! else ! { ! /* addRangeTableEntryForFunction should've caught this */ ! elog(ERROR, "function in FROM has unsupported return type"); ! } } } break; *** a/src/backend/rewrite/rewriteHandler.c --- b/src/backend/rewrite/rewriteHandler.c *************** *** 388,394 **** rewriteRuleAction(Query *parsetree, { case RTE_FUNCTION: sub_action->hasSubLinks = ! checkExprHasSubLink(rte->funcexpr); break; case RTE_VALUES: sub_action->hasSubLinks = --- 388,394 ---- { case RTE_FUNCTION: sub_action->hasSubLinks = ! checkExprHasSubLink((Node *) rte->funcexprs); break; case RTE_VALUES: sub_action->hasSubLinks = *** a/src/backend/utils/adt/ruleutils.c --- b/src/backend/utils/adt/ruleutils.c *************** *** 8003,8009 **** get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) break; case RTE_FUNCTION: /* Function RTE */ ! get_rule_expr(rte->funcexpr, context, true); if (rte->funcordinality) appendStringInfoString(buf, " WITH ORDINALITY"); break; --- 8003,8025 ---- break; case RTE_FUNCTION: /* Function RTE */ ! if (list_length(rte->funcexprs) == 1) ! { ! get_rule_expr(linitial(rte->funcexprs), context, true); ! } ! else ! { ! ListCell *lc; ! ! appendStringInfoString(buf, "TABLE("); ! foreach(lc, rte->funcexprs) ! { ! get_rule_expr(lfirst(lc), context, true); ! if (lnext(lc)) ! appendStringInfoString(buf, ", "); ! } ! appendStringInfoChar(buf, ')'); ! } if (rte->funcordinality) appendStringInfoString(buf, " WITH ORDINALITY"); break; *** a/src/include/access/tupdesc.h --- b/src/include/access/tupdesc.h *************** *** 88,93 **** extern TupleDesc CreateTupleDesc(int natts, bool hasoid, --- 88,94 ---- extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc); extern TupleDesc CreateTupleDescCopyExtend(TupleDesc tupdesc, int moreatts); + extern TupleDesc CreateTupleDescCopyMany(TupleDesc *tupdescs, int numtupdescs); extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc); *** a/src/include/nodes/execnodes.h --- b/src/include/nodes/execnodes.h *************** *** 1396,1418 **** typedef struct SubqueryScanState * * eflags node's capability flags * ordinal column value for WITH ORDINALITY * scan_tupdesc scan tuple descriptor ! * func_tupdesc function tuple descriptor ! * func_slot function result slot, or null ! * tuplestorestate private state of tuplestore.c ! * funcexpr state for function expression being evaluated * ---------------- */ typedef struct FunctionScanState { ScanState ss; /* its first field is NodeTag */ int eflags; int64 ordinal; TupleDesc scan_tupdesc; ! TupleDesc func_tupdesc; ! TupleTableSlot *func_slot; ! Tuplestorestate *tuplestorestate; ! ExprState *funcexpr; } FunctionScanState; /* ---------------- --- 1396,1421 ---- * * eflags node's capability flags * ordinal column value for WITH ORDINALITY + * rowcounts number of result rows for each func, when known * scan_tupdesc scan tuple descriptor ! * func_tupdescs function tuple descriptors ! * func_slots function result slots, or null ! * tuplestorestates private states of tuplestore.c ! * funcexprs state for function expressions being evaluated * ---------------- */ typedef struct FunctionScanState { ScanState ss; /* its first field is NodeTag */ int eflags; + bool ordinality; int64 ordinal; + int64 *rowcounts; TupleDesc scan_tupdesc; ! TupleDesc *func_tupdescs; ! TupleTableSlot **func_slots; ! Tuplestorestate **tuplestorestates; ! List *funcexprs; } FunctionScanState; /* ---------------- *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** *** 466,478 **** typedef struct RangeSubselect /* * RangeFunction - function call appearing in a FROM clause */ typedef struct RangeFunction { NodeTag type; bool lateral; /* does it have LATERAL prefix? */ bool ordinality; /* does it have WITH ORDINALITY suffix? */ ! Node *funccallnode; /* untransformed function call tree */ Alias *alias; /* table alias & optional column aliases */ List *coldeflist; /* list of ColumnDef nodes to describe result * of function returning RECORD */ --- 466,481 ---- /* * RangeFunction - function call appearing in a FROM clause + * + * funccallnodes is a list because we use this to represent the construct + * TABLE(func1(...),func2(...),...) AS ... */ typedef struct RangeFunction { NodeTag type; bool lateral; /* does it have LATERAL prefix? */ bool ordinality; /* does it have WITH ORDINALITY suffix? */ ! List *funccallnodes; /* untransformed function call trees */ Alias *alias; /* table alias & optional column aliases */ List *coldeflist; /* list of ColumnDef nodes to describe result * of function returning RECORD */ *************** *** 767,773 **** typedef struct RangeTblEntry * derived from the funcexpr while treating the ordinal column, if * present, as a special case. (see get_rte_attribute_*) */ ! Node *funcexpr; /* expression tree for func call */ List *funccoltypes; /* OID list of column type OIDs */ List *funccoltypmods; /* integer list of column typmods */ List *funccolcollations; /* OID list of column collation OIDs */ --- 770,776 ---- * derived from the funcexpr while treating the ordinal column, if * present, as a special case. (see get_rte_attribute_*) */ ! List *funcexprs; /* expression tree for func call */ List *funccoltypes; /* OID list of column type OIDs */ List *funccoltypmods; /* integer list of column typmods */ List *funccolcollations; /* OID list of column collation OIDs */ *** a/src/include/nodes/plannodes.h --- b/src/include/nodes/plannodes.h *************** *** 424,430 **** typedef struct SubqueryScan typedef struct FunctionScan { Scan scan; ! Node *funcexpr; /* expression tree for func call */ bool funcordinality; /* WITH ORDINALITY */ List *funccolnames; /* output column names (string Value nodes) */ List *funccoltypes; /* OID list of column type OIDs */ --- 424,430 ---- typedef struct FunctionScan { Scan scan; ! List *funcexprs; /* expression trees for func calls */ bool funcordinality; /* WITH ORDINALITY */ List *funccolnames; /* output column names (string Value nodes) */ List *funccoltypes; /* OID list of column type OIDs */ *** a/src/include/optimizer/pathnode.h --- b/src/include/optimizer/pathnode.h *************** *** 70,76 **** extern UniquePath *create_unique_path(PlannerInfo *root, RelOptInfo *rel, extern Path *create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, List *pathkeys, Relids required_outer); extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, ! Relids required_outer); extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer); extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel, --- 70,76 ---- extern Path *create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, List *pathkeys, Relids required_outer); extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, ! List *pathkeys, Relids required_outer); extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer); extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel, *** a/src/include/optimizer/paths.h --- b/src/include/optimizer/paths.h *************** *** 165,170 **** extern Path *get_cheapest_fractional_path_for_pathkeys(List *paths, --- 165,172 ---- double fraction); extern List *build_index_pathkeys(PlannerInfo *root, IndexOptInfo *index, ScanDirection scandir); + extern List *build_expression_pathkey(PlannerInfo *root, RelOptInfo *rel, + Expr *expr, Oid operator, bool nulls_first); extern List *convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, List *subquery_pathkeys); extern List *build_join_pathkeys(PlannerInfo *root, *** a/src/include/parser/parse_relation.h --- b/src/include/parser/parse_relation.h *************** *** 59,65 **** extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate, bool inFromCl); extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate, char *funcname, ! Node *funcexpr, RangeFunction *rangefunc, bool lateral, bool inFromCl); --- 59,65 ---- bool inFromCl); extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate, char *funcname, ! List *funcexprs, RangeFunction *rangefunc, bool lateral, bool inFromCl);