📄 execqual.c
字号:
/*------------------------------------------------------------------------- * * execQual.c * Routines to evaluate qualification and targetlist expressions * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.183.2.3 2006/03/10 01:51:34 tgl Exp $ * *------------------------------------------------------------------------- *//* * 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 ExecEvalVar(), * 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 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 "access/heapam.h"#include "catalog/pg_type.h"#include "commands/typecmds.h"#include "executor/execdebug.h"#include "executor/functions.h"#include "executor/nodeSubplan.h"#include "funcapi.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "optimizer/planmain.h"#include "parser/parse_expr.h"#include "utils/acl.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/typcache.h"/* static function decls */static Datum ExecEvalArrayRef(ArrayRefExprState *astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo, List *argList, ExprContext *econtext);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 ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalNullTest(GenericExprState *nstate, 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);/* ---------------------------------------------------------------- * 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 subexpression, we return NULL when * it's an array reference, or the unmodified source array when it's an * array assignment. This may seem peculiar, but if we return NULL (as was * done in versions up through 7.0) then an assignment like * UPDATE table SET arrayfield[4] = NULL * will result in setting the whole array to NULL, which is certainly not * very desirable. By returning the source array we make the assignment * into a no-op, instead. (Eventually we need to redesign arrays so that * individual elements can be NULL, but for now, let's try to protect users * from shooting themselves in the foot.) * * NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here, * even though that might seem natural, because this code needs to support * both varlena arrays and fixed-length array types. DatumGetArrayTypeP() * only works for the varlena kind. The routines we call in arrayfuncs.c * have to know the difference (that's what they need refattrlength for). *---------- */static DatumExecEvalArrayRef(ArrayRefExprState *astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone){ ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr; ArrayType *array_source; ArrayType *resultArray; bool isAssignment = (arrayRef->refassgnexpr != NULL); bool eisnull; ListCell *l; int i = 0, j = 0; IntArray upper, lower; int *lIndex; 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; } 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, MAXDIM))); upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL)); /* If any index expr yields NULL, result is NULL or source array */ if (eisnull) { if (!isAssignment) { *isNull = true; return (Datum) NULL; } return PointerGetDatum(array_source); } } 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)", i, MAXDIM))); lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL)); /* * If any index expr yields NULL, result is NULL or source array */ if (eisnull) { if (!isAssignment) { *isNull = true; return (Datum) NULL; } return PointerGetDatum(array_source); } } /* this can't happen unless parser messed up */ if (i != j) elog(ERROR, "upper and lower index lists are not same length"); lIndex = lower.indx; } else lIndex = NULL; if (isAssignment) { Datum sourceData; /* * Evaluate the value to be assigned into the array. * * XXX At some point we'll need to look into making the old value of * the array element available via CaseTestExpr, as is done by * ExecEvalFieldStore. This is not needed now but will be needed to * support arrays of composite types; in an assignment to a field of * an array member, the parser would generate a FieldStore that * expects to fetch its input tuple via CaseTestExpr. */ sourceData = ExecEvalExpr(astate->refassgnexpr, econtext, &eisnull, NULL); /* * For now, can't cope with inserting NULL into an array, so make it a * no-op per discussion above... */ if (eisnull) return PointerGetDatum(array_source); /* * For an assignment, if all the subscripts and the input expression * are non-null but the original array is null, then substitute an * empty (zero-dimensional) array and proceed with the assignment. * This only works for varlena arrays, though; for fixed-length array * types we punt and return the null input array. */ if (*isNull) { if (astate->refattrlength > 0) /* fixed-length array? */ return PointerGetDatum(array_source); array_source = construct_md_array(NULL, 0, NULL, NULL, arrayRef->refelemtype, astate->refelemlength, astate->refelembyval, astate->refelemalign); *isNull = false; } if (lIndex == NULL) resultArray = array_set(array_source, i, upper.indx, sourceData, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign, isNull); else resultArray = array_set_slice(array_source, i, upper.indx, lower.indx, (ArrayType *) DatumGetPointer(sourceData), astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign, isNull); return PointerGetDatum(resultArray); } if (lIndex == NULL) 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, isNull); return PointerGetDatum(resultArray); }}/* ---------------------------------------------------------------- * ExecEvalAggref * * Returns a Datum whose value is the value of the precomputed * aggregate found in the given expression context. * ---------------------------------------------------------------- */static DatumExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone){ if (isDone) *isDone = ExprSingleResult; if (econtext->ecxt_aggvalues == NULL) /* safety check */ elog(ERROR, "no aggregates in this expression context"); *isNull = econtext->ecxt_aggnulls[aggref->aggno]; return econtext->ecxt_aggvalues[aggref->aggno];}/* ---------------------------------------------------------------- * ExecEvalVar * * Returns a Datum whose value is the value of a range * variable with respect to given expression context. * ---------------------------------------------------------------- */static DatumExecEvalVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone){ Var *variable = (Var *) exprstate->expr; TupleTableSlot *slot; AttrNumber attnum; if (isDone) *isDone = ExprSingleResult; /* * Get the slot and attribute number we want * * The asserts check that references to system attributes only appear at * the level of a relation scan; at higher levels, system attributes must * be treated as ordinary variables (since we no longer have access to the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -