Files
openGauss-server/src/gausskernel/runtime/executor/execQual.cpp
2023-09-12 17:41:11 +08:00

7235 lines
276 KiB
C++

/* -------------------------------------------------------------------------
*
* 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 "executor/executor.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"
#include "parser/parse_expr.h"
#include "auditfuncs.h"
#include "rewrite/rewriteHandler.h"
/* static function decls */
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 <bool vectorized>
void init_fcache(
Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool allowSRF, bool needDescForSets);
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 <bool has_refcursor>
static ExprDoneCond ExecEvalFuncArgs(
FunctionCallInfo fcinfo, List* argList, ExprContext* econtext, int* plpgsql_var_dno = NULL);
void ExecPrepareTuplestoreResult(
FuncExprState* fcache, ExprContext* econtext, Tuplestorestate* resultStore, TupleDesc resultDesc);
static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
template <bool has_refcursor, bool has_cursor_return, bool isSetReturnFunc>
static Datum ExecMakeFunctionResult(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone);
template <bool has_refcursor, bool has_cursor_return>
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);
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).
* ----------
*/
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;
}
int returnNestTableLayer = 0;
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 = (Datum)ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
plpgsql_estate = old_estate;
if (unlikely(execTableOfIndexInfo.tableOfIndexType))
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;
returnNestTableLayer = var->nest_layers;
if (plpgsql_estate)
plpgsql_estate->curr_nested_table_type = var->datatype->typoid;
continue;
}
} else {
returnNestTableLayer = 0;
}
if (index == -1) {
eisnull = true;
} else {
upper.indx[i++] = index;
}
} else {
PLpgSQL_execstate* old_estate = plpgsql_estate;
upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL));
plpgsql_estate = old_estate;
returnNestTableLayer = 0;
}
/* 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 {
PLpgSQL_execstate* old_estate = plpgsql_estate;
lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL));
plpgsql_estate = old_estate;
}
/* 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 (plpgsql_estate) {
plpgsql_estate->curr_nested_table_layers = returnNestTableLayer;
}
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;
}
attnum = variable->varattno;
/* This was checked by ExecInitExpr */
Assert(attnum != InvalidAttrNumber);
RightRefState* refState = econtext->rightRefState;
int index = attnum - 1;
if (refState && refState->values &&
(IS_ENABLE_INSERT_RIGHT_REF(refState) ||
(IS_ENABLE_UPSERT_RIGHT_REF(refState) && refState->hasExecs[index] && index < refState->colCnt))) {
*isNull = refState->isNulls[index];
return refState->values[index];
}
if (slot == nullptr) {
ereport(ERROR, (errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR),
errmsg("attribute number %d does not exists.", attnum)));
}
/*
* 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),
TableAmHeap);
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;
/*
* 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. (Note: we must
* do this here, not in ExecEvalWholeRowVar, because some plan trees may
* return different slots at different times. We have to be ready to
* bless additional slots during the run.)
*/
if (variable->vartype == RECORDOID &&
tupleDesc->tdtypeid == RECORDOID &&
tupleDesc->tdtypmod < 0)
assign_record_type_typmod(tupleDesc);
/*
* 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 = NULL;
if (IsA(exprstate->expr, UserVar)) {
bool found = false;
UserVar *uservar = (UserVar *)exprstate->expr;
GucUserParamsEntry *entry = (GucUserParamsEntry *)hash_search(u_sess->utils_cxt.set_user_params_htab, uservar->name, HASH_FIND, &found);
/* if not found, return a null const */
if (found) {
if (entry->isParse) {
con = (Const *)uservar->value;
} else {
Node *node = coerce_type(NULL, (Node *)entry->value, entry->value->consttype, ((Const *)uservar->value)->consttype,
-1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1);
node = eval_const_expression_value(NULL, node, NULL);
if (nodeTag(node) != T_Const) {
ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION),
errmsg("The value of a user_defined variable must be convertible to a constant.")));
}
con = (Const *)node;
}
} else {
con = makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false);
}
} else if (IsA(exprstate->expr, SetVariableExpr)) {
SetVariableExpr* setvar = (SetVariableExpr*)transformSetVariableExpr((SetVariableExpr*)exprstate->expr);
con = (Const*)setvar->value;
} else {
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);
}
}
/*----------------------------------------------------------------
* find_uservar_in_expr: A recursive function
* For UserSetElemnt like @var := sin(@var), already remember root
* as Sin's exprState, and this function is used to find wheter @var
* is used in here.
* if_use : true means this @var is used inside a correct expression
*/
static void find_uservar_in_expr(ExprState *root, char *return_name, bool *if_use)
{
if(root == NULL) {
return;
}
switch(root->type) {
case T_FuncExprState: {
FuncExprState* parent = (FuncExprState*)root;
ListCell* arg = NULL;
foreach(arg,parent->args) {
ExprState* child = (ExprState*)lfirst(arg);
find_uservar_in_expr(child, return_name, if_use);
}
} break;
case T_ExprState: {
if (root->expr != NULL && root->expr->type == T_UserVar) {
UserVar* temp = (UserVar*)root->expr;
char* usename = temp->name;
if(strcmp(return_name, usename) == 0) {
*if_use = true;
}
}
} break;
case T_AggrefExprState: {
AggrefExprState* parent = (AggrefExprState*)root;
ListCell* arg = NULL;
foreach(arg, parent->args) {
ExprState* child = (ExprState*)lfirst(arg);
find_uservar_in_expr(child, return_name, if_use);
}
} break;
case T_MinMaxExprState: {
MinMaxExprState* parent = (MinMaxExprState*)root;
ListCell* arg = NULL;
foreach(arg, parent->args) {
ExprState* child = (ExprState*)lfirst(arg);
find_uservar_in_expr(child, return_name, if_use);
}
} break;
case T_GenericExprState: {
GenericExprState* parent = (GenericExprState*)root;
find_uservar_in_expr(parent->arg, return_name, if_use);
} break;
case T_CaseExprState: {
CaseExprState* parent = (CaseExprState*)root;
ListCell* arg = NULL;
foreach(arg, parent->args) {
ExprState* child = (ExprState*)lfirst(arg);
find_uservar_in_expr(child, return_name, if_use);
}
} break;
case T_CaseWhenState: {
CaseWhenState* parent = (CaseWhenState*)root;
find_uservar_in_expr(parent->expr, return_name, if_use);
find_uservar_in_expr(parent->result, return_name, if_use);
} break;
case T_WindowFuncExprState: {
WindowFuncExprState* parent = (WindowFuncExprState*)root;
ListCell* arg = NULL;
foreach(arg, parent->args) {
ExprState* child =(ExprState*)lfirst(arg);
find_uservar_in_expr(child, return_name, if_use);
}
} break;
case T_BoolExprState: {
BoolExprState* parent = (BoolExprState*)root;
ListCell* arg = NULL;
foreach(arg, parent->args) {
ExprState* child = (ExprState*)lfirst(arg);
find_uservar_in_expr(child, return_name, if_use);
}
} break;
case T_CoalesceExprState: {
CoalesceExprState* parent = (CoalesceExprState*)root;
ListCell* arg = NULL;
foreach(arg, parent->args) {
ExprState* child = (ExprState*)lfirst(arg);
find_uservar_in_expr(child, return_name, if_use);
}
} break;
case T_List: {
List* parent = (List*)root;
ListCell* arg = NULL;
foreach(arg, parent) {
ExprState* child = (ExprState*)lfirst(arg);
find_uservar_in_expr(child, return_name, if_use);
}
} break;
case T_NullTestState: {
NullTestState* parent = (NullTestState*)root;
find_uservar_in_expr(parent->arg,return_name, if_use);
} break;
case T_SubPlanState:
break;
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_DFS),
errmsg("Unsupported expr type for select @i:= expr.")));
}
}
}
static char* CStringFromDatum(Oid typeoid, Datum d)
{
bool isVarlena;
Oid outOid = InvalidOid;
getTypeOutputInfo(typeoid, &outOid, &isVarlena);
char* outStr = OidOutputFunctionCall(outOid, d);
if (outStr == NULL)
return "";
return outStr;
}
/* ----------------------------------------------------------------
* ExecEvalUserSetElm: set and Returns the user_define variable value
* ----------------------------------------------------------------
*/
static Datum ExecEvalUserSetElm(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
UserSetElemState* usestate = (UserSetElemState*)exprstate;
UserSetElem* elem = usestate->use;
UserSetElem elemcopy;
Oid collid = exprCollation((Node*)elem->val);
elemcopy.xpr = elem->xpr;
elemcopy.name = elem->name;
if (isDone != NULL)
*isDone = ExprSingleResult;
Assert(isNull);
*isNull = false;
bool is_in_table = false;
if (econtext->ecxt_innertuple != NULL || econtext->ecxt_outertuple != NULL ||
econtext->ecxt_scantuple != NULL) {
is_in_table = true;
}
Const* con = NULL;
Node* res = NULL;
char* value = NULL;
Datum result = ExecEvalExpr(usestate->instate, econtext, isNull, isDone);
if (*isNull) {
con = makeConst(UNKNOWNOID, -1, collid, -2, result, true, false);
res = (Node*)con;
elemcopy.val = (Expr*)const_expression_to_const(res);
} else {
bool found = false;
GucUserParamsEntry *entry = NULL;
if (u_sess->utils_cxt.set_user_params_htab != NULL) {
UserVar *uservar = (UserVar*)linitial(elem->name);
entry = (GucUserParamsEntry*)hash_search(u_sess->utils_cxt.set_user_params_htab,
uservar->name, HASH_FIND, &found);
if (found) {
Const* expr = entry->value;
bool if_use = false;
if (expr->consttype != (usestate->xprstate).resultType && is_in_table) {
find_uservar_in_expr(usestate->instate, uservar->name, &if_use);
if (if_use) {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("Can not change type of user defined variable when use relations.")));
}
}
}
}
Oid atttypid = exprType((Node*)elem->val);
value = CStringFromDatum(atttypid, result);
con = processResToConst(value, atttypid, collid);
if (atttypid == BOOLOID)
res = (Node*)con;
else
res = type_transfer((Node *)con, atttypid, true);
elemcopy.val = (Expr*)const_expression_to_const(res);
}
check_set_user_message(&elemcopy);
return result;
}
/* ----------------------------------------------------------------
* 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 = &paramInfo->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;
if (prm->tabInfo && prm->tabInfo->isnestedtable && plpgsql_estate) {
plpgsql_estate->curr_nested_table_type = prm->ptype;
plpgsql_estate->curr_nested_table_layers = prm->tabInfo->tableOfLayers;
}
/* 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 = &paramInfo->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 <bool vectorized>
static void init_fcache(
Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF)
{
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);
/* If function returns set, check if that's allowed by caller */
if (fcache->xprstate.is_flt_frame) {
if (fcache->func.fn_retset && !allowSRF)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set")));
/* Otherwise, ExecInitExpr should have marked the fcache correctly */
Assert(fcache->func.fn_retset == fcache->funcReturnsSet);
}
/* 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 function returns set, check if that's allowed by caller */
if (fcache->xprstate.is_flt_frame) {
if (fcache->func.fn_retset && !allowSRF)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set")));
/* Otherwise, ExecInitExpr should have marked the fcache correctly */
Assert(fcache->func.fn_retset == fcache->funcReturnsSet);
}
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 && needDescForSRF) {
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, TableAmHeap);
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;
fcache->setArgByVal = false;
if(fcache->xprstate.is_flt_frame){
fcache->is_plpgsql_func_with_outparam = is_function_with_plpgsql_language_and_outparam(fcache->func.fn_oid);
fcache->has_refcursor = func_has_refcursor_args(fcache->func.fn_oid, &fcache->fcinfo_data);
}
}
void initVectorFcache(Oid foid, Oid input_collation, FuncExprState* fcache, MemoryContext fcacheCxt)
{
init_fcache<true>(foid, input_collation, fcache, fcacheCxt, false, false);
}
/*
* callback function in case a FuncExpr returning a set needs to be shut down
* before it has been run to completion
*/
extern 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;
fcache->setArgByVal = 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 <bool has_refcursor>
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.
*/
extern 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)));
}
}
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);
}
bool ExecSetArgIsByValue(FunctionCallInfo fcinfo)
{
for (int i = 0; i < fcinfo->nargs; i++) {
if (!fcinfo->argnull[i] && !get_typbyval(fcinfo->argTypes[i])) {
return false;
}
}
return true;
}
/*
* 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 <bool has_refcursor, bool has_cursor_return, bool isSetReturnFunc>
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;
int func_encoding = PG_INVALID_ENCODING;
int db_encoding = PG_INVALID_ENCODING;
econtext->plpgsql_estate = plpgsql_estate;
plpgsql_estate = NULL;
restart:
/* Guard against stack overflow due to overly complex expressions */
check_stack_depth();
if (fcache->xprstate.is_flt_frame) {
/*
* Initialize function cache if first time through. The expression node
* could be either a FuncExpr or an OpExpr.
*/
if (fcache->func.fn_oid == InvalidOid) {
if (IsA(fcache->xprstate.expr, FuncExpr)) {
FuncExpr *func = (FuncExpr *)fcache->xprstate.expr;
init_fcache<false>(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, true, true);
} else if (IsA(fcache->xprstate.expr, OpExpr)) {
OpExpr *op = (OpExpr *)fcache->xprstate.expr;
init_fcache<false>(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, true, true);
} else
elog(ERROR, "unrecognized node type: %d", (int)nodeTag(fcache->xprstate.expr));
}
}
/*
* 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")));
}
econtext->hasSetResultStore = true;
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<true>(fcinfo, arguments, econtext, var_dno);
else
argDone = ExecEvalFuncArgs<false>(fcinfo, arguments, econtext);
if (fcache->xprstate.is_flt_frame) {
if (argDone != ExprSingleResult) {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set")));
return (Datum)0;
}
} else {
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;
}
}
if (fcache->xprstate.is_flt_frame) {
hasSetArg = false;
} else {
hasSetArg = (argDone != ExprSingleResult);
}
fcache->setArgByVal = ExecSetArgIsByValue(fcinfo);
} else {
/* Re-use callinfo from previous evaluation */
hasSetArg = fcache->setHasSetArg;
/* Reset flag (we may set it again below) */
fcache->setArgsValid = false;
}
if (DB_IS_CMPT(B_FORMAT)) {
func_encoding = get_valid_charset_by_collation(fcinfo->fncollation);
db_encoding = GetDatabaseEncoding();
}
/*
* 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;
if (func_encoding != db_encoding) {
DB_ENCODING_SWITCH_TO(func_encoding);
result = FunctionCallInvoke(fcinfo);
DB_ENCODING_SWITCH_BACK(db_encoding);
} else {
result = FunctionCallInvoke(fcinfo);
}
if (AUDIT_SYSTEM_EXEC_ENABLED) {
audit_system_function(fcinfo, AUDIT_OK);
}
*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;
/* arg not by value, memory can not be reset */
if (!fcache->setArgByVal) {
econtext->hasSetResultStore = 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<true>(fcinfo, arguments, econtext, var_dno);
} else {
argDone = ExecEvalFuncArgs<false>(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;
if (func_encoding != db_encoding) {
DB_ENCODING_SWITCH_TO(func_encoding);
result = FunctionCallInvoke(fcinfo);
DB_ENCODING_SWITCH_BACK(db_encoding);
} else {
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 <bool has_refcursor, bool has_cursor_return>
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;
int func_encoding = PG_INVALID_ENCODING;
int db_encoding = PG_INVALID_ENCODING;
#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;
}
if (econtext) {
fcinfo->can_ignore = econtext->can_ignore || (econtext->ecxt_estate && econtext->ecxt_estate->es_plannedstmt &&
econtext->ecxt_estate->es_plannedstmt->hasIgnore);
}
/*
* 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);
fcinfo->swinfo.sw_is_flt_frame = false;
}
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;
}
}
}
if (DB_IS_CMPT(B_FORMAT)) {
func_encoding = get_valid_charset_by_collation(fcinfo->fncollation);
db_encoding = GetDatabaseEncoding();
}
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;
if (func_encoding != db_encoding) {
DB_ENCODING_SWITCH_TO(func_encoding);
result = FunctionCallInvoke(fcinfo); // node will be free at here or else;
DB_ENCODING_SWITCH_BACK(db_encoding);
} else {
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);
}
}
if (func_encoding != db_encoding) {
DB_ENCODING_SWITCH_TO(func_encoding);
result = FunctionCallInvoke(fcinfo);
DB_ENCODING_SWITCH_BACK(db_encoding);
} else {
result = FunctionCallInvoke(fcinfo);
}
}
*isNull = fcinfo->isnull;
if (AUDIT_SYSTEM_EXEC_ENABLED) {
audit_system_function(fcinfo, AUDIT_OK);
}
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
*/
extern 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 */
fcinfo->refcursor_data.return_number = 0;
fcinfo->refcursor_data.returnCursor = NULL;
if (IsSystemObjOid(Funcid) && Funcid != CURSORTOXMLOID && Funcid != CURSORTOXMLSCHEMAOID) {
return false;
}
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);
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;
}
/* func_has_out_param means whether a func with out param and with GUC proc_outparam_override. */
bool func_has_out_param = is_function_with_plpgsql_language_and_outparam((fcinfo->flinfo)->fn_oid);
if (func_has_out_param && (return_refcursor || procStruct->prorettype == REFCURSOROID)) {
fcinfo->refcursor_data.return_number = out_count + 1;
}
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<FuncExprState*>(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<FuncExprState*>(funcexpr))->prokind = 'p';
} else {
(reinterpret_cast<FuncExprState*>(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 (!funcexpr->is_flt_frame && (fcache->func.fn_oid == InvalidOid)) {
FuncExpr* func = (FuncExpr*)fcache->xprstate.expr;
init_fcache<false>(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, true, 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<true>(&fcinfo, fcache->args, econtext, var_dno);
else
argDone = ExecEvalFuncArgs<false>(&fcinfo, fcache->args, econtext);
/* We don't allow sets in the arguments of the table function */
if (!funcexpr->is_flt_frame && (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 (AUDIT_SYSTEM_EXEC_ENABLED) {
audit_system_function(&fcinfo, AUDIT_OK);
}
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, TableAmHeap);
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;
bool has_refcursor = false;
int cursor_return_number = 0;
if (fcache->xprstate.is_flt_frame) {
init_fcache<false>(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, false, false);
has_refcursor = func_has_refcursor_args(func->funcid, &fcache->fcinfo_data);
cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number;
Assert(!fcache->func.fn_retset);
if (has_refcursor) {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, true>;
return ExecMakeFunctionResultNoSets<true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, false>;
return ExecMakeFunctionResultNoSets<true, false>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, true>;
return ExecMakeFunctionResultNoSets<false, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, false>;
return ExecMakeFunctionResultNoSets<false, false>(fcache, econtext, isNull, isDone);
}
}
}
/* Initialize function lookup info */
init_fcache<false>(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, false, true);
has_refcursor = func_has_refcursor_args(func->funcid, &fcache->fcinfo_data);
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<true, true, true>;
return ExecMakeFunctionResult<true, true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, true>;
return ExecMakeFunctionResult<true, false, true>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, true>;
return ExecMakeFunctionResult<false, true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, true>;
return ExecMakeFunctionResult<false, false, true>(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<true, true, false>;
return ExecMakeFunctionResult<true, true, false>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, false>;
return ExecMakeFunctionResult<true, false, false>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, false>;
return ExecMakeFunctionResult<false, true, false>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, false>;
return ExecMakeFunctionResult<false, false, false>(fcache, econtext, isNull, isDone);
}
}
} else {
if (has_refcursor) {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, true>;
return ExecMakeFunctionResultNoSets<true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, false>;
return ExecMakeFunctionResultNoSets<true, false>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, true>;
return ExecMakeFunctionResultNoSets<false, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, false>;
return ExecMakeFunctionResultNoSets<false, false>(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;
int cursor_return_number = 0;
if (fcache->xprstate.is_flt_frame) {
init_fcache<false>(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, false, false);
has_refcursor = func_has_refcursor_args(op->opfuncid, &fcache->fcinfo_data);
cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number;
Assert(!fcache->func.fn_retset);
if (has_refcursor) {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, true>;
return ExecMakeFunctionResultNoSets<true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, false>;
return ExecMakeFunctionResultNoSets<true, false>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, true>;
return ExecMakeFunctionResultNoSets<false, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, false>;
return ExecMakeFunctionResultNoSets<false, false>(fcache, econtext, isNull, isDone);
}
}
}
/* Initialize function lookup info */
init_fcache<false>(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, false, true);
has_refcursor = func_has_refcursor_args(op->opfuncid, &fcache->fcinfo_data);
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<true, true, true>;
return ExecMakeFunctionResult<true, true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, true>;
return ExecMakeFunctionResult<true, false, true>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, true>;
return ExecMakeFunctionResult<false, true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, true>;
return ExecMakeFunctionResult<false, false, true>(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<true, true, false>;
return ExecMakeFunctionResult<true, true, false>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, false>;
return ExecMakeFunctionResult<true, false, false>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, false>;
return ExecMakeFunctionResult<false, true, false>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, false>;
return ExecMakeFunctionResult<false, false, false>(fcache, econtext, isNull, isDone);
}
}
} else {
if (has_refcursor) {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, true>;
return ExecMakeFunctionResultNoSets<true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, false>;
return ExecMakeFunctionResultNoSets<true, false>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, true>;
return ExecMakeFunctionResultNoSets<false, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, false>;
return ExecMakeFunctionResultNoSets<false, false>(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;
if (fcache->xprstate.is_flt_frame) {
init_fcache<false>(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, false, false);
} else {
init_fcache<false>(op->opfuncid, op->inputcollid, fcache, econtext->ecxt_per_query_memory, false, true);
Assert(!fcache->func.fn_retset);
}
}
/*
* Evaluate arguments
*/
fcinfo = &fcache->fcinfo_data;
argDone = ExecEvalFuncArgs<false>(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) {
if (sstate->fxprstate.xprstate.is_flt_frame) {
init_fcache<false>(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate,
econtext->ecxt_per_query_memory, false, false);
} else {
init_fcache<false>(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate,
econtext->ecxt_per_query_memory, false, 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<false>(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;
fcinfo->argTypes[1] = ARR_ELEMTYPE(arr);
}
/* 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);
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</%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;
if (nullIfExpr->xprstate.is_flt_frame) {
init_fcache<false>(op->opfuncid, op->inputcollid, nullIfExpr,
econtext->ecxt_per_query_memory, false, false);
} else {
init_fcache<false>(op->opfuncid, op->inputcollid, nullIfExpr,
econtext->ecxt_per_query_memory, false, true);
Assert(!nullIfExpr->func.fn_retset);
}
}
/*
* Evaluate arguments
*/
fcinfo = &nullIfExpr->fcinfo_data;
argDone = ExecEvalFuncArgs<false>(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);
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 */
}
/* ----------------------------------------------------------------
* ExecEvalPrefixText
* ----------------------------------------------------------------
*/
static Datum ExecEvalPrefixText(PrefixKeyState* state, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
PrefixKey* pkey = (PrefixKey*)state->xprstate.expr;
Datum result = ExecEvalExpr(state->arg, econtext, isNull, isDone);
if (*isNull) {
return (Datum)0;
}
return PointerGetDatum(text_substring_with_encoding(result, 1, pkey->length, false, state->encoding));
}
/* ----------------------------------------------------------------
* ExecEvalPrefixBytea
* ----------------------------------------------------------------
*/
static Datum ExecEvalPrefixBytea(PrefixKeyState* state, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
PrefixKey* pkey = (PrefixKey*)state->xprstate.expr;
Datum result = ExecEvalExpr(state->arg, econtext, isNull, isDone);
if (*isNull) {
return (Datum)0;
}
return PointerGetDatum(bytea_substring(result, 1, pkey->length, false));
}
/*
* 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){
ExprState* state = NULL;
bool is_flt_frame = (parent != NULL) ?
parent->state->es_is_flt_frame :
(u_sess->attr.attr_common.enable_expr_fusion && u_sess->attr.attr_sql.query_dop_tmp ==1);
if(is_flt_frame) {
state = ExecInitExprByFlatten(node, parent);
} else {
state = ExecInitExprByRecursion(node, parent);
}
return state;
}
ExprState* ExecInitExprByRecursion(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:
case T_UserVar:
case T_SetVariableExpr:
state = (ExprState*)makeNode(ExprState);
state->is_flt_frame = false;
state->evalfunc = ExecEvalConst;
break;
case T_Param:
state = (ExprState*)makeNode(ExprState);
state->is_flt_frame = false;
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->is_flt_frame = false;
state->evalfunc = ExecEvalCaseTestExpr;
break;
case T_Aggref: {
Aggref* aggref = (Aggref*)node;
AggrefExprState* astate = makeNode(AggrefExprState);
astate->xprstate.is_flt_frame = false;
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*)ExecInitExprByRecursion((Expr*)aggref->aggdirectargs, parent);
astate->args = (List*)ExecInitExprByRecursion((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);
grp_state->xprstate.is_flt_frame = false;
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);
grp_id_state->xprstate.is_flt_frame = false;
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.is_flt_frame = false;
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*)ExecInitExprByRecursion((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.is_flt_frame = false;
astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayRef;
astate->refupperindexpr = (List*)ExecInitExprByRecursion((Expr*)aref->refupperindexpr, parent);
astate->reflowerindexpr = (List*)ExecInitExprByRecursion((Expr*)aref->reflowerindexpr, parent);
astate->refexpr = ExecInitExprByRecursion(aref->refexpr, parent);
astate->refassgnexpr = ExecInitExprByRecursion(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.is_flt_frame = false;
fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFunc;
fstate->args = (List*)ExecInitExprByRecursion((Expr*)funcexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
fstate->funcReturnsSet = false;
state = (ExprState*)fstate;
} break;
case T_OpExpr: {
OpExpr* opexpr = (OpExpr*)node;
FuncExprState* fstate = makeNode(FuncExprState);
fstate->xprstate.is_flt_frame = false;
fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalOper;
fstate->args = (List*)ExecInitExprByRecursion((Expr*)opexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
fstate->funcReturnsSet = false;
state = (ExprState*)fstate;
} break;
case T_DistinctExpr: {
DistinctExpr* distinctexpr = (DistinctExpr*)node;
FuncExprState* fstate = makeNode(FuncExprState);
fstate->xprstate.is_flt_frame = false;
fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalDistinct;
fstate->args = (List*)ExecInitExprByRecursion((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.is_flt_frame = false;
fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullIf;
fstate->args = (List*)ExecInitExprByRecursion((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.is_flt_frame = false;
sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalScalarArrayOp;
sstate->fxprstate.args = (List*)ExecInitExprByRecursion((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);
bstate->xprstate.is_flt_frame = false;
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*)ExecInitExprByRecursion((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.is_flt_frame = false;
fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldSelect;
fstate->arg = ExecInitExprByRecursion(fselect->arg, parent);
fstate->argdesc = NULL;
state = (ExprState*)fstate;
} break;
case T_FieldStore: {
FieldStore* fstore = (FieldStore*)node;
FieldStoreState* fstate = makeNode(FieldStoreState);
fstate->xprstate.is_flt_frame = false;
fstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalFieldStore;
fstate->arg = ExecInitExprByRecursion(fstore->arg, parent);
fstate->newvals = (List*)ExecInitExprByRecursion((Expr*)fstore->newvals, parent);
fstate->argdesc = NULL;
state = (ExprState*)fstate;
} break;
case T_RelabelType: {
RelabelType* relabel = (RelabelType*)node;
GenericExprState* gstate = makeNode(GenericExprState);
gstate->xprstate.is_flt_frame = false;
gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalRelabelType;
gstate->arg = ExecInitExprByRecursion(relabel->arg, parent);
state = (ExprState*)gstate;
} break;
case T_CoerceViaIO: {
CoerceViaIO* iocoerce = (CoerceViaIO*)node;
CoerceViaIOState* iostate = makeNode(CoerceViaIOState);
iostate->xprstate.is_flt_frame = false;
Oid iofunc;
bool typisvarlena = false;
iostate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceViaIO;
iostate->arg = ExecInitExprByRecursion(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.is_flt_frame = false;
astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArrayCoerceExpr;
astate->arg = ExecInitExprByRecursion(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.is_flt_frame = false;
cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalConvertRowtype;
cstate->arg = ExecInitExprByRecursion(convert->arg, parent);
state = (ExprState*)cstate;
} break;
case T_CaseExpr: {
CaseExpr* caseexpr = (CaseExpr*)node;
CaseExprState* cstate = makeNode(CaseExprState);
cstate->xprstate.is_flt_frame = false;
List* outlist = NIL;
ListCell* l = NULL;
cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCase;
cstate->arg = ExecInitExprByRecursion(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->xprstate.is_flt_frame = false;
wstate->expr = ExecInitExprByRecursion(when->expr, parent);
wstate->result = ExecInitExprByRecursion(when->result, parent);
outlist = lappend(outlist, wstate);
}
cstate->args = outlist;
cstate->defresult = ExecInitExprByRecursion(caseexpr->defresult, parent);
state = (ExprState*)cstate;
} break;
case T_ArrayExpr: {
ArrayExpr* arrayexpr = (ArrayExpr*)node;
ArrayExprState* astate = makeNode(ArrayExprState);
astate->xprstate.is_flt_frame = false;
List* outlist = NIL;
ListCell* l = NULL;
astate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalArray;
foreach (l, arrayexpr->elements) {
Expr* e = (Expr*)lfirst(l);
ExprState* estate = NULL;
estate = ExecInitExprByRecursion(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);
rstate->xprstate.is_flt_frame = false;
FormData_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);
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 = ExecInitExprByRecursion(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);
rstate->xprstate.is_flt_frame = false;
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 = ExecInitExprByRecursion(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 = ExecInitExprByRecursion(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);
cstate->xprstate.is_flt_frame = false;
List* outlist = NIL;
ListCell* l = NULL;
cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoalesce;
foreach (l, coalesceexpr->args) {
Expr* e = (Expr*)lfirst(l);
ExprState* estate = NULL;
estate = ExecInitExprByRecursion(e, parent);
outlist = lappend(outlist, estate);
}
cstate->args = outlist;
state = (ExprState*)cstate;
} break;
case T_MinMaxExpr: {
MinMaxExpr* minmaxexpr = (MinMaxExpr*)node;
MinMaxExprState* mstate = makeNode(MinMaxExprState);
mstate->xprstate.is_flt_frame = false;
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 = ExecInitExprByRecursion(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);
xstate->xprstate.is_flt_frame = false;
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 = ExecInitExprByRecursion(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 = ExecInitExprByRecursion(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.is_flt_frame = false;
nstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalNullTest;
nstate->arg = ExecInitExprByRecursion(ntest->arg, parent);
nstate->argdesc = NULL;
state = (ExprState*)nstate;
} break;
case T_HashFilter: {
HashFilter* htest = (HashFilter*)node;
HashFilterState* hstate = makeNode(HashFilterState);
hstate->xprstate.is_flt_frame = false;
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 = ExecInitExprByRecursion(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.is_flt_frame = false;
gstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalBooleanTest;
gstate->arg = ExecInitExprByRecursion(btest->arg, parent);
state = (ExprState*)gstate;
} break;
case T_CoerceToDomain: {
CoerceToDomain* ctest = (CoerceToDomain*)node;
CoerceToDomainState* cstate = makeNode(CoerceToDomainState);
cstate->xprstate.is_flt_frame = false;
cstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalCoerceToDomain;
cstate->arg = ExecInitExprByRecursion(ctest->arg, parent);
cstate->constraints = GetDomainConstraints(ctest->resulttype);
state = (ExprState*)cstate;
} break;
case T_CurrentOfExpr:
state = (ExprState*)makeNode(ExprState);
state->is_flt_frame = false;
state->evalfunc = ExecEvalCurrentOfExpr;
break;
case T_TargetEntry: {
TargetEntry* tle = (TargetEntry*)node;
GenericExprState* gstate = makeNode(GenericExprState);
gstate->xprstate.is_flt_frame = false;
gstate->xprstate.evalfunc = NULL; /* not used */
gstate->arg = ExecInitExprByRecursion(tle->expr, parent);
state = (ExprState*)gstate;
} break;
case T_List: {
List* outlist = NIL;
ListCell* l = NULL;
foreach (l, (List*)node) {
outlist = lappend(outlist, ExecInitExprByRecursion((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->xprstate.is_flt_frame = false;
rnstate->ps = parent;
state = (ExprState*)rnstate;
state->evalfunc = (ExprStateEvalFunc)ExecEvalRownum;
} break;
case T_PrefixKey: {
PrefixKey* pkey = (PrefixKey*)node;
PrefixKeyState* pstate = makeNode(PrefixKeyState);
Oid argtype = exprType((Node*)pkey->arg);
if (argtype == BYTEAOID || argtype == RAWOID || argtype == BLOBOID) {
pstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalPrefixBytea;
pstate->encoding = PG_INVALID_ENCODING;
} else {
Oid argcollation = exprCollation((Node*)pkey->arg);
pstate->xprstate.evalfunc = (ExprStateEvalFunc)ExecEvalPrefixText;
pstate->encoding = get_valid_charset_by_collation(argcollation);
}
pstate->arg = ExecInitExpr(pkey->arg, parent);
state = (ExprState*)pstate;
} break;
case T_UserSetElem: {
UserSetElem* useexpr = (UserSetElem*)node;
UserSetElemState* usestate = (UserSetElemState*)makeNode(UserSetElemState);
usestate->use = useexpr;
state = (ExprState*)usestate;
state->evalfunc = (ExprStateEvalFunc)ExecEvalUserSetElm;
usestate->instate = ExecInitExpr((Expr *)useexpr->val, parent);
} 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;
}
/*
* Call ExecInitExpr() on a list of expressions, return a list of ExprStates.
*/
List* ExecInitExprList(List *nodes, PlanState *parent)
{
List *result = NIL;
ListCell *lc;
foreach(lc, nodes)
{
Expr *e = (Expr*)lfirst(lc);
result = lappend(result, ExecInitExpr(e, parent));
}
return result;
}
/*
* Call ExecInitExpr() on a list of expressions, return a list of ExprStates.
*/
List* ExecInitExprListByRecursion(List *nodes, PlanState *parent)
{
List *result = NIL;
ListCell *lc;
foreach(lc, nodes)
{
Expr *e = (Expr*)lfirst(lc);
result = lappend(result, ExecInitExprByRecursion(e, parent));
}
return result;
}
/*
* Call ExecInitExpr() on a list of expressions, return a list of ExprStates.
*/
List* ExecInitExprListByFlatten(List *nodes, PlanState *parent)
{
List *result = NIL;
ListCell *lc;
foreach(lc, nodes)
{
Expr *e = (Expr*)lfirst(lc);
result = lappend(result, ExecInitExprByFlatten(e, parent));
}
return result;
}
/*
* 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 is_flt_frame = (qual && IsA(qual, ExprState)) ?
((ExprState *)qual)->is_flt_frame : false;
if(is_flt_frame) {
return ExecQualByFlatten((ExprState*)qual, econtext);
} else {
return ExecQualByRecursion(qual, econtext, resultForNull);
}
}
bool ExecQualByRecursion(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;
}
template<bool resultForNull>
static Datum ExecEvalQual(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone){
List* qual = (List*)exprstate->expr;
bool result = true;
ListCell* l = NULL;
foreach (l, qual){
ExprState* clause = (ExprState*)lfirst(l);
Datum expr_value;
expr_value = ExecEvalExpr(clause, econtext, isNull, isDone);
if(*isNull){
if(resultForNull == false) {
result = false;
}
} else {
if (!DatumGetBool(expr_value)) {
result =false;
break;
}
}
}
return Datum(result);
}
ExprState *ExecInitQualByRecursion(Expr *node, PlanState * parent, bool resultForNull){
ExprState* qualState = makeNode(ExprState);
qualState->is_flt_frame = false;
if(resultForNull)
qualState->evalfunc = ExecEvalQual<true>;
else
qualState->evalfunc = ExecEvalQual<false>;
qualState->expr =(Expr*)ExecInitExprByRecursion(node, parent);
return qualState;
}
/*
* 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;
}
/*
* Return true if objid is a partition oid, and set relationid to objid's parent relation oid.
* Return false if objid is a relation oid.
*/
static bool getRelIdForPartition(Oid objid, Oid *relationid)
{
HeapTuple reltuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objid));
if (HeapTupleIsValid(reltuple)) {
ReleaseSysCache(reltuple);
/* is relation oid */
return false;
} else {
HeapTuple partuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(objid));
if (HeapTupleIsValid(partuple)) {
/* is partition oid */
Form_pg_partition partition = (Form_pg_partition)GETSTRUCT(partuple);
Oid relid = InvalidOid;
if (partition->parttype == PART_OBJ_TYPE_TABLE_PARTITION) {
relid = partition->parentid;
ReleaseSysCache(partuple);
} else if (partition->parttype == PART_OBJ_TYPE_TABLE_SUB_PARTITION) {
Oid partid = partition->parentid;
ReleaseSysCache(partuple);
HeapTuple partuple_subparent = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partid));
if (HeapTupleIsValid(partuple_subparent)) {
relid = ((Form_pg_partition)GETSTRUCT(partuple_subparent))->parentid;
ReleaseSysCache(partuple_subparent);
} else {
/* wrong sub partition oid */
ereport(ERROR, (errcode(ERRCODE_RELATION_OPEN_ERROR),
errmsg("could not fine relation for subpartition OID %u", objid)));
}
}
HeapTuple parenttuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (HeapTupleIsValid(parenttuple)) {
ReleaseSysCache(parenttuple);
*relationid = relid;
return true;
} else {
/* wrong partition oid */
ereport(ERROR, (errcode(ERRCODE_RELATION_OPEN_ERROR),
errmsg("could not fine relation for partition OID %u", objid)));
}
}
}
/* is relation oid */
return false;
}
/*
* 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;
bool haveDoneSets = false;
/*
* Run in short-lived per-tuple context while computing expressions.
*/
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
RightRefState* refState = econtext->rightRefState;
int targetCount = list_length(targetlist);
GenericExprState* targetArr[targetCount];
int colCnt = (IS_ENABLE_RIGHT_REF(refState) && refState->colCnt > 0) ? refState->colCnt : 1;
bool hasExecs[colCnt];
SortTargetListAsArray(refState, targetlist, targetArr);
InitOutputValues(refState, targetArr, values, isnull, targetCount, hasExecs);
/*
* evaluate all the expressions in the target list
*/
haveDoneSets = false; /* any exhausted set exprs in tlist? */
for (GenericExprState* gstate : targetArr) {
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 (IS_ENABLE_RIGHT_REF(refState) && resind < refState->colCnt) {
hasExecs[resind] = true;
}
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 && econtext->ecxt_scantuple != NULL) {
/* 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 = ((HeapTuple)(econtext->ecxt_scantuple->tts_tuple))->t_tableOid;
Oid parent_oid = InvalidOid;
Relation parent_rel = NULL;
Relation part_rel = NULL;
Partition part = NULL;
bool ispartition = getRelIdForPartition(update_oid, &parent_oid);
if (ispartition) {
parent_rel = heap_open(parent_oid, RowExclusiveLock);
part = partitionOpen(parent_rel, update_oid, RowExclusiveLock);
part_rel = partitionGetRelation(parent_rel, part);
} else {
parent_rel = heap_open(update_oid, RowExclusiveLock);
}
struct varlena *old_value = (struct varlena *)DatumGetPointer(values[resind]);
struct varlena *new_value = heap_tuple_fetch_and_copy(part_rel == NULL ? parent_rel : part_rel,
old_value, false);
if (new_value == NULL) {
isnull[resind] = true;
}
new_attr = PointerGetDatum(new_value);
if (ispartition) {
releaseDummyRelation(&part_rel);
partitionClose(parent_rel, part, NoLock);
heap_close(parent_rel, NoLock);
} else {
heap_close(parent_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).
*/
for (GenericExprState* gstate : targetArr) {
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) {
for (GenericExprState* gstate : targetArr) {
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;
}
}
}
if (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) {
econtext->rightRefState->values = nullptr;
econtext->rightRefState->isNulls = nullptr;
econtext->rightRefState->hasExecs = nullptr;
}
/* 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){
if (projInfo->pi_state.is_flt_frame){
return ExecProjectByFlatten(projInfo, isDone);
} else {
return ExecProjectByRecursion(projInfo, isDone);
}
}
TupleTableSlot* ExecProjectByRecursion(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 && econtext->ecxt_scantuple) {
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 && !IS_ENABLE_RIGHT_REF(projInfo->pi_exprContext->rightRefState)) {
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 (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) {
econtext->rightRefState->isUpsert = projInfo->isUpsertHasRightRef;
}
bool flag = !ExecTargetList(projInfo->pi_targetlist, econtext, slot->tts_values,
slot->tts_isnull, projInfo->pi_itemIsDone, isDone);
if (econtext->rightRefState) {
econtext->rightRefState->isUpsert = false;
}
if (flag) {
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;
}