📄 execqual.c
字号:
/*------------------------------------------------------------------------- * * execQual.c * Routines to evaluate qualification and targetlist expressions * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.150.2.1 2003/12/18 22:23:54 tgl Exp $ * *------------------------------------------------------------------------- *//* * INTERFACE ROUTINES * ExecEvalExpr - evaluate an expression and 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 * ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster * will speed up the entire system. Unfortunately they are currently * implemented recursively. Eliminating the recursion is bound to * improve the speed of the executor. * * 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 * */#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 "miscadmin.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"/* static function decls */static Datum ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull);static Datum ExecEvalArrayRef(ArrayRefExprState *astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);static Datum ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull);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);static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, ExprContext *econtext, bool *isNull);static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo, List *argList, ExprContext *econtext);static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, bool *isNull);static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, bool *isNull);static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull);static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);static Datum ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, bool *isNull);static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, bool *isNull);static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, bool *isNull);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(CoerceToDomainValue *conVal, ExprContext *econtext, bool *isNull);static Datum ExecEvalFieldSelect(GenericExprState *fstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone);/*---------- * 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); List *elt; int i = 0, j = 0; IntArray upper, lower; int *lIndex; if (arrayRef->refexpr != NULL) { array_source = (ArrayType *) DatumGetPointer(ExecEvalExpr(astate->refexpr, econtext, isNull, isDone)); /* * If refexpr yields NULL, result is always NULL, for now anyway. * (This means you cannot assign to an element or slice of an * array that's NULL; it'll just stay NULL.) */ if (*isNull) return (Datum) NULL; } else { /* * Empty refexpr indicates we are doing an INSERT into an array * column. For now, we just take the refassgnexpr (which the * parser will have ensured is an array value) and return it * as-is, ignoring any subscripts that may have been supplied in * the INSERT column list. This is a kluge, but it's not real * clear what the semantics ought to be... */ array_source = NULL; } foreach(elt, astate->refupperindexpr) { 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((ExprState *) lfirst(elt), econtext, isNull, NULL)); /* If any index expr yields NULL, result is NULL or source array */ if (*isNull) { if (!isAssignment || array_source == NULL) return (Datum) NULL; *isNull = false; return PointerGetDatum(array_source); } } if (astate->reflowerindexpr != NIL) { foreach(elt, astate->reflowerindexpr) { 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((ExprState *) lfirst(elt), econtext, isNull, NULL)); /* * If any index expr yields NULL, result is NULL or source * array */ if (*isNull) { if (!isAssignment || array_source == NULL) return (Datum) NULL; *isNull = false; 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 = ExecEvalExpr(astate->refassgnexpr, econtext, isNull, NULL); /* * For now, can't cope with inserting NULL into an array, so make * it a no-op per discussion above... */ if (*isNull) { if (array_source == NULL) return (Datum) NULL; *isNull = false; return PointerGetDatum(array_source); } if (array_source == NULL) return sourceData; /* XXX do something else? */ 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){ 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(Var *variable, ExprContext *econtext, bool *isNull){ Datum result; TupleTableSlot *slot; AttrNumber attnum; HeapTuple heapTuple; TupleDesc tuple_type; /* * get the slot we want */ switch (variable->varno) { case INNER: /* get the tuple from the inner node */ slot = econtext->ecxt_innertuple; break; case OUTER: /* get the tuple from the outer node */ slot = econtext->ecxt_outertuple; break; default: /* get the tuple from the relation being * scanned */ slot = econtext->ecxt_scantuple; break; } /* * extract tuple information from the slot */ heapTuple = slot->val; tuple_type = slot->ttc_tupleDescriptor; attnum = variable->varattno; /* * Some checks that are only applied for user attribute numbers * (bogus system attnums will be caught inside heap_getattr). */ if (attnum > 0) { /* * This assert checks that the attnum is valid. */ Assert(attnum <= tuple_type->natts && tuple_type->attrs[attnum - 1] != NULL); /* * If the attribute's column has been dropped, we force a NULL result. * This case should not happen in normal use, but it could happen if * we are executing a plan cached before the column was dropped. */ if (tuple_type->attrs[attnum - 1]->attisdropped) { *isNull = true; return (Datum) 0; } /* * This assert checks that the datatype the plan expects to get (as * told by our "variable" argument) is in fact the datatype of the * attribute being fetched (as seen in the current context, identified * by our "econtext" argument). Otherwise crashes are likely. * * Note that we can't check dropped columns, since their atttypid * has been zeroed. */ Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid); } /* * If the attribute number is invalid, then we are supposed to return * the entire tuple; we give back a whole slot so that callers know * what the tuple looks like. * * XXX this is a horrid crock: since the pointer to the slot might live * longer than the current evaluation context, we are forced to copy * the tuple and slot into a long-lived context --- we use the * econtext's per-query memory which should be safe enough. This * represents a serious memory leak if many such tuples are processed * in one command, however. We ought to redesign the representation * of whole-tuple datums so that this is not necessary. * * We assume it's OK to point to the existing tupleDescriptor, rather * than copy that too. */ if (attnum == InvalidAttrNumber) { MemoryContext oldContext; TupleTableSlot *tempSlot; HeapTuple tup; oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tempSlot = MakeTupleTableSlot(); tup = heap_copytuple(heapTuple); ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); ExecSetSlotDescriptor(tempSlot, tuple_type, false); MemoryContextSwitchTo(oldContext); return PointerGetDatum(tempSlot); } result = heap_getattr(heapTuple, /* tuple containing attribute */ attnum, /* attribute number of desired * attribute */ tuple_type, /* tuple descriptor of tuple */ isNull); /* return: is attribute null? */ return result;}/* ---------------------------------------------------------------- * ExecEvalParam * * Returns the value of a parameter. A param node contains * something like ($.name) and the expression context contains * the current parameter bindings (name = "sam") (age = 34)... * so our job is to find and return the appropriate datum ("sam"). * * Q: if we have a parameter ($.foo) without a binding, i.e. * there is no (foo = xxx) in the parameter list info, * is this a fatal error or should this be a "not available" * (in which case we could return NULL)? -cim 10/13/89 * ---------------------------------------------------------------- */static DatumExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull){ int thisParamKind = expression->paramkind; AttrNumber thisParamId = expression->paramid; if (thisParamKind == PARAM_EXEC) { /* * PARAM_EXEC params (internal executor parameters) are stored in * the ecxt_param_exec_vals array, and can be accessed by array * index. */ ParamExecData *prm; prm = &(econtext->ecxt_param_exec_vals[thisParamId]); if (prm->execPlan != NULL) { /* Parameter not evaluated yet, so go do it */ ExecSetParamPlan(prm->execPlan, econtext); /* ExecSetParamPlan should have processed this param... */ Assert(prm->execPlan == NULL); } *isNull = prm->isnull; return prm->value; } else { /* * All other parameter types must be sought in * ecxt_param_list_info. NOTE: The last entry in the param array * is always an entry with kind == PARAM_INVALID. */ ParamListInfo paramList = econtext->ecxt_param_list_info;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -