/* ---------------------------------------------------------------- * FunctionNext * * This is a workhorse for ExecFunctionScan * ---------------------------------------------------------------- */ static TupleTableSlot * FunctionNext(FunctionScanState *node) { TupleTableSlot *slot; EState *estate; ScanDirection direction; Tuplestorestate *tuplestorestate; bool should_free; HeapTuple heapTuple; /* * get information from the estate and scan state */ 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) { ExprContext *econtext = node->ss.ps.ps_ExprContext; TupleDesc funcTupdesc; node->tuplestorestate = tuplestorestate = ExecMakeTableFunctionResult(node->funcexpr, econtext, node->tupdesc, &funcTupdesc); /* * If function provided a tupdesc, cross-check it. We only really * need to do this for functions returning RECORD, but might as well * do it always. */ if (funcTupdesc) tupledesc_match(node->tupdesc, funcTupdesc); } /* * Get the next tuple from tuplestore. Return NULL if no more tuples. */ heapTuple = tuplestore_getheaptuple(tuplestorestate, ScanDirectionIsForward(direction), &should_free); slot = node->ss.ss_ScanTupleSlot; if (heapTuple) return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free); else return ExecClearTuple(slot); }
/* ---------------------------------------------------------------- * FunctionNext * * This is a workhorse for ExecFunctionScan * ---------------------------------------------------------------- */ static TupleTableSlot * FunctionNext(FunctionScanState *node) { TupleTableSlot *slot; EState *estate; ScanDirection direction; Tuplestorestate *tuplestorestate; bool should_free; HeapTuple heapTuple; /* * get information from the estate and scan state */ 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) { ExprContext *econtext = node->ss.ps.ps_ExprContext; TupleDesc funcTupdesc; node->tuplestorestate = tuplestorestate = ExecMakeTableFunctionResult(node->funcexpr, econtext, node->tupdesc, &funcTupdesc); /* * If function provided a tupdesc, cross-check it. We only really * need to do this for functions returning RECORD, but might as * well do it always. */ if (funcTupdesc && !tupledesc_match(node->tupdesc, funcTupdesc)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("query-specified return row and actual function return row do not match"))); } /* * Get the next tuple from tuplestore. Return NULL if no more tuples. */ heapTuple = tuplestore_getheaptuple(tuplestorestate, ScanDirectionIsForward(direction), &should_free); slot = node->ss.ss_ScanTupleSlot; return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free); }
TupleCheckStatus FilterInit(Filter *filter, TupleDesc desc, Oid collation) { int i; ParsedFunction func; HeapTuple ftup; HeapTuple ltup; Form_pg_proc pp; Form_pg_language lp; TupleCheckStatus status = NEED_COERCION_CHECK; if (filter->funcstr == NULL) return NO_COERCION; /* parse filter function */ func = ParseFunction(filter->funcstr, true); filter->funcid = func.oid; filter->nargs = func.nargs; for (i = 0; i < filter->nargs; i++) { /* Check for polymorphic types and internal pseudo-type argument */ if (IsPolymorphicType(func.argtypes[i]) || func.argtypes[i] == INTERNALOID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("filter function does not support a polymorphic function and having a internal pseudo-type argument function: %s", get_func_name(filter->funcid)))); filter->argtypes[i] = func.argtypes[i]; } ftup = SearchSysCache(PROCOID, ObjectIdGetDatum(filter->funcid), 0, 0, 0); pp = (Form_pg_proc) GETSTRUCT(ftup); if (pp->proretset) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("filter function must not return set"))); /* Check data type of the function result value */ if (pp->prorettype == desc->tdtypeid && pp->prorettype != RECORDOID) status = NO_COERCION; else if (pp->prorettype == RECORDOID) { TupleDesc resultDesc = NULL; /* Check for OUT parameters defining a RECORD result */ resultDesc = build_function_result_tupdesc_t(ftup); if (resultDesc) { if (tupledesc_match(desc, resultDesc)) status = NO_COERCION; FreeTupleDesc(resultDesc); } } else if (get_typtype(pp->prorettype) != TYPTYPE_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return data type and target table data type do not match"))); /* Get default values */ #if PG_VERSION_NUM >= 80400 filter->fn_ndargs = pp->pronargdefaults; if (filter->fn_ndargs > 0) { Datum proargdefaults; bool isnull; char *str; List *defaults; ListCell *l; filter->defaultValues = palloc(sizeof(Datum) * filter->fn_ndargs); filter->defaultIsnull = palloc(sizeof(bool) * filter->fn_ndargs); proargdefaults = SysCacheGetAttr(PROCOID, ftup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); str = TextDatumGetCString(proargdefaults); defaults = (List *) stringToNode(str); Assert(IsA(defaults, List)); pfree(str); filter->econtext = CreateStandaloneExprContext(); i = 0; foreach(l, defaults) { Expr *expr = (Expr *) lfirst(l); ExprState *argstate; ExprDoneCond thisArgIsDone; argstate = ExecInitExpr(expr, NULL); filter->defaultValues[i] = ExecEvalExpr(argstate, filter->econtext, &filter->defaultIsnull[i], &thisArgIsDone); if (thisArgIsDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("functions and operators can take at most one set argument"))); i++; }
static void FunctionParserInit(FunctionParser *self, Checker *checker, const char *infile, TupleDesc desc, bool multi_process, Oid collation) { int i; ParsedFunction function; int nargs; Oid funcid; HeapTuple ftup; Form_pg_proc pp; bool tupledesc_matched = false; if (pg_strcasecmp(infile, "stdin") == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot load from STDIN in the case of \"TYPE = FUNCTION\""))); if (checker->encoding != -1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("does not support parameter \"ENCODING\" in \"TYPE = FUNCTION\""))); function = ParseFunction(infile, false); funcid = function.oid; fmgr_info(funcid, &self->flinfo); if (!self->flinfo.fn_retset) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function must return set"))); ftup = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); pp = (Form_pg_proc) GETSTRUCT(ftup); /* Check data type of the function result value */ if (pp->prorettype == desc->tdtypeid && desc->tdtypeid != RECORDOID) tupledesc_matched = true; else if (pp->prorettype == RECORDOID) { TupleDesc resultDesc = NULL; /* Check for OUT parameters defining a RECORD result */ resultDesc = build_function_result_tupdesc_t(ftup); if (resultDesc) { tupledesc_match(desc, resultDesc); tupledesc_matched = true; FreeTupleDesc(resultDesc); } } else if (get_typtype(pp->prorettype) != TYPTYPE_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return data type and target table data type do not match"))); if (tupledesc_matched && checker->tchecker) checker->tchecker->status = NO_COERCION; /* * assign arguments */ nargs = function.nargs; for (i = 0; #if PG_VERSION_NUM >= 80400 i < nargs - function.nvargs; #else i < nargs; #endif ++i) { if (function.args[i] == NULL) { if (self->flinfo.fn_strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function is strict, but argument %d is NULL", i))); self->fcinfo.argnull[i] = true; } else { Oid typinput; Oid typioparam; getTypeInputInfo(pp->proargtypes.values[i], &typinput, &typioparam); self->fcinfo.arg[i] = OidInputFunctionCall(typinput, (char *) function.args[i], typioparam, -1); self->fcinfo.argnull[i] = false; pfree(function.args[i]); } } /* * assign variadic arguments */ #if PG_VERSION_NUM >= 80400 if (function.nvargs > 0) { int nfixedarg; Oid func; Oid element_type; int16 elmlen; bool elmbyval; char elmalign; char elmdelim; Oid elmioparam; Datum *elems; bool *nulls; int dims[1]; int lbs[1]; ArrayType *arry; nfixedarg = i; element_type = pp->provariadic; /* * Get info about element type, including its input conversion proc */ get_type_io_data(element_type, IOFunc_input, &elmlen, &elmbyval, &elmalign, &elmdelim, &elmioparam, &func); elems = (Datum *) palloc(function.nvargs * sizeof(Datum)); nulls = (bool *) palloc0(function.nvargs * sizeof(bool)); for (i = 0; i < function.nvargs; i++) { if (function.args[nfixedarg + i] == NULL) nulls[i] = true; else { elems[i] = OidInputFunctionCall(func, (char *) function.args[nfixedarg + i], elmioparam, -1); pfree(function.args[nfixedarg + i]); } } dims[0] = function.nvargs; lbs[0] = 1; arry = construct_md_array(elems, nulls, 1, dims, lbs, element_type, elmlen, elmbyval, elmalign); self->fcinfo.arg[nfixedarg] = PointerGetDatum(arry); } /* * assign default arguments */ if (function.ndargs > 0) { Datum proargdefaults; bool isnull; char *str; List *defaults; int ndelete; ListCell *l; /* shouldn't happen, FuncnameGetCandidates messed up */ if (function.ndargs > pp->pronargdefaults) elog(ERROR, "not enough default arguments"); proargdefaults = SysCacheGetAttr(PROCOID, ftup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); str = TextDatumGetCString(proargdefaults); defaults = (List *) stringToNode(str); Assert(IsA(defaults, List)); pfree(str); /* Delete any unused defaults from the returned list */ ndelete = list_length(defaults) - function.ndargs; while (ndelete-- > 0) defaults = list_delete_first(defaults); self->arg_econtext = CreateStandaloneExprContext(); foreach(l, defaults) { Expr *expr = (Expr *) lfirst(l); ExprState *argstate; ExprDoneCond thisArgIsDone; /* probably shouldn't happen ... */ if (nargs >= FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("cannot pass more than %d arguments to a function", FUNC_MAX_ARGS))); argstate = ExecInitExpr(expr, NULL); self->fcinfo.arg[nargs] = ExecEvalExpr(argstate, self->arg_econtext, &self->fcinfo.argnull[nargs], &thisArgIsDone); if (thisArgIsDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("functions and operators can take at most one set argument"))); nargs++; }
/* * ExecMakeTableFunctionResult * * Evaluate a table function, producing a materialized result in a Tuplestore * object. * * This is used by nodeFunctionscan.c. */ Tuplestorestate * ExecMakeTableFunctionResult(SetExprState *setexpr, ExprContext *econtext, MemoryContext argContext, TupleDesc expectedDesc, bool randomAccess) { Tuplestorestate *tupstore = NULL; TupleDesc tupdesc = NULL; Oid funcrettype; bool returnsTuple; bool returnsSet = false; FunctionCallInfoData fcinfo; PgStat_FunctionCallUsage fcusage; ReturnSetInfo rsinfo; HeapTupleData tmptup; MemoryContext callerContext; MemoryContext oldcontext; bool first_time = true; callerContext = CurrentMemoryContext; funcrettype = exprType((Node *) setexpr->expr); returnsTuple = type_is_rowtype(funcrettype); /* * Prepare a resultinfo node for communication. We always do this even if * not expecting a set result, so that we can pass expectedDesc. In the * generic-expression case, the expression doesn't actually get to see the * resultinfo, but set it up anyway because we use some of the fields as * our own state variables. */ rsinfo.type = T_ReturnSetInfo; rsinfo.econtext = econtext; rsinfo.expectedDesc = expectedDesc; rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred); if (randomAccess) rsinfo.allowedModes |= (int) SFRM_Materialize_Random; rsinfo.returnMode = SFRM_ValuePerCall; /* isDone is filled below */ rsinfo.setResult = NULL; rsinfo.setDesc = NULL; /* * Normally the passed expression tree will be a SetExprState, since the * grammar only allows a function call at the top level of a table * function reference. However, if the function doesn't return set then * the planner might have replaced the function call via constant-folding * or inlining. So if we see any other kind of expression node, execute * it via the general ExecEvalExpr() code; the only difference is that we * don't get a chance to pass a special ReturnSetInfo to any functions * buried in the expression. */ if (!setexpr->elidedFuncState) { /* * This path is similar to ExecMakeFunctionResultSet. */ returnsSet = setexpr->funcReturnsSet; InitFunctionCallInfoData(fcinfo, &(setexpr->func), list_length(setexpr->args), setexpr->fcinfo_data.fncollation, NULL, (Node *) &rsinfo); /* * Evaluate the function's argument list. * * We can't do this in the per-tuple context: the argument values * would disappear when we reset that context in the inner loop. And * the caller's CurrentMemoryContext is typically a query-lifespan * context, so we don't want to leak memory there. We require the * caller to pass a separate memory context that can be used for this, * and can be reset each time through to avoid bloat. */ MemoryContextReset(argContext); oldcontext = MemoryContextSwitchTo(argContext); ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext); MemoryContextSwitchTo(oldcontext); /* * If function is strict, and there are any NULL arguments, skip * calling the function and act like it returned NULL (or an empty * set, in the returns-set case). */ if (setexpr->func.fn_strict) { int i; for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) goto no_function_result; } } } else { /* Treat setexpr as a generic expression */ InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL); } /* * Switch to short-lived context for calling the function or expression. */ MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * Loop to handle the ValuePerCall protocol (which is also the same * behavior needed in the generic ExecEvalExpr path). */ for (;;) { Datum result; CHECK_FOR_INTERRUPTS(); /* * reset per-tuple memory context before each call of the function or * expression. This cleans up any local memory the function may leak * when called. */ ResetExprContext(econtext); /* Call the function or expression one time */ if (!setexpr->elidedFuncState) { pgstat_init_function_usage(&fcinfo, &fcusage); fcinfo.isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(&fcinfo); pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); } else { result = ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull); rsinfo.isDone = ExprSingleResult; } /* Which protocol does function want to use? */ if (rsinfo.returnMode == SFRM_ValuePerCall) { /* * Check for end of result set. */ if (rsinfo.isDone == ExprEndResult) break; /* * If first time through, build tuplestore for result. For a * scalar function result type, also make a suitable tupdesc. */ if (first_time) { oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); rsinfo.setResult = tupstore; if (!returnsTuple) { tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "column", funcrettype, -1, 0); rsinfo.setDesc = tupdesc; } MemoryContextSwitchTo(oldcontext); } /* * Store current resultset item. */ if (returnsTuple) { if (!fcinfo.isnull) { HeapTupleHeader td = DatumGetHeapTupleHeader(result); if (tupdesc == NULL) { /* * This is the first non-NULL result from the * function. Use the type info embedded in the * rowtype Datum to look up the needed tupdesc. Make * a copy for the query. */ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); rsinfo.setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); } else { /* * Verify all later returned rows have same subtype; * necessary in case the type is RECORD. */ if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid || HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("rows returned by function are not all of the same row type"))); } /* * tuplestore_puttuple needs a HeapTuple not a bare * HeapTupleHeader, but it doesn't need all the fields. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; tuplestore_puttuple(tupstore, &tmptup); } else { /* * NULL result from a tuple-returning function; expand it * to a row of all nulls. We rely on the expectedDesc to * form such rows. (Note: this would be problematic if * tuplestore_putvalues saved the tdtypeid/tdtypmod from * the provided descriptor, since that might not match * what we get from the function itself. But it doesn't.) */ int natts = expectedDesc->natts; bool *nullflags; nullflags = (bool *) palloc(natts * sizeof(bool)); memset(nullflags, true, natts * sizeof(bool)); tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags); } } else { /* Scalar-type case: just store the function result */ tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull); } /* * Are we done? */ if (rsinfo.isDone != ExprMultipleResult) break; } else if (rsinfo.returnMode == SFRM_Materialize) { /* check we're on the same page as the function author */ if (!first_time || rsinfo.isDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("table-function protocol for materialize mode was not followed"))); /* Done evaluating the set result */ break; } else ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("unrecognized table-function returnMode: %d", (int) rsinfo.returnMode))); first_time = false; } no_function_result: /* * If we got nothing from the function (ie, an empty-set or NULL result), * we have to create the tuplestore to return, and if it's a * non-set-returning function then insert a single all-nulls row. As * above, we depend on the expectedDesc to manufacture the dummy row. */ if (rsinfo.setResult == NULL) { MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); rsinfo.setResult = tupstore; if (!returnsSet) { int natts = expectedDesc->natts; bool *nullflags; MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); nullflags = (bool *) palloc(natts * sizeof(bool)); memset(nullflags, true, natts * sizeof(bool)); tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags); } } /* * If function provided a tupdesc, cross-check it. We only really need to * do this for functions returning RECORD, but might as well do it always. */ if (rsinfo.setDesc) { tupledesc_match(expectedDesc, rsinfo.setDesc); /* * If it is a dynamically-allocated TupleDesc, free it: it is * typically allocated in a per-query context, so we must avoid * leaking it across multiple usages. */ if (rsinfo.setDesc->tdrefcount == -1) FreeTupleDesc(rsinfo.setDesc); } MemoryContextSwitchTo(callerContext); /* All done, pass back the tuplestore */ return rsinfo.setResult; }