* nodeSubplan.c
* routines to support subselects
*
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.67 2005/03/16 21:38:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.80 2006/10/04 00:29:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
*/
#include "postgres.h"
+#include <math.h>
+
#include "access/heapam.h"
#include "executor/executor.h"
#include "executor/nodeSubplan.h"
#include "nodes/makefuncs.h"
-#include "parser/parse_expr.h"
+#include "optimizer/clauses.h"
#include "utils/array.h"
-#include "utils/datum.h"
#include "utils/lsyscache.h"
+#include "utils/memutils.h"
static Datum ExecHashSubPlan(SubPlanState *node,
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 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.
+ * The result for an empty subplan is always FALSE; no need to evaluate
+ * lefthand side.
*/
*isNull = false;
if (!node->havehashrows && !node->havenullrows)
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.
+ * 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.
+ * 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.
+ * 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.
+ * 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))
{
}
/*
- * 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.
+ * 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)
{
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
PlanState *planstate = node->planstate;
SubLinkType subLinkType = subplan->subLinkType;
- bool useOr = subplan->useOr;
MemoryContext oldcontext;
TupleTableSlot *slot;
Datum result;
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.
+ * 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);
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 all sublink types except EXPR_SUBLINK and ARRAY_SUBLINK, the result
+ * is boolean as are the results of the combining operators. We combine
+ * results across tuples (if the subplan produces more than one) using OR
+ * semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK.
+ * (ROWCOMPARE_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
+ * ROWCOMPARE_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).
+ * 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;
slot = ExecProcNode(planstate))
{
TupleDesc tdesc = slot->tts_tupleDescriptor;
- Datum rowresult = BoolGetDatum(!useOr);
- bool rownull = false;
- int col = 1;
+ Datum rowresult;
+ bool rownull;
+ int col;
ListCell *plst;
if (subLinkType == EXISTS_SUBLINK)
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.
+ * 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)
node->curTuple = ExecCopySlotTuple(slot);
MemoryContextSwitchTo(node->sub_estate->es_query_cxt);
- result = heap_getattr(node->curTuple, col, tdesc, isNull);
+ result = heap_getattr(node->curTuple, 1, tdesc, isNull);
/* keep scanning subplan to make sure there's only one tuple */
continue;
}
continue;
}
- /* cannot allow multiple input tuples for MULTIEXPR sublink either */
- if (subLinkType == MULTIEXPR_SUBLINK && found)
+ /* cannot allow multiple input tuples for ROWCOMPARE sublink either */
+ if (subLinkType == ROWCOMPARE_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.
+ * For ALL, ANY, and ROWCOMPARE sublinks, load up the Params
+ * representing the columns of the sub-select, and then evaluate the
+ * combining expression.
*/
- Assert(list_length(node->exprs) == list_length(subplan->paramIds));
-
- forboth(l, node->exprs, plst, subplan->paramIds)
+ col = 1;
+ foreach(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)
- {
- rowresult = expresult;
- rownull = expnull;
- }
- else if (useOr)
- {
- /* combine within row per OR semantics */
- if (expnull)
- rownull = true;
- else if (DatumGetBool(expresult))
- {
- rowresult = BoolGetDatum(true);
- rownull = false;
- break; /* needn't look at any more columns */
- }
- }
- else
- {
- /* combine within row per AND semantics */
- if (expnull)
- rownull = true;
- else if (!DatumGetBool(expresult))
- {
- rowresult = BoolGetDatum(false);
- rownull = false;
- break; /* needn't look at any more columns */
- }
- }
-
+ prmdata->value = slot_getattr(slot, col, &(prmdata->isnull));
col++;
}
+ rowresult = ExecEvalExprSwitchContext(node->testexpr, econtext,
+ &rownull, NULL);
+
if (subLinkType == ANY_SUBLINK)
{
/* combine across rows per OR semantics */
}
else
{
- /* must be MULTIEXPR_SUBLINK */
+ /* must be ROWCOMPARE_SUBLINK */
result = rowresult;
*isNull = rownull;
}
{
/*
* deal with empty subplan result. result/isNull were previously
- * initialized correctly for all sublink types except EXPR, ARRAY,
- * and MULTIEXPR; for those, return NULL.
+ * initialized correctly for all sublink types except EXPR, ARRAY, and
+ * ROWCOMPARE; for those, return NULL.
*/
if (subLinkType == EXPR_SUBLINK ||
subLinkType == ARRAY_SUBLINK ||
- subLinkType == MULTIEXPR_SUBLINK)
+ subLinkType == ROWCOMPARE_SUBLINK)
{
result = (Datum) 0;
*isNull = true;
{
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
PlanState *planstate = node->planstate;
- int ncols = list_length(node->exprs);
+ int ncols = list_length(subplan->paramIds);
ExprContext *innerecontext = node->innerecontext;
MemoryContext tempcxt = innerecontext->ecxt_per_tuple_memory;
MemoryContext oldcontext;
TupleTableSlot *slot;
Assert(subplan->subLinkType == ANY_SUBLINK);
- Assert(!subplan->useOr);
/*
- * If we already had any hash tables, destroy 'em; then create empty
- * hash table(s).
+ * If we already had any hash tables, destroy 'em; then create empty hash
+ * table(s).
*
* If we need to distinguish accurately between FALSE and UNKNOWN (i.e.,
- * NULL) results of the IN operation, then we have to store subplan
- * output rows that are partly or wholly NULL. We store such rows in
- * a separate hash table that we expect will be much smaller than the
- * main table. (We can use hashing to eliminate partly-null rows that
- * are not distinct. We keep them separate to minimize the cost of
- * the inevitable full-table searches; see findPartialMatch.)
+ * NULL) results of the IN operation, then we have to store subplan output
+ * rows that are partly or wholly NULL. We store such rows in a separate
+ * hash table that we expect will be much smaller than the main table. (We
+ * can use hashing to eliminate partly-null rows that are not distinct. We
+ * keep them separate to minimize the cost of the inevitable full-table
+ * searches; see findPartialMatch.)
*
* If it's not necessary to distinguish FALSE and UNKNOWN, then we don't
* need to store subplan output rows that contain NULL.
}
/*
- * We are probably in a short-lived expression-evaluation context.
- * Switch to the child plan's per-query context for calling
- * ExecProcNode.
+ * We are probably in a short-lived expression-evaluation context. Switch
+ * to the child plan's per-query context for calling ExecProcNode.
*/
oldcontext = MemoryContextSwitchTo(node->sub_estate->es_query_cxt);
ExecReScan(planstate, NULL);
/*
- * Scan the subplan and load the hash table(s). Note that when there
- * are duplicate rows coming out of the sub-select, only one copy is
- * stored.
+ * Scan the subplan and load the hash table(s). Note that when there are
+ * duplicate rows coming out of the sub-select, only one copy is stored.
*/
for (slot = ExecProcNode(planstate);
!TupIsNull(slot);
bool isnew;
/*
- * Load up the Params representing the raw sub-select outputs,
- * then form the projection tuple to store in the hashtable.
+ * Load up the Params representing the raw sub-select outputs, then
+ * form the projection tuple to store in the hashtable.
*/
foreach(plst, subplan->paramIds)
{
}
/*
- * Reset innerecontext after each inner tuple to free any memory
- * used in hash computation or comparison routines.
+ * Reset innerecontext after each inner tuple to free any memory used
+ * in hash computation or comparison routines.
*/
ResetExprContext(innerecontext);
}
/*
- * Since the projected tuples are in the sub-query's context and not
- * the main context, we'd better clear the tuple slot before there's
- * any chance of a reset of the sub-query's context. Else we will
- * have the potential for a double free attempt. (XXX possibly
- * no longer needed, but can't hurt.)
+ * Since the projected tuples are in the sub-query's context and not the
+ * main context, we'd better clear the tuple slot before there's any
+ * chance of a reset of the sub-query's context. Else we will have the
+ * potential for a double free attempt. (XXX possibly no longer needed,
+ * but can't hurt.)
*/
ExecClearTuple(node->projRight->pi_slot);
ResetTupleHashIterator(hashtable, &hashiter);
while ((entry = ScanTupleHashTable(&hashiter)) != NULL)
{
- ExecStoreTuple(entry->firstTuple, hashtable->tableslot,
- InvalidBuffer, false);
+ ExecStoreMinimalTuple(entry->firstTuple, hashtable->tableslot, false);
if (!execTuplesUnequal(hashtable->tableslot, slot,
numCols, keyColIdx,
hashtable->eqfunctions,
/* ----------------------------------------------------------------
* ExecInitSubPlan
+ *
+ * Note: the eflags are those passed to the parent plan node of this
+ * subplan; they don't directly describe the execution conditions the
+ * subplan will face.
* ----------------------------------------------------------------
*/
void
-ExecInitSubPlan(SubPlanState *node, EState *estate)
+ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
{
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
EState *sp_estate;
/*
* create an EState for the subplan
*
- * The subquery needs its own EState because it has its own rangetable.
- * It shares our Param ID space, however. XXX if rangetable access
- * were done differently, the subquery could share our EState, which
- * would eliminate some thrashing about in this module...
+ * The subquery needs its own EState because it has its own rangetable. It
+ * shares our Param ID space, however. XXX if rangetable access were done
+ * differently, the subquery could share our EState, which would eliminate
+ * some thrashing about in this module...
*/
sp_estate = CreateExecutorState();
node->sub_estate = sp_estate;
/*
* Start up the subplan (this is a very cut-down form of InitPlan())
+ *
+ * The subplan will never need to do BACKWARD scan or MARK/RESTORE. If it
+ * is a parameterless subplan (not initplan), we suggest that it be
+ * prepared to handle REWIND efficiently; otherwise there is no need.
*/
- node->planstate = ExecInitNode(subplan->plan, sp_estate);
+ eflags &= EXEC_FLAG_EXPLAIN_ONLY;
+ if (subplan->parParam == NIL && subplan->setParam == NIL)
+ eflags |= EXEC_FLAG_REWIND;
+
+ node->planstate = ExecInitNode(subplan->plan, sp_estate, eflags);
node->needShutdown = true; /* now we need to shutdown the subplan */
MemoryContextSwitchTo(oldcontext);
/*
- * If this plan is un-correlated or undirect correlated one and want
- * to set params for parent plan then mark parameters as needing
- * evaluation.
+ * If this plan is un-correlated or undirect correlated one and want to
+ * set params for parent plan then mark parameters as needing evaluation.
*
* Note that in the case of un-correlated subqueries we don't care about
- * setting parent->chgParam here: indices take care about it, for
- * others - it doesn't matter...
+ * setting parent->chgParam here: indices take care about it, for others -
+ * it doesn't matter...
*/
if (subplan->setParam != NIL)
{
}
/*
- * If we are going to hash the subquery output, initialize relevant
- * stuff. (We don't create the hashtable until needed, though.)
+ * If we are going to hash the subquery output, initialize relevant stuff.
+ * (We don't create the hashtable until needed, though.)
*/
if (subplan->useHashTable)
{
TupleDesc tupDesc;
TupleTable tupTable;
TupleTableSlot *slot;
- List *lefttlist,
+ List *oplist,
+ *lefttlist,
*righttlist,
*leftptlist,
*rightptlist;
- ListCell *lexpr;
+ ListCell *l;
/* We need a memory context to hold the hash table(s) */
node->tablecxt =
/* and a short-lived exprcontext for function evaluation */
node->innerecontext = CreateExprContext(estate);
/* Silly little array of column numbers 1..n */
- ncols = list_length(node->exprs);
+ ncols = list_length(subplan->paramIds);
node->keyColIdx = (AttrNumber *) palloc(ncols * sizeof(AttrNumber));
for (i = 0; i < ncols; i++)
node->keyColIdx[i] = i + 1;
/*
* We use ExecProject to evaluate the lefthand and righthand
- * expression lists and form tuples. (You might think that we
- * could use the sub-select's output tuples directly, but that is
- * not the case if we had to insert any run-time coercions of the
- * sub-select's output datatypes; anyway this avoids storing any
- * resjunk columns that might be in the sub-select's output.) Run
- * through the combining expressions to build tlists for the
- * lefthand and righthand sides. We need both the ExprState list
- * (for ExecProject) and the underlying parse Exprs (for
- * ExecTypeFromTL).
+ * expression lists and form tuples. (You might think that we could
+ * use the sub-select's output tuples directly, but that is not the
+ * case if we had to insert any run-time coercions of the sub-select's
+ * output datatypes; anyway this avoids storing any resjunk columns
+ * that might be in the sub-select's output.) Run through the
+ * combining expressions to build tlists for the lefthand and
+ * righthand sides. We need both the ExprState list (for ExecProject)
+ * and the underlying parse Exprs (for ExecTypeFromTL).
*
* We also extract the combining operators themselves to initialize
* the equality and hashing functions for the hash tables.
*/
+ if (IsA(node->testexpr->expr, OpExpr))
+ {
+ /* single combining operator */
+ oplist = list_make1(node->testexpr);
+ }
+ else if (and_clause((Node *) node->testexpr->expr))
+ {
+ /* multiple combining operators */
+ Assert(IsA(node->testexpr, BoolExprState));
+ oplist = ((BoolExprState *) node->testexpr)->args;
+ }
+ else
+ {
+ /* shouldn't see anything else in a hashable subplan */
+ elog(ERROR, "unrecognized testexpr type: %d",
+ (int) nodeTag(node->testexpr->expr));
+ oplist = NIL; /* keep compiler quiet */
+ }
+ Assert(list_length(oplist) == ncols);
+
lefttlist = righttlist = NIL;
leftptlist = rightptlist = NIL;
node->eqfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
node->hashfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
i = 1;
- foreach(lexpr, node->exprs)
+ foreach(l, oplist)
{
- FuncExprState *fstate = (FuncExprState *) lfirst(lexpr);
+ FuncExprState *fstate = (FuncExprState *) lfirst(l);
OpExpr *opexpr = (OpExpr *) fstate->xprstate.expr;
ExprState *exstate;
Expr *expr;
/* Process lefthand argument */
exstate = (ExprState *) linitial(fstate->args);
expr = exstate->expr;
- tle = makeTargetEntry(makeResdom(i,
- exprType((Node *) expr),
- exprTypmod((Node *) expr),
- NULL,
- false),
- expr);
+ tle = makeTargetEntry(expr,
+ i,
+ NULL,
+ false);
tlestate = makeNode(GenericExprState);
tlestate->xprstate.expr = (Expr *) tle;
tlestate->xprstate.evalfunc = NULL;
/* Process righthand argument */
exstate = (ExprState *) lsecond(fstate->args);
expr = exstate->expr;
- tle = makeTargetEntry(makeResdom(i,
- exprType((Node *) expr),
- exprTypmod((Node *) expr),
- NULL,
- false),
- expr);
+ tle = makeTargetEntry(expr,
+ i,
+ NULL,
+ false);
tlestate = makeNode(GenericExprState);
tlestate->xprstate.expr = (Expr *) tle;
tlestate->xprstate.evalfunc = NULL;
}
/*
- * Create a tupletable to hold these tuples. (Note: we never
- * bother to free the tupletable explicitly; that's okay because
- * it will never store raw disk tuples that might have associated
- * buffer pins. The only resource involved is memory, which will
- * be cleaned up by freeing the query context.)
+ * Create a tupletable to hold these tuples. (Note: we never bother
+ * to free the tupletable explicitly; that's okay because it will
+ * never store raw disk tuples that might have associated buffer pins.
+ * The only resource involved is memory, which will be cleaned up by
+ * freeing the query context.)
*/
tupTable = ExecCreateTupleTable(2);
/*
- * Construct tupdescs, slots and projection nodes for left and
- * right sides. The lefthand expressions will be evaluated in the
- * parent plan node's exprcontext, which we don't have access to
- * here. Fortunately we can just pass NULL for now and fill it in
- * later (hack alert!). The righthand expressions will be
- * evaluated in our own innerecontext.
+ * Construct tupdescs, slots and projection nodes for left and right
+ * sides. The lefthand expressions will be evaluated in the parent
+ * plan node's exprcontext, which we don't have access to here.
+ * Fortunately we can just pass NULL for now and fill it in later
+ * (hack alert!). The righthand expressions will be evaluated in our
+ * own innerecontext.
*/
tupDesc = ExecTypeFromTL(leftptlist, false);
slot = ExecAllocTableSlot(tupTable);
- ExecSetSlotDescriptor(slot, tupDesc, true);
+ ExecSetSlotDescriptor(slot, tupDesc);
node->projLeft = ExecBuildProjectionInfo(lefttlist,
NULL,
slot);
tupDesc = ExecTypeFromTL(rightptlist, false);
slot = ExecAllocTableSlot(tupTable);
- ExecSetSlotDescriptor(slot, tupDesc, true);
+ ExecSetSlotDescriptor(slot, tupDesc);
node->projRight = ExecBuildProjectionInfo(righttlist,
node->innerecontext,
slot);
if (found &&
(subLinkType == EXPR_SUBLINK ||
- subLinkType == MULTIEXPR_SUBLINK))
+ subLinkType == ROWCOMPARE_SUBLINK))
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 into our own context, in
- * case any of the params are pass-by-ref type --- the pointers
- * stored in the param structs will point at this copied tuple!
- * node->curTuple keeps track of the copied tuple for eventual
- * freeing.
+ * We need to copy the subplan's tuple into our own context, in case
+ * any of the params are pass-by-ref type --- the pointers stored in
+ * the param structs will point at this copied tuple! node->curTuple
+ * keeps track of the copied tuple for eventual freeing.
*/
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
if (node->curTuple)