/* ------------------------------------------------------------------------- * * execQual.cpp * Routines to evaluate qualification and targetlist expressions * * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/gausskernel/runtime/executor/execQual.cpp * * ------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES * ExecEvalExpr - (now a macro) evaluate an expression, return a datum * ExecEvalExprSwitchContext - same, but switch into eval memory context * ExecQual - return true/false if qualification is satisfied * ExecProject - form a new tuple by projecting the given tuple * * NOTES * The more heavily used ExecEvalExpr routines, such as ExecEvalScalarVar, * are hotspots. Making these faster will speed up the entire system. * * ExecProject() is used to make tuple projections. Rather then * trying to speed it up, the execution plan should be pre-processed * to facilitate attribute sharing between nodes wherever possible, * instead of doing needless copying. -cim 5/31/91 * * During expression evaluation, we check_stack_depth only in * ExecMakeFunctionResult (and substitute routines) rather than at every * single node. This is a compromise that trades off precision of the * stack limit setting to gain speed. */ #include "postgres.h" #include "knl/knl_variable.h" #include "access/nbtree.h" #include "access/tupconvert.h" #include "access/tableam.h" #include "catalog/pg_cast.h" #include "catalog/pg_type.h" #include "commands/typecmds.h" #include "executor/exec/execdebug.h" #include "executor/node/nodeSubplan.h" #include "executor/node/nodeAgg.h" #include "executor/node/nodeCtescan.h" #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/planner.h" #include "parser/parse_coerce.h" #include "pgstat.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/typcache.h" #include "utils/xml.h" #include "access/hash.h" #include "access/transam.h" #ifdef PGXC #include "pgxc/groupmgr.h" #include "pgxc/pgxc.h" #endif #include "optimizer/streamplan.h" #include "gstrace/gstrace_infra.h" #include "gstrace/executer_gstrace.h" #include "commands/trigger.h" #include "db4ai/gd.h" #include "catalog/pg_proc_fn.h" #include "access/tuptoaster.h" /* static function decls */ static Datum ExecEvalArrayRef(ArrayRefExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static bool isAssignmentIndirectionExpr(ExprState* exprstate); static Datum ExecEvalAggref(AggrefExprState* aggref, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalWindowFunc(WindowFuncExprState* wfunc, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalScalarVar(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalScalarVarFast(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalWholeRowVar( WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalWholeRowFast( WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalWholeRowSlow( WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalParamExec(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalParamExtern(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static bool isVectorEngineSupportSetFunc(Oid funcid); template static void init_fcache( Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool needDescForSets); static void ShutdownFuncExpr(Datum arg); static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod, TupleDesc* cache_field, ExprContext* econtext); static void ShutdownTupleDescRef(Datum arg); template static ExprDoneCond ExecEvalFuncArgs( FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno = NULL); static void ExecPrepareTuplestoreResult( FuncExprState* fcache, ExprContext* econtext, Tuplestorestate* resultStore, TupleDesc resultDesc); static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc); template static Datum ExecMakeFunctionResult(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); template static Datum ExecMakeFunctionResultNoSets( FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalOper(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalDistinct(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalScalarArrayOp( ScalarArrayOpExprState* sstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalNot(BoolExprState* notclause, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalOr(BoolExprState* orExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalAnd(BoolExprState* andExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalConvertRowtype( ConvertRowtypeExprState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCase(CaseExprState* caseExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCaseTestExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalArray(ArrayExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalRow(RowExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalRowCompare(RowCompareExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCoalesce( CoalesceExprState* coalesceExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalMinMax(MinMaxExprState* minmaxExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalXml(XmlExprState* xmlExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalNullIf(FuncExprState* nullIfExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalNullTest(NullTestState* nstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalHashFilter(HashFilterState* hstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalBooleanTest(GenericExprState* bstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCoerceToDomain( CoerceToDomainState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCoerceToDomainValue( ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalFieldSelect(FieldSelectState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalFieldStore(FieldStoreState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalRelabelType( GenericExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCoerceViaIO(CoerceViaIOState* iostate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalArrayCoerceExpr( ArrayCoerceExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalCurrentOfExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalGroupingFuncExpr( GroupingFuncExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static Datum ExecEvalGroupingIdExpr( GroupingIdExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone); static bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo); extern struct varlena *heap_tuple_fetch_and_copy(Relation rel, struct varlena *attr, bool needcheck); static void check_huge_clob_paramter(FunctionCallInfoData* fcinfo, bool is_have_huge_clob); THR_LOCAL PLpgSQL_execstate* plpgsql_estate = NULL; /* ---------------------------------------------------------------- * ExecEvalExpr routines * * Recursively evaluate a targetlist or qualification expression. * * Each of the following routines having the signature * Datum ExecEvalFoo(ExprState *expression, * ExprContext *econtext, * bool *isNull, * ExprDoneCond *isDone); * is responsible for evaluating one type or subtype of ExprState node. * They are normally called via the ExecEvalExpr macro, which makes use of * the function pointer set up when the ExprState node was built by * ExecInitExpr. (In some cases, we change this pointer later to avoid * re-executing one-time overhead.) * * Note: for notational simplicity we declare these functions as taking the * specific type of ExprState that they work on. This requires casting when * assigning the function pointer in ExecInitExpr. Be careful that the * function signature is declared correctly, because the cast suppresses * automatic checking! * * * All these functions share this calling convention: * * Inputs: * expression: the expression state tree to evaluate * econtext: evaluation context information * * Outputs: * return value: Datum value of result * *isNull: set to TRUE if result is NULL (actual return value is * meaningless if so); set to FALSE if non-null result * *isDone: set to indicator of set-result status * * A caller that can only accept a singleton (non-set) result should pass * NULL for isDone; if the expression computes a set result then an error * will be reported via ereport. If the caller does pass an isDone pointer * then *isDone is set to one of these three states: * ExprSingleResult singleton result (not a set) * ExprMultipleResult return value is one element of a set * ExprEndResult there are no more elements in the set * When ExprMultipleResult is returned, the caller should invoke * ExecEvalExpr() repeatedly until ExprEndResult is returned. ExprEndResult * is returned after the last real set element. For convenience isNull will * always be set TRUE when ExprEndResult is returned, but this should not be * taken as indicating a NULL element of the set. Note that these return * conventions allow us to distinguish among a singleton NULL, a NULL element * of a set, and an empty set. * * The caller should already have switched into the temporary memory * context econtext->ecxt_per_tuple_memory. The convenience entry point * ExecEvalExprSwitchContext() is provided for callers who don't prefer to * do the switch in an outer loop. We do not do the switch in these routines * because it'd be a waste of cycles during nested expression evaluation. * ---------------------------------------------------------------- */ /* ---------- * ExecEvalArrayRef * * This function takes an ArrayRef and returns the extracted Datum * if it's a simple reference, or the modified array value if it's * an array assignment (i.e., array element or slice insertion). * * NOTE: if we get a NULL result from a subscript expression, we return NULL * when it's an array reference, or raise an error when it's an assignment. * * NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here, * even though that might seem natural, because this code needs to support * both varlena arrays and fixed-length array types. DatumGetArrayTypeP() * only works for the varlena kind. The routines we call in arrayfuncs.c * have to know the difference (that's what they need refattrlength for). * ---------- */ static Datum ExecEvalArrayRef(ArrayRefExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { ArrayRef* arrayRef = (ArrayRef*)astate->xprstate.expr; ArrayType* array_source = NULL; ArrayType* resultArray = NULL; bool isAssignment = (arrayRef->refassgnexpr != NULL); bool eisnull = false; ListCell* l = NULL; int i = 0; int j = 0; IntArray upper, lower; int* lIndex = NULL; Oid typOid = astate->xprstate.resultType; array_source = (ArrayType*)DatumGetPointer(ExecEvalExpr(astate->refexpr, econtext, isNull, isDone)); /* * If refexpr yields NULL, and it's a fetch, then result is NULL. In the * assignment case, we'll cons up something below. */ if (*isNull) { if (isDone && *isDone == ExprEndResult) return (Datum)NULL; /* end of set result */ if (!isAssignment) return (Datum)NULL; } ExecTableOfIndexInfo execTableOfIndexInfo; initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); ExecEvalParamExternTableOfIndex((Node*)astate->refexpr->expr, &execTableOfIndexInfo); if (u_sess->SPI_cxt.cur_tableof_index != NULL) { u_sess->SPI_cxt.cur_tableof_index->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; u_sess->SPI_cxt.cur_tableof_index->tableOfIndex = execTableOfIndexInfo.tableOfIndex; u_sess->SPI_cxt.cur_tableof_index->tableOfGetNestLayer = list_length(astate->refupperindexpr); } foreach (l, astate->refupperindexpr) { ExprState* eltstate = (ExprState*)lfirst(l); if (i >= MAXDIM) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", i + 1, MAXDIM))); if (OidIsValid(execTableOfIndexInfo.tableOfIndexType) || execTableOfIndexInfo.isnestedtable) { bool isTran = false; PLpgSQL_execstate* old_estate = plpgsql_estate; Datum exprValue = ExecEvalExpr(eltstate, econtext, &eisnull, NULL); plpgsql_estate = old_estate; if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID && !eisnull && VARATT_IS_1B(exprValue)) { exprValue = transVaratt1BTo4B(exprValue); isTran = true; } TableOfIndexKey key; PLpgSQL_var* node = NULL; key.exprtypeid = execTableOfIndexInfo.tableOfIndexType; key.exprdatum = exprValue; int index = getTableOfIndexByDatumValue(key, execTableOfIndexInfo.tableOfIndex, &node); if (isTran) { pfree(DatumGetPointer(exprValue)); } if (execTableOfIndexInfo.isnestedtable) { /* for nested table, we should take inner table's array and skip current indx */ if (node == NULL || index == -1) { eisnull = true; } else { PLpgSQL_var* var = node; execTableOfIndexInfo.isnestedtable = (var->nest_table != NULL); array_source = (ArrayType*)DatumGetPointer(var->value); execTableOfIndexInfo.tableOfIndexType = var->datatype->tableOfIndexType; execTableOfIndexInfo.tableOfIndex = var->tableOfIndex; eisnull = var->isnull; if (plpgsql_estate) plpgsql_estate->curr_nested_table_type = var->datatype->typoid; continue; } } if (index == -1) { eisnull = true; } else { upper.indx[i++] = index; } } else { upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL)); } /* If any index expr yields NULL, result is NULL or error */ if (eisnull) { if (isAssignment) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array subscript in assignment must not be null"))); *isNull = true; return (Datum)NULL; } } if (astate->reflowerindexpr != NIL) { foreach (l, astate->reflowerindexpr) { ExprState* eltstate = (ExprState*)lfirst(l); if (j >= MAXDIM) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", j + 1, MAXDIM))); if (execTableOfIndexInfo.tableOfIndexType == VARCHAROID || execTableOfIndexInfo.isnestedtable) { ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("index by varchar or nested table don't support two subscripts"))); } else { lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL)); } /* If any index expr yields NULL, result is NULL or error */ if (eisnull) { if (isAssignment) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array subscript in assignment must not be null"))); *isNull = true; return (Datum)NULL; } } /* this can't happen unless parser messed up */ if (i != j) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmodule(MOD_EXECUTOR), (errmsg("upper and lower index lists are not same length (%d, %d)", i, j)))); lIndex = lower.indx; } else lIndex = NULL; if (isAssignment) { Datum sourceData; Datum save_datum; bool save_isNull = false; /* * We might have a nested-assignment situation, in which the * refassgnexpr is itself a FieldStore or ArrayRef that needs to * obtain and modify the previous value of the array element or slice * being replaced. If so, we have to extract that value from the * array and pass it down via the econtext's caseValue. It's safe to * reuse the CASE mechanism because there cannot be a CASE between * here and where the value would be needed, and an array assignment * can't be within a CASE either. (So saving and restoring the * caseValue is just paranoia, but let's do it anyway.) * * Since fetching the old element might be a nontrivial expense, do it * only if the argument appears to actually need it. */ save_datum = econtext->caseValue_datum; save_isNull = econtext->caseValue_isNull; if (isAssignmentIndirectionExpr(astate->refassgnexpr)) { if (*isNull) { /* whole array is null, so any element or slice is too */ econtext->caseValue_datum = (Datum)0; econtext->caseValue_isNull = true; } else if (lIndex == NULL) { econtext->caseValue_datum = array_ref(array_source, i, upper.indx, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign, &econtext->caseValue_isNull); } else { resultArray = array_get_slice(array_source, i, upper.indx, lower.indx, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign); econtext->caseValue_datum = PointerGetDatum(resultArray); econtext->caseValue_isNull = false; } } else { /* argument shouldn't need caseValue, but for safety set it null */ econtext->caseValue_datum = (Datum)0; econtext->caseValue_isNull = true; } /* * Evaluate the value to be assigned into the array. */ sourceData = ExecEvalExpr(astate->refassgnexpr, econtext, &eisnull, NULL); econtext->caseValue_datum = save_datum; econtext->caseValue_isNull = save_isNull; /* * For an assignment to a fixed-length array type, both the original * array and the value to be assigned into it must be non-NULL, else * we punt and return the original array. */ if (astate->refattrlength > 0) /* fixed-length array? */ if (eisnull || *isNull) return PointerGetDatum(array_source); /* * For assignment to varlena arrays, we handle a NULL original array * by substituting an empty (zero-dimensional) array; insertion of the * new element will result in a singleton array value. It does not * matter whether the new element is NULL. */ if (*isNull) { array_source = construct_empty_array(arrayRef->refelemtype); *isNull = false; } if (lIndex == NULL) resultArray = array_set(array_source, i, upper.indx, sourceData, eisnull, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign); else resultArray = array_set_slice(array_source, i, upper.indx, lower.indx, (ArrayType*)DatumGetPointer(sourceData), eisnull, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign); return PointerGetDatum(resultArray); } /* for nested table, if get inner table's elem, need cover elem type */ if (list_length(astate->refupperindexpr) > i && i > 0 && plpgsql_estate) { if (plpgsql_estate->curr_nested_table_type != typOid) { plpgsql_estate->curr_nested_table_type = ARR_ELEMTYPE(array_source); get_typlenbyvalalign(plpgsql_estate->curr_nested_table_type, &astate->refelemlength, &astate->refelembyval, &astate->refelemalign); } } if (lIndex == NULL) { if (unlikely(i == 0)) { /* get nested table's inner table */ *isNull = eisnull; return (Datum)array_source; } else { return array_ref(array_source, i, upper.indx, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign, isNull); } } else { resultArray = array_get_slice(array_source, i, upper.indx, lower.indx, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign); return PointerGetDatum(resultArray); } } /* * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef * that might need the old element value passed down? * * (We could use this in ExecEvalFieldStore too, but in that case passing * the old value is so cheap there's no need.) */ static bool isAssignmentIndirectionExpr(ExprState* exprstate) { if (exprstate == NULL) return false; /* just paranoia */ if (IsA(exprstate, FieldStoreState)) { FieldStore* fstore = (FieldStore*)exprstate->expr; if (fstore->arg && IsA(fstore->arg, CaseTestExpr)) return true; } else if (IsA(exprstate, ArrayRefExprState)) { ArrayRef* arrayRef = (ArrayRef*)exprstate->expr; if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr)) return true; } return false; } /* ---------------------------------------------------------------- * ExecEvalAggref * * Returns a Datum whose value is the value of the precomputed * aggregate found in the given expression context. * ---------------------------------------------------------------- */ static Datum ExecEvalAggref(AggrefExprState* aggref, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { if (isDone != NULL) *isDone = ExprSingleResult; if (econtext->ecxt_aggvalues == NULL) /* safety check */ ereport(ERROR, (errcode(ERRCODE_INVALID_AGG), errmodule(MOD_EXECUTOR), errmsg("no aggregates in this expression context"))); *isNull = econtext->ecxt_aggnulls[aggref->aggno]; return econtext->ecxt_aggvalues[aggref->aggno]; } /* ---------------------------------------------------------------- * ExecEvalWindowFunc * * Returns a Datum whose value is the value of the precomputed * window function found in the given expression context. * ---------------------------------------------------------------- */ static Datum ExecEvalWindowFunc(WindowFuncExprState* wfunc, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { if (isDone != NULL) *isDone = ExprSingleResult; if (econtext->ecxt_aggvalues == NULL) /* safety check */ ereport(ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmodule(MOD_EXECUTOR), errmsg("no window functions in this expression context"))); *isNull = econtext->ecxt_aggnulls[wfunc->wfuncno]; return econtext->ecxt_aggvalues[wfunc->wfuncno]; } /* ---------------------------------------------------------------- * ExecEvalScalarVar * * Returns a Datum whose value is the value of a scalar (not whole-row) * range variable with respect to given expression context. * * Note: ExecEvalScalarVar is executed only the first time through in a given * plan; it changes the ExprState's function pointer to pass control directly * to ExecEvalScalarVarFast after making one-time checks. * ---------------------------------------------------------------- */ static Datum ExecEvalScalarVar(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Var* variable = (Var*)exprstate->expr; TupleTableSlot* slot = NULL; AttrNumber attnum; if (isDone != NULL) *isDone = ExprSingleResult; /* Get the input slot and attribute number we want */ switch (variable->varno) { case INNER_VAR: /* get the tuple from the inner node */ slot = econtext->ecxt_innertuple; break; case OUTER_VAR: /* get the tuple from the outer node */ slot = econtext->ecxt_outertuple; break; /* INDEX_VAR is handled by default case */ default: /* get the tuple from the relation being scanned */ slot = econtext->ecxt_scantuple; break; } Assert(slot != NULL); attnum = variable->varattno; /* This was checked by ExecInitExpr */ Assert(attnum != InvalidAttrNumber); /* * If it's a user attribute, check validity (bogus system attnums will be * caught inside table's getattr). What we have to check for here is the * possibility of an attribute having been changed in type since the plan * tree was created. Ideally the plan will get invalidated and not * re-used, but just in case, we keep these defenses. Fortunately it's * sufficient to check once on the first time through. * * Note: we allow a reference to a dropped attribute. table's getattr will * force a NULL result in such cases. * * Note: ideally we'd check typmod as well as typid, but that seems * impractical at the moment: in many cases the tupdesc will have been * generated by ExecTypeFromTL(), and that can't guarantee to generate an * accurate typmod in all cases, because some expression node types don't * carry typmod. */ if (attnum > 0) { TupleDesc slot_tupdesc = slot->tts_tupleDescriptor; Form_pg_attribute attr; if (attnum > slot_tupdesc->natts) /* should never happen */ ereport(ERROR, (errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR), errmsg("attribute number %d exceeds number of columns %d", attnum, slot_tupdesc->natts))); attr = slot_tupdesc->attrs[attnum - 1]; /* can't check type if dropped, since atttypid is probably 0 */ if (!attr->attisdropped) { if (variable->vartype != attr->atttypid) ereport(ERROR, (errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR), errmsg("attribute %d has wrong type", attnum), errdetail("Table has type %s, but query expects %s.", format_type_be(attr->atttypid), format_type_be(variable->vartype)))); } } /* Skip the checking on future executions of node */ exprstate->evalfunc = ExecEvalScalarVarFast; /* Fetch the value from the slot */ return tableam_tslot_getattr(slot, attnum, isNull); } /* ---------------------------------------------------------------- * ExecEvalScalarVarFast * * Returns a Datum for a scalar variable. * ---------------------------------------------------------------- */ static Datum ExecEvalScalarVarFast(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Var* variable = (Var*)exprstate->expr; TupleTableSlot* slot = NULL; AttrNumber attnum; if (isDone != NULL) *isDone = ExprSingleResult; /* Get the input slot and attribute number we want */ switch (variable->varno) { case INNER_VAR: /* get the tuple from the inner node */ slot = econtext->ecxt_innertuple; break; case OUTER_VAR: /* get the tuple from the outer node */ slot = econtext->ecxt_outertuple; break; /* INDEX_VAR is handled by default case */ default: /* get the tuple from the relation being scanned */ slot = econtext->ecxt_scantuple; break; } attnum = variable->varattno; Assert(slot != NULL); /* Fetch the value from the slot */ return tableam_tslot_getattr(slot, attnum, isNull); } /* ---------------------------------------------------------------- * ExecEvalWholeRowVar * * Returns a Datum whose value is the value of a whole-row range * variable with respect to given expression context. * * Note: ExecEvalWholeRowVar is executed only the first time through in a * given plan; it changes the ExprState's function pointer to pass control * directly to ExecEvalWholeRowFast or ExecEvalWholeRowSlow after making * one-time checks. * ---------------------------------------------------------------- */ static Datum ExecEvalWholeRowVar( WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Var* variable = (Var*)wrvstate->xprstate.expr; TupleTableSlot* slot = NULL; bool needslow = false; if (isDone != NULL) *isDone = ExprSingleResult; /* This was checked by ExecInitExpr */ Assert(variable->varattno == InvalidAttrNumber); /* Get the input slot we want */ switch (variable->varno) { case INNER_VAR: /* get the tuple from the inner node */ slot = econtext->ecxt_innertuple; break; case OUTER_VAR: /* get the tuple from the outer node */ slot = econtext->ecxt_outertuple; break; /* INDEX_VAR is handled by default case */ default: /* get the tuple from the relation being scanned */ slot = econtext->ecxt_scantuple; break; } /* * If the input tuple came from a subquery, it might contain "resjunk" * columns (such as GROUP BY or ORDER BY columns), which we don't want to * keep in the whole-row result. We can get rid of such columns by * passing the tuple through a JunkFilter --- but to make one, we have to * lay our hands on the subquery's targetlist. Fortunately, there are not * very many cases where this can happen, and we can identify all of them * by examining our parent PlanState. We assume this is not an issue in * standalone expressions that don't have parent plans. (Whole-row Vars * can occur in such expressions, but they will always be referencing * table rows.) */ if (wrvstate->parent) { PlanState* subplan = NULL; switch (nodeTag(wrvstate->parent)) { case T_SubqueryScanState: subplan = ((SubqueryScanState*)wrvstate->parent)->subplan; break; case T_CteScanState: subplan = ((CteScanState*)wrvstate->parent)->cteplanstate; break; default: break; } if (subplan != NULL) { bool junk_filter_needed = false; ListCell* tlist = NULL; /* Detect whether subplan tlist actually has any junk columns */ foreach (tlist, subplan->plan->targetlist) { TargetEntry* tle = (TargetEntry*)lfirst(tlist); if (tle->resjunk) { junk_filter_needed = true; break; } } /* If so, build the junkfilter in the query memory context */ if (junk_filter_needed) { MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); wrvstate->wrv_junkFilter = ExecInitJunkFilter(subplan->plan->targetlist, ExecGetResultType(subplan)->tdhasoid, ExecInitExtraTupleSlot(wrvstate->parent->state)); MemoryContextSwitchTo(oldcontext); } } } /* Apply the junkfilter if any */ if (wrvstate->wrv_junkFilter != NULL) slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); /* * If the Var identifies a named composite type, we must check that the * actual tuple type is compatible with it. */ if (variable->vartype != RECORDOID) { TupleDesc var_tupdesc; TupleDesc slot_tupdesc; int i; /* * We really only care about numbers of attributes and data types. * Also, we can ignore type mismatch on columns that are dropped in * the destination type, so long as (1) the physical storage matches * or (2) the actual column value is NULL. Case (1) is helpful in * some cases involving out-of-date cached plans, while case (2) is * expected behavior in situations such as an INSERT into a table with * dropped columns (the planner typically generates an INT4 NULL * regardless of the dropped column type). If we find a dropped * column and cannot verify that case (1) holds, we have to use * ExecEvalWholeRowSlow to check (2) for each row. */ var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); slot_tupdesc = slot->tts_tupleDescriptor; if (var_tupdesc->natts != slot_tupdesc->natts) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("table row type and query-specified row type do not match"), errdetail_plural("Table row contains %d attribute, but query expects %d.", "Table row contains %d attributes, but query expects %d.", slot_tupdesc->natts, slot_tupdesc->natts, var_tupdesc->natts))); for (i = 0; i < var_tupdesc->natts; i++) { Form_pg_attribute vattr = var_tupdesc->attrs[i]; Form_pg_attribute sattr = slot_tupdesc->attrs[i]; if (vattr->atttypid == sattr->atttypid) continue; /* no worries */ if (!vattr->attisdropped) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("table row type and query-specified row type do not match"), errdetail("Table has type %s at ordinal position %d, but query expects %s.", format_type_be(sattr->atttypid), i + 1, format_type_be(vattr->atttypid)))); if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign) needslow = true; /* need runtime check for null */ } ReleaseTupleDesc(var_tupdesc); } /* Skip the checking on future executions of node */ if (needslow) wrvstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowSlow; else wrvstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowFast; /* Fetch the value */ return (*wrvstate->xprstate.evalfunc)((ExprState*)wrvstate, econtext, isNull, isDone); } /* ---------------------------------------------------------------- * ExecEvalWholeRowFast * * Returns a Datum for a whole-row variable. * ---------------------------------------------------------------- */ static Datum ExecEvalWholeRowFast( WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Var* variable = (Var*)wrvstate->xprstate.expr; TupleTableSlot* slot = NULL; TupleDesc slot_tupdesc; HeapTuple tuple; TupleDesc tupleDesc; HeapTupleHeader dtuple; errno_t rc = EOK; if (isDone != NULL) *isDone = ExprSingleResult; *isNull = false; /* Get the input slot we want */ switch (variable->varno) { case INNER_VAR: /* get the tuple from the inner node */ slot = econtext->ecxt_innertuple; break; case OUTER_VAR: /* get the tuple from the outer node */ slot = econtext->ecxt_outertuple; break; /* INDEX_VAR is handled by default case */ default: /* get the tuple from the relation being scanned */ slot = econtext->ecxt_scantuple; break; } /* Apply the junkfilter if any */ if (wrvstate->wrv_junkFilter != NULL) slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); /* * If it's a RECORD Var, we'll use the slot's type ID info. It's likely * that the slot's type is also RECORD; if so, make sure it's been * "blessed", so that the Datum can be interpreted later. */ slot_tupdesc = slot->tts_tupleDescriptor; if (variable->vartype == RECORDOID) { if (slot_tupdesc->tdtypeid == RECORDOID && slot_tupdesc->tdtypmod < 0) assign_record_type_typmod(slot_tupdesc); } tuple = ExecFetchSlotTuple(slot); tupleDesc = slot->tts_tupleDescriptor; /* * We have to make a copy of the tuple so we can safely insert the Datum * overhead fields, which are not set in on-disk tuples. */ dtuple = (HeapTupleHeader)palloc(tuple->t_len); rc = memcpy_s((char*)dtuple, tuple->t_len, (char*)tuple->t_data, tuple->t_len); securec_check(rc, "\0", "\0"); HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len); /* * If the Var identifies a named composite type, label the tuple with that * type; otherwise use what is in the tupleDesc. */ if (variable->vartype != RECORDOID) { HeapTupleHeaderSetTypeId(dtuple, variable->vartype); HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); } else { HeapTupleHeaderSetTypeId(dtuple, tupleDesc->tdtypeid); HeapTupleHeaderSetTypMod(dtuple, tupleDesc->tdtypmod); } return PointerGetDatum(dtuple); } /* ---------------------------------------------------------------- * ExecEvalWholeRowSlow * * Returns a Datum for a whole-row variable, in the "slow" case where * we can't just copy the subplan's output. * ---------------------------------------------------------------- */ static Datum ExecEvalWholeRowSlow( WholeRowVarExprState* wrvstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Var* variable = (Var*)wrvstate->xprstate.expr; TupleTableSlot* slot = NULL; HeapTuple tuple; TupleDesc tupleDesc; TupleDesc var_tupdesc; HeapTupleHeader dtuple; int i; errno_t rc = EOK; if (isDone != NULL) *isDone = ExprSingleResult; *isNull = false; /* Get the input slot we want */ switch (variable->varno) { case INNER_VAR: /* get the tuple from the inner node */ slot = econtext->ecxt_innertuple; break; case OUTER_VAR: /* get the tuple from the outer node */ slot = econtext->ecxt_outertuple; break; /* INDEX_VAR is handled by default case */ default: /* get the tuple from the relation being scanned */ slot = econtext->ecxt_scantuple; break; } /* Apply the junkfilter if any */ if (wrvstate->wrv_junkFilter != NULL) slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); tuple = ExecFetchSlotTuple(slot); tupleDesc = slot->tts_tupleDescriptor; Assert(variable->vartype != RECORDOID); var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); /* Check to see if any dropped attributes are non-null */ for (i = 0; i < var_tupdesc->natts; i++) { Form_pg_attribute vattr = var_tupdesc->attrs[i]; Form_pg_attribute sattr = tupleDesc->attrs[i]; if (!vattr->attisdropped) continue; /* already checked non-dropped cols */ if (tableam_tops_tuple_attisnull(tuple, i + 1, tupleDesc)) continue; /* null is always okay */ if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("table row type and query-specified row type do not match"), errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", i + 1))); } /* * We have to make a copy of the tuple so we can safely insert the Datum * overhead fields, which are not set in on-disk tuples. */ dtuple = (HeapTupleHeader)palloc(tuple->t_len); rc = memcpy_s((char*)dtuple, tuple->t_len, (char*)tuple->t_data, tuple->t_len); securec_check(rc, "\0", "\0"); HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len); HeapTupleHeaderSetTypeId(dtuple, variable->vartype); HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); ReleaseTupleDesc(var_tupdesc); return PointerGetDatum(dtuple); } /* ---------------------------------------------------------------- * ExecEvalConst * * Returns the value of a constant. * * Note that for pass-by-ref datatypes, we return a pointer to the * actual constant node. This is one of the reasons why functions * must treat their input arguments as read-only. * ---------------------------------------------------------------- */ static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Const* con = (Const*)exprstate->expr; if (isDone != NULL) *isDone = ExprSingleResult; *isNull = con->constisnull; /* if a const cursor, copy cursor option data to econtext */ if (econtext->is_cursor && con->consttype == REFCURSOROID) { CopyCursorInfoData(&econtext->cursor_data, &con->cursor_data); econtext->dno = con->cursor_data.cur_dno; } return con->constvalue; } /* ---------------------------------------------------------------- * ExecEvalRownum: Returns the rownum * ---------------------------------------------------------------- */ static Datum ExecEvalRownum(RownumState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { if (isDone != NULL) *isDone = ExprSingleResult; *isNull = false; if (ROWNUM_TYPE_COMPAT) { return DirectFunctionCall1(int8_numeric, Int64GetDatum(exprstate->ps->ps_rownum + 1)); } else { return Int64GetDatum(exprstate->ps->ps_rownum + 1); } } /* ---------------------------------------------------------------- * ExecEvalParamExec * * Returns the value of a PARAM_EXEC parameter. * ---------------------------------------------------------------- */ static Datum ExecEvalParamExec(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Param* expression = (Param*)exprstate->expr; int thisParamId = expression->paramid; ParamExecData* prm = NULL; if (isDone != NULL) *isDone = ExprSingleResult; /* * PARAM_EXEC params (internal executor parameters) are stored in the * ecxt_param_exec_vals array, and can be accessed by array index. */ prm = &(econtext->ecxt_param_exec_vals[thisParamId]); if (prm->execPlan != NULL) { /* Parameter not evaluated yet, so go do it */ ExecSetParamPlan((SubPlanState*)prm->execPlan, econtext); /* ExecSetParamPlan should have processed this param... */ Assert(prm->execPlan == NULL); prm->isConst = true; prm->valueType = expression->paramtype; } *isNull = prm->isnull; prm->isChanged = true; return prm->value; } /* ---------------------------------------------------------------- * ExecEvalParamExtern * * Returns the value of a PARAM_EXTERN parameter. * ---------------------------------------------------------------- */ static Datum ExecEvalParamExtern(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Param* expression = (Param*)exprstate->expr; int thisParamId = expression->paramid; ParamListInfo paramInfo = econtext->ecxt_param_list_info; if (isDone != NULL) *isDone = ExprSingleResult; /* * PARAM_EXTERN parameters must be sought in ecxt_param_list_info. */ if (paramInfo && thisParamId > 0 && thisParamId <= paramInfo->numParams) { ParamExternData* prm = ¶mInfo->params[thisParamId - 1]; /* give hook a chance in case parameter is dynamic */ if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) (*paramInfo->paramFetch)(paramInfo, thisParamId); if (OidIsValid(prm->ptype)) { /* safety check in case hook did something unexpected */ if (prm->ptype != expression->paramtype) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)", thisParamId, format_type_be(prm->ptype), format_type_be(expression->paramtype)))); *isNull = prm->isnull; /* copy cursor option from param to econtext */ if (econtext->is_cursor && prm->ptype == REFCURSOROID) { CopyCursorInfoData(&econtext->cursor_data, &prm->cursor_data); econtext->dno = thisParamId - 1; } return prm->value; } } ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no value found for parameter %d", thisParamId))); return (Datum)0; /* keep compiler quiet */ } void initExecTableOfIndexInfo(ExecTableOfIndexInfo* execTableOfIndexInfo, ExprContext* econtext) { execTableOfIndexInfo->econtext = econtext; execTableOfIndexInfo->tableOfIndex = NULL; execTableOfIndexInfo->tableOfIndexType = InvalidOid; execTableOfIndexInfo->isnestedtable = false; execTableOfIndexInfo->tableOfLayers = 0; execTableOfIndexInfo->paramid = -1; execTableOfIndexInfo->paramtype = InvalidOid; } /* this function is only used for getting table of index inout param */ static bool get_tableofindex_param(Node* node, ExecTableOfIndexInfo* execTableOfIndexInfo) { if (node == NULL) return false; if (IsA(node, Param)) { execTableOfIndexInfo->paramid = ((Param*)node)->paramid; execTableOfIndexInfo->paramtype = ((Param*)node)->paramtype; return true; } return false; } static bool IsTableOfFunc(Oid funcOid) { const Oid array_function_start_oid = 7881; const Oid array_function_end_oid = 7892; const Oid array_indexby_delete_oid = 7896; return (funcOid >= array_function_start_oid && funcOid <= array_function_end_oid) || funcOid == array_indexby_delete_oid; } /* ---------------------------------------------------------------- * ExecEvalParamExternTableOfIndex * * Returns the value of a PARAM_EXTERN table of index and type parameter . * ---------------------------------------------------------------- */ void ExecEvalParamExternTableOfIndex(Node* node, ExecTableOfIndexInfo* execTableOfIndexInfo) { if (get_tableofindex_param(node, execTableOfIndexInfo)) { ExecEvalParamExternTableOfIndexById(execTableOfIndexInfo); } } bool ExecEvalParamExternTableOfIndexById(ExecTableOfIndexInfo* execTableOfIndexInfo) { if (execTableOfIndexInfo->paramid == -1) { return false; } int thisParamId = execTableOfIndexInfo->paramid; ParamListInfo paramInfo = execTableOfIndexInfo->econtext->ecxt_param_list_info; /* * PARAM_EXTERN parameters must be sought in ecxt_param_list_info. */ if (paramInfo && thisParamId > 0 && thisParamId <= paramInfo->numParams) { ParamExternData* prm = ¶mInfo->params[thisParamId - 1]; /* give hook a chance in case parameter is dynamic */ if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) (*paramInfo->paramFetch)(paramInfo, thisParamId); if (OidIsValid(prm->ptype) && prm->tabInfo != NULL && prm->tabInfo->tableOfIndex != NULL && OidIsValid(prm->tabInfo->tableOfIndexType)) { /* safety check in case hook did something unexpected */ if (prm->ptype != execTableOfIndexInfo->paramtype) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)", thisParamId, format_type_be(prm->ptype), format_type_be(execTableOfIndexInfo->paramtype)))); execTableOfIndexInfo->tableOfIndexType = prm->tabInfo->tableOfIndexType; execTableOfIndexInfo->isnestedtable = prm->tabInfo->isnestedtable; execTableOfIndexInfo->tableOfLayers = prm->tabInfo->tableOfLayers; execTableOfIndexInfo->tableOfIndex = prm->tabInfo->tableOfIndex; return true; } } return false; } /* ---------------------------------------------------------------- * ExecEvalOper / ExecEvalFunc support routines * ---------------------------------------------------------------- */ /* * GetAttributeByName * GetAttributeByNum * * These functions return the value of the requested attribute * out of the given tuple Datum. * C functions which take a tuple as an argument are expected * to use these. Ex: overpaid(EMP) might call GetAttributeByNum(). * Note: these are actually rather slow because they do a typcache * lookup on each call. */ Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno, bool* isNull) { Datum result; Oid tupType; int32 tupTypmod; TupleDesc tupDesc; HeapTupleData tmptup; if (!AttributeNumberIsValid(attrno)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid attribute number %d", attrno))); if (isNull == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("a NULL isNull pointer was passed when get attribute by number."))); if (tuple == NULL) { /* Kinda bogus but compatible with old behavior... */ *isNull = true; return (Datum)0; } tupType = HeapTupleHeaderGetTypeId(tuple); tupTypmod = HeapTupleHeaderGetTypMod(tuple); tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); /* * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all * the fields in the struct just in case user tries to inspect system * columns. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_bucketId = InvalidBktId; HeapTupleSetZeroBase(&tmptup); #ifdef PGXC tmptup.t_xc_node_id = 0; #endif tmptup.t_data = tuple; if (attrno == -3 || attrno == -5) { elog(WARNING, "system attribute xmin or xmax, the results about this attribute are untrustworthy."); } result = tableam_tops_tuple_getattr(&tmptup, attrno, tupDesc, isNull); ReleaseTupleDesc(tupDesc); return result; } Datum GetAttributeByName(HeapTupleHeader tuple, const char* attname, bool* isNull) { AttrNumber attrno; Datum result; Oid tupType; int32 tupTypmod; TupleDesc tupDesc; HeapTupleData tmptup; int i; if (attname == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid null attribute name"))); if (isNull == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("a NULL isNull pointer was passed when get attribute by name."))); if (tuple == NULL) { /* Kinda bogus but compatible with old behavior... */ *isNull = true; return (Datum)0; } tupType = HeapTupleHeaderGetTypeId(tuple); tupTypmod = HeapTupleHeaderGetTypMod(tuple); tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); attrno = InvalidAttrNumber; for (i = 0; i < tupDesc->natts; i++) { if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0) { attrno = tupDesc->attrs[i]->attnum; break; } } if (attrno == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR), errmsg("attribute \"%s\" does not exist", attname))); /* * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all * the fields in the struct just in case user tries to inspect system * columns. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_bucketId = InvalidBktId; HeapTupleSetZeroBase(&tmptup); #ifdef PGXC tmptup.t_xc_node_id = 0; #endif tmptup.t_data = tuple; if (attrno == -3 || attrno == -5) { elog(WARNING, "system attribute \"%s\", the results about this attribute are untrustworthy.", attname); } result = tableam_tops_tuple_getattr(&tmptup, attrno, tupDesc, isNull); ReleaseTupleDesc(tupDesc); return result; } /* * Find the real function return type based on the actual func args' types. * @inPara arg_num: the number of func's args. * @inPara actual_arg_types: the type array of actual func args'. * @inPara fcache: the FuncExprState of this functin. * @return Oid: the real func return type. */ static Oid getRealFuncRetype(int arg_num, Oid* actual_arg_types, FuncExprState* fcache) { Oid funcid = fcache->func.fn_oid; Oid rettype = fcache->func.fn_rettype; /* Find the declared arg types in PROCOID by funcid. */ HeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)) ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmodule(MOD_EXECUTOR), errmsg("cache lookup failed for function %u", funcid))); oidvector* proargs = ProcedureGetArgTypes(proctup); Oid* declared_arg_types = proargs->values; /* Find the real return type based on the declared arg types and actual arg types.*/ rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, arg_num, rettype, false); ReleaseSysCache(proctup); return rettype; } /* * Check whether the function is a set function supported by the vector engine. */ static bool isVectorEngineSupportSetFunc(Oid funcid) { switch (funcid) { case OID_REGEXP_SPLIT_TO_TABLE: // regexp_split_to_table case OID_REGEXP_SPLIT_TO_TABLE_NO_FLAG: // regexp_split_to_table case OID_ARRAY_UNNEST: // unnest return true; break; default: return false; break; } } /* * init_fcache - initialize a FuncExprState node during first use */ template static void init_fcache( Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool needDescForSets) { AclResult aclresult; MemoryContext oldcontext; Form_pg_proc procStruct; HeapTuple procTup; Oid definer = GetUserId(); if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) { /* Get the function's pg_proc entry */ procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(foid)); if (!HeapTupleIsValid(procTup)) { ereport(ERROR, (errmodule(MOD_EXECUTOR), errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", foid))); } procStruct = (Form_pg_proc)GETSTRUCT(procTup); if (procStruct->prosecdef) definer = procStruct->proowner; ReleaseSysCache(procTup); } /* Check permission to call function */ aclresult = pg_proc_aclcheck(foid, definer, ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); /* * Safety check on nargs. Under normal circumstances this should never * fail, as parser should check sooner. But possibly it might fail if * server has been compiled with FUNC_MAX_ARGS smaller than some functions * declared in pg_proc? */ if (list_length(fcache->args) > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("cannot pass more than %d argument to a function", "cannot pass more than %d arguments to a function", FUNC_MAX_ARGS, FUNC_MAX_ARGS))); /* Set up the primary fmgr lookup information */ fmgr_info_cxt(foid, &(fcache->func), fcacheCxt); fmgr_info_set_expr((Node*)fcache->xprstate.expr, &(fcache->func)); /* palloc args in fcache's context */ oldcontext = MemoryContextSwitchTo(fcacheCxt); /* Initialize the function call parameter struct as well */ if (vectorized) InitVecFunctionCallInfoData( &fcache->fcinfo_data, &(fcache->func), list_length(fcache->args), input_collation, NULL, NULL); else InitFunctionCallInfoData( fcache->fcinfo_data, &(fcache->func), list_length(fcache->args), input_collation, NULL, NULL); if (vectorized) { int nargs = list_length(fcache->args); ListCell* cell = NULL; GenericFunRuntime* genericRuntime = NULL; errno_t rc; if (fcache->fcinfo_data.flinfo->genericRuntime == NULL) { genericRuntime = (GenericFunRuntime*)palloc0(sizeof(GenericFunRuntime)); InitGenericFunRuntimeInfo(*genericRuntime, nargs); fcache->fcinfo_data.flinfo->genericRuntime = genericRuntime; } else { genericRuntime = fcache->fcinfo_data.flinfo->genericRuntime; /* if internalFinfo is not null, release the internalFinfo's memory and set the pointer to null */ if (genericRuntime->internalFinfo != NULL) { FreeFunctionCallInfoData(*(genericRuntime->internalFinfo)); genericRuntime->internalFinfo = NULL; } /* reset the memory for reuse */ rc = memset_s(genericRuntime->args, sizeof(GenericFunRuntimeArg) * genericRuntime->compacity, 0, sizeof(GenericFunRuntimeArg) * genericRuntime->compacity); securec_check(rc, "\0", "\0"); rc = memset_s(genericRuntime->inputargs, sizeof(Datum) * genericRuntime->compacity, 0, sizeof(Datum) * genericRuntime->compacity); securec_check(rc, "\0", "\0"); rc = memset_s(genericRuntime->nulls, sizeof(bool) * genericRuntime->compacity, 0, sizeof(bool) * genericRuntime->compacity); securec_check(rc, "\0", "\0"); /* we have to adjust the GenericFunRuntimeArg when * a) nargs is larger than genericRuntime->compacity, which means the allocated memory is not enough to hold * all the argumnets here, we should enlarge the memory. * b) nargs is less than VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS while the allocated memory is much more * than that. As VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS is already enough in most senerios, we should * reduce the memory. * * NOTE: To avoid memory wasting and memory fragments, we free and initilized a new GenericFunRuntimeArg. */ if (unlikely(nargs > genericRuntime->compacity) || (unlikely(genericRuntime->compacity > VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS) && nargs <= VECTOR_GENERIC_FUNCTION_PREALLOCED_ARGS)) { FreeGenericFunRuntimeInfo(*genericRuntime); InitGenericFunRuntimeInfo(*genericRuntime, nargs); } } ScalarVector* pVector = New(CurrentMemoryContext) ScalarVector[nargs]; int i = 0; if (fcache->args && fcache->args->length > 0) { Oid* actual_arg_types = (Oid*)palloc0(fcache->args->length * sizeof(Oid)); foreach (cell, fcache->args) { ExprState* argstate = (ExprState*)lfirst(cell); Oid funcrettype; TupleDesc tupdesc; ScalarDesc desc; (void)get_expr_result_type((Node*)argstate->expr, &funcrettype, &tupdesc); desc.typeId = funcrettype; desc.encoded = COL_IS_ENCODE(funcrettype); fcache->fcinfo_data.flinfo->genericRuntime->args[i].argType = funcrettype; pVector[i].init(CurrentMemoryContext, desc); /* Record the real arg types from sub functions. */ actual_arg_types[i] = funcrettype; i++; } /* Find the real return type for func with return type like ANYELEMENT. */ fcache->fcinfo_data.flinfo->fn_rettype = getRealFuncRetype(i, actual_arg_types, fcache); pfree_ext(actual_arg_types); } fcache->fcinfo_data.argVector = pVector; } (void)MemoryContextSwitchTo(oldcontext); if (vectorized) { if (fcache->func.fn_retset == true) { if (!isVectorEngineSupportSetFunc(fcache->func.fn_oid)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_EXECUTOR), errmsg("set-return function not supported in vector eninge"))); } } fcache->funcResultDesc = NULL; } else { /* If function returns set, prepare expected tuple descriptor */ if (fcache->func.fn_retset && needDescForSets) { TypeFuncClass functypclass; Oid funcrettype; TupleDesc tupdesc; MemoryContext oldmemcontext; functypclass = get_expr_result_type(fcache->func.fn_expr, &funcrettype, &tupdesc); /* Must save tupdesc in fcache's context */ oldmemcontext = MemoryContextSwitchTo(fcacheCxt); if (functypclass == TYPEFUNC_COMPOSITE) { /* Composite data type, e.g. a table's row type */ Assert(tupdesc); /* Must copy it out of typcache for safety */ fcache->funcResultDesc = CreateTupleDescCopy(tupdesc); fcache->funcReturnsTuple = true; } else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ tupdesc = CreateTemplateTupleDesc(1, false, TAM_HEAP); TupleDescInitEntry(tupdesc, (AttrNumber)1, NULL, funcrettype, -1, 0); fcache->funcResultDesc = tupdesc; fcache->funcReturnsTuple = false; } else if (functypclass == TYPEFUNC_RECORD) { /* This will work if function doesn't need an expectedDesc */ fcache->funcResultDesc = NULL; fcache->funcReturnsTuple = true; } else { /* Else, we will fail if function needs an expectedDesc */ fcache->funcResultDesc = NULL; } MemoryContextSwitchTo(oldmemcontext); } else fcache->funcResultDesc = NULL; } /* Initialize additional state */ fcache->funcResultStore = NULL; fcache->funcResultSlot = NULL; fcache->setArgsValid = false; fcache->shutdown_reg = false; } void initVectorFcache(Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt) { init_fcache(foid, input_collation, fcache, fcacheCxt, false); } /* * callback function in case a FuncExpr returning a set needs to be shut down * before it has been run to completion */ static void ShutdownFuncExpr(Datum arg) { FuncExprState* fcache = (FuncExprState*)DatumGetPointer(arg); /* If we have a slot, make sure it's let go of any tuplestore pointer */ if (fcache->funcResultSlot) (void)ExecClearTuple(fcache->funcResultSlot); /* Release any open tuplestore */ if (fcache->funcResultStore) tuplestore_end(fcache->funcResultStore); fcache->funcResultStore = NULL; /* Clear any active set-argument state */ fcache->setArgsValid = false; /* execUtils will deregister the callback... */ fcache->shutdown_reg = false; } /* * get_cached_rowtype: utility function to lookup a rowtype tupdesc * * type_id, typmod: identity of the rowtype * cache_field: where to cache the TupleDesc pointer in expression state node * (field must be initialized to NULL) * econtext: expression context we are executing in * * NOTE: because the shutdown callback will be called during plan rescan, * must be prepared to re-do this during any node execution; cannot call * just once during expression initialization */ static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod, TupleDesc* cache_field, ExprContext* econtext) { TupleDesc tupDesc = *cache_field; /* Do lookup if no cached value or if requested type changed */ if (tupDesc == NULL || type_id != tupDesc->tdtypeid || typmod != tupDesc->tdtypmod) { tupDesc = lookup_rowtype_tupdesc(type_id, typmod); if (*cache_field) { /* Release old tupdesc; but callback is already registered */ ReleaseTupleDesc(*cache_field); } else { /* Need to register shutdown callback to release tupdesc */ RegisterExprContextCallback(econtext, ShutdownTupleDescRef, PointerGetDatum(cache_field)); } *cache_field = tupDesc; } return tupDesc; } /* * Callback function to release a tupdesc refcount at expression tree shutdown */ static void ShutdownTupleDescRef(Datum arg) { TupleDesc* cache_field = (TupleDesc*)DatumGetPointer(arg); if (*cache_field) ReleaseTupleDesc(*cache_field); *cache_field = NULL; } /* * Evaluate arguments for a function. */ template static ExprDoneCond ExecEvalFuncArgs( FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno) { ExprDoneCond argIsDone; int i; ListCell* arg = NULL; argIsDone = ExprSingleResult; /* default assumption */ i = 0; econtext->is_cursor = false; u_sess->plsql_cxt.func_tableof_index = NIL; bool is_have_huge_clob = false; foreach (arg, argList) { ExprState* argstate = (ExprState*)lfirst(arg); ExprDoneCond thisArgIsDone; if (has_refcursor && argstate->resultType == REFCURSOROID) econtext->is_cursor = true; fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], &thisArgIsDone); ExecTableOfIndexInfo execTableOfIndexInfo; initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); ExecEvalParamExternTableOfIndex((Node*)argstate->expr, &execTableOfIndexInfo); if (execTableOfIndexInfo.tableOfIndex != NULL) { MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); PLpgSQL_func_tableof_index* func_tableof = (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index)); func_tableof->varno = i; func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex); u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof); MemoryContextSwitchTo(oldCxt); } if (has_refcursor && econtext->is_cursor && plpgsql_var_dno != NULL) { plpgsql_var_dno[i] = econtext->dno; CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data); } fcinfo->argTypes[i] = argstate->resultType; econtext->is_cursor = false; if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) { is_have_huge_clob = true; } if (thisArgIsDone != ExprSingleResult) { /* * We allow only one argument to have a set value; we'd need much * more complexity to keep track of multiple set arguments (cf. * ExecTargetList) and it doesn't seem worth it. */ if (argIsDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("functions and operators can take at most one set argument"))); argIsDone = thisArgIsDone; } i++; } check_huge_clob_paramter(fcinfo, is_have_huge_clob); Assert(i == fcinfo->nargs); return argIsDone; } /* * ExecPrepareTuplestoreResult * * Subroutine for ExecMakeFunctionResult: prepare to extract rows from a * tuplestore function result. We must set up a funcResultSlot (unless * already done in a previous call cycle) and verify that the function * returned the expected tuple descriptor. */ static void ExecPrepareTuplestoreResult( FuncExprState* fcache, ExprContext* econtext, Tuplestorestate* resultStore, TupleDesc resultDesc) { fcache->funcResultStore = resultStore; if (fcache->funcResultSlot == NULL) { /* Create a slot so we can read data out of the tuplestore */ TupleDesc slotDesc; MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt); /* * If we were not able to determine the result rowtype from context, * and the function didn't return a tupdesc, we have to fail. */ if (fcache->funcResultDesc) slotDesc = fcache->funcResultDesc; else if (resultDesc) { /* don't assume resultDesc is long-lived */ slotDesc = CreateTupleDescCopy(resultDesc); } else { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning setof record called in context that cannot accept type record"))); slotDesc = NULL; /* keep compiler quiet */ } fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc); MemoryContextSwitchTo(oldcontext); } /* * 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 (resultDesc) { if (fcache->funcResultDesc) tupledesc_match(fcache->funcResultDesc, resultDesc); /* * 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 (resultDesc->tdrefcount == -1) FreeTupleDesc(resultDesc); } /* Register cleanup callback if we didn't already */ if (!fcache->shutdown_reg) { RegisterExprContextCallback(econtext, ShutdownFuncExpr, PointerGetDatum(fcache)); fcache->shutdown_reg = true; } } /* * Check that function result tuple type (src_tupdesc) matches or can * be considered to match what the query expects (dst_tupdesc). If * they don't match, ereport. * * We really only care about number of attributes and data type. * Also, we can ignore type mismatch on columns that are dropped in the * destination type, so long as the physical storage matches. This is * helpful in some cases involving out-of-date cached plans. */ static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc) { int i; if (dst_tupdesc->natts != src_tupdesc->natts) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return row and query-specified return row do not match"), errdetail_plural("Returned row contains %d attribute, but query expects %d.", "Returned row contains %d attributes, but query expects %d.", src_tupdesc->natts, src_tupdesc->natts, dst_tupdesc->natts))); for (i = 0; i < dst_tupdesc->natts; i++) { Form_pg_attribute dattr = dst_tupdesc->attrs[i]; Form_pg_attribute sattr = src_tupdesc->attrs[i]; if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid)) continue; /* no worries */ if (!dattr->attisdropped) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return row and query-specified return row do not match"), errdetail("Returned type %s at ordinal position %d, but query expects %s.", format_type_be(sattr->atttypid), i + 1, format_type_be(dattr->atttypid)))); if (dattr->attlen != sattr->attlen || dattr->attalign != sattr->attalign) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return row and query-specified return row do not match"), errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", i + 1))); } } static void set_result_for_plpgsql_language_function_with_outparam(FuncExprState *fcache, Datum *result, bool *isNull) { if (!IsA(fcache->xprstate.expr, FuncExpr)) { return; } FuncExpr *func = (FuncExpr *)fcache->xprstate.expr; if (!is_function_with_plpgsql_language_and_outparam(func->funcid)) { return; } HeapTupleHeader td = DatumGetHeapTupleHeader(*result); TupleDesc tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); HeapTupleData tup; tup.t_len = HeapTupleHeaderGetDatumLength(td); tup.t_data = td; Datum *values = (Datum *)palloc(sizeof(Datum) * tupdesc->natts); bool *nulls = (bool *)palloc(sizeof(bool) * tupdesc->natts); heap_deform_tuple(&tup, tupdesc, values, nulls); *result = values[0]; *isNull = nulls[0]; pfree(values); pfree(nulls); } /* * ExecMakeFunctionResult * * Evaluate the arguments to a function and then the function itself. * init_fcache is presumed already run on the FuncExprState. * * This function handles the most general case, wherein the function or * one of its arguments can return a set. * * Note: This function use template parameter can compile different function, * reduce the assembly instructions so as to improve performance. * * Template parameter: * @bool has_cursor_return - need store out-args cursor info. * @bool has_refcursor - need store in-args cursor info. * @bool isSetReturnFunc - indicate function returns a set. */ template static Datum ExecMakeFunctionResult(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { List* arguments = NIL; Datum result; FunctionCallInfo fcinfo; PgStat_FunctionCallUsage fcusage; ReturnSetInfo rsinfo; /* for functions returning sets */ ExprDoneCond argDone; bool hasSetArg = false; int i; int* var_dno = NULL; econtext->plpgsql_estate = plpgsql_estate; plpgsql_estate = NULL; restart: /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); /* * If a previous call of the function returned a set result in the form of * a tuplestore, continue reading rows from the tuplestore until it's * empty. */ if (fcache->funcResultStore) { /* it was provided before ... */ if (unlikely(isDone == NULL)) { ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("set-valued function called in context that cannot accept a set"))); } if (tuplestore_gettupleslot(fcache->funcResultStore, true, false, fcache->funcResultSlot)) { *isDone = ExprMultipleResult; if (fcache->funcReturnsTuple) { /* We must return the whole tuple as a Datum. */ *isNull = false; return ExecFetchSlotTupleDatum(fcache->funcResultSlot); } else { /* Extract the first column and return it as a scalar. */ Assert(fcache->funcResultSlot != NULL); /* Get the Table Accessor Method*/ return tableam_tslot_getattr(fcache->funcResultSlot, 1, isNull); } } /* Exhausted the tuplestore, so clean up */ tuplestore_end(fcache->funcResultStore); fcache->funcResultStore = NULL; /* We are done unless there was a set-valued argument */ if (!fcache->setHasSetArg) { *isDone = ExprEndResult; *isNull = true; return (Datum)0; } /* If there was, continue evaluating the argument values */ Assert(!fcache->setArgsValid); } /* * arguments is a list of expressions to evaluate before passing to the * function manager. We skip the evaluation if it was already done in the * previous call (ie, we are continuing the evaluation of a set-valued * function). Otherwise, collect the current argument values into fcinfo. */ fcinfo = &fcache->fcinfo_data; if (has_cursor_return) { /* init returnCursor to store out-args cursor info on ExprContext*/ fcinfo->refcursor_data.returnCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); } else { fcinfo->refcursor_data.returnCursor = NULL; } if (has_refcursor) { /* init argCursor to store in-args cursor info on ExprContext*/ fcinfo->refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->nargs); var_dno = (int*)palloc0(sizeof(int) * fcinfo->nargs); for (i = 0; i < fcinfo->nargs; i++) { var_dno[i] = -1; } } arguments = fcache->args; if (!fcache->setArgsValid) { if (has_refcursor) argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext, var_dno); else argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext); if (argDone == ExprEndResult) { /* input is an empty set, so return an empty set. */ *isNull = true; if (isDone != NULL) *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 { /* Re-use callinfo from previous evaluation */ hasSetArg = fcache->setHasSetArg; /* Reset flag (we may set it again below) */ fcache->setArgsValid = false; } /* * Now call the function, passing 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"))); /* * Prepare a resultinfo node for communication. If the function * doesn't itself return set, we don't pass the resultinfo to the * function, but we need to fill it in anyway for internal use. */ if (fcache->func.fn_retset) fcinfo->resultinfo = (Node*)&rsinfo; rsinfo.type = T_ReturnSetInfo; rsinfo.econtext = econtext; rsinfo.expectedDesc = fcache->funcResultDesc; rsinfo.allowedModes = (int)(SFRM_ValuePerCall | SFRM_Materialize); /* note we do not set SFRM_Materialize_Random or _Preferred */ rsinfo.returnMode = SFRM_ValuePerCall; /* isDone is filled below */ rsinfo.setResult = NULL; rsinfo.setDesc = NULL; /* * 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) { pgstat_init_function_usage(fcinfo, &fcusage); fcinfo->isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(fcinfo); *isNull = fcinfo->isnull; *isDone = rsinfo.isDone; pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); } else if (isSetReturnFunc) { /* * For a strict SRF, result for NULL is an empty set * If SRF is strict and has any NULL arguments, this SRF * need return empty set, so such rows were omitted entirely * from the result set. */ result = (Datum)0; *isNull = true; *isDone = ExprEndResult; } else { /* * For a strict non-SRF, result for NULL is a NULL. * This branch in order to deal strict nested functions * like "select plain_function(set_returning_function(...))". * If some of the SRF outputs are NULL, and the plain function * is strict, we expect to get NULL results for such rows */ result = (Datum)0; *isNull = true; *isDone = ExprSingleResult; } if (has_refcursor && econtext->plpgsql_estate != NULL) { PLpgSQL_execstate* estate = econtext->plpgsql_estate; /* copy in-args cursor option info */ for (i = 0; i < fcinfo->nargs; i++) { if (var_dno[i] >= 0) { int dno = var_dno[i]; Cursor_Data* cursor_data = &fcinfo->refcursor_data.argCursor[i]; #ifdef USE_ASSERT_CHECKING PLpgSQL_datum* datum = estate->datums[dno]; #endif Assert(datum->dtype == PLPGSQL_DTYPE_VAR); Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); ExecCopyDataToDatum(estate->datums, dno, cursor_data); } } if (fcinfo->refcursor_data.return_number > 0) { /* copy function returns cursor option info. * for simple expr in exec_eval_expr, we can not get the result type, * so cursor_return_data mallocs here. */ if (estate->cursor_return_data == NULL && estate->tuple_store_cxt != NULL) { MemoryContext oldcontext = MemoryContextSwitchTo(estate->tuple_store_cxt); estate->cursor_return_data = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); estate->cursor_return_numbers = fcinfo->refcursor_data.return_number; (void)MemoryContextSwitchTo(oldcontext); } if (estate->cursor_return_data != NULL) { for (i = 0; i < fcinfo->refcursor_data.return_number; i++) { int rc = memcpy_s(&estate->cursor_return_data[i], sizeof(Cursor_Data), &fcinfo->refcursor_data.returnCursor[i], sizeof(Cursor_Data)); securec_check(rc, "\0", "\0"); } } } } /* Which protocol does function want to use? */ if (rsinfo.returnMode == SFRM_ValuePerCall) { 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) { 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 if (rsinfo.returnMode == SFRM_Materialize) { /* check we're on the same page as the function author */ if (rsinfo.isDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("table-function protocol for materialize mode was not followed"))); if (rsinfo.setResult != NULL) { /* prepare to return values from the tuplestore */ ExecPrepareTuplestoreResult(fcache, econtext, rsinfo.setResult, rsinfo.setDesc); /* remember whether we had set arguments */ fcache->setHasSetArg = hasSetArg; /* loop back to top to start returning from tuplestore */ goto restart; } /* if setResult was left null, treat it as empty set */ *isDone = ExprEndResult; *isNull = true; result = (Datum)0; } else { ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("unrecognized table-function returnMode: %d", (int)rsinfo.returnMode))); } /* 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 */ if (has_refcursor) { argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext, var_dno); } else { 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. * * In common cases, this code path is unreachable because we'd have * selected ExecMakeFunctionResultNoSets instead. However, it's * possible to get here if an argument sometimes produces set results * and sometimes scalar results. For example, a CASE expression might * call a set-returning function in only some of its arms. */ if (isDone != NULL) *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; } } } pgstat_init_function_usage(fcinfo, &fcusage); fcinfo->isnull = false; result = FunctionCallInvoke(fcinfo); *isNull = fcinfo->isnull; pgstat_end_function_usage(&fcusage, true); } if (has_refcursor) { pfree_ext(fcinfo->refcursor_data.argCursor); pfree_ext(var_dno); } set_result_for_plpgsql_language_function_with_outparam(fcache, &result, isNull); return result; } /* * ExecMakeFunctionResultNoSets * * Simplified version of ExecMakeFunctionResult that can only handle * non-set cases. Hand-tuned for speed. * * Note: This function use template parameter can compile different function, * reduce the assembly instructions so as to improve performance. * * Template parameter: * @bool has_cursor_return - need store out-args cursor info. * @bool has_refcursor - need store in-args cursor info. */ template static Datum ExecMakeFunctionResultNoSets( FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { ListCell* arg = NULL; Datum result; FunctionCallInfo fcinfo; PgStat_FunctionCallUsage fcusage; int i; int* var_dno = NULL; FunctionScanState *node = NULL; FuncExpr *fexpr = NULL; bool savedIsSTP = u_sess->SPI_cxt.is_stp; bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set; bool proIsProcedure = false; bool supportTranaction = false; bool is_have_huge_clob = false; #ifdef ENABLE_MULTIPLE_NODES if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum >= STP_SUPPORT_COMMIT_ROLLBACK)) { supportTranaction = true; } #else supportTranaction = true; #endif bool needResetErrMsg = (u_sess->SPI_cxt.forbidden_commit_rollback_err_msg[0] == '\0'); /* Only allow commit at CN, therefore only need to set atomic and * relevant check at CN level. */ if (supportTranaction && IsA(fcache->xprstate.expr, FuncExpr)) { fexpr = (FuncExpr *) fcache->xprstate.expr; node = makeNode(FunctionScanState); if (!u_sess->SPI_cxt.is_allow_commit_rollback) { node->atomic = true; } else if (IsAfterTriggerBegin()) { node->atomic = true; stp_set_commit_rollback_err_msg(STP_XACT_AFTER_TRIGGER_BEGIN); } /* * If proconfig is set we can't allow transaction commands because of the * way the GUC stacking works: The transaction boundary would have to pop * the proconfig setting off the stack. That restriction could be lifted * by redesigning the GUC nesting mechanism a bit. */ if (!fcache->prokind) { bool isNullSTP = false; HeapTuple tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid)); if (!HeapTupleIsValid(tp)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("cache lookup failed for function %u", fexpr->funcid))); } if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL) || u_sess->SPI_cxt.is_proconfig_set) { u_sess->SPI_cxt.is_proconfig_set = true; node->atomic = true; stp_set_commit_rollback_err_msg(STP_XACT_GUC_IN_OPT_CLAUSE); } /* immutable or stable function should not support commit/rollback */ bool isNullVolatile = false; Datum provolatile = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isNullVolatile); if (!isNullVolatile && CharGetDatum(provolatile) != PROVOLATILE_VOLATILE) { node->atomic = true; stp_set_commit_rollback_err_msg(STP_XACT_IMMUTABLE); } Datum datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prokind, &isNullSTP); proIsProcedure = PROC_IS_PRO(CharGetDatum(datum)); if (proIsProcedure) { fcache->prokind = 'p'; } else { fcache->prokind = 'f'; } /* if proIsProcedure is ture means it was a stored procedure */ u_sess->SPI_cxt.is_stp = savedIsSTP; ReleaseSysCache(tp); } else { proIsProcedure = PROC_IS_PRO(fcache->prokind); u_sess->SPI_cxt.is_stp = savedIsSTP; } } /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); if (isDone != NULL) *isDone = ExprSingleResult; econtext->plpgsql_estate = plpgsql_estate; plpgsql_estate = NULL; /* inlined, simplified version of ExecEvalFuncArgs */ fcinfo = &fcache->fcinfo_data; /* init the number of arguments to a function*/ InitFunctionCallInfoArgs(*fcinfo, list_length(fcache->args), 1); /* Only allow commit at CN, therefore need to set callcontext in CN only */ if (supportTranaction) { fcinfo->context = (Node *)node; } /* * Incause of connet_by_root() and sys_connect_by_path() we need get the * current scan tuple slot so attach the econtext here * * NOTE: Have to revisit!! so I don't have better solution to handle the case * where scantuple is available in built in funct */ if (fcinfo->flinfo->fn_oid == CONNECT_BY_ROOT_FUNCOID || fcinfo->flinfo->fn_oid == SYS_CONNECT_BY_PATH_FUNCOID) { fcinfo->swinfo.sw_econtext = (Node *)econtext; fcinfo->swinfo.sw_exprstate = (Node *)linitial(fcache->args); } if (has_cursor_return) { /* init returnCursor to store out-args cursor info on ExprContext*/ fcinfo->refcursor_data.returnCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->refcursor_data.return_number); } else { fcinfo->refcursor_data.returnCursor = NULL; } if (has_refcursor) { /* init argCursor to store in-args cursor info on ExprContext */ fcinfo->refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo->nargs); var_dno = (int*)palloc0(sizeof(int) * fcinfo->nargs); for (i = 0; i < fcinfo->nargs; i++) { var_dno[i] = -1; } } i = 0; econtext->is_cursor = false; u_sess->plsql_cxt.func_tableof_index = NIL; foreach (arg, fcache->args) { ExprState* argstate = (ExprState*)lfirst(arg); fcinfo->argTypes[i] = argstate->resultType; if (has_refcursor && fcinfo->argTypes[i] == REFCURSOROID) econtext->is_cursor = true; fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], NULL); if (is_huge_clob(fcinfo->argTypes[i], fcinfo->argnull[i], fcinfo->arg[i])) { is_have_huge_clob = true; } ExecTableOfIndexInfo execTableOfIndexInfo; initExecTableOfIndexInfo(&execTableOfIndexInfo, econtext); ExecEvalParamExternTableOfIndex((Node*)argstate->expr, &execTableOfIndexInfo); if (execTableOfIndexInfo.tableOfIndex != NULL) { if (!IsTableOfFunc(fcache->func.fn_oid)) { MemoryContext oldCxt = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); PLpgSQL_func_tableof_index* func_tableof = (PLpgSQL_func_tableof_index*)palloc0(sizeof(PLpgSQL_func_tableof_index)); func_tableof->varno = i; func_tableof->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; func_tableof->tableOfIndex = copyTableOfIndex(execTableOfIndexInfo.tableOfIndex); u_sess->plsql_cxt.func_tableof_index = lappend(u_sess->plsql_cxt.func_tableof_index, func_tableof); MemoryContextSwitchTo(oldCxt); } u_sess->SPI_cxt.cur_tableof_index->tableOfIndexType = execTableOfIndexInfo.tableOfIndexType; u_sess->SPI_cxt.cur_tableof_index->tableOfIndex = execTableOfIndexInfo.tableOfIndex; u_sess->SPI_cxt.cur_tableof_index->tableOfNestLayer = execTableOfIndexInfo.tableOfLayers; /* for nest table of output, save layer of this var tableOfGetNestLayer in ExecEvalArrayRef, or set to zero for get whole nest table. */ u_sess->SPI_cxt.cur_tableof_index->tableOfGetNestLayer = -1; } if (has_refcursor && econtext->is_cursor) { var_dno[i] = econtext->dno; CopyCursorInfoData(&fcinfo->refcursor_data.argCursor[i], &econtext->cursor_data); } econtext->is_cursor = false; i++; } /* * 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; u_sess->SPI_cxt.is_stp = savedIsSTP; u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; if (needResetErrMsg) { stp_reset_commit_rolback_err_msg(); } return (Datum)0; } } } pgstat_init_function_usage(fcinfo, &fcusage); fcinfo->isnull = false; check_huge_clob_paramter(fcinfo, is_have_huge_clob); if (u_sess->instr_cxt.global_instr != NULL && fcinfo->flinfo->fn_addr == plpgsql_call_handler) { StreamInstrumentation* save_global_instr = u_sess->instr_cxt.global_instr; u_sess->instr_cxt.global_instr = NULL; result = FunctionCallInvoke(fcinfo); // node will be free at here or else; u_sess->instr_cxt.global_instr = save_global_instr; } else { if (fcinfo->argTypes[0] == CLOBOID && fcinfo->argTypes[1] == CLOBOID && fcinfo->flinfo->fn_addr == textcat) { bool is_null = false; if (fcinfo->arg[0] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[0])) { struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[0])); fcinfo->arg[0] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null); } if (fcinfo->arg[1] != 0 && VARATT_IS_EXTERNAL_LOB(fcinfo->arg[1])) { struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(fcinfo->arg[1])); fcinfo->arg[1] = fetch_lob_value_from_tuple(lob_pointer, InvalidOid, &is_null); } } result = FunctionCallInvoke(fcinfo); } *isNull = fcinfo->isnull; if (has_refcursor && econtext->plpgsql_estate != NULL) { PLpgSQL_execstate* estate = econtext->plpgsql_estate; for (i = 0; i < fcinfo->nargs; i++) { /* copy in-args cursor option info */ if (var_dno[i] >= 0) { int dno = var_dno[i]; Cursor_Data* cursor_data = &fcinfo->refcursor_data.argCursor[i]; #ifdef USE_ASSERT_CHECKING PLpgSQL_datum* datum = estate->datums[dno]; #endif Assert(datum->dtype == PLPGSQL_DTYPE_VAR); Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); ExecCopyDataToDatum(estate->datums, dno, cursor_data); } } if (fcinfo->flinfo->fn_rettype == REFCURSOROID) { /* copy function returns cursor option info. * for simple expr in exec_eval_expr, we can not get the result type, * so cursor_return_data mallocs here. */ if (estate->cursor_return_data == NULL) { estate->cursor_return_data = (Cursor_Data*)palloc0(sizeof(Cursor_Data)); estate->cursor_return_numbers = 1; } int rc = memcpy_s(estate->cursor_return_data, sizeof(Cursor_Data), fcinfo->refcursor_data.returnCursor, sizeof(Cursor_Data)); securec_check(rc, "\0", "\0"); } } pgstat_end_function_usage(&fcusage, true); if (has_refcursor) { if (fcinfo->refcursor_data.argCursor != NULL) pfree_ext(fcinfo->refcursor_data.argCursor); if (var_dno != NULL) pfree_ext(var_dno); } u_sess->SPI_cxt.is_stp = savedIsSTP; u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; if (needResetErrMsg) { stp_reset_commit_rolback_err_msg(); } set_result_for_plpgsql_language_function_with_outparam(fcache, &result, isNull); return result; } /* * @Description: jugde function has parameter that is refcursor or return type is refcursor * @in Funcid - function oid * @in fcinfo - function call info * @return - has refcursor */ static bool func_has_refcursor_args(Oid Funcid, FunctionCallInfoData* fcinfo) { HeapTuple proctup = NULL; Form_pg_proc procStruct; int allarg; Oid* p_argtypes = NULL; char** p_argnames = NULL; char* p_argmodes = NULL; bool use_cursor = false; bool return_refcursor = false; int out_count = 0; /* out arg count */ proctup = SearchSysCache(PROCOID, ObjectIdGetDatum(Funcid), 0, 0, 0); /* * function may be deleted after clist be searched. */ if (!HeapTupleIsValid(proctup)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function doesn't exist "))); } /* get the all args informations, only "in" parameters if p_argmodes is null */ allarg = get_func_arg_info(proctup, &p_argtypes, &p_argnames, &p_argmodes); procStruct = (Form_pg_proc)GETSTRUCT(proctup); fcinfo->refcursor_data.return_number = 0; fcinfo->refcursor_data.returnCursor = NULL; for (int i = 0; i < allarg; i++) { if (p_argmodes != NULL && (p_argmodes[i] == 'o' || p_argmodes[i] == 'b')) { out_count++; if (p_argtypes[i] == REFCURSOROID) return_refcursor = true; } else { if (p_argtypes[i] == REFCURSOROID) use_cursor = true; } } if (procStruct->prorettype == REFCURSOROID) { use_cursor = true; fcinfo->refcursor_data.return_number = 1; } else if (return_refcursor) { fcinfo->refcursor_data.return_number = out_count; } ReleaseSysCache(proctup); return use_cursor; } /* * ExecMakeTableFunctionResult * * Evaluate a table function, producing a materialized result in a Tuplestore * object. */ Tuplestorestate* ExecMakeTableFunctionResult( ExprState* funcexpr, ExprContext* econtext, TupleDesc expectedDesc, bool randomAccess, FunctionScanState* node) { Tuplestorestate* tupstore = NULL; TupleDesc tupdesc = NULL; Oid funcrettype; bool returnsTuple = false; bool returnsSet = false; FunctionCallInfoData fcinfo; PgStat_FunctionCallUsage fcusage; ReturnSetInfo rsinfo; HeapTupleData tmptup; MemoryContext callerContext; MemoryContext oldcontext; bool direct_function_call = false; bool first_time = true; int* var_dno = NULL; bool has_refcursor = false; bool has_out_param = false; FuncExpr *fexpr = NULL; bool savedIsSTP = u_sess->SPI_cxt.is_stp; bool savedProConfigIsSet = u_sess->SPI_cxt.is_proconfig_set; bool proIsProcedure = false; bool supportTranaction = false; #ifdef ENABLE_MULTIPLE_NODES if (IS_PGXC_COORDINATOR && (t_thrd.proc->workingVersionNum >= STP_SUPPORT_COMMIT_ROLLBACK)) { supportTranaction = true; } #else supportTranaction = true; #endif bool needResetErrMsg = (u_sess->SPI_cxt.forbidden_commit_rollback_err_msg[0] == '\0'); /* Only allow commit at CN, therefore only need to set atomic and relevant check at CN level. */ if (supportTranaction && IsA(funcexpr->expr, FuncExpr)) { fexpr = (FuncExpr*)funcexpr->expr; char prokind = (reinterpret_cast(funcexpr))->prokind; if (!u_sess->SPI_cxt.is_allow_commit_rollback) { node->atomic = true; } else if (IsAfterTriggerBegin()) { node->atomic = true; stp_set_commit_rollback_err_msg(STP_XACT_AFTER_TRIGGER_BEGIN); } /* * If proconfig is set we can't allow transaction commands because of the * way the GUC stacking works: The transaction boundary would have to pop * the proconfig setting off the stack. That restriction could be lifted * by redesigning the GUC nesting mechanism a bit. */ if (!prokind) { HeapTuple tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid)); bool isNull = false; if (!HeapTupleIsValid(tp)) { elog(ERROR, "cache lookup failed for function %u", fexpr->funcid); } /* immutable or stable function do not support commit/rollback */ bool isNullVolatile = false; Datum provolatile = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_provolatile, &isNullVolatile); if (!isNullVolatile && CharGetDatum(provolatile) != PROVOLATILE_VOLATILE) { node->atomic = true; stp_set_commit_rollback_err_msg(STP_XACT_IMMUTABLE); } Datum datum = SysCacheGetAttr(PROCOID, tp, Anum_pg_proc_prokind, &isNull); proIsProcedure = PROC_IS_PRO(CharGetDatum(datum)); if (proIsProcedure) { (reinterpret_cast(funcexpr))->prokind = 'p'; } else { (reinterpret_cast(funcexpr))->prokind = 'f'; } /* if proIsProcedure means it was a stored procedure */ u_sess->SPI_cxt.is_stp = savedIsSTP; if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL) || u_sess->SPI_cxt.is_proconfig_set) { u_sess->SPI_cxt.is_proconfig_set = true; node->atomic = true; stp_set_commit_rollback_err_msg(STP_XACT_GUC_IN_OPT_CLAUSE); } ReleaseSysCache(tp); } else { proIsProcedure = PROC_IS_PRO(prokind); u_sess->SPI_cxt.is_stp = savedIsSTP; } } callerContext = CurrentMemoryContext; if (unlikely(funcexpr == NULL)) { ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("The input function expression is NULL."))); } funcrettype = exprType((Node*)funcexpr->expr); returnsTuple = type_is_rowtype(funcrettype); econtext->plpgsql_estate = plpgsql_estate; plpgsql_estate = NULL; /* * 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 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, func->inputcollid, fcache, econtext->ecxt_per_query_memory, false); } returnsSet = fcache->func.fn_retset; InitFunctionCallInfoData(fcinfo, &(fcache->func), list_length(fcache->args), fcache->fcinfo_data.fncollation, (Node*)node, (Node*)&rsinfo); has_refcursor = func_has_refcursor_args(fcinfo.flinfo->fn_oid, &fcinfo); has_out_param = (is_function_with_plpgsql_language_and_outparam(fcinfo.flinfo->fn_oid) != InvalidOid); if (u_sess->attr.attr_sql.sql_compatibility == A_FORMAT && has_out_param) { returnsTuple = type_is_rowtype(RECORDOID); } int cursor_return_number = fcinfo.refcursor_data.return_number; if (cursor_return_number > 0) { /* init returnCursor to store out-args cursor info on FunctionScan context*/ fcinfo.refcursor_data.returnCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * cursor_return_number); } else { fcinfo.refcursor_data.returnCursor = NULL; } if (has_refcursor) { /* init argCursor to store in-args cursor info on FunctionScan context*/ fcinfo.refcursor_data.argCursor = (Cursor_Data*)palloc0(sizeof(Cursor_Data) * fcinfo.nargs); var_dno = (int*)palloc0(sizeof(int) * fcinfo.nargs); int rc = memset_s(var_dno, sizeof(int) * fcinfo.nargs, -1, sizeof(int) * fcinfo.nargs); securec_check(rc, "\0", "\0"); } /* * 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? */ if (has_refcursor) argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext, var_dno); else 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; InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, (Node*)node, 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 (direct_function_call) { pgstat_init_function_usage(&fcinfo, &fcusage); fcinfo.isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(&fcinfo); if (econtext->plpgsql_estate != NULL) { PLpgSQL_execstate* estate = econtext->plpgsql_estate; bool isVaildReturn = (fcinfo.refcursor_data.return_number > 0 && estate->cursor_return_data != NULL && fcinfo.refcursor_data.returnCursor != NULL); if (isVaildReturn) { bool isVaildReturnNum = (fcinfo.refcursor_data.return_number > estate->cursor_return_numbers); if (isVaildReturnNum) { pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmodule(MOD_PLSQL), errmsg("The expected output of the cursor:%d and function:%d does not match", estate->cursor_return_numbers, fcinfo.refcursor_data.return_number))); } for (int i = 0; i < fcinfo.refcursor_data.return_number; i++) { CopyCursorInfoData(&estate->cursor_return_data[i], &fcinfo.refcursor_data.returnCursor[i]); } } if (var_dno != NULL) { for (int i = 0; i < fcinfo.nargs; i++) { if (var_dno[i] >= 0) { int dno = var_dno[i]; Cursor_Data* cursor_data = &fcinfo.refcursor_data.argCursor[i]; PLpgSQL_execstate* execstate = econtext->plpgsql_estate; #ifdef USE_ASSERT_CHECKING PLpgSQL_datum* datum = execstate->datums[dno]; #endif Assert(datum->dtype == PLPGSQL_DTYPE_VAR); Assert(((PLpgSQL_var*)datum)->datatype->typoid == REFCURSOROID); ExecCopyDataToDatum(execstate->datums, dno, cursor_data); } } } } pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); } 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 && !has_out_param) { 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); if (IsA(funcexpr->expr, Const)) { tupdesc = lookup_rowtype_tupdesc_copy( ((Const*)funcexpr->expr)->consttype, HeapTupleHeaderGetTypMod(td)); } else { tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); } } else { /* * Scalar type, so make a single-column descriptor */ tupdesc = CreateTemplateTupleDesc(1, false, TAM_HEAP); TupleDescInitEntry(tupdesc, (AttrNumber)1, "column", funcrettype, -1, 0); } tupstore = tuplestore_begin_heap(randomAccess, false, u_sess->attr.attr_memory.work_mem); MemoryContextSwitchTo(oldcontext); rsinfo.setResult = tupstore; rsinfo.setDesc = tupdesc; } /* * Store current resultset item. */ if (returnsTuple) { HeapTupleHeader td; td = DatumGetHeapTupleHeader(result); /* * Verify all returned rows have same subtype; necessary in * case the type is RECORD. */ if ((HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid || HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod) && nodeTag(funcexpr->expr) != T_Const) { ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("rows returned by function are not all of the same row type"), errdetail("return type id %u, tuple decription id %u, return typmod %d " "tuple decription, typmod %d", HeapTupleHeaderGetTypeId(td), tupdesc->tdtypeid, HeapTupleHeaderGetTypMod(td), tupdesc->tdtypmod))); } /* * 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 { 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. */ if (rsinfo.setResult == NULL) { MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupstore = tuplestore_begin_heap(randomAccess, false, u_sess->attr.attr_memory.work_mem); rsinfo.setResult = tupstore; if (!returnsSet) { int natts = expectedDesc->natts; Datum* nulldatums = NULL; bool* nullflags = NULL; errno_t rc = EOK; MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); nulldatums = (Datum*)palloc0(natts * sizeof(Datum)); nullflags = (bool*)palloc(natts * sizeof(bool)); rc = memset_s(nullflags, natts * sizeof(bool), true, natts * sizeof(bool)); securec_check(rc, "\0", "\0"); MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tuplestore_putvalues(tupstore, expectedDesc, nulldatums, 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); econtext->plpgsql_estate = NULL; if (has_refcursor) { if (fcinfo.refcursor_data.argCursor != NULL) pfree_ext(fcinfo.refcursor_data.argCursor); if (fcinfo.refcursor_data.returnCursor != NULL) pfree_ext(fcinfo.refcursor_data.returnCursor); if (var_dno != NULL) pfree_ext(var_dno); } /* reset the u_sess->SPI_cxt.is_stp, u_sess->SPI_cxt.is_proconfig_set and error message value */ u_sess->SPI_cxt.is_stp = savedIsSTP; u_sess->SPI_cxt.is_proconfig_set = savedProConfigIsSet; if (needResetErrMsg) { stp_reset_commit_rolback_err_msg(); } /* All done, pass back the tuplestore */ return rsinfo.setResult; } /* ---------------------------------------------------------------- * ExecEvalFunc * ExecEvalOper * * Evaluate the functional result of a list of arguments by calling the * function manager. * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- * ExecEvalFunc * ---------------------------------------------------------------- */ static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { /* This is called only the first time through */ FuncExpr* func = (FuncExpr*)fcache->xprstate.expr; Oid target_type = InvalidOid; Oid source_type = InvalidOid; /* Initialize function lookup info */ init_fcache(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, true); bool has_refcursor = func_has_refcursor_args(func->funcid, &fcache->fcinfo_data); int cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number; if (func->funcformat == COERCE_EXPLICIT_CAST || func->funcformat == COERCE_IMPLICIT_CAST) { target_type = func->funcresulttype; source_type = fcache->fcinfo_data.argTypes[0]; HeapTuple proc_tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(func->funcid), 0, 0, 0); if (HeapTupleIsValid(proc_tuple)) { Form_pg_proc proc_struct = (Form_pg_proc)GETSTRUCT(proc_tuple); source_type = proc_struct->proargtypes.values[0]; ReleaseSysCache(proc_tuple); } HeapTuple cast_tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(source_type), ObjectIdGetDatum(target_type)); if (HeapTupleIsValid(cast_tuple)) { Relation cast_rel = heap_open(CastRelationId, AccessShareLock); int castowner_Anum = Anum_pg_cast_castowner; if (castowner_Anum <= (int)HeapTupleHeaderGetNatts(cast_tuple->t_data, cast_rel->rd_att)) { bool isnull = true; Datum datum = fastgetattr(cast_tuple, Anum_pg_cast_castowner, cast_rel->rd_att, &isnull); if (!isnull) { u_sess->exec_cxt.cast_owner = DatumGetObjectId(datum); } else { u_sess->exec_cxt.cast_owner = InvalidCastOwnerId; } } heap_close(cast_rel, AccessShareLock); ReleaseSysCache(cast_tuple); } } /* * We need to invoke ExecMakeFunctionResult if either the function itself * or any of its input expressions can return a set. Otherwise, invoke * ExecMakeFunctionResultNoSets. In either case, change the evalfunc * pointer to go directly there on subsequent uses. */ if (fcache->func.fn_retset) { if (has_refcursor) { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } } else { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } } } else if (expression_returns_set((Node*)func->args)) { if (has_refcursor) { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } } else { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } } } else { if (has_refcursor) { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); } } else { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); } } } } /* ---------------------------------------------------------------- * ExecEvalOper * ---------------------------------------------------------------- */ static Datum ExecEvalOper(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { /* This is called only the first time through */ OpExpr* op = (OpExpr*)fcache->xprstate.expr; bool has_refcursor = false; /* Initialize function lookup info */ init_fcache(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, true); has_refcursor = func_has_refcursor_args(op->opfuncid, &fcache->fcinfo_data); int cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number; /* * We need to invoke ExecMakeFunctionResult if either the function itself * or any of its input expressions can return a set. Otherwise, invoke * ExecMakeFunctionResultNoSets. In either case, change the evalfunc * pointer to go directly there on subsequent uses. */ if (fcache->func.fn_retset) { if (has_refcursor) { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } } else { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } } } else if (expression_returns_set((Node*)op->args)) { if (has_refcursor) { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } } else { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } } } else { if (has_refcursor) { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); } } else { if (cursor_return_number > 0) { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone); } else { fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets; return ExecMakeFunctionResultNoSets(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 Datum ExecEvalDistinct(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Datum result; FunctionCallInfo fcinfo; ExprDoneCond argDone; /* Set default values for result flags: non-null, not a set result */ *isNull = false; if (isDone != NULL) *isDone = ExprSingleResult; /* * Initialize function cache if first time through */ if (fcache->func.fn_oid == InvalidOid) { DistinctExpr* op = (DistinctExpr*)fcache->xprstate.expr; init_fcache(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, true); Assert(!fcache->func.fn_retset); } /* * Evaluate arguments */ fcinfo = &fcache->fcinfo_data; argDone = ExecEvalFuncArgs(fcinfo, fcache->args, 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 Datum ExecEvalScalarArrayOp( ScalarArrayOpExprState* sstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { ScalarArrayOpExpr* opexpr = (ScalarArrayOpExpr*)sstate->fxprstate.xprstate.expr; bool useOr = opexpr->useOr; ArrayType* arr = NULL; int nitems; Datum result; bool resultnull = false; FunctionCallInfo fcinfo; ExprDoneCond argDone; int i; int16 typlen; bool typbyval = false; char typalign; char* s = NULL; bits8* bitmap = NULL; int bitmask; /* Set default values for result flags: non-null, not a set result */ *isNull = false; if (isDone != NULL) *isDone = ExprSingleResult; /* * Initialize function cache if first time through */ if (sstate->fxprstate.func.fn_oid == InvalidOid) { init_fcache( opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate, econtext->ecxt_per_query_memory, true); Assert(!sstate->fxprstate.func.fn_retset); } /* * Evaluate arguments */ fcinfo = &sstate->fxprstate.fcinfo_data; /* init the number of arguments to a function. */ InitFunctionCallInfoArgs(*fcinfo, 2, 1); 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; no * point in iterating the loop. */ 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); bitmap = ARR_NULLBITMAP(arr); bitmask = 1; for (i = 0; i < nitems; i++) { Datum elt; Datum thisresult; /* Get array element, checking for NULL */ if (bitmap && (*bitmap & bitmask) == 0) { fcinfo->arg[1] = (Datum)0; fcinfo->argnull[1] = true; } else { elt = fetch_att(s, typbyval, typlen); s = att_addlength_pointer(s, typlen, s); s = (char*)att_align_nominal(s, typalign); fcinfo->arg[1] = elt; fcinfo->argnull[1] = false; } /* Call comparison function */ if (fcinfo->argnull[1] && sstate->fxprstate.func.fn_strict) { fcinfo->isnull = true; thisresult = (Datum)0; } else { fcinfo->isnull = false; thisresult = FunctionCallInvoke(fcinfo); } /* Combine results per OR or AND semantics */ if (fcinfo->isnull) resultnull = true; else if (useOr) { if (DatumGetBool(thisresult)) { result = BoolGetDatum(true); resultnull = false; break; /* needn't look at any more elements */ } } else { if (!DatumGetBool(thisresult)) { result = BoolGetDatum(false); resultnull = false; break; /* needn't look at any more elements */ } } /* advance bitmap pointer if any */ if (bitmap != NULL) { bitmask <<= 1; if (bitmask == 0x100) { bitmap++; bitmask = 1; } } } *isNull = resultnull; return result; } /* ---------------------------------------------------------------- * ExecEvalNot * ExecEvalOr * ExecEvalAnd * * Evaluate boolean expressions, with appropriate short-circuiting. * * The query planner reformulates clause expressions in the * qualification to conjunctive normal form. If we ever get * an AND to evaluate, we can be sure that it's not a top-level * clause in the qualification, but appears lower (as a function * argument, for example), or in the target list. Not that you * need to know this, mind you... * ---------------------------------------------------------------- */ static Datum ExecEvalNot(BoolExprState* notclause, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { ExprState* clause = (ExprState*)linitial(notclause->args); Datum expr_value; if (isDone != NULL) *isDone = ExprSingleResult; expr_value = ExecEvalExpr(clause, econtext, isNull, NULL); /* * if the expression evaluates to null, then we just cascade the null back * to whoever called us. */ if (*isNull) return expr_value; /* * evaluation of 'not' is simple.. expr is false, then return 'true' and * vice versa. */ return BoolGetDatum(!DatumGetBool(expr_value)); } /* ---------------------------------------------------------------- * ExecEvalOr * ---------------------------------------------------------------- */ static Datum ExecEvalOr(BoolExprState* orExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { List* clauses = orExpr->args; ListCell* clause = NULL; bool AnyNull = false; if (isDone != NULL) *isDone = ExprSingleResult; AnyNull = false; /* * If any of the clauses is TRUE, the OR result is TRUE regardless of the * states of the rest of the clauses, so we can stop evaluating and return * TRUE immediately. If none are TRUE and one or more is NULL, we return * NULL; otherwise we return FALSE. This makes sense when you interpret * NULL as "don't know": if we have a TRUE then the OR is TRUE even if we * aren't sure about some of the other inputs. If all the known inputs are * FALSE, but we have one or more "don't knows", then we have to report * that we "don't know" what the OR's result should be --- perhaps one of * the "don't knows" would have been TRUE if we'd known its value. Only * when all the inputs are known to be FALSE can we state confidently that * the OR's result is FALSE. */ foreach (clause, clauses) { ExprState* clausestate = (ExprState*)lfirst(clause); Datum clause_value; clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); /* * if we have a non-null true result, then return it. */ if (*isNull) AnyNull = true; /* remember we got a null */ else if (DatumGetBool(clause_value)) return clause_value; } /* AnyNull is true if at least one clause evaluated to NULL */ *isNull = AnyNull; return BoolGetDatum(false); } /* ---------------------------------------------------------------- * ExecEvalAnd * ---------------------------------------------------------------- */ static Datum ExecEvalAnd(BoolExprState* andExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { List* clauses = andExpr->args; ListCell* clause = NULL; bool AnyNull = false; if (isDone != NULL) *isDone = ExprSingleResult; AnyNull = false; /* * If any of the clauses is FALSE, the AND result is FALSE regardless of * the states of the rest of the clauses, so we can stop evaluating and * return FALSE immediately. If none are FALSE and one or more is NULL, * we return NULL; otherwise we return TRUE. This makes sense when you * interpret NULL as "don't know", using the same sort of reasoning as for * OR, above. */ foreach (clause, clauses) { ExprState* clausestate = (ExprState*)lfirst(clause); Datum clause_value; clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); /* * if we have a non-null false result, then return it. */ if (*isNull) AnyNull = true; /* remember we got a null */ else if (!DatumGetBool(clause_value)) return clause_value; } /* AnyNull is true if at least one clause evaluated to NULL */ *isNull = AnyNull; return BoolGetDatum(!AnyNull); } /* ---------------------------------------------------------------- * ExecEvalConvertRowtype * * Evaluate a rowtype coercion operation. This may require * rearranging field positions. * ---------------------------------------------------------------- */ static Datum ExecEvalConvertRowtype( ConvertRowtypeExprState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { ConvertRowtypeExpr* convert = (ConvertRowtypeExpr*)cstate->xprstate.expr; HeapTuple result; Datum tupDatum; HeapTupleHeader tuple; HeapTupleData tmptup; tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); /* this test covers the isDone exception too: */ if (*isNull) return tupDatum; tuple = DatumGetHeapTupleHeader(tupDatum); /* Lookup tupdescs if first time through or after rescan */ if (cstate->indesc == NULL) { get_cached_rowtype(exprType((Node*)convert->arg), -1, &cstate->indesc, econtext); cstate->initialized = false; } if (cstate->outdesc == NULL) { get_cached_rowtype(convert->resulttype, -1, &cstate->outdesc, econtext); cstate->initialized = false; } Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid); Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod); /* if first time through, initialize conversion map */ if (!cstate->initialized) { MemoryContext old_cxt; /* allocate map in long-lived memory context */ old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); /* prepare map from old to new attribute numbers */ cstate->map = convert_tuples_by_name(cstate->indesc, cstate->outdesc, gettext_noop("could not convert row type")); cstate->initialized = true; MemoryContextSwitchTo(old_cxt); } /* * No-op if no conversion needed (not clear this can happen here). */ if (cstate->map == NULL) return tupDatum; /* * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); tmptup.t_data = tuple; result = do_convert_tuple(&tmptup, cstate->map); return HeapTupleGetDatum(result); } /* ---------------------------------------------------------------- * ExecEvalCase * * Evaluate a CASE clause. Will have boolean expressions * inside the WHEN clauses, and will have expressions * for results. * - thomas 1998-11-09 * ---------------------------------------------------------------- */ static Datum ExecEvalCase(CaseExprState* caseExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { List* clauses = caseExpr->args; ListCell* clause = NULL; Datum save_datum; bool save_isNull = false; if (isDone != NULL) *isDone = ExprSingleResult; /* * If there's a test expression, we have to evaluate it and save the value * where the CaseTestExpr placeholders can find it. We must save and * restore prior setting of econtext's caseValue fields, in case this node * is itself within a larger CASE.Furthermore, don't assign to the * econtext fields until after returning from evaluation of the test * expression. We used to pass &econtext->caseValue_isNull to the * recursive call, but that leads to aliasing that variable within said * call, which can (and did) produce bugs when the test expression itself * contains a CASE. * * If there's no test expression, we don't actually need to save and * restore these fields; but it's less code to just do so unconditionally. */ save_datum = econtext->caseValue_datum; save_isNull = econtext->caseValue_isNull; if (caseExpr->arg) { bool arg_isNull = false; econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg, econtext, &arg_isNull, NULL); econtext->caseValue_isNull = arg_isNull; } /* * we evaluate each of the WHEN clauses in turn, as soon as one is true we * return the corresponding result. If none are true then we return the * value of the default clause, or NULL if there is none. */ foreach (clause, clauses) { CaseWhenState* wclause = (CaseWhenState*)lfirst(clause); Datum clause_value; bool clause_isNull = false; clause_value = ExecEvalExpr(wclause->expr, econtext, &clause_isNull, NULL); /* * if we have a true test, then we return the result, since the case * statement is satisfied. A NULL result from the test is not * considered true. */ if (DatumGetBool(clause_value) && !clause_isNull) { econtext->caseValue_datum = save_datum; econtext->caseValue_isNull = save_isNull; return ExecEvalExpr(wclause->result, econtext, isNull, isDone); } } econtext->caseValue_datum = save_datum; econtext->caseValue_isNull = save_isNull; if (caseExpr->defresult) { return ExecEvalExpr(caseExpr->defresult, econtext, isNull, isDone); } *isNull = true; return (Datum)0; } /* * ExecEvalCaseTestExpr * * Return the value stored by CASE. */ static Datum ExecEvalCaseTestExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { if (isDone != NULL) *isDone = ExprSingleResult; *isNull = econtext->caseValue_isNull; return econtext->caseValue_datum; } /* * ExecEvalGroupingFuncExpr * * Return a bitmask with a bit for each (unevaluated) argument expression * (rightmost arg is least significant bit). * * A bit is set if the corresponding expression is NOT part of the set of * grouping expressions in the current grouping set. */ static Datum ExecEvalGroupingFuncExpr( GroupingFuncExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { int result = 0; int attnum = 0; Bitmapset* grouped_cols = gstate->aggstate->grouped_cols; ListCell* lc = NULL; if (isDone != NULL) *isDone = ExprSingleResult; *isNull = false; foreach (lc, (gstate->clauses)) { attnum = lfirst_int(lc); result = (uint32)result << 1; if (!bms_is_member(attnum, grouped_cols)) result = (uint32)result | 1; } return (Datum)result; } /* ---------------------------------------------------------------- * ExecEvalArray - ARRAY[] expressions * ---------------------------------------------------------------- */ static Datum ExecEvalArray(ArrayExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { ArrayExpr* arrayExpr = (ArrayExpr*)astate->xprstate.expr; ArrayType* result = NULL; ListCell* element = NULL; Oid element_type = arrayExpr->element_typeid; int ndims = 0; int dims[MAXDIM]; int lbs[MAXDIM]; /* Set default values for result flags: non-null, not a set result */ *isNull = false; if (isDone != NULL) *isDone = ExprSingleResult; if (!arrayExpr->multidims) { /* Elements are presumably of scalar type */ int nelems; Datum* dvalues = NULL; bool* dnulls = NULL; int i = 0; ndims = 1; nelems = list_length(astate->elements); /* Shouldn't happen here, but if length is 0, return empty array */ if (nelems == 0) return PointerGetDatum(construct_empty_array(element_type)); dvalues = (Datum*)palloc(nelems * sizeof(Datum)); dnulls = (bool*)palloc(nelems * sizeof(bool)); /* loop through and build array of datums */ foreach (element, astate->elements) { ExprState* e = (ExprState*)lfirst(element); dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i], NULL); i++; } /* setup for 1-D array of the given length */ dims[0] = nelems; lbs[0] = 1; result = construct_md_array( dvalues, dnulls, ndims, dims, lbs, element_type, astate->elemlength, astate->elembyval, astate->elemalign); } else { /* Must be nested array expressions */ int nbytes = 0; int nitems = 0; int outer_nelems = 0; int elem_ndims = 0; int* elem_dims = NULL; int* elem_lbs = NULL; bool firstone = true; bool havenulls = false; bool haveempty = false; char** subdata; bits8** subbitmaps; int* subbytes = NULL; int* subnitems = NULL; int i; int32 dataoffset; char* dat = NULL; int iitem; errno_t rc = 0; i = list_length(astate->elements); subdata = (char**)palloc(i * sizeof(char*)); subbitmaps = (bits8**)palloc(i * sizeof(bits8*)); subbytes = (int*)palloc(i * sizeof(int)); subnitems = (int*)palloc(i * sizeof(int)); /* loop through and get data area from each element */ foreach (element, astate->elements) { ExprState* e = (ExprState*)lfirst(element); bool eisnull = false; Datum arraydatum; ArrayType* array = NULL; int this_ndims; arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL); /* temporarily ignore null subarrays */ if (eisnull) { haveempty = true; continue; } array = DatumGetArrayTypeP(arraydatum); /* run-time double-check on element type */ if (element_type != ARR_ELEMTYPE(array)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot merge incompatible arrays"), errdetail("Array with element type %s cannot be " "included in ARRAY construct with element type %s.", format_type_be(ARR_ELEMTYPE(array)), format_type_be(element_type)))); this_ndims = ARR_NDIM(array); /* temporarily ignore zero-dimensional subarrays */ if (this_ndims <= 0) { haveempty = true; continue; } if (firstone) { /* Get sub-array details from first member */ elem_ndims = this_ndims; ndims = elem_ndims + 1; if (ndims <= 0 || ndims > MAXDIM) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds " "the maximum allowed (%d)", ndims, MAXDIM))); elem_dims = (int*)palloc(elem_ndims * sizeof(int)); rc = memcpy_s(elem_dims, elem_ndims * sizeof(int), ARR_DIMS(array), elem_ndims * sizeof(int)); securec_check(rc, "\0", "\0"); elem_lbs = (int*)palloc(elem_ndims * sizeof(int)); rc = memcpy_s(elem_lbs, elem_ndims * sizeof(int), ARR_LBOUND(array), elem_ndims * sizeof(int)); securec_check(rc, "\0", "\0"); firstone = false; } else { /* Check other sub-arrays are compatible */ if (elem_ndims != this_ndims || memcmp(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)) != 0 || memcmp(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)) != 0) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("multidimensional arrays must have array expressions with matching dimensions"))); } subdata[outer_nelems] = ARR_DATA_PTR(array); subbitmaps[outer_nelems] = ARR_NULLBITMAP(array); subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array); nbytes += subbytes[outer_nelems]; subnitems[outer_nelems] = ArrayGetNItems(this_ndims, ARR_DIMS(array)); nitems += subnitems[outer_nelems]; if (ARR_HASNULL(array)) havenulls = true; outer_nelems++; } /* * If all items were null or empty arrays, return an empty array; * otherwise, if some were and some weren't, raise error. (Note: we * must special-case this somehow to avoid trying to generate a 1-D * array formed from empty arrays. It's not ideal...) */ if (haveempty) { if (ndims == 0) /* didn't find any nonempty array */ return PointerGetDatum(construct_empty_array(element_type)); ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("multidimensional arrays must have array expressions with matching dimensions"))); } /* setup for multi-D array */ dims[0] = outer_nelems; lbs[0] = 1; for (i = 1; i < ndims; i++) { dims[i] = elem_dims[i - 1]; lbs[i] = elem_lbs[i - 1]; } if (havenulls) { dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems); nbytes += dataoffset; } else { dataoffset = 0; /* marker for no null bitmap */ nbytes += ARR_OVERHEAD_NONULLS(ndims); } result = (ArrayType*)palloc(nbytes); SET_VARSIZE(result, nbytes); result->ndim = ndims; result->dataoffset = dataoffset; result->elemtype = element_type; rc = memcpy_s(ARR_DIMS(result), ndims * sizeof(int), dims, ndims * sizeof(int)); securec_check(rc, "\0", "\0"); rc = memcpy_s(ARR_LBOUND(result), ndims * sizeof(int), lbs, ndims * sizeof(int)); securec_check(rc, "\0", "\0"); dat = ARR_DATA_PTR(result); int len = (nbytes - ARR_DATA_OFFSET(result)); iitem = 0; for (i = 0; i < outer_nelems; i++) { /* make sure the destMax of memcpy_s should never be zero. */ if (subbytes[i] != 0) { rc = memcpy_s(dat, len, subdata[i], subbytes[i]); securec_check(rc, "\0", "\0"); } dat += subbytes[i]; len -= subbytes[i]; if (havenulls) array_bitmap_copy(ARR_NULLBITMAP(result), iitem, subbitmaps[i], 0, subnitems[i]); iitem += subnitems[i]; } } return PointerGetDatum(result); } /* ---------------------------------------------------------------- * ExecEvalRow - ROW() expressions * ---------------------------------------------------------------- */ static Datum ExecEvalRow(RowExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { HeapTuple tuple; Datum* values = NULL; bool* isnull = NULL; int natts; ListCell* arg = NULL; int i; errno_t rc = EOK; /* Set default values for result flags: non-null, not a set result */ *isNull = false; if (isDone != NULL) *isDone = ExprSingleResult; /* Allocate workspace */ natts = rstate->tupdesc->natts; values = (Datum*)palloc0(natts * sizeof(Datum)); isnull = (bool*)palloc(natts * sizeof(bool)); /* preset to nulls in case rowtype has some later-added columns */ rc = memset_s(isnull, natts * sizeof(bool), true, natts * sizeof(bool)); securec_check(rc, "\0", "\0"); /* Evaluate field values */ i = 0; foreach (arg, rstate->args) { ExprState* e = (ExprState*)lfirst(arg); values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL); i++; } tuple = (HeapTuple)tableam_tops_form_tuple(rstate->tupdesc, values, isnull, HEAP_TUPLE); pfree_ext(values); pfree_ext(isnull); return HeapTupleGetDatum(tuple); } /* ---------------------------------------------------------------- * ExecEvalRowCompare - ROW() comparison-op ROW() * ---------------------------------------------------------------- */ static Datum ExecEvalRowCompare(RowCompareExprState* rstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { bool result = false; RowCompareType rctype = ((RowCompareExpr*)rstate->xprstate.expr)->rctype; int32 cmpresult = 0; ListCell* l = NULL; ListCell* r = NULL; int i; if (isDone != NULL) *isDone = ExprSingleResult; *isNull = true; /* until we get a result */ i = 0; forboth(l, rstate->largs, r, rstate->rargs) { ExprState* le = (ExprState*)lfirst(l); ExprState* re = (ExprState*)lfirst(r); FunctionCallInfoData locfcinfo; InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2, rstate->collations[i], NULL, NULL); locfcinfo.arg[0] = ExecEvalExpr(le, econtext, &locfcinfo.argnull[0], NULL); locfcinfo.arg[1] = ExecEvalExpr(re, econtext, &locfcinfo.argnull[1], NULL); if (rstate->funcs[i].fn_strict && (locfcinfo.argnull[0] || locfcinfo.argnull[1])) return (Datum)0; /* force NULL result */ locfcinfo.isnull = false; cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo)); if (locfcinfo.isnull) return (Datum)0; /* force NULL result */ if (cmpresult != 0) break; /* no need to compare remaining columns */ i++; } switch (rctype) { /* EQ and NE cases aren't allowed here */ case ROWCOMPARE_LT: result = (cmpresult < 0); break; case ROWCOMPARE_LE: result = (cmpresult <= 0); break; case ROWCOMPARE_GE: result = (cmpresult >= 0); break; case ROWCOMPARE_GT: result = (cmpresult > 0); break; default: ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized RowCompareType: %d", (int)rctype))); result = 0; /* keep compiler quiet */ break; } *isNull = false; return BoolGetDatum(result); } /* ---------------------------------------------------------------- * ExecEvalCoalesce * ---------------------------------------------------------------- */ static Datum ExecEvalCoalesce( CoalesceExprState* coalesceExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { ListCell* arg = NULL; if (isDone != NULL) *isDone = ExprSingleResult; /* Simply loop through until something NOT NULL is found */ foreach (arg, coalesceExpr->args) { ExprState* e = (ExprState*)lfirst(arg); Datum value; value = ExecEvalExpr(e, econtext, isNull, NULL); if (!*isNull) return value; } /* Else return NULL */ *isNull = true; return (Datum)0; } /* ---------------------------------------------------------------- * ExecEvalMinMax * ---------------------------------------------------------------- */ static Datum ExecEvalMinMax(MinMaxExprState* minmaxExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Datum result = (Datum)0; MinMaxExpr* minmax = (MinMaxExpr*)minmaxExpr->xprstate.expr; Oid collation = minmax->inputcollid; MinMaxOp op = minmax->op; FunctionCallInfoData locfcinfo; ListCell* arg = NULL; if (isDone != NULL) *isDone = ExprSingleResult; *isNull = true; /* until we get a result */ InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2, collation, NULL, NULL); locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; foreach (arg, minmaxExpr->args) { ExprState* e = (ExprState*)lfirst(arg); Datum value; bool valueIsNull = false; int32 cmpresult; value = ExecEvalExpr(e, econtext, &valueIsNull, NULL); if (valueIsNull) continue; /* ignore NULL inputs */ if (*isNull) { /* first nonnull input, adopt value */ result = value; *isNull = false; } else { /* apply comparison function */ locfcinfo.arg[0] = result; locfcinfo.arg[1] = value; locfcinfo.isnull = false; cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo)); if (locfcinfo.isnull) /* probably should not happen */ continue; if (cmpresult > 0 && op == IS_LEAST) result = value; else if (cmpresult < 0 && op == IS_GREATEST) result = value; } } return result; } /* ---------------------------------------------------------------- * ExecEvalXml * ---------------------------------------------------------------- */ static Datum ExecEvalXml(XmlExprState* xmlExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { XmlExpr* xexpr = (XmlExpr*)xmlExpr->xprstate.expr; Datum value; bool isnull = false; ListCell* arg = NULL; ListCell* narg = NULL; if (isDone != NULL) *isDone = ExprSingleResult; *isNull = true; /* until we get a result */ switch (xexpr->op) { case IS_XMLCONCAT: { List* values = NIL; foreach (arg, xmlExpr->args) { ExprState* e = (ExprState*)lfirst(arg); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (!isnull) values = lappend(values, DatumGetPointer(value)); } if (list_length(values) > 0) { *isNull = false; return PointerGetDatum(xmlconcat(values)); } else return (Datum)0; } break; case IS_XMLFOREST: { StringInfoData buf; initStringInfo(&buf); forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names) { ExprState* e = (ExprState*)lfirst(arg); char* argname = strVal(lfirst(narg)); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (!isnull) { appendStringInfo(&buf, "<%s>%s", argname, map_sql_value_to_xml_value(value, exprType((Node*)e->expr), true), argname); *isNull = false; } } if (*isNull) { pfree_ext(buf.data); return (Datum)0; } else { text* result = NULL; result = cstring_to_text_with_len(buf.data, buf.len); pfree_ext(buf.data); return PointerGetDatum(result); } } break; case IS_XMLELEMENT: *isNull = false; return PointerGetDatum(xmlelement(xmlExpr, econtext)); break; case IS_XMLPARSE: { ExprState* e = NULL; text* data = NULL; bool preserve_whitespace = false; /* arguments are known to be text, bool */ Assert(list_length(xmlExpr->args) == 2); e = (ExprState*)linitial(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (isnull) return (Datum)0; data = DatumGetTextP(value); e = (ExprState*)lsecond(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (isnull) /* probably can't happen */ return (Datum)0; preserve_whitespace = DatumGetBool(value); *isNull = false; return PointerGetDatum(xmlparse(data, xexpr->xmloption, preserve_whitespace)); } break; case IS_XMLPI: { ExprState* e = NULL; text* argument = NULL; /* optional argument is known to be text */ Assert(list_length(xmlExpr->args) <= 1); if (xmlExpr->args) { e = (ExprState*)linitial(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (isnull) argument = NULL; else argument = DatumGetTextP(value); } else { argument = NULL; isnull = false; } return PointerGetDatum(xmlpi(xexpr->name, argument, isnull, isNull)); } break; case IS_XMLROOT: { ExprState* e = NULL; xmltype* data = NULL; text* version = NULL; int standalone; /* arguments are known to be xml, text, int */ Assert(list_length(xmlExpr->args) == 3); e = (ExprState*)linitial(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (isnull) return (Datum)0; data = DatumGetXmlP(value); e = (ExprState*)lsecond(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (isnull) version = NULL; else version = DatumGetTextP(value); e = (ExprState*)lthird(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); standalone = DatumGetInt32(value); *isNull = false; return PointerGetDatum(xmlroot(data, version, standalone)); } break; case IS_XMLSERIALIZE: { ExprState* e = NULL; /* argument type is known to be xml */ Assert(list_length(xmlExpr->args) == 1); e = (ExprState*)linitial(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (isnull) return (Datum)0; *isNull = false; return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption)); } break; case IS_DOCUMENT: { ExprState* e = NULL; /* optional argument is known to be xml */ Assert(list_length(xmlExpr->args) == 1); e = (ExprState*)linitial(xmlExpr->args); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (isnull) return (Datum)0; else { *isNull = false; return BoolGetDatum(xml_is_document(DatumGetXmlP(value))); } } break; default: break; } ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized XML operation %d", xexpr->op))); return (Datum)0; } /* ---------------------------------------------------------------- * ExecEvalNullIf * * 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 Datum ExecEvalNullIf(FuncExprState* nullIfExpr, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Datum result; FunctionCallInfo fcinfo; ExprDoneCond argDone; if (isDone != NULL) *isDone = ExprSingleResult; /* * Initialize function cache if first time through */ if (nullIfExpr->func.fn_oid == InvalidOid) { NullIfExpr* op = (NullIfExpr*)nullIfExpr->xprstate.expr; init_fcache(op->opfuncid, op->inputcollid, nullIfExpr, econtext->ecxt_per_query_memory, true); Assert(!nullIfExpr->func.fn_retset); } /* * Evaluate arguments */ fcinfo = &nullIfExpr->fcinfo_data; argDone = ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext); if (argDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("NULLIF does not support set arguments"))); Assert(fcinfo->nargs == 2); /* if either argument is NULL they can't be equal */ if (!fcinfo->argnull[0] && !fcinfo->argnull[1]) { fcinfo->isnull = false; result = FunctionCallInvoke(fcinfo); /* if the arguments are equal return null */ if (!fcinfo->isnull && DatumGetBool(result)) { *isNull = true; return (Datum)0; } } /* else return first argument */ *isNull = fcinfo->argnull[0]; return fcinfo->arg[0]; } static Datum CheckRowTypeIsNull(TupleDesc tupDesc, HeapTupleData tmptup, NullTest *ntest) { int att; for (att = 1; att <= tupDesc->natts; att++) { /* ignore dropped columns */ if (tupDesc->attrs[att - 1]->attisdropped) continue; if (tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) { /* null field disproves IS NOT NULL */ if (ntest->nulltesttype == IS_NOT_NULL) return BoolGetDatum(false); } else { /* non-null field disproves IS NULL */ if (ntest->nulltesttype == IS_NULL) return BoolGetDatum(false); } } return BoolGetDatum(true); } static Datum CheckRowTypeIsNullForAFormat(TupleDesc tupDesc, HeapTupleData tmptup, NullTest *ntest) { int att; for (att = 1; att <= tupDesc->natts; att++) { /* ignore dropped columns */ if (tupDesc->attrs[att - 1]->attisdropped) continue; if (!tableam_tops_tuple_attisnull(&tmptup, att, tupDesc)) { /* non-null field disproves IS NULL */ if (ntest->nulltesttype == IS_NULL) { return BoolGetDatum(false); } else { return BoolGetDatum(true); } } } /* non-null field disproves IS NULL */ if (ntest->nulltesttype == IS_NULL) { return BoolGetDatum(true); } else { return BoolGetDatum(false); } } /* ---------------------------------------------------------------- * ExecEvalNullTest * * Evaluate a NullTest node. * ---------------------------------------------------------------- */ static Datum ExecEvalNullTest(NullTestState* nstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { NullTest* ntest = (NullTest*)nstate->xprstate.expr; Datum result; result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return result; /* nothing to check */ if (ntest->argisrow && !(*isNull)) { /* * The SQL standard defines IS [NOT] NULL for a non-null rowtype * argument as: * * "R IS NULL" is true if every field is the null value. * * "R IS NOT NULL" is true if no field is the null value. * * This definition is (apparently intentionally) not recursive; so our * tests on the fields are primitive attisnull tests, not recursive * checks to see if they are all-nulls or no-nulls rowtypes. * * The standard does not consider the possibility of zero-field rows, * but here we consider them to vacuously satisfy both predicates. * * e.g. * r | isnull | isnotnull * -------------+--------+----------- * (1,"(1,2)") | f | t * (1,"(,)") | f | t * (1,) | f | f * (,"(1,2)") | f | f * (,"(,)") | f | f * (,) | t | f * */ HeapTupleHeader tuple; Oid tupType; int32 tupTypmod; TupleDesc tupDesc; HeapTupleData tmptup; tuple = DatumGetHeapTupleHeader(result); tupType = HeapTupleHeaderGetTypeId(tuple); tupTypmod = HeapTupleHeaderGetTypMod(tuple); /* Lookup tupdesc if first time through or if type changes */ tupDesc = get_cached_rowtype(tupType, tupTypmod, &nstate->argdesc, econtext); /* * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); tmptup.t_data = tuple; if (AFORMAT_NULL_TEST_MODE) { return CheckRowTypeIsNullForAFormat(tupDesc, tmptup, ntest); } else { return CheckRowTypeIsNull(tupDesc, tmptup, ntest); } } else { /* Simple scalar-argument case, or a null rowtype datum */ switch (ntest->nulltesttype) { case IS_NULL: if (*isNull) { *isNull = false; return BoolGetDatum(true); } else return BoolGetDatum(false); case IS_NOT_NULL: if (*isNull) { *isNull = false; return BoolGetDatum(false); } else return BoolGetDatum(true); default: ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized nulltesttype: %d", (int)ntest->nulltesttype))); return (Datum)0; /* keep compiler quiet */ } } } /* ---------------------------------------------------------------- * ExecEvalHashFilter * * Evaluate a HashFilter node. * ---------------------------------------------------------------- */ static Datum ExecEvalHashFilter(HashFilterState* hstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { HashFilter* htest = (HashFilter*)hstate->xprstate.expr; Datum result = 0; Datum value = 0; uint64 hashValue = 0; int modulo = 0; int nodeIndex = 0; ListCell *distkey = NULL; ListCell *vartypes = NULL; bool isFirst = true; bool hasNonNullValue = false; if (isDone != NULL) *isDone = ExprSingleResult; *isNull = true; /* until we get a result */ /* Get every distribute key in arg and compute hash value */ forboth(distkey, hstate->arg, vartypes, htest->typeOids) { ExprState* e = (ExprState*)lfirst(distkey); Oid vartype = (Oid)lfirst_oid(vartypes); value = ExecEvalExpr(e, econtext, isNull, isDone); int null_value_dn_index = (hstate->nodelist != NULL) ? hstate->nodelist[0] : /* fetch first dn in group's dn list */ 0; /* fetch first dn index */ if (*isNull) { if (null_value_dn_index == u_sess->pgxc_cxt.PGXCNodeId) { *isNull = false; result = BoolGetDatum(true); } else result = BoolGetDatum(false); } else { if (isFirst) { hashValue = compute_hash(vartype, value, LOCATOR_TYPE_HASH); isFirst = false; } else { hashValue = (hashValue << 1) | ((hashValue & 0x80000000) ? 1 : 0); hashValue ^= compute_hash(vartype, value, LOCATOR_TYPE_HASH); } hasNonNullValue = true; } } /* If has non null value, it should get nodeId and deside if need filter the value or not. */ if (hasNonNullValue) { modulo = hstate->bucketMap[abs((int)hashValue) & (hstate->bucketCnt - 1)]; nodeIndex = hstate->nodelist[modulo]; /* If there are null value and non null value, and the last value in distkey is null, we should set isNull is false. */ *isNull = false; /* Look into the handles and return correct position in array */ if (nodeIndex == u_sess->pgxc_cxt.PGXCNodeId) return BoolGetDatum(true); else return BoolGetDatum(false); } else /* If all the value is null, return result. */ return result; } /* ---------------------------------------------------------------- * ExecEvalBooleanTest * * Evaluate a BooleanTest node. * ---------------------------------------------------------------- */ static Datum ExecEvalBooleanTest(GenericExprState* bstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { BooleanTest* btest = (BooleanTest*)bstate->xprstate.expr; Datum result; result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return result; /* nothing to check */ switch (btest->booltesttype) { case IS_TRUE: if (*isNull) { *isNull = false; return BoolGetDatum(false); } else if (DatumGetBool(result)) return BoolGetDatum(true); else return BoolGetDatum(false); case IS_NOT_TRUE: if (*isNull) { *isNull = false; return BoolGetDatum(true); } else if (DatumGetBool(result)) return BoolGetDatum(false); else return BoolGetDatum(true); case IS_FALSE: if (*isNull) { *isNull = false; return BoolGetDatum(false); } else if (DatumGetBool(result)) return BoolGetDatum(false); else return BoolGetDatum(true); case IS_NOT_FALSE: if (*isNull) { *isNull = false; return BoolGetDatum(true); } else if (DatumGetBool(result)) return BoolGetDatum(true); else return BoolGetDatum(false); case IS_UNKNOWN: if (*isNull) { *isNull = false; return BoolGetDatum(true); } else return BoolGetDatum(false); case IS_NOT_UNKNOWN: if (*isNull) { *isNull = false; return BoolGetDatum(false); } else return BoolGetDatum(true); default: ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized booltesttype: %d", (int)btest->booltesttype))); return (Datum)0; /* keep compiler quiet */ } } /* * ExecEvalCoerceToDomain * * Test the provided data against the domain constraint(s). If the data * passes the constraint specifications, pass it through (return the * datum) otherwise throw an error. */ static Datum ExecEvalCoerceToDomain( CoerceToDomainState* cstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { CoerceToDomain* ctest = (CoerceToDomain*)cstate->xprstate.expr; Datum result; ListCell* l = NULL; result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return result; /* nothing to check */ foreach (l, cstate->constraints) { DomainConstraintState* con = (DomainConstraintState*)lfirst(l); switch (con->constrainttype) { case DOM_CONSTRAINT_NOTNULL: if (*isNull) ereport(ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("domain %s does not allow null values", format_type_be(ctest->resulttype)))); break; case DOM_CONSTRAINT_CHECK: { Datum conResult; bool conIsNull = false; Datum save_datum; bool save_isNull = false; /* * Set up value to be returned by CoerceToDomainValue * nodes. We must save and restore prior setting of * econtext's domainValue fields, in case this node is * itself within a check expression for another domain. */ save_datum = econtext->domainValue_datum; save_isNull = econtext->domainValue_isNull; econtext->domainValue_datum = result; econtext->domainValue_isNull = *isNull; conResult = ExecEvalExpr(con->check_expr, econtext, &conIsNull, NULL); if (!conIsNull && !DatumGetBool(conResult)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("value for domain %s violates check constraint \"%s\"", format_type_be(ctest->resulttype), con->name))); econtext->domainValue_datum = save_datum; econtext->domainValue_isNull = save_isNull; break; } default: ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized constraint type: %d", (int)con->constrainttype))); break; } } /* If all has gone well (constraints did not fail) return the datum */ return result; } /* * ExecEvalCoerceToDomainValue * * Return the value stored by CoerceToDomain. */ static Datum ExecEvalCoerceToDomainValue( ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { if (isDone != NULL) *isDone = ExprSingleResult; *isNull = econtext->domainValue_isNull; return econtext->domainValue_datum; } /* ---------------------------------------------------------------- * ExecEvalFieldSelect * * Evaluate a FieldSelect node. * ---------------------------------------------------------------- */ static Datum ExecEvalFieldSelect(FieldSelectState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { FieldSelect* fselect = (FieldSelect*)fstate->xprstate.expr; AttrNumber fieldnum = fselect->fieldnum; Datum result; Datum tupDatum; HeapTupleHeader tuple; Oid tupType; int32 tupTypmod; TupleDesc tupDesc; Form_pg_attribute attr; HeapTupleData tmptup; tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); /* this test covers the isDone exception too: */ if (*isNull) return tupDatum; tuple = DatumGetHeapTupleHeader(tupDatum); tupType = HeapTupleHeaderGetTypeId(tuple); tupTypmod = HeapTupleHeaderGetTypMod(tuple); /* Lookup tupdesc if first time through or if type changes */ tupDesc = get_cached_rowtype(tupType, tupTypmod, &fstate->argdesc, econtext); /* * Find field's attr record. Note we don't support system columns here: a * datum tuple doesn't have valid values for most of the interesting * system columns anyway. */ if (fieldnum <= 0) /* should never happen */ ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmodule(MOD_EXECUTOR), errmsg("unsupported reference to system column %d in FieldSelect", fieldnum))); if (fieldnum > tupDesc->natts) /* should never happen */ ereport(ERROR, (errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR), errmsg("attribute number %d exceeds number of columns %d", fieldnum, tupDesc->natts))); attr = tupDesc->attrs[fieldnum - 1]; /* Check for dropped column, and force a NULL result if so */ if (attr->attisdropped) { *isNull = true; return (Datum)0; } /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */ /* As in ExecEvalScalarVar, we should but can't check typmod */ if (fselect->resulttype != attr->atttypid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("attribute %d has wrong type", fieldnum), errdetail("Table has type %s, but query expects %s.", format_type_be(attr->atttypid), format_type_be(fselect->resulttype)))); /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); tmptup.t_data = tuple; result = tableam_tops_tuple_getattr(&tmptup, fieldnum, tupDesc, isNull); return result; } /* ---------------------------------------------------------------- * ExecEvalFieldStore * * Evaluate a FieldStore node. * ---------------------------------------------------------------- */ static Datum ExecEvalFieldStore(FieldStoreState* fstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { FieldStore* fstore = (FieldStore*)fstate->xprstate.expr; HeapTuple tuple; Datum tupDatum; TupleDesc tupDesc; Datum* values = NULL; bool* isnull = NULL; Datum save_datum; bool save_isNull = false; ListCell* l1 = NULL; ListCell* l2 = NULL; errno_t rc = EOK; tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); if (isDone != NULL && *isDone == ExprEndResult) return tupDatum; /* Lookup tupdesc if first time through or after rescan */ tupDesc = get_cached_rowtype(fstore->resulttype, -1, &fstate->argdesc, econtext); /* Allocate workspace */ values = (Datum*)palloc(tupDesc->natts * sizeof(Datum)); isnull = (bool*)palloc(tupDesc->natts * sizeof(bool)); if (!*isNull) { /* * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We * set all the fields in the struct just in case. */ HeapTupleHeader tuphdr; HeapTupleData tmptup; tuphdr = DatumGetHeapTupleHeader(tupDatum); tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_bucketId = InvalidBktId; #ifdef PGXC tmptup.t_xc_node_id = 0; #endif HeapTupleSetZeroBase(&tmptup); tmptup.t_data = tuphdr; tableam_tops_deform_tuple(&tmptup, tupDesc, values, isnull); } else { /* Convert null input tuple into an all-nulls row */ rc = memset_s(isnull, tupDesc->natts * sizeof(bool), true, tupDesc->natts * sizeof(bool)); securec_check(rc, "\0", "\0"); } /* Result is never null */ *isNull = false; save_datum = econtext->caseValue_datum; save_isNull = econtext->caseValue_isNull; forboth(l1, fstate->newvals, l2, fstore->fieldnums) { ExprState* newval = (ExprState*)lfirst(l1); AttrNumber fieldnum = lfirst_int(l2); Assert(fieldnum > 0 && fieldnum <= tupDesc->natts); /* * Use the CaseTestExpr mechanism to pass down the old value of the * field being replaced; this is needed in case the newval is itself a * FieldStore or ArrayRef that has to obtain and modify the old value. * It's safe to reuse the CASE mechanism because there cannot be a * CASE between here and where the value would be needed, and a field * assignment can't be within a CASE either. (So saving and restoring * the caseValue is just paranoia, but let's do it anyway.) */ econtext->caseValue_datum = values[fieldnum - 1]; econtext->caseValue_isNull = isnull[fieldnum - 1]; values[fieldnum - 1] = ExecEvalExpr(newval, econtext, &isnull[fieldnum - 1], NULL); } econtext->caseValue_datum = save_datum; econtext->caseValue_isNull = save_isNull; tuple = (HeapTuple)tableam_tops_form_tuple(tupDesc, values, isnull, HEAP_TUPLE); pfree_ext(values); pfree_ext(isnull); return HeapTupleGetDatum(tuple); } /* ---------------------------------------------------------------- * ExecEvalRelabelType * * Evaluate a RelabelType node. * ---------------------------------------------------------------- */ static Datum ExecEvalRelabelType(GenericExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); } /* ---------------------------------------------------------------- * ExecEvalCoerceViaIO * * Evaluate a CoerceViaIO node. * ---------------------------------------------------------------- */ static Datum ExecEvalCoerceViaIO(CoerceViaIOState* iostate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Datum result; Datum inputval; char* string = NULL; inputval = ExecEvalExpr(iostate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return inputval; /* nothing to do */ if (*isNull) string = NULL; /* output functions are not called on nulls */ else string = OutputFunctionCall(&iostate->outfunc, inputval); result = InputFunctionCall(&iostate->infunc, string, iostate->intypioparam, -1); /* The input function cannot change the null/not-null status */ return result; } /* ---------------------------------------------------------------- * ExecEvalArrayCoerceExpr * * Evaluate an ArrayCoerceExpr node. * ---------------------------------------------------------------- */ static Datum ExecEvalArrayCoerceExpr( ArrayCoerceExprState* astate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)astate->xprstate.expr; Datum result; ArrayType* array = NULL; FunctionCallInfoData locfcinfo; result = ExecEvalExpr(astate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return result; /* nothing to do */ if (*isNull) return result; /* nothing to do */ /* * If it's binary-compatible, modify the element type in the array header, * but otherwise leave the array as we received it. */ if (!OidIsValid(acoerce->elemfuncid)) { /* Detoast input array if necessary, and copy in any case */ array = DatumGetArrayTypePCopy(result); ARR_ELEMTYPE(array) = astate->resultelemtype; PG_RETURN_ARRAYTYPE_P(array); } /* Detoast input array if necessary, but don't make a useless copy */ array = DatumGetArrayTypeP(result); /* Initialize function cache if first time through */ if (astate->elemfunc.fn_oid == InvalidOid) { AclResult aclresult; /* Check permission to call function */ aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(acoerce->elemfuncid)); /* Set up the primary fmgr lookup information */ fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), econtext->ecxt_per_query_memory); fmgr_info_set_expr((Node*)acoerce, &(astate->elemfunc)); } /* * Use array_map to apply the function to each array element. * * We pass on the desttypmod and isExplicit flags whether or not the * function wants them. * * Note: coercion functions are assumed to not use collation. */ InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3, InvalidOid, NULL, NULL); locfcinfo.arg[0] = PointerGetDatum(array); locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod); locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit); locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; locfcinfo.argnull[2] = false; return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype, astate->amstate); } /* ---------------------------------------------------------------- * ExecEvalCurrentOfExpr * * The planner must convert CURRENT OF into a TidScan qualification. * So, we have to be able to do ExecInitExpr on a CurrentOfExpr, * but we shouldn't ever actually execute it. * ---------------------------------------------------------------- */ static Datum ExecEvalCurrentOfExpr(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_EXECUTOR), errmsg("CURRENT OF cannot be executed"))); return 0; /* keep compiler quiet */ } /* * ExecEvalExprSwitchContext * * Same as ExecEvalExpr, but get into the right allocation context explicitly. */ Datum ExecEvalExprSwitchContext(ExprState* expression, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { Datum retDatum; MemoryContext oldContext; oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); retDatum = ExecEvalExpr(expression, econtext, isNull, isDone); MemoryContextSwitchTo(oldContext); return retDatum; } /* * ExecInitExpr: prepare an expression tree for execution * * This function builds and returns an ExprState tree paralleling the given * Expr node tree. The ExprState tree can then be handed to ExecEvalExpr * for execution. Because the Expr tree itself is read-only as far as * ExecInitExpr and ExecEvalExpr are concerned, several different executions * of the same plan tree can occur concurrently. * * This must be called in a memory context that will last as long as repeated * executions of the expression are needed. Typically the context will be * the same as the per-query context of the associated ExprContext. * * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the * lists of such nodes held by the parent PlanState. Otherwise, we do very * little initialization here other than building the state-node tree. Any * nontrivial work associated with initializing runtime info for a node should * happen during the first actual evaluation of that node. (This policy lets * us avoid work if the node is never actually evaluated.) * * Note: there is no ExecEndExpr function; we assume that any resource * cleanup needed will be handled by just releasing the memory context * in which the state tree is built. Functions that require additional * cleanup work can register a shutdown callback in the ExprContext. * * 'node' is the root of the expression tree to examine * 'parent' is the PlanState node that owns the expression. * * 'parent' may be NULL if we are preparing an expression that is not * associated with a plan tree. (If so, it can't have aggs or subplans.) * This case should usually come through ExecPrepareExpr, not directly here. */ ExprState* ExecInitExpr(Expr* node, PlanState* parent) { if (u_sess->hook_cxt.execInitExprHook != NULL) { ExprState* expr = ((execInitExprFunc)(u_sess->hook_cxt.execInitExprHook))(node, parent); if (expr != NULL) return expr; } ExprState* state = NULL; gstrace_entry(GS_TRC_ID_ExecInitExpr); if (node == NULL) { gstrace_exit(GS_TRC_ID_ExecInitExpr); return NULL; } /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); switch (nodeTag(node)) { case T_Var: /* varattno == InvalidAttrNumber means it's a whole-row Var */ if (((Var*)node)->varattno == InvalidAttrNumber) { WholeRowVarExprState* wstate = makeNode(WholeRowVarExprState); wstate->parent = parent; wstate->wrv_junkFilter = NULL; state = (ExprState*)wstate; state->evalfunc = (ExprStateEvalFunc)ExecEvalWholeRowVar; } else { state = (ExprState*)makeNode(ExprState); state->evalfunc = ExecEvalScalarVar; } break; case T_Const: state = (ExprState*)makeNode(ExprState); state->evalfunc = ExecEvalConst; break; case T_Param: state = (ExprState*)makeNode(ExprState); switch (((Param*)node)->paramkind) { case PARAM_EXEC: state->evalfunc = ExecEvalParamExec; break; case PARAM_EXTERN: state->evalfunc = ExecEvalParamExtern; break; default: ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_EXECUTOR), errmsg("unrecognized paramkind: %d", (int)((Param*)node)->paramkind))); break; } break; case T_CoerceToDomainValue: state = (ExprState*)makeNode(ExprState); state->evalfunc = ExecEvalCoerceToDomainValue; break; case T_CaseTestExpr: state = (ExprState*)makeNode(ExprState); state->evalfunc = ExecEvalCaseTestExpr; break; case T_Aggref: { Aggref* aggref = (Aggref*)node; AggrefExprState* astate = makeNode(AggrefExprState); astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalAggref; if (parent && (IsA(parent, AggState) || IsA(parent, VecAggState))) { AggState* aggstate = (AggState*)parent; int naggs; aggstate->aggs = lcons(astate, aggstate->aggs); naggs = ++aggstate->numaggs; astate->aggdirectargs = (List*)ExecInitExpr((Expr*)aggref->aggdirectargs, parent); astate->args = (List*)ExecInitExpr((Expr*)aggref->args, parent); /* * Complain if the aggregate's arguments contain any * aggregates; nested agg functions are semantically * nonsensical. (This should have been caught earlier, * but we defend against it here anyway.) */ if (naggs != aggstate->numaggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("aggregate function calls cannot be nested"))); } else { /* planner messed up */ ereport(ERROR, (errcode(ERRCODE_INVALID_AGG), errmodule(MOD_OPT), errmsg("Aggref found in non-Agg plan node"))); } state = (ExprState*)astate; } break; case T_GroupingFunc: { GroupingFunc* grp_node = (GroupingFunc*)node; GroupingFuncExprState* grp_state = makeNode(GroupingFuncExprState); Agg* agg = NULL; if (parent && (IsA(parent, AggState) || IsA(parent, VecAggState))) { grp_state->aggstate = (AggState*)parent; agg = (Agg*)(parent->plan); if (agg->groupingSets) grp_state->clauses = grp_node->cols; else grp_state->clauses = NIL; state = (ExprState*)grp_state; state->evalfunc = (ExprStateEvalFunc)ExecEvalGroupingFuncExpr; } else ereport(ERROR, (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), errmodule(MOD_OPT), errmsg("parent of GROUPING is not Agg node"))); } break; case T_GroupingId: { GroupingIdExprState* grp_id_state = makeNode(GroupingIdExprState); if (parent == NULL || !IsA(parent, AggState) || !IsA(parent->plan, Agg)) { ereport(ERROR, (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), errmodule(MOD_OPT), errmsg("parent of GROUPINGID is not Agg node"))); } grp_id_state->aggstate = (AggState*)parent; state = (ExprState*)grp_id_state; state->evalfunc = (ExprStateEvalFunc)ExecEvalGroupingIdExpr; } break; case T_WindowFunc: { WindowFunc* wfunc = (WindowFunc*)node; WindowFuncExprState* wfstate = makeNode(WindowFuncExprState); wfstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalWindowFunc; if (parent && (IsA(parent, WindowAggState) || IsA(parent, VecWindowAggState))) { WindowAggState* winstate = (WindowAggState*)parent; int nfuncs; winstate->funcs = lcons(wfstate, winstate->funcs); nfuncs = ++winstate->numfuncs; if (wfunc->winagg) winstate->numaggs++; wfstate->args = (List*)ExecInitExpr((Expr*)wfunc->args, parent); /* * Complain if the windowfunc's arguments contain any * windowfuncs; nested window functions are semantically * nonsensical. (This should have been caught earlier, * but we defend against it here anyway.) */ if (nfuncs != winstate->numfuncs) ereport( ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("window function calls cannot be nested"))); } else { /* planner messed up */ ereport( ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("WindowFunc found in non-WindowAgg plan node"))); } state = (ExprState*)wfstate; } break; case T_ArrayRef: { ArrayRef* aref = (ArrayRef*)node; ArrayRefExprState* astate = makeNode(ArrayRefExprState); astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayRef; astate->refupperindexpr = (List*)ExecInitExpr((Expr*)aref->refupperindexpr, parent); astate->reflowerindexpr = (List*)ExecInitExpr((Expr*)aref->reflowerindexpr, parent); astate->refexpr = ExecInitExpr(aref->refexpr, parent); astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr, parent); /* do one-time catalog lookups for type info */ astate->refattrlength = get_typlen(aref->refarraytype); get_typlenbyvalalign( aref->refelemtype, &astate->refelemlength, &astate->refelembyval, &astate->refelemalign); state = (ExprState*)astate; } break; case T_FuncExpr: { FuncExpr* funcexpr = (FuncExpr*)node; FuncExprState* fstate = makeNode(FuncExprState); fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFunc; fstate->args = (List*)ExecInitExpr((Expr*)funcexpr->args, parent); fstate->func.fn_oid = InvalidOid; /* not initialized */ state = (ExprState*)fstate; } break; case T_OpExpr: { OpExpr* opexpr = (OpExpr*)node; FuncExprState* fstate = makeNode(FuncExprState); fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalOper; fstate->args = (List*)ExecInitExpr((Expr*)opexpr->args, parent); fstate->func.fn_oid = InvalidOid; /* not initialized */ state = (ExprState*)fstate; } break; case T_DistinctExpr: { DistinctExpr* distinctexpr = (DistinctExpr*)node; FuncExprState* fstate = makeNode(FuncExprState); fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalDistinct; fstate->args = (List*)ExecInitExpr((Expr*)distinctexpr->args, parent); fstate->func.fn_oid = InvalidOid; /* not initialized */ state = (ExprState*)fstate; } break; case T_NullIfExpr: { NullIfExpr* nullifexpr = (NullIfExpr*)node; FuncExprState* fstate = makeNode(FuncExprState); fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullIf; fstate->args = (List*)ExecInitExpr((Expr*)nullifexpr->args, parent); fstate->func.fn_oid = InvalidOid; /* not initialized */ state = (ExprState*)fstate; } break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr* opexpr = (ScalarArrayOpExpr*)node; ScalarArrayOpExprState* sstate = makeNode(ScalarArrayOpExprState); sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalScalarArrayOp; sstate->fxprstate.args = (List*)ExecInitExpr((Expr*)opexpr->args, parent); sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */ sstate->element_type = InvalidOid; /* ditto */ state = (ExprState*)sstate; } break; case T_BoolExpr: { BoolExpr* boolexpr = (BoolExpr*)node; BoolExprState* bstate = makeNode(BoolExprState); switch (boolexpr->boolop) { case AND_EXPR: bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalAnd; break; case OR_EXPR: bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalOr; break; case NOT_EXPR: bstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNot; break; default: ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmodule(MOD_OPT), errmsg("unrecognized boolop: %d", (int)boolexpr->boolop))); break; } bstate->args = (List*)ExecInitExpr((Expr*)boolexpr->args, parent); state = (ExprState*)bstate; } break; case T_SubPlan: { SubPlan* subplan = (SubPlan*)node; SubPlanState* sstate = NULL; if (parent == NULL) ereport(ERROR, (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), errmodule(MOD_OPT), errmsg("SubPlan found with no parent plan"))); sstate = ExecInitSubPlan(subplan, parent); /* Add SubPlanState nodes to parent->subPlan */ parent->subPlan = lappend(parent->subPlan, sstate); state = (ExprState*)sstate; } break; case T_AlternativeSubPlan: { AlternativeSubPlan* asplan = (AlternativeSubPlan*)node; AlternativeSubPlanState* asstate = NULL; if (parent == NULL) ereport(ERROR, (errcode(ERRCODE_PLAN_PARENT_NOT_FOUND), errmodule(MOD_OPT), errmsg("AlternativeSubPlan found with no parent plan"))); asstate = ExecInitAlternativeSubPlan(asplan, parent); state = (ExprState*)asstate; } break; case T_FieldSelect: { FieldSelect* fselect = (FieldSelect*)node; FieldSelectState* fstate = makeNode(FieldSelectState); fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldSelect; fstate->arg = ExecInitExpr(fselect->arg, parent); fstate->argdesc = NULL; state = (ExprState*)fstate; } break; case T_FieldStore: { FieldStore* fstore = (FieldStore*)node; FieldStoreState* fstate = makeNode(FieldStoreState); fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldStore; fstate->arg = ExecInitExpr(fstore->arg, parent); fstate->newvals = (List*)ExecInitExpr((Expr*)fstore->newvals, parent); fstate->argdesc = NULL; state = (ExprState*)fstate; } break; case T_RelabelType: { RelabelType* relabel = (RelabelType*)node; GenericExprState* gstate = makeNode(GenericExprState); gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRelabelType; gstate->arg = ExecInitExpr(relabel->arg, parent); state = (ExprState*)gstate; } break; case T_CoerceViaIO: { CoerceViaIO* iocoerce = (CoerceViaIO*)node; CoerceViaIOState* iostate = makeNode(CoerceViaIOState); Oid iofunc; bool typisvarlena = false; iostate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceViaIO; iostate->arg = ExecInitExpr(iocoerce->arg, parent); /* lookup the result type's input function */ getTypeInputInfo(iocoerce->resulttype, &iofunc, &iostate->intypioparam); fmgr_info(iofunc, &iostate->infunc); /* lookup the input type's output function */ getTypeOutputInfo(exprType((Node*)iocoerce->arg), &iofunc, &typisvarlena); fmgr_info(iofunc, &iostate->outfunc); state = (ExprState*)iostate; } break; case T_ArrayCoerceExpr: { ArrayCoerceExpr* acoerce = (ArrayCoerceExpr*)node; ArrayCoerceExprState* astate = makeNode(ArrayCoerceExprState); astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayCoerceExpr; astate->arg = ExecInitExpr(acoerce->arg, parent); astate->resultelemtype = get_element_type(acoerce->resulttype); if (astate->resultelemtype == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("target type is not an array"))); /* Arrays over domains aren't supported yet */ Assert(getBaseType(astate->resultelemtype) == astate->resultelemtype); astate->elemfunc.fn_oid = InvalidOid; /* not initialized */ astate->amstate = (ArrayMapState*)palloc0(sizeof(ArrayMapState)); state = (ExprState*)astate; } break; case T_ConvertRowtypeExpr: { ConvertRowtypeExpr* convert = (ConvertRowtypeExpr*)node; ConvertRowtypeExprState* cstate = makeNode(ConvertRowtypeExprState); cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalConvertRowtype; cstate->arg = ExecInitExpr(convert->arg, parent); state = (ExprState*)cstate; } break; case T_CaseExpr: { CaseExpr* caseexpr = (CaseExpr*)node; CaseExprState* cstate = makeNode(CaseExprState); List* outlist = NIL; ListCell* l = NULL; cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCase; cstate->arg = ExecInitExpr(caseexpr->arg, parent); foreach (l, caseexpr->args) { CaseWhen* when = (CaseWhen*)lfirst(l); CaseWhenState* wstate = makeNode(CaseWhenState); Assert(IsA(when, CaseWhen)); wstate->xprstate.evalfunc = NULL; /* not used */ wstate->xprstate.expr = (Expr*)when; wstate->expr = ExecInitExpr(when->expr, parent); wstate->result = ExecInitExpr(when->result, parent); outlist = lappend(outlist, wstate); } cstate->args = outlist; cstate->defresult = ExecInitExpr(caseexpr->defresult, parent); state = (ExprState*)cstate; } break; case T_ArrayExpr: { ArrayExpr* arrayexpr = (ArrayExpr*)node; ArrayExprState* astate = makeNode(ArrayExprState); List* outlist = NIL; ListCell* l = NULL; astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArray; foreach (l, arrayexpr->elements) { Expr* e = (Expr*)lfirst(l); ExprState* estate = NULL; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } astate->elements = outlist; /* do one-time catalog lookup for type info */ get_typlenbyvalalign( arrayexpr->element_typeid, &astate->elemlength, &astate->elembyval, &astate->elemalign); state = (ExprState*)astate; } break; case T_RowExpr: { RowExpr* rowexpr = (RowExpr*)node; RowExprState* rstate = makeNode(RowExprState); Form_pg_attribute* attrs = NULL; List* outlist = NIL; ListCell* l = NULL; int i; rstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRow; /* Build tupdesc to describe result tuples */ if (rowexpr->row_typeid == RECORDOID) { /* generic record, use runtime type assignment */ rstate->tupdesc = ExecTypeFromExprList(rowexpr->args, rowexpr->colnames, TAM_HEAP); BlessTupleDesc(rstate->tupdesc); /* we won't need to redo this at runtime */ } else { /* it's been cast to a named type, use that */ rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1); } /* Set up evaluation, skipping any deleted columns */ Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts); attrs = rstate->tupdesc->attrs; i = 0; foreach (l, rowexpr->args) { Expr* e = (Expr*)lfirst(l); ExprState* estate = NULL; if (!attrs[i]->attisdropped) { /* * Guard against ALTER COLUMN TYPE on rowtype since * the RowExpr was created. XXX should we check * typmod too? Not sure we can be sure it'll be the * same. */ if (exprType((Node*)e) != attrs[i]->atttypid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("ROW() column has type %s instead of type %s", format_type_be(exprType((Node*)e)), format_type_be(attrs[i]->atttypid)))); } else { /* * Ignore original expression and insert a NULL. We * don't really care what type of NULL it is, so * always make an int4 NULL. */ e = (Expr*)makeNullConst(INT4OID, -1, InvalidOid); } estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); i++; } rstate->args = outlist; state = (ExprState*)rstate; } break; case T_RowCompareExpr: { RowCompareExpr* rcexpr = (RowCompareExpr*)node; RowCompareExprState* rstate = makeNode(RowCompareExprState); int nopers = list_length(rcexpr->opnos); List* outlist = NIL; ListCell* l = NULL; ListCell* l2 = NULL; ListCell* l3 = NULL; int i; rstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRowCompare; Assert(list_length(rcexpr->largs) == nopers); outlist = NIL; foreach (l, rcexpr->largs) { Expr* e = (Expr*)lfirst(l); ExprState* estate = NULL; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } rstate->largs = outlist; Assert(list_length(rcexpr->rargs) == nopers); outlist = NIL; foreach (l, rcexpr->rargs) { Expr* e = (Expr*)lfirst(l); ExprState* estate = NULL; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } rstate->rargs = outlist; Assert(list_length(rcexpr->opfamilies) == nopers); rstate->funcs = (FmgrInfo*)palloc(nopers * sizeof(FmgrInfo)); rstate->collations = (Oid*)palloc(nopers * sizeof(Oid)); i = 0; forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids) { Oid opno = lfirst_oid(l); Oid opfamily = lfirst_oid(l2); Oid inputcollid = lfirst_oid(l3); int strategy; Oid lefttype; Oid righttype; Oid proc; get_op_opfamily_properties(opno, opfamily, false, &strategy, &lefttype, &righttype); proc = get_opfamily_proc(opfamily, lefttype, righttype, BTORDER_PROC); /* * If we enforced permissions checks on index support * functions, we'd need to make a check here. But the * index support machinery doesn't do that, and neither * does this code. */ fmgr_info(proc, &(rstate->funcs[i])); rstate->collations[i] = inputcollid; i++; } state = (ExprState*)rstate; } break; case T_CoalesceExpr: { CoalesceExpr* coalesceexpr = (CoalesceExpr*)node; CoalesceExprState* cstate = makeNode(CoalesceExprState); List* outlist = NIL; ListCell* l = NULL; cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoalesce; foreach (l, coalesceexpr->args) { Expr* e = (Expr*)lfirst(l); ExprState* estate = NULL; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } cstate->args = outlist; state = (ExprState*)cstate; } break; case T_MinMaxExpr: { MinMaxExpr* minmaxexpr = (MinMaxExpr*)node; MinMaxExprState* mstate = makeNode(MinMaxExprState); List* outlist = NIL; ListCell* l = NULL; TypeCacheEntry* typentry = NULL; mstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalMinMax; foreach (l, minmaxexpr->args) { Expr* e = (Expr*)lfirst(l); ExprState* estate = NULL; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } mstate->args = outlist; /* Look up the btree comparison function for the datatype */ typentry = lookup_type_cache(minmaxexpr->minmaxtype, TYPECACHE_CMP_PROC); if (!OidIsValid(typentry->cmp_proc)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify a comparison function for type %s", format_type_be(minmaxexpr->minmaxtype)))); /* * If we enforced permissions checks on index support * functions, we'd need to make a check here. But the index * support machinery doesn't do that, and neither does this * code. */ fmgr_info(typentry->cmp_proc, &(mstate->cfunc)); state = (ExprState*)mstate; } break; case T_XmlExpr: { XmlExpr* xexpr = (XmlExpr*)node; XmlExprState* xstate = makeNode(XmlExprState); List* outlist = NIL; ListCell* arg = NULL; xstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalXml; outlist = NIL; foreach (arg, xexpr->named_args) { Expr* e = (Expr*)lfirst(arg); ExprState* estate = NULL; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } xstate->named_args = outlist; outlist = NIL; foreach (arg, xexpr->args) { Expr* e = (Expr*)lfirst(arg); ExprState* estate = NULL; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } xstate->args = outlist; state = (ExprState*)xstate; } break; case T_NullTest: { NullTest* ntest = (NullTest*)node; NullTestState* nstate = makeNode(NullTestState); nstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullTest; nstate->arg = ExecInitExpr(ntest->arg, parent); nstate->argdesc = NULL; state = (ExprState*)nstate; } break; case T_HashFilter: { HashFilter* htest = (HashFilter*)node; HashFilterState* hstate = makeNode(HashFilterState); List* outlist = NIL; ListCell* l = NULL; int idx = 0; hstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalHashFilter; foreach (l, htest->arg) { Expr* e = (Expr*)lfirst(l); ExprState* estate = NULL; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } hstate->arg = outlist; hstate->bucketMap = get_bucketmap_by_execnode(parent->plan->exec_nodes, parent->state->es_plannedstmt, &hstate->bucketCnt); hstate->nodelist = (uint2*)palloc(list_length(htest->nodeList) * sizeof(uint2)); foreach (l, htest->nodeList) hstate->nodelist[idx++] = lfirst_int(l); state = (ExprState*)hstate; } break; case T_BooleanTest: { BooleanTest* btest = (BooleanTest*)node; GenericExprState* gstate = makeNode(GenericExprState); gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalBooleanTest; gstate->arg = ExecInitExpr(btest->arg, parent); state = (ExprState*)gstate; } break; case T_CoerceToDomain: { CoerceToDomain* ctest = (CoerceToDomain*)node; CoerceToDomainState* cstate = makeNode(CoerceToDomainState); cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceToDomain; cstate->arg = ExecInitExpr(ctest->arg, parent); cstate->constraints = GetDomainConstraints(ctest->resulttype); state = (ExprState*)cstate; } break; case T_CurrentOfExpr: state = (ExprState*)makeNode(ExprState); state->evalfunc = ExecEvalCurrentOfExpr; break; case T_TargetEntry: { TargetEntry* tle = (TargetEntry*)node; GenericExprState* gstate = makeNode(GenericExprState); gstate->xprstate.evalfunc = NULL; /* not used */ gstate->arg = ExecInitExpr(tle->expr, parent); state = (ExprState*)gstate; } break; case T_List: { List* outlist = NIL; ListCell* l = NULL; foreach (l, (List*)node) { outlist = lappend(outlist, ExecInitExpr((Expr*)lfirst(l), parent)); } /* Don't fall through to the "common" code below */ gstrace_exit(GS_TRC_ID_ExecInitExpr); return (ExprState*)outlist; } case T_Rownum: { RownumState* rnstate = (RownumState*)makeNode(RownumState); rnstate->ps = parent; state = (ExprState*)rnstate; state->evalfunc = (ExprStateEvalFunc)ExecEvalRownum; } break; default: ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized node type: %d when initializing expression.", (int)nodeTag(node)))); state = NULL; /* keep compiler quiet */ break; } /* Common code for all state-node types */ state->expr = node; if (nodeTag(node) != T_TargetEntry) state->resultType = exprType((Node*)node); gstrace_exit(GS_TRC_ID_ExecInitExpr); return state; } /* * ExecPrepareExpr --- initialize for expression execution outside a normal * Plan tree context. * * This differs from ExecInitExpr in that we don't assume the caller is * already running in the EState's per-query context. Also, we run the * passed expression tree through expression_planner() to prepare it for * execution. (In ordinary Plan trees the regular planning process will have * made the appropriate transformations on expressions, but for standalone * expressions this won't have happened.) */ ExprState* ExecPrepareExpr(Expr* node, EState* estate) { ExprState* result = NULL; MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); node = expression_planner(node); result = ExecInitExpr(node, NULL); MemoryContextSwitchTo(oldcontext); return result; } /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- * ExecQual * * Evaluates a conjunctive boolean expression (qual list) and * returns true iff none of the subexpressions are false. * (We also return true if the list is empty.) * * If some of the subexpressions yield NULL but none yield FALSE, * then the result of the conjunction is NULL (ie, unknown) * according to three-valued boolean logic. In this case, * we return the value specified by the "resultForNull" parameter. * * Callers evaluating WHERE clauses should pass resultForNull=FALSE, * since SQL specifies that tuples with null WHERE results do not * get selected. On the other hand, callers evaluating constraint * conditions should pass resultForNull=TRUE, since SQL also specifies * that NULL constraint conditions are not failures. * * NOTE: it would not be correct to use this routine to evaluate an * AND subclause of a boolean expression; for that purpose, a NULL * result must be returned as NULL so that it can be properly treated * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). * This routine is only used in contexts where a complete expression * is being evaluated and we know that NULL can be treated the same * as one boolean result or the other. * * ---------------------------------------------------------------- */ bool ExecQual(List* qual, ExprContext* econtext, bool resultForNull) { bool result = false; MemoryContext oldContext; ListCell* l = NULL; /* * debugging stuff */ EV_printf("ExecQual: qual is "); EV_nodeDisplay(qual); EV_printf("\n"); /* * Run in short-lived per-tuple context while computing expressions. */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * Evaluate the qual conditions one at a time. If we find a FALSE result, * we can stop evaluating and return FALSE --- the AND result must be * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we * can stop and return FALSE --- the AND result must be FALSE or NULL in * that case, and the caller doesn't care which. * * If we get to the end of the list, we can return TRUE. This will happen * when the AND result is indeed TRUE, or when the AND result is NULL (one * or more NULL subresult, with all the rest TRUE) and the caller has * specified resultForNull = TRUE. */ result = true; foreach (l, qual) { ExprState* clause = (ExprState*)lfirst(l); Datum expr_value; bool isNull = false; expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); if (isNull) { if (resultForNull == false) { result = false; /* treat NULL as FALSE */ break; } } else { if (!DatumGetBool(expr_value)) { result = false; /* definitely FALSE */ break; } } } MemoryContextSwitchTo(oldContext); return result; } /* * Number of items in a tlist (including any resjunk items!) */ int ExecTargetListLength(List* targetlist) { /* This used to be more complex, but fjoins are dead */ return list_length(targetlist); } /* * Number of items in a tlist, not including any resjunk items */ int ExecCleanTargetListLength(List* targetlist) { int len = 0; ListCell* tl = NULL; foreach (tl, targetlist) { TargetEntry* curTle = (TargetEntry*)lfirst(tl); Assert(IsA(curTle, TargetEntry)); if (!curTle->resjunk) len++; } return len; } static HeapTuple get_tuple(Relation relation, ItemPointer tid) { Buffer user_buf = InvalidBuffer; HeapTuple tuple = NULL; HeapTuple new_tuple = NULL; /* alloc mem for old tuple and set tuple id */ tuple = (HeapTupleData *)heaptup_alloc(BLCKSZ); tuple->t_data = (HeapTupleHeader)((char *)tuple + HEAPTUPLESIZE); Assert(tid != NULL); tuple->t_self = *tid; if (heap_fetch(relation, SnapshotAny, tuple, &user_buf, false, NULL)) { new_tuple = heapCopyTuple((HeapTuple)tuple, relation->rd_att, NULL); ReleaseBuffer(user_buf); } else { ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("The tuple is not found"), errdetail("Another user is getting tuple or the datum is NULL"))); } heap_freetuple(tuple); return new_tuple; } static void check_huge_clob_paramter(FunctionCallInfoData* fcinfo, bool is_have_huge_clob) { if (!is_have_huge_clob || IsSystemObjOid(fcinfo->flinfo->fn_oid)) { return; } Oid schema_oid = get_func_namespace(fcinfo->flinfo->fn_oid); if (IsPackageSchemaOid(schema_oid)) { return; } ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("huge clob do not support as function in parameter"))); } bool is_external_clob(Oid type_oid, bool is_null, Datum value) { if (type_oid == CLOBOID && !is_null && VARATT_IS_EXTERNAL_LOB(value)) { return true; } return false; } bool is_huge_clob(Oid type_oid, bool is_null, Datum value) { if (!is_external_clob(type_oid, is_null, value)) { return false; } struct varatt_lob_pointer* lob_pointer = (varatt_lob_pointer*)(VARDATA_EXTERNAL(value)); bool is_huge_clob = false; /* get relation by relid */ ItemPointerData tuple_ctid; tuple_ctid.ip_blkid.bi_hi = lob_pointer->bi_hi; tuple_ctid.ip_blkid.bi_lo = lob_pointer->bi_lo; tuple_ctid.ip_posid = lob_pointer->ip_posid; Relation relation = heap_open(lob_pointer->relid, RowExclusiveLock); HeapTuple origin_tuple = get_tuple(relation, &tuple_ctid); if (!HeapTupleIsValid(origin_tuple)) { ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for tuple from relation %u", lob_pointer->relid))); } bool attr_is_null = false; Datum attr = fastgetattr(origin_tuple, lob_pointer->columid, relation->rd_att, &attr_is_null); if (!attr_is_null && VARATT_IS_HUGE_TOAST_POINTER(attr)) { is_huge_clob = true; } heap_close(relation, NoLock); heap_freetuple(origin_tuple); return is_huge_clob; } Datum fetch_lob_value_from_tuple(varatt_lob_pointer* lob_pointer, Oid update_oid, bool* is_null) { /* get relation by relid */ ItemPointerData tuple_ctid; tuple_ctid.ip_blkid.bi_hi = lob_pointer->bi_hi; tuple_ctid.ip_blkid.bi_lo = lob_pointer->bi_lo; tuple_ctid.ip_posid = lob_pointer->ip_posid; Relation relation = heap_open(lob_pointer->relid, RowExclusiveLock); HeapTuple origin_tuple = get_tuple(relation, &tuple_ctid); if (!HeapTupleIsValid(origin_tuple)) { ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for tuple from relation %u", lob_pointer->relid))); } Datum attr = fastgetattr(origin_tuple, lob_pointer->columid, relation->rd_att, is_null); if (!OidIsValid(update_oid)) { heap_close(relation, NoLock); return attr; } Datum new_attr = (Datum)0; if (*is_null) { new_attr = (Datum)0; } else { if (VARATT_IS_HUGE_TOAST_POINTER(attr)) { if (unlikely(origin_tuple->tupTableType == UHEAP_TUPLE)) { ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("UStore cannot update clob column that larger than 1GB"))); } Relation update_rel = heap_open(update_oid, RowExclusiveLock); struct varlena *old_value = (struct varlena *)DatumGetPointer(attr); struct varlena *new_value = heap_tuple_fetch_and_copy(update_rel, old_value, false); new_attr = PointerGetDatum(new_value); heap_close(update_rel, NoLock); } else if (VARATT_IS_SHORT(attr) || VARATT_IS_EXTERNAL(attr) || VARATT_IS_4B(attr)) { new_attr = PointerGetDatum(attr); } else { ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("lob value which fetch from tuple type is not recognized."), errdetail("lob type is not one of the existing types"))); } } heap_close(relation, NoLock); return new_attr; } /* * ExecTargetList * Evaluates a targetlist with respect to the given * expression context. Returns TRUE if we were able to create * a result, FALSE if we have exhausted a set-valued expression. * * Results are stored into the passed values and isnull arrays. * The caller must provide an itemIsDone array that persists across calls. * * As with ExecEvalExpr, the caller should pass isDone = NULL if not * prepared to deal with sets of result tuples. Otherwise, a return * of *isDone = ExprMultipleResult signifies a set element, and a return * of *isDone = ExprEndResult signifies end of the set of tuple. * We assume that *isDone has been initialized to ExprSingleResult by caller. */ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* values, bool* isnull, ExprDoneCond* itemIsDone, ExprDoneCond* isDone) { MemoryContext oldContext; ListCell* tl = NULL; bool haveDoneSets = false; /* * Run in short-lived per-tuple context while computing expressions. */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * evaluate all the expressions in the target list */ haveDoneSets = false; /* any exhausted set exprs in tlist? */ foreach (tl, targetlist) { GenericExprState* gstate = (GenericExprState*)lfirst(tl); TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; AttrNumber resind = tle->resno - 1; ELOG_FIELD_NAME_START(tle->resname); values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); if (T_Var == nodeTag(tle->expr) && !isnull[resind]) { Var *var = (Var *)tle->expr; if (var->vartype == TIDOID) { Assert(ItemPointerIsValid((ItemPointer)values[resind])); } } bool isClobAndNotNull = false; isClobAndNotNull = (IsA(tle->expr, Param)) && (!isnull[resind]) && (((Param*)tle->expr)->paramtype == CLOBOID || ((Param*)tle->expr)->paramtype == BLOBOID); if (isClobAndNotNull) { /* if is big lob, fetch and copy from toast */ if (VARATT_IS_HUGE_TOAST_POINTER(values[resind])) { Datum new_attr = (Datum)0; Oid update_oid = econtext->ecxt_scantuple != NULL ? ((HeapTuple)(econtext->ecxt_scantuple->tts_tuple))->t_tableOid : InvalidOid; Relation update_rel = heap_open(update_oid, RowExclusiveLock); struct varlena *old_value = (struct varlena *)DatumGetPointer(values[resind]); struct varlena *new_value = heap_tuple_fetch_and_copy(update_rel, old_value,true); new_attr = PointerGetDatum(new_value); heap_close(update_rel, NoLock); values[resind] = new_attr; } } ELOG_FIELD_NAME_END; if (itemIsDone[resind] != ExprSingleResult) { /* We have a set-valued expression in the tlist */ if (isDone == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context when calculate targetlist that cannot accept a " "set"))); if (itemIsDone[resind] == ExprMultipleResult) { /* we have undone sets in the tlist, set flag */ *isDone = ExprMultipleResult; } else { /* we have done sets in the tlist, set flag for that */ haveDoneSets = true; } } } if (haveDoneSets) { /* * note: can't get here unless we verified isDone != NULL */ if (*isDone == ExprSingleResult) { /* * all sets are done, so report that tlist expansion is complete. */ *isDone = ExprEndResult; MemoryContextSwitchTo(oldContext); return false; } else { /* * We have some done and some undone sets. Restart the done ones * so that we can deliver a tuple (if possible). */ foreach (tl, targetlist) { GenericExprState* gstate = (GenericExprState*)lfirst(tl); TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; AttrNumber resind = tle->resno - 1; if (itemIsDone[resind] == ExprEndResult) { values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); if (itemIsDone[resind] == ExprEndResult) { /* * Oh dear, this item is returning an empty set. Guess * we can't make a tuple after all. */ *isDone = ExprEndResult; break; } } } /* * If we cannot make a tuple because some sets are empty, we still * have to cycle the nonempty sets to completion, else resources * will not be released from subplans etc. * * XXX is that still necessary? */ if (*isDone == ExprEndResult) { foreach (tl, targetlist) { GenericExprState* gstate = (GenericExprState*)lfirst(tl); TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; AttrNumber resind = tle->resno - 1; while (itemIsDone[resind] == ExprMultipleResult) { values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); } } MemoryContextSwitchTo(oldContext); return false; } } } /* Report success */ MemoryContextSwitchTo(oldContext); return true; } /* * ExecProject * * projects a tuple based on projection info and stores * it in the previously specified tuple table slot. * * Note: the result is always a virtual tuple; therefore it * may reference the contents of the exprContext's scan tuples * and/or temporary results constructed in the exprContext. * If the caller wishes the result to be valid longer than that * data will be valid, he must call ExecMaterializeSlot on the * result slot. */ TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone) { /* * sanity checks */ Assert(projInfo != NULL); /* * get the projection info we want */ TupleTableSlot *slot = projInfo->pi_slot; ExprContext *econtext = projInfo->pi_exprContext; /* Assume single result row until proven otherwise */ if (isDone != NULL) *isDone = ExprSingleResult; /* * Clear any former contents of the result slot. This makes it safe for * us to use the slot's Datum/isnull arrays as workspace. (Also, we can * return the slot as-is if we decide no rows can be projected.) */ (void)ExecClearTuple(slot); /* * Force extraction of all input values that we'll need. The * Var-extraction loops below depend on this, and we are also prefetching * all attributes that will be referenced in the generic expressions. */ if (projInfo->pi_lastInnerVar > 0) { tableam_tslot_getsomeattrs(econtext->ecxt_innertuple, projInfo->pi_lastInnerVar); } if (projInfo->pi_lastOuterVar > 0) { tableam_tslot_getsomeattrs(econtext->ecxt_outertuple, projInfo->pi_lastOuterVar); } if (projInfo->pi_lastScanVar > 0) { tableam_tslot_getsomeattrs(econtext->ecxt_scantuple, projInfo->pi_lastScanVar); } /* * Assign simple Vars to result by direct extraction of fields from source * slots ... a mite ugly, but fast ... */ int numSimpleVars = projInfo->pi_numSimpleVars; if (numSimpleVars > 0) { Datum* values = slot->tts_values; bool* isnull = slot->tts_isnull; int* varSlotOffsets = projInfo->pi_varSlotOffsets; int* varNumbers = projInfo->pi_varNumbers; int i; if (projInfo->pi_directMap) { /* especially simple case where vars go to output in order */ for (i = 0; i < numSimpleVars; i++) { char* slotptr = ((char*)econtext) + varSlotOffsets[i]; TupleTableSlot* varSlot = *((TupleTableSlot**)slotptr); int varNumber = varNumbers[i] - 1; Assert (varNumber < varSlot->tts_tupleDescriptor->natts); Assert (i < slot->tts_tupleDescriptor->natts); values[i] = varSlot->tts_values[varNumber]; isnull[i] = varSlot->tts_isnull[varNumber]; } } else { /* we have to pay attention to varOutputCols[] */ int* varOutputCols = projInfo->pi_varOutputCols; for (i = 0; i < numSimpleVars; i++) { char* slotptr = ((char*)econtext) + varSlotOffsets[i]; TupleTableSlot* varSlot = *((TupleTableSlot**)slotptr); int varNumber = varNumbers[i] - 1; int varOutputCol = varOutputCols[i] - 1; Assert (varNumber < varSlot->tts_tupleDescriptor->natts); Assert (varOutputCol < slot->tts_tupleDescriptor->natts); values[varOutputCol] = varSlot->tts_values[varNumber]; isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; } } } /* * If there are any generic expressions, evaluate them. It's possible * that there are set-returning functions in such expressions; if so and * we have reached the end of the set, we return the result slot, which we * already marked empty. */ if (projInfo->pi_targetlist) { if (!ExecTargetList( projInfo->pi_targetlist, econtext, slot->tts_values, slot->tts_isnull, projInfo->pi_itemIsDone, isDone)) return slot; /* no more result rows, return empty slot */ } /* * Successfully formed a result row. Mark the result slot as containing a * valid virtual tuple. */ return ExecStoreVirtualTuple(slot); } static Datum ExecEvalGroupingIdExpr( GroupingIdExprState* gstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { int groupingId = 0; if (isDone != NULL) { *isDone = ExprSingleResult; } *isNull = false; for (int i = 0; i < gstate->aggstate->current_phase; i++) { groupingId += gstate->aggstate->phases[i].numsets; } groupingId += gstate->aggstate->projected_set + 1; return (Datum)groupingId; } /* * @Description: copy cursor data from estate->datums to target_cursor * @in datums - estate->datums * @in dno - varno in datums * @in target_cursor - target cursor data * @return -void */ void ExecCopyDataFromDatum(PLpgSQL_datum** datums, int dno, Cursor_Data* target_cursor) { PLpgSQL_var *cursor_var = (PLpgSQL_var *)(datums[dno]); /* only copy cursor option to refcursor */ if (cursor_var->datatype->typoid != REFCURSOROID) { return; } cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ISOPEN]); target_cursor->is_open = DatumGetBool(cursor_var->value); cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_FOUND]); target_cursor->found = DatumGetBool(cursor_var->value); target_cursor->null_fetch = cursor_var->isnull; cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_NOTFOUND]); target_cursor->not_found = DatumGetBool(cursor_var->value); cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ROWCOUNT]); target_cursor->row_count = DatumGetInt32(cursor_var->value); target_cursor->null_open = cursor_var->isnull; target_cursor->cur_dno = dno; } /* * @Description: copy cursor data to estate->datums * @in datums - estate->datums * @in dno - varno in datums * @in target_cursor - source cursor data * @return -void */ void ExecCopyDataToDatum(PLpgSQL_datum** datums, int dno, Cursor_Data* source_cursor) { PLpgSQL_var *cursor_var = (PLpgSQL_var *)(datums[dno]); /* only copy cursor option to refcursor */ if (cursor_var->datatype->typoid != REFCURSOROID) { return; } cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ISOPEN]); cursor_var->value = BoolGetDatum(source_cursor->is_open); cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_FOUND]); cursor_var->value = BoolGetDatum(source_cursor->found); cursor_var->isnull = source_cursor->null_fetch; cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_NOTFOUND]); cursor_var->value = BoolGetDatum(source_cursor->not_found); cursor_var->isnull = source_cursor->null_fetch; cursor_var = (PLpgSQL_var*)(datums[dno + CURSOR_ROWCOUNT]); cursor_var->value = Int32GetDatum(source_cursor->row_count); cursor_var->isnull = source_cursor->null_open; }