1
0
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:
Tom Lane
2005-12-28 01:30:02 +00:00
parent a37422e042
commit 6e07709760
26 changed files with 1452 additions and 659 deletions

View File

@ -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")));