📄 nodesubplan.c
字号:
/*------------------------------------------------------------------------- * * nodeSubplan.c * routines to support subselects * * 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/nodeSubplan.c,v 1.70.2.1 2005/11/22 18:23:09 momjian Exp $ * *------------------------------------------------------------------------- *//* * INTERFACE ROUTINES * ExecSubPlan - process a subselect * ExecInitSubPlan - initialize a subselect * ExecEndSubPlan - shut down a subselect */#include "postgres.h"#include "access/heapam.h"#include "executor/executor.h"#include "executor/nodeSubplan.h"#include "nodes/makefuncs.h"#include "parser/parse_expr.h"#include "utils/array.h"#include "utils/datum.h"#include "utils/lsyscache.h"#include "utils/memutils.h"static Datum ExecHashSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull);static Datum ExecScanSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull);static void buildSubPlanHash(SubPlanState *node);static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);static bool slotAllNulls(TupleTableSlot *slot);static bool slotNoNulls(TupleTableSlot *slot);/* ---------------------------------------------------------------- * ExecSubPlan * ---------------------------------------------------------------- */DatumExecSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone){ SubPlan *subplan = (SubPlan *) node->xprstate.expr; /* Set default values for result flags: non-null, not a set result */ *isNull = false; if (isDone) *isDone = ExprSingleResult; if (subplan->setParam != NIL) elog(ERROR, "cannot set parent params from subquery"); if (subplan->useHashTable) return ExecHashSubPlan(node, econtext, isNull); else return ExecScanSubPlan(node, econtext, isNull);}/* * ExecHashSubPlan: store subselect result in an in-memory hash table */static DatumExecHashSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull){ SubPlan *subplan = (SubPlan *) node->xprstate.expr; PlanState *planstate = node->planstate; ExprContext *innerecontext = node->innerecontext; TupleTableSlot *slot; /* Shouldn't have any direct correlation Vars */ if (subplan->parParam != NIL || node->args != NIL) elog(ERROR, "hashed subplan with direct correlation not supported"); /* * If first time through or we need to rescan the subplan, build the hash * table. */ if (node->hashtable == NULL || planstate->chgParam != NULL) buildSubPlanHash(node); /* * The result for an empty subplan is always FALSE; no need to evaluate * lefthand side. */ *isNull = false; if (!node->havehashrows && !node->havenullrows) return BoolGetDatum(false); /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). */ node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); /* * Note: because we are typically called in a per-tuple context, we have * to explicitly clear the projected tuple before returning. Otherwise, * we'll have a double-free situation: the per-tuple context will probably * be reset before we're called again, and then the tuple slot will think * it still needs to free the tuple. */ /* * Since the hashtable routines will use innerecontext's per-tuple memory * as working memory, be sure to reset it for each tuple. */ ResetExprContext(innerecontext); /* * If the LHS is all non-null, probe for an exact match in the main hash * table. If we find one, the result is TRUE. Otherwise, scan the * partly-null table to see if there are any rows that aren't provably * unequal to the LHS; if so, the result is UNKNOWN. (We skip that part * if we don't care about UNKNOWN.) Otherwise, the result is FALSE. * * Note: the reason we can avoid a full scan of the main hash table is * that the combining operators are assumed never to yield NULL when both * inputs are non-null. If they were to do so, we might need to produce * UNKNOWN instead of FALSE because of an UNKNOWN result in comparing the * LHS to some main-table entry --- which is a comparison we will not even * make, unless there's a chance match of hash keys. */ if (slotNoNulls(slot)) { if (node->havehashrows && LookupTupleHashEntry(node->hashtable, slot, NULL) != NULL) { ExecClearTuple(slot); return BoolGetDatum(true); } if (node->havenullrows && findPartialMatch(node->hashnulls, slot)) { ExecClearTuple(slot); *isNull = true; return BoolGetDatum(false); } ExecClearTuple(slot); return BoolGetDatum(false); } /* * When the LHS is partly or wholly NULL, we can never return TRUE. If we * don't care about UNKNOWN, just return FALSE. Otherwise, if the LHS is * wholly NULL, immediately return UNKNOWN. (Since the combining * operators are strict, the result could only be FALSE if the sub-select * were empty, but we already handled that case.) Otherwise, we must scan * both the main and partly-null tables to see if there are any rows that * aren't provably unequal to the LHS; if so, the result is UNKNOWN. * Otherwise, the result is FALSE. */ if (node->hashnulls == NULL) { ExecClearTuple(slot); return BoolGetDatum(false); } if (slotAllNulls(slot)) { ExecClearTuple(slot); *isNull = true; return BoolGetDatum(false); } /* Scan partly-null table first, since more likely to get a match */ if (node->havenullrows && findPartialMatch(node->hashnulls, slot)) { ExecClearTuple(slot); *isNull = true; return BoolGetDatum(false); } if (node->havehashrows && findPartialMatch(node->hashtable, slot)) { ExecClearTuple(slot); *isNull = true; return BoolGetDatum(false); } ExecClearTuple(slot); return BoolGetDatum(false);}/* * ExecScanSubPlan: default case where we have to rescan subplan each time */static DatumExecScanSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull){ SubPlan *subplan = (SubPlan *) node->xprstate.expr; PlanState *planstate = node->planstate; SubLinkType subLinkType = subplan->subLinkType; bool useOr = subplan->useOr; MemoryContext oldcontext; TupleTableSlot *slot; Datum result; bool found = false; /* TRUE if got at least one subplan tuple */ ListCell *pvar; ListCell *l; ArrayBuildState *astate = NULL; /* * We are probably in a short-lived expression-evaluation context. Switch * to the child plan's per-query context for manipulating its chgParam, * calling ExecProcNode on it, etc. */ oldcontext = MemoryContextSwitchTo(node->sub_estate->es_query_cxt); /* * Set Params of this plan from parent plan correlation values. (Any * calculation we have to do is done in the parent econtext, since the * Param values don't need to have per-query lifetime.) */ Assert(list_length(subplan->parParam) == list_length(node->args)); forboth(l, subplan->parParam, pvar, node->args) { int paramid = lfirst_int(l); ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]); prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar), econtext, &(prm->isnull), NULL); planstate->chgParam = bms_add_member(planstate->chgParam, paramid); } ExecReScan(planstate, NULL); /* * For all sublink types except EXPR_SUBLINK and ARRAY_SUBLINK, the result * is boolean as are the results of the combining operators. We combine * results within a tuple (if there are multiple columns) using OR * semantics if "useOr" is true, AND semantics if not. We then combine * results across tuples (if the subplan produces more than one) using OR * semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK. * (MULTIEXPR_SUBLINK doesn't allow multiple tuples from the subplan.) * NULL results from the combining operators are handled according to the * usual SQL semantics for OR and AND. The result for no input tuples is * FALSE for ANY_SUBLINK, TRUE for ALL_SUBLINK, NULL for * MULTIEXPR_SUBLINK. * * For EXPR_SUBLINK we require the subplan to produce no more than one * tuple, else an error is raised. For ARRAY_SUBLINK we allow the subplan * to produce more than one tuple. In either case, if zero tuples are * produced, we return NULL. Assuming we get a tuple, we just use its * first column (there can be only one non-junk column in this case). */ result = BoolGetDatum(subLinkType == ALL_SUBLINK); *isNull = false; for (slot = ExecProcNode(planstate); !TupIsNull(slot); slot = ExecProcNode(planstate)) { TupleDesc tdesc = slot->tts_tupleDescriptor; Datum rowresult = BoolGetDatum(!useOr); bool rownull = false; int col = 1; ListCell *plst; if (subLinkType == EXISTS_SUBLINK) { found = true; result = BoolGetDatum(true); break; } if (subLinkType == EXPR_SUBLINK) { /* cannot allow multiple input tuples for EXPR sublink */ if (found) ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), errmsg("more than one row returned by a subquery used as an expression"))); found = true; /* * We need to copy the subplan's tuple in case the result is of * pass-by-ref type --- our return value will point into this * copied tuple! Can't use the subplan's instance of the tuple * since it won't still be valid after next ExecProcNode() call. * node->curTuple keeps track of the copied tuple for eventual * freeing. */ MemoryContextSwitchTo(econtext->ecxt_per_query_memory); if (node->curTuple) heap_freetuple(node->curTuple); node->curTuple = ExecCopySlotTuple(slot); MemoryContextSwitchTo(node->sub_estate->es_query_cxt); result = heap_getattr(node->curTuple, col, tdesc, isNull); /* keep scanning subplan to make sure there's only one tuple */ continue; } if (subLinkType == ARRAY_SUBLINK) { Datum dvalue; bool disnull; found = true; /* stash away current value */ dvalue = slot_getattr(slot, 1, &disnull); astate = accumArrayResult(astate, dvalue, disnull, tdesc->attrs[0]->atttypid, oldcontext); /* keep scanning subplan to collect all values */ continue; } /* cannot allow multiple input tuples for MULTIEXPR sublink either */ if (subLinkType == MULTIEXPR_SUBLINK && found) ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), errmsg("more than one row returned by a subquery used as an expression"))); found = true; /* * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining * operators for columns of tuple. */ Assert(list_length(node->exprs) == list_length(subplan->paramIds)); forboth(l, node->exprs, plst, subplan->paramIds) { ExprState *exprstate = (ExprState *) lfirst(l); int paramid = lfirst_int(plst); ParamExecData *prmdata; Datum expresult; bool expnull; /* * Load up the Param representing this column of the sub-select. */ prmdata = &(econtext->ecxt_param_exec_vals[paramid]); Assert(prmdata->execPlan == NULL); prmdata->value = slot_getattr(slot, col, &(prmdata->isnull)); /* * Now we can eval the combining operator for this column. */ expresult = ExecEvalExprSwitchContext(exprstate, econtext, &expnull, NULL); /* * Combine the result into the row result as appropriate. */ if (col == 1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -