mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Implement SQL-compliant treatment of row comparisons for < <= > >= cases
(previously we only did = and <> correctly). Also, allow row comparisons with any operators that are in btree opclasses, not only those with these specific names. This gets rid of a whole lot of indefensible assumptions about the behavior of particular operators based on their names ... though it's still true that IN and NOT IN expand to "= ANY". The patch adds a RowCompareExpr expression node type, and makes some changes in the representation of ANY/ALL/ROWCOMPARE SubLinks so that they can share code with RowCompareExpr. I have not yet done anything about making RowCompareExpr an indexable operator, but will look at that soon. initdb forced due to changes in stored rules.
This commit is contained in:
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.71 2005/11/22 18:17:10 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.72 2005/12/28 01:29:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -23,6 +23,7 @@
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeSubplan.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/datum.h"
|
||||
@ -205,7 +206,6 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
|
||||
PlanState *planstate = node->planstate;
|
||||
SubLinkType subLinkType = subplan->subLinkType;
|
||||
bool useOr = subplan->useOr;
|
||||
MemoryContext oldcontext;
|
||||
TupleTableSlot *slot;
|
||||
Datum result;
|
||||
@ -245,15 +245,13 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
/*
|
||||
* 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.)
|
||||
* (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
|
||||
* MULTIEXPR_SUBLINK.
|
||||
* 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
|
||||
@ -269,9 +267,9 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
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)
|
||||
@ -304,7 +302,7 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
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;
|
||||
}
|
||||
@ -324,8 +322,8 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
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")));
|
||||
@ -333,69 +331,25 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
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 */
|
||||
@ -422,7 +376,7 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
}
|
||||
else
|
||||
{
|
||||
/* must be MULTIEXPR_SUBLINK */
|
||||
/* must be ROWCOMPARE_SUBLINK */
|
||||
result = rowresult;
|
||||
*isNull = rownull;
|
||||
}
|
||||
@ -433,11 +387,11 @@ ExecScanSubPlan(SubPlanState *node,
|
||||
/*
|
||||
* deal with empty subplan result. result/isNull were previously
|
||||
* initialized correctly for all sublink types except EXPR, ARRAY, and
|
||||
* MULTIEXPR; for those, return NULL.
|
||||
* ROWCOMPARE; for those, return NULL.
|
||||
*/
|
||||
if (subLinkType == EXPR_SUBLINK ||
|
||||
subLinkType == ARRAY_SUBLINK ||
|
||||
subLinkType == MULTIEXPR_SUBLINK)
|
||||
subLinkType == ROWCOMPARE_SUBLINK)
|
||||
{
|
||||
result = (Datum) 0;
|
||||
*isNull = true;
|
||||
@ -463,7 +417,7 @@ buildSubPlanHash(SubPlanState *node)
|
||||
{
|
||||
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;
|
||||
@ -471,7 +425,6 @@ buildSubPlanHash(SubPlanState *node)
|
||||
TupleTableSlot *slot;
|
||||
|
||||
Assert(subplan->subLinkType == ANY_SUBLINK);
|
||||
Assert(!subplan->useOr);
|
||||
|
||||
/*
|
||||
* If we already had any hash tables, destroy 'em; then create empty hash
|
||||
@ -764,11 +717,12 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
|
||||
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 =
|
||||
@ -780,7 +734,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
|
||||
/* 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;
|
||||
@ -799,14 +753,34 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
|
||||
* 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;
|
||||
@ -967,7 +941,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
||||
|
||||
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")));
|
||||
|
Reference in New Issue
Block a user