📄 execqual.c
字号:
/* * 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); } /* * 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? */ MemSet(&fcinfo, 0, sizeof(fcinfo)); 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 return NULL (actually an empty set). */ if (fcache->func.fn_strict) { int i; for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) { *returnDesc = NULL; return NULL; } } } } else { /* Treat funcexpr as a generic expression */ direct_function_call = false; } funcrettype = exprType((Node *) funcexpr->expr); /* * 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. */ fcinfo.resultinfo = (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; /* * Switch to short-lived context for calling the function or * expression. */ callerContext = 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; /* * 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. * * Note: if function returns an empty set, we don't build a * tupdesc or tuplestore (since we can't get a tupdesc in the * function-returning-tuple case) */ if (rsinfo.isDone == ExprEndResult) break; /* * If first time through, build tupdesc and tuplestore for * result */ if (first_time) { oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); if (funcrettype == RECORDOID || get_typtype(funcrettype) == 'c') { /* * Composite type, so function should have returned a * TupleTableSlot; use its descriptor */ slot = (TupleTableSlot *) DatumGetPointer(result); if (fcinfo.isnull || !slot) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("function returning row cannot return null value"))); if (!IsA(slot, TupleTableSlot) || !slot->ttc_tupleDescriptor) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function returning row did not return a valid tuple slot"))); tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor); returnsTuple = true; } else { /* * Scalar type, so make a single-column descriptor */ tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "column", funcrettype, -1, 0, false); } tupstore = tuplestore_begin_heap(true, false, SortMem); MemoryContextSwitchTo(oldcontext); rsinfo.setResult = tupstore; rsinfo.setDesc = tupdesc; } /* * Store current resultset item. */ if (returnsTuple) { slot = (TupleTableSlot *) DatumGetPointer(result); if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) || TupIsNull(slot)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("function returning row cannot return null value"))); tuple = slot->val; } else { char nullflag; nullflag = fcinfo.isnull ? 'n' : ' '; tuple = heap_formtuple(tupdesc, &result, &nullflag); } oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tuplestore_puttuple(tupstore, tuple); MemoryContextSwitchTo(oldcontext); /* * 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; } MemoryContextSwitchTo(callerContext); /* The returned pointers are those in rsinfo */ *returnDesc = rsinfo.setDesc; return rsinfo.setResult;}/* ---------------------------------------------------------------- * ExecEvalFunc * ExecEvalOper * * Evaluate the functional result of a list of arguments by calling the * function manager. * ---------------------------------------------------------------- *//* ---------------------------------------------------------------- * ExecEvalFunc * ---------------------------------------------------------------- */static DatumExecEvalFunc(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone){ /* * 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); } return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);}/* ---------------------------------------------------------------- * ExecEvalOper * ---------------------------------------------------------------- */static DatumExecEvalOper(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone){ /* * Initialize function cache if first time through */ if (fcache->func.fn_oid == InvalidOid) { OpExpr *op = (OpExpr *) fcache->xprstate.expr; init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory); } return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);}/* ---------------------------------------------------------------- * ExecEvalDistinct * * IS DISTINCT FROM must evaluate arguments to determine whether * they are NULL; if either is NULL then the result is already * known. If neither is NULL, then proceed to evaluate the * function. Note that this is *always* derived from the equals * operator, but since we need special processing of the arguments * we can not simply reuse ExecEvalOper() or ExecEvalFunc(). * ---------------------------------------------------------------- */static DatumExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext, bool *isNull){ Datum result; FunctionCallInfoData fcinfo; ExprDoneCond argDone; List *argList; /* * Initialize function cache if first time through */ if (fcache->func.fn_oid == InvalidOid) { DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr; init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory); Assert(!fcache->func.fn_retset); } /* * extract info from fcache */ argList = fcache->args; /* Need to prep callinfo structure */ MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &(fcache->func); argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext); if (argDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("IS DISTINCT FROM does not support set arguments"))); Assert(fcinfo.nargs == 2); if (fcinfo.argnull[0] && fcinfo.argnull[1]) { /* Both NULL? Then is not distinct... */ result = BoolGetDatum(FALSE); } else if (fcinfo.argnull[0] || fcinfo.argnull[1]) { /* Only one is NULL? Then is distinct... */ result = BoolGetDatum(TRUE); } else { fcinfo.isnull = false; result = FunctionCallInvoke(&fcinfo); *isNull = fcinfo.isnull; /* Must invert result of "=" */ result = BoolGetDatum(!DatumGetBool(result)); } return result;}/* * ExecEvalScalarArrayOp * * Evaluate "scalar op ANY/ALL (array)". The operator always yields boolean, * and we combine the results across all array elements using OR and AND * (for ANY and ALL respectively). Of course we short-circuit as soon as * the result is known. */static DatumExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, ExprContext *econtext, bool *isNull){ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr; bool useOr = opexpr->useOr; ArrayType *arr; int nitems; Datum result; bool resultnull; FunctionCallInfoData fcinfo; ExprDoneCond argDone; int i; int16 typlen; bool typbyval; char typalign; char *s; /* * Initialize function cache if first time through */ if (sstate->fxprstate.func.fn_oid == InvalidOid) { init_fcache(opexpr->opfuncid, &sstate->fxprstate, econtext->ecxt_per_query_memory); Assert(!sstate->fxprstate.func.fn_retset); } /* Need to prep callinfo structure */ MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &(sstate->fxprstate.func); argDone = ExecEvalFuncArgs(&fcinfo, sstate->fxprstate.args, econtext); if (argDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("op ANY/ALL (array) does not support set arguments"))); Assert(fcinfo.nargs == 2); /* * If the array is NULL then we return NULL --- it's not very * meaningful to do anything else, even if the operator isn't strict. */ if (fcinfo.argnull[1]) { *isNull = true; return (Datum) 0; } /* Else okay to fetch and detoast the array */ arr = DatumGetArrayTypeP(fcinfo.arg[1]); /* * If the array is empty, we return either FALSE or TRUE per the useOr * flag. This is correct even if the scalar is NULL; since we would * evaluate the operator zero times, it matters not whether it would * want to return NULL. */ nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); if (nitems <= 0) return BoolGetDatum(!useOr); /* * If the scalar is NULL, and the function is strict, return NULL. * This is just to avoid having to test for strictness inside the * loop. (XXX but if arrays could have null elements, we'd need a * test anyway.) */ if (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict) { *isNull = true; return (Datum) 0; } /* * We arrange to look up info about the element type only once per * series of calls, assuming the element type doesn't change * underneath us. */ if (sstate->element_type != ARR_ELEMTYPE(arr)) { get_typlenbyvalalign(ARR_ELEMTYPE(arr), &sstate->typlen, &sstate->typbyval, &sstate->typalign); sstate->element_type = ARR_ELEMTYPE(arr); } typlen = sstate->typlen; typbyval = sstate->typbyval; typalign = sstate->typalign; result = BoolGetDatum(!useOr); resultnull = false; /* Loop over the array elements */ s = (char *) ARR_DATA_PTR(arr); for (i = 0; i < nitems; i++) { Datum elt; Datum thisresult; /* Get array element */ elt = fetch_att(s, typbyval, typlen); s = att_addlength(s, typlen, PointerGetDatum(s)); s = (char *) att_align(s, typalign); /* Call comparison function */ fcinfo.arg[1] = elt; fcinfo.argnull[1] = false; fcinfo.isnull = false; thisresult = FunctionCallInvoke(&fcinfo); /* Combine results per OR or AND semantics */ if (fcinfo.isnull) resultnull = true; else if (useOr)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -