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:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.186 2005/12/14 16:28:32 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.187 2005/12/28 01:29:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -37,6 +37,7 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "access/nbtree.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/typecmds.h"
|
||||
#include "executor/execdebug.h"
|
||||
@ -104,6 +105,9 @@ static Datum ExecEvalArray(ArrayExprState *astate,
|
||||
static Datum ExecEvalRow(RowExprState *rstate,
|
||||
ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone);
|
||||
static Datum ExecEvalRowCompare(RowCompareExprState *rstate,
|
||||
ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone);
|
||||
static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
|
||||
ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone);
|
||||
@ -2306,6 +2310,76 @@ ExecEvalRow(RowExprState *rstate,
|
||||
return HeapTupleGetDatum(tuple);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEvalRowCompare - ROW() comparison-op ROW()
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static Datum
|
||||
ExecEvalRowCompare(RowCompareExprState *rstate,
|
||||
ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone)
|
||||
{
|
||||
bool result;
|
||||
RowCompareType rctype = ((RowCompareExpr *) rstate->xprstate.expr)->rctype;
|
||||
int32 cmpresult = 0;
|
||||
ListCell *l;
|
||||
ListCell *r;
|
||||
int i;
|
||||
|
||||
if (isDone)
|
||||
*isDone = ExprSingleResult;
|
||||
*isNull = true; /* until we get a result */
|
||||
|
||||
i = 0;
|
||||
forboth(l, rstate->largs, r, rstate->rargs)
|
||||
{
|
||||
ExprState *le = (ExprState *) lfirst(l);
|
||||
ExprState *re = (ExprState *) lfirst(r);
|
||||
FunctionCallInfoData locfcinfo;
|
||||
|
||||
InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2,
|
||||
NULL, NULL);
|
||||
locfcinfo.arg[0] = ExecEvalExpr(le, econtext,
|
||||
&locfcinfo.argnull[0], NULL);
|
||||
locfcinfo.arg[1] = ExecEvalExpr(re, econtext,
|
||||
&locfcinfo.argnull[1], NULL);
|
||||
if (rstate->funcs[i].fn_strict &&
|
||||
(locfcinfo.argnull[0] || locfcinfo.argnull[1]))
|
||||
return (Datum) 0; /* force NULL result */
|
||||
locfcinfo.isnull = false;
|
||||
cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
|
||||
if (locfcinfo.isnull)
|
||||
return (Datum) 0; /* force NULL result */
|
||||
if (cmpresult != 0)
|
||||
break; /* no need to compare remaining columns */
|
||||
i++;
|
||||
}
|
||||
|
||||
switch (rctype)
|
||||
{
|
||||
/* EQ and NE cases aren't allowed here */
|
||||
case ROWCOMPARE_LT:
|
||||
result = (cmpresult < 0);
|
||||
break;
|
||||
case ROWCOMPARE_LE:
|
||||
result = (cmpresult <= 0);
|
||||
break;
|
||||
case ROWCOMPARE_GE:
|
||||
result = (cmpresult >= 0);
|
||||
break;
|
||||
case ROWCOMPARE_GT:
|
||||
result = (cmpresult > 0);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized RowCompareType: %d", (int) rctype);
|
||||
result = 0; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
|
||||
*isNull = false;
|
||||
return BoolGetDatum(result);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEvalCoalesce
|
||||
* ----------------------------------------------------------------
|
||||
@ -3118,8 +3192,8 @@ ExecInitExpr(Expr *node, PlanState *parent)
|
||||
sstate->sub_estate = NULL;
|
||||
sstate->planstate = NULL;
|
||||
|
||||
sstate->exprs = (List *)
|
||||
ExecInitExpr((Expr *) subplan->exprs, parent);
|
||||
sstate->testexpr =
|
||||
ExecInitExpr((Expr *) subplan->testexpr, parent);
|
||||
sstate->args = (List *)
|
||||
ExecInitExpr((Expr *) subplan->args, parent);
|
||||
|
||||
@ -3336,6 +3410,66 @@ ExecInitExpr(Expr *node, PlanState *parent)
|
||||
state = (ExprState *) rstate;
|
||||
}
|
||||
break;
|
||||
case T_RowCompareExpr:
|
||||
{
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
RowCompareExprState *rstate = makeNode(RowCompareExprState);
|
||||
int nopers = list_length(rcexpr->opnos);
|
||||
List *outlist;
|
||||
ListCell *l;
|
||||
ListCell *l2;
|
||||
int i;
|
||||
|
||||
rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
|
||||
Assert(list_length(rcexpr->largs) == nopers);
|
||||
outlist = NIL;
|
||||
foreach(l, rcexpr->largs)
|
||||
{
|
||||
Expr *e = (Expr *) lfirst(l);
|
||||
ExprState *estate;
|
||||
|
||||
estate = ExecInitExpr(e, parent);
|
||||
outlist = lappend(outlist, estate);
|
||||
}
|
||||
rstate->largs = outlist;
|
||||
Assert(list_length(rcexpr->rargs) == nopers);
|
||||
outlist = NIL;
|
||||
foreach(l, rcexpr->rargs)
|
||||
{
|
||||
Expr *e = (Expr *) lfirst(l);
|
||||
ExprState *estate;
|
||||
|
||||
estate = ExecInitExpr(e, parent);
|
||||
outlist = lappend(outlist, estate);
|
||||
}
|
||||
rstate->rargs = outlist;
|
||||
Assert(list_length(rcexpr->opclasses) == nopers);
|
||||
rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
|
||||
i = 0;
|
||||
forboth(l, rcexpr->opnos, l2, rcexpr->opclasses)
|
||||
{
|
||||
Oid opno = lfirst_oid(l);
|
||||
Oid opclass = lfirst_oid(l2);
|
||||
int strategy;
|
||||
Oid subtype;
|
||||
bool recheck;
|
||||
Oid proc;
|
||||
|
||||
get_op_opclass_properties(opno, opclass,
|
||||
&strategy, &subtype, &recheck);
|
||||
proc = get_opclass_proc(opclass, subtype, BTORDER_PROC);
|
||||
/*
|
||||
* If we enforced permissions checks on index support
|
||||
* functions, we'd need to make a check here. But the
|
||||
* index support machinery doesn't do that, and neither
|
||||
* does this code.
|
||||
*/
|
||||
fmgr_info(proc, &(rstate->funcs[i]));
|
||||
i++;
|
||||
}
|
||||
state = (ExprState *) rstate;
|
||||
}
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
{
|
||||
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
|
||||
@ -3382,6 +3516,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
|
||||
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
||||
errmsg("could not identify a comparison function for type %s",
|
||||
format_type_be(minmaxexpr->minmaxtype))));
|
||||
/*
|
||||
* If we enforced permissions checks on index support
|
||||
* functions, we'd need to make a check here. But the
|
||||
* index support machinery doesn't do that, and neither
|
||||
* does this code.
|
||||
*/
|
||||
fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
|
||||
state = (ExprState *) mstate;
|
||||
}
|
||||
@ -3484,7 +3624,7 @@ ExecInitExprInitPlan(SubPlan *node, PlanState *parent)
|
||||
sstate->sub_estate = NULL;
|
||||
sstate->planstate = NULL;
|
||||
|
||||
sstate->exprs = (List *) ExecInitExpr((Expr *) node->exprs, parent);
|
||||
sstate->testexpr = ExecInitExpr((Expr *) node->testexpr, parent);
|
||||
sstate->args = (List *) ExecInitExpr((Expr *) node->args, parent);
|
||||
|
||||
sstate->xprstate.expr = (Expr *) node;
|
||||
|
@ -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