📄 execqual.c
字号:
/* Need to prep callinfo structure */ InitFunctionCallInfoData(fcinfo, &(fcache->func), 0, NULL, NULL); argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext); if (argDone == ExprEndResult) { /* input is an empty set, so return an empty set. */ *isNull = true; if (isDone) *isDone = ExprEndResult; else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); return (Datum) 0; } hasSetArg = (argDone != ExprSingleResult); } else { /* Copy callinfo from previous evaluation */ memcpy(&fcinfo, &fcache->setArgs, sizeof(fcinfo)); hasSetArg = fcache->setHasSetArg; /* Reset flag (we may set it again below) */ fcache->setArgsValid = false; } /* * If function returns set, prepare a resultinfo node for communication */ if (fcache->func.fn_retset) { fcinfo.resultinfo = (Node *) &rsinfo; rsinfo.type = T_ReturnSetInfo; rsinfo.econtext = econtext; rsinfo.expectedDesc = NULL; rsinfo.allowedModes = (int) SFRM_ValuePerCall; rsinfo.returnMode = SFRM_ValuePerCall; /* isDone is filled below */ rsinfo.setResult = NULL; rsinfo.setDesc = NULL; } /* * now return the value gotten by calling the function manager, passing * the function the evaluated parameter values. */ if (fcache->func.fn_retset || hasSetArg) { /* * We need to return a set result. Complain if caller not ready to * accept one. */ if (isDone == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); /* * This loop handles the situation where we have both a set argument * and a set-valued function. Once we have exhausted the function's * value(s) for a particular argument value, we have to get the next * argument value and start the function over again. We might have to * do it more than once, if the function produces an empty result set * for a particular input value. */ for (;;) { /* * If function is strict, and there are any NULL arguments, skip * calling the function (at least for this set of args). */ bool callit = true; if (fcache->func.fn_strict) { for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) { callit = false; break; } } } if (callit) { fcinfo.isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(&fcinfo); *isNull = fcinfo.isnull; *isDone = rsinfo.isDone; } else { result = (Datum) 0; *isNull = true; *isDone = ExprEndResult; } if (*isDone != ExprEndResult) { /* * Got a result from current argument. If function itself * returns set, save the current argument values to re-use on * the next call. */ if (fcache->func.fn_retset && *isDone == ExprMultipleResult) { memcpy(&fcache->setArgs, &fcinfo, sizeof(fcinfo)); fcache->setHasSetArg = hasSetArg; fcache->setArgsValid = true; /* Register cleanup callback if we didn't already */ if (!fcache->shutdown_reg) { RegisterExprContextCallback(econtext, ShutdownFuncExpr, PointerGetDatum(fcache)); fcache->shutdown_reg = true; } } /* * Make sure we say we are returning a set, even if the * function itself doesn't return sets. */ if (hasSetArg) *isDone = ExprMultipleResult; break; } /* Else, done with this argument */ if (!hasSetArg) break; /* input not a set, so done */ /* Re-eval args to get the next element of the input set */ argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext); if (argDone != ExprMultipleResult) { /* End of argument set, so we're done. */ *isNull = true; *isDone = ExprEndResult; result = (Datum) 0; break; } /* * If we reach here, loop around to run the function on the new * argument. */ } } else { /* * Non-set case: much easier. * * We change the ExprState function pointer to use the simpler * ExecMakeFunctionResultNoSets on subsequent calls. This amounts to * assuming that no argument can return a set if it didn't do so the * first time. */ fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets; if (isDone) *isDone = ExprSingleResult; /* * If function is strict, and there are any NULL arguments, skip * calling the function and return NULL. */ if (fcache->func.fn_strict) { for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) { *isNull = true; return (Datum) 0; } } } fcinfo.isnull = false; result = FunctionCallInvoke(&fcinfo); *isNull = fcinfo.isnull; } return result;}/* * ExecMakeFunctionResultNoSets * * Simplified version of ExecMakeFunctionResult that can only handle * non-set cases. Hand-tuned for speed. */static DatumExecMakeFunctionResultNoSets(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone){ ListCell *arg; Datum result; FunctionCallInfoData fcinfo; int i; /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); if (isDone) *isDone = ExprSingleResult; /* inlined, simplified version of ExecEvalFuncArgs */ i = 0; foreach(arg, fcache->args) { ExprState *argstate = (ExprState *) lfirst(arg); fcinfo.arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo.argnull[i], NULL); i++; } InitFunctionCallInfoData(fcinfo, &(fcache->func), i, NULL, NULL); /* * If function is strict, and there are any NULL arguments, skip calling * the function and return NULL. */ if (fcache->func.fn_strict) { while (--i >= 0) { if (fcinfo.argnull[i]) { *isNull = true; return (Datum) 0; } } } /* fcinfo.isnull = false; */ /* handled by InitFunctionCallInfoData */ result = FunctionCallInvoke(&fcinfo); *isNull = fcinfo.isnull; return result;}/* * ExecMakeTableFunctionResult * * Evaluate a table function, producing a materialized result in a Tuplestore * object. *returnDesc is set to the tupledesc actually returned by the * function, or NULL if it didn't provide one. */Tuplestorestate *ExecMakeTableFunctionResult(ExprState *funcexpr, ExprContext *econtext, TupleDesc expectedDesc, TupleDesc *returnDesc){ Tuplestorestate *tupstore = NULL; TupleDesc tupdesc = NULL; Oid funcrettype; bool returnsTuple; bool returnsSet = false; FunctionCallInfoData fcinfo; ReturnSetInfo rsinfo; HeapTupleData tmptup; MemoryContext callerContext; MemoryContext oldcontext; bool direct_function_call; bool first_time = true; callerContext = CurrentMemoryContext; funcrettype = exprType((Node *) funcexpr->expr); returnsTuple = (funcrettype == RECORDOID || get_typtype(funcrettype) == 'c'); /* * 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. */ InitFunctionCallInfoData(fcinfo, NULL, 0, NULL, (Node *) &rsinfo); rsinfo.type = T_ReturnSetInfo; rsinfo.econtext = econtext; rsinfo.expectedDesc = expectedDesc; rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize); rsinfo.returnMode = SFRM_ValuePerCall; /* isDone is filled below */ rsinfo.setResult = NULL; rsinfo.setDesc = NULL; /* * Normally the passed expression tree will be a FuncExprState, 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 (funcexpr && IsA(funcexpr, FuncExprState) && IsA(funcexpr->expr, FuncExpr)) { FuncExprState *fcache = (FuncExprState *) funcexpr; ExprDoneCond argDone; /* * This path is similar to ExecMakeFunctionResult. */ direct_function_call = true; /* * Initialize function cache if first time through */ if (fcache->func.fn_oid == InvalidOid) { FuncExpr *func = (FuncExpr *) fcache->xprstate.expr; init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory); } returnsSet = fcache->func.fn_retset; /* * Evaluate the function's argument list. * * Note: ideally, we'd do this in the per-tuple context, but then the * argument values would disappear when we reset the context in the * inner loop. So do it in caller context. Perhaps we should make a * separate context just to hold the evaluated arguments? */ fcinfo.flinfo = &(fcache->func); argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext); /* We don't allow sets in the arguments of the table function */ if (argDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); /* * 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 (fcache->func.fn_strict) { int i; for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) goto no_function_result; } } } else { /* Treat funcexpr as a generic expression */ direct_function_call = false; } /* * 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; HeapTuple tuple; 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 (direct_function_call) { fcinfo.isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(&fcinfo); } else { result = ExecEvalExpr(funcexpr, econtext, &fcinfo.isnull, &rsinfo.isDone); } /* Which protocol does function want to use? */ if (rsinfo.returnMode == SFRM_ValuePerCall) { /* * Check for end of result set. */ if (rsinfo.isDone == ExprEndResult) break; /* * Can't do anything very useful with NULL rowtype values. For a * function returning set, we consider this a protocol violation * (but another alternative would be to just ignore the result and * "continue" to get another row). For a function not returning * set, we fall out of the loop; we'll cons up an all-nulls result * row below. */ if (returnsTuple && fcinfo.isnull) { if (!returnsSet) break; ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("function returning set of rows cannot return null value"))); } /* * If first time through, build tupdesc and tuplestore for result */ if (first_time) { oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); if (returnsTuple) { /* * Use the type info embedded in the rowtype Datum to look * up the needed tupdesc. Make a copy for the query. */ HeapTupleHeader td; td = DatumGetHeapTupleHeader(result); tupdesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); tupdesc = CreateTupleDescCopy(tupdesc); } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -