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:
parent
a37422e042
commit
6e07709760
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.300 2005/12/21 23:22:55 momjian Exp $
|
||||
$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.301 2005/12/28 01:29:58 tgl Exp $
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
@ -350,18 +350,18 @@ PostgreSQL documentation
|
||||
</indexterm>
|
||||
The ordinary comparison operators yield null (signifying <quote>unknown</>)
|
||||
when either input is null. Another way to do comparisons is with the
|
||||
<literal>IS DISTINCT FROM</literal> construct:
|
||||
<literal>IS <optional> NOT </> DISTINCT FROM</literal> construct:
|
||||
<synopsis>
|
||||
<replaceable>expression</replaceable> IS DISTINCT FROM <replaceable>expression</replaceable>
|
||||
<replaceable>expression</replaceable> IS NOT DISTINCT FROM <replaceable>expression</replaceable>
|
||||
</synopsis>
|
||||
For non-null inputs, <literal>IS DISTINCT FROM</literal> this is
|
||||
For non-null inputs, <literal>IS DISTINCT FROM</literal> is
|
||||
the same as the <literal><></> operator. However, when both
|
||||
inputs are null it will return false, and when just one input is
|
||||
null it will return true. Similarly, <literal>IS NOT DISTINCT
|
||||
FROM</literal> is identical to <literal>=</literal> for non-null
|
||||
inputs, returns true when both inputs are null, and false
|
||||
otherwise. Thus, these constructs effectively act as though null
|
||||
inputs, but it returns true when both inputs are null, and false when only
|
||||
one input is null. Thus, these constructs effectively act as though null
|
||||
were a normal data value, rather than <quote>unknown</>.
|
||||
</para>
|
||||
|
||||
@ -7999,8 +7999,8 @@ SELECT col1 FROM tab1
|
||||
equal if all their corresponding members are non-null and equal; the rows
|
||||
are unequal if any corresponding members are non-null and unequal;
|
||||
otherwise the result of that row comparison is unknown (null).
|
||||
If all the row results are either unequal or null, with at least one null,
|
||||
then the result of <token>IN</token> is null.
|
||||
If all the per-row results are either unequal or null, with at least one
|
||||
null, then the result of <token>IN</token> is null.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@ -8055,8 +8055,8 @@ SELECT col1 FROM tab1
|
||||
equal if all their corresponding members are non-null and equal; the rows
|
||||
are unequal if any corresponding members are non-null and unequal;
|
||||
otherwise the result of that row comparison is unknown (null).
|
||||
If all the row results are either unequal or null, with at least one null,
|
||||
then the result of <token>NOT IN</token> is null.
|
||||
If all the per-row results are either unequal or null, with at least one
|
||||
null, then the result of <token>NOT IN</token> is null.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@ -8109,23 +8109,19 @@ SELECT col1 FROM tab1
|
||||
subquery, which must return exactly as many columns as there are
|
||||
expressions in the left-hand row. The left-hand expressions are
|
||||
evaluated and compared row-wise to each row of the subquery result,
|
||||
using the given <replaceable>operator</replaceable>. Presently,
|
||||
only <literal>=</literal> and <literal><></literal> operators are allowed
|
||||
in row-wise <token>ANY</token> constructs.
|
||||
The result of <token>ANY</token> is <quote>true</> if any equal or unequal row is
|
||||
found, respectively.
|
||||
The result is <quote>false</> if no such row is found (including the special
|
||||
case where the subquery returns no rows).
|
||||
using the given <replaceable>operator</replaceable>.
|
||||
The result of <token>ANY</token> is <quote>true</> if the comparison
|
||||
returns true for any subquery row.
|
||||
The result is <quote>false</> if the comparison returns false for every
|
||||
subquery row (including the special case where the subquery returns no
|
||||
rows).
|
||||
The result is NULL if the comparison does not return true for any row,
|
||||
and it returns NULL for at least one row.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As usual, null values in the rows are combined per
|
||||
the normal rules of SQL Boolean expressions. Two rows are considered
|
||||
equal if all their corresponding members are non-null and equal; the rows
|
||||
are unequal if any corresponding members are non-null and unequal;
|
||||
otherwise the result of that row comparison is unknown (null).
|
||||
If there is at least one null row result, then the result of <token>ANY</token>
|
||||
cannot be false; it will be true or null.
|
||||
See <xref linkend="row-wise-comparison"> for details about the meaning
|
||||
of a row-wise comparison.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@ -8145,20 +8141,14 @@ SELECT col1 FROM tab1
|
||||
The result of <token>ALL</token> is <quote>true</> if all rows yield true
|
||||
(including the special case where the subquery returns no rows).
|
||||
The result is <quote>false</> if any false result is found.
|
||||
The result is NULL if the comparison does not return false for any row,
|
||||
and it returns NULL for at least one row.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<token>NOT IN</token> is equivalent to <literal><> ALL</literal>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that if there are no failures but at least one right-hand row yields
|
||||
null for the operator's result, the result of the <token>ALL</token> construct
|
||||
will be null, not true.
|
||||
This is in accordance with SQL's normal rules for Boolean combinations
|
||||
of null values.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As with <token>EXISTS</token>, it's unwise to assume that the subquery will
|
||||
be evaluated completely.
|
||||
@ -8175,24 +8165,19 @@ SELECT col1 FROM tab1
|
||||
subquery, which must return exactly as many columns as there are
|
||||
expressions in the left-hand row. The left-hand expressions are
|
||||
evaluated and compared row-wise to each row of the subquery result,
|
||||
using the given <replaceable>operator</replaceable>. Presently,
|
||||
only <literal>=</literal> and <literal><></literal> operators are allowed
|
||||
in row-wise <token>ALL</token> queries.
|
||||
The result of <token>ALL</token> is <quote>true</> if all subquery rows are equal
|
||||
or unequal, respectively (including the special
|
||||
using the given <replaceable>operator</replaceable>.
|
||||
The result of <token>ALL</token> is <quote>true</> if the comparison
|
||||
returns true for all subquery rows (including the special
|
||||
case where the subquery returns no rows).
|
||||
The result is <quote>false</> if any row is found to be unequal or equal,
|
||||
respectively.
|
||||
The result is <quote>false</> if the comparison returns false for any
|
||||
subquery row.
|
||||
The result is NULL if the comparison does not return false for any
|
||||
subquery row, and it returns NULL for at least one row.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As usual, null values in the rows are combined per
|
||||
the normal rules of SQL Boolean expressions. Two rows are considered
|
||||
equal if all their corresponding members are non-null and equal; the rows
|
||||
are unequal if any corresponding members are non-null and unequal;
|
||||
otherwise the result of that row comparison is unknown (null).
|
||||
If there is at least one null row result, then the result of <token>ALL</token>
|
||||
cannot be true; it will be false or null.
|
||||
See <xref linkend="row-wise-comparison"> for details about the meaning
|
||||
of a row-wise comparison.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@ -8216,17 +8201,11 @@ SELECT col1 FROM tab1
|
||||
the subquery cannot return more than one row. (If it returns zero rows,
|
||||
the result is taken to be null.) The left-hand side is evaluated and
|
||||
compared row-wise to the single subquery result row.
|
||||
Presently, only <literal>=</literal> and <literal><></literal> operators are allowed
|
||||
in row-wise comparisons.
|
||||
The result is <quote>true</> if the two rows are equal or unequal, respectively.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As usual, null values in the rows are combined per
|
||||
the normal rules of SQL Boolean expressions. Two rows are considered
|
||||
equal if all their corresponding members are non-null and equal; the rows
|
||||
are unequal if any corresponding members are non-null and unequal;
|
||||
otherwise the result of the row comparison is unknown (null).
|
||||
See <xref linkend="row-wise-comparison"> for details about the meaning
|
||||
of a row-wise comparison.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
@ -8255,6 +8234,10 @@ SELECT col1 FROM tab1
|
||||
<primary>SOME</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>row-wise comparison</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>comparison</primary>
|
||||
<secondary>row-wise</secondary>
|
||||
@ -8264,6 +8247,10 @@ SELECT col1 FROM tab1
|
||||
<primary>IS DISTINCT FROM</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>IS NOT DISTINCT FROM</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm>
|
||||
<primary>IS NULL</primary>
|
||||
</indexterm>
|
||||
@ -8288,7 +8275,7 @@ SELECT col1 FROM tab1
|
||||
<title><literal>IN</literal></title>
|
||||
|
||||
<synopsis>
|
||||
<replaceable>expression</replaceable> IN (<replaceable>value</replaceable><optional>, ...</optional>)
|
||||
<replaceable>expression</replaceable> IN (<replaceable>value</replaceable> <optional>, ...</optional>)
|
||||
</synopsis>
|
||||
|
||||
<para>
|
||||
@ -8319,7 +8306,7 @@ OR
|
||||
<title><literal>NOT IN</literal></title>
|
||||
|
||||
<synopsis>
|
||||
<replaceable>expression</replaceable> NOT IN (<replaceable>value</replaceable><optional>, ...</optional>)
|
||||
<replaceable>expression</replaceable> NOT IN (<replaceable>value</replaceable> <optional>, ...</optional>)
|
||||
</synopsis>
|
||||
|
||||
<para>
|
||||
@ -8425,7 +8412,7 @@ AND
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<sect2 id="row-wise-comparison">
|
||||
<title>Row-wise Comparison</title>
|
||||
|
||||
<synopsis>
|
||||
@ -8436,23 +8423,52 @@ AND
|
||||
Each side is a row constructor,
|
||||
as described in <xref linkend="sql-syntax-row-constructors">.
|
||||
The two row values must have the same number of fields.
|
||||
Each side is evaluated and they are compared row-wise.
|
||||
Presently, only <literal>=</literal> and <literal><></literal> operators are allowed
|
||||
in row-wise comparisons.
|
||||
The result is <quote>true</> if the two rows are equal or unequal, respectively.
|
||||
Each side is evaluated and they are compared row-wise. Row comparisons
|
||||
are allowed when the <replaceable>operator</replaceable> is
|
||||
<literal>=</>,
|
||||
<literal><></>,
|
||||
<literal><</>,
|
||||
<literal><=</>,
|
||||
<literal>></> or
|
||||
<literal>>=</>,
|
||||
or has semantics similar to one of these. (To be specific, an operator
|
||||
can be a row comparison operator if it is a member of a btree operator
|
||||
class, or is the negator of the <literal>=</> member of a btree operator
|
||||
class.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As usual, null values in the rows are combined per
|
||||
the normal rules of SQL Boolean expressions. Two rows are considered
|
||||
The <literal>=</> and <literal><></> cases work slightly differently
|
||||
from the others. Two rows are considered
|
||||
equal if all their corresponding members are non-null and equal; the rows
|
||||
are unequal if any corresponding members are non-null and unequal;
|
||||
otherwise the result of the row comparison is unknown (null).
|
||||
</para>
|
||||
|
||||
<indexterm>
|
||||
<primary>IS DISTINCT FROM</primary>
|
||||
</indexterm>
|
||||
<para>
|
||||
For the <literal><</>, <literal><=</>, <literal>></> and
|
||||
<literal>>=</> cases, the row elements are compared left-to-right,
|
||||
stopping as soon as an unequal or null pair of elements is found.
|
||||
If either of this pair of elements is null, the result of the
|
||||
row comparison is unknown (null); otherwise comparison of this pair
|
||||
of elements determines the result. For example,
|
||||
<literal>ROW(1,2,NULL) < ROW(1,3,0)</>
|
||||
yields true, not null, because the third pair of elements are not
|
||||
considered.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
Prior to <productname>PostgreSQL</productname> 8.2, the
|
||||
<literal><</>, <literal><=</>, <literal>></> and <literal>>=</>
|
||||
cases were not handled per SQL specification. A comparison like
|
||||
<literal>ROW(a,b) < ROW(c,d)</>
|
||||
was implemented as
|
||||
<literal>a < c AND b < d</>
|
||||
whereas the correct behavior is equivalent to
|
||||
<literal>a < c OR (a = c AND b < d)</>.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<synopsis>
|
||||
<replaceable>row_constructor</replaceable> IS DISTINCT FROM <replaceable>row_constructor</replaceable>
|
||||
@ -8466,6 +8482,18 @@ AND
|
||||
be either true or false, never null.
|
||||
</para>
|
||||
|
||||
<synopsis>
|
||||
<replaceable>row_constructor</replaceable> IS NOT DISTINCT FROM <replaceable>row_constructor</replaceable>
|
||||
</synopsis>
|
||||
|
||||
<para>
|
||||
This construct is similar to a <literal>=</literal> row comparison,
|
||||
but it does not yield null for null inputs. Instead, any null value is
|
||||
considered unequal to (distinct from) any non-null value, and any two
|
||||
nulls are considered equal (not distinct). Thus the result will always
|
||||
be either true or false, never null.
|
||||
</para>
|
||||
|
||||
<synopsis>
|
||||
<replaceable>row_constructor</replaceable> IS NULL
|
||||
<replaceable>row_constructor</replaceable> IS NOT NULL
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.48 2005/11/22 18:17:07 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.49 2005/12/28 01:29:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1129,23 +1129,28 @@ find_expr_references_walker(Node *node,
|
||||
&context->addrs);
|
||||
/* fall through to examine arguments */
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
ListCell *opid;
|
||||
|
||||
foreach(opid, sublink->operOids)
|
||||
{
|
||||
add_object_address(OCLASS_OPERATOR, lfirst_oid(opid), 0,
|
||||
&context->addrs);
|
||||
}
|
||||
/* fall through to examine arguments */
|
||||
}
|
||||
if (is_subplan(node))
|
||||
{
|
||||
/* Extra work needed here if we ever need this case */
|
||||
elog(ERROR, "already-planned subqueries not supported");
|
||||
}
|
||||
if (IsA(node, RowCompareExpr))
|
||||
{
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
ListCell *l;
|
||||
|
||||
foreach(l, rcexpr->opnos)
|
||||
{
|
||||
add_object_address(OCLASS_OPERATOR, lfirst_oid(l), 0,
|
||||
&context->addrs);
|
||||
}
|
||||
foreach(l, rcexpr->opclasses)
|
||||
{
|
||||
add_object_address(OCLASS_OPCLASS, lfirst_oid(l), 0,
|
||||
&context->addrs);
|
||||
}
|
||||
/* fall through to examine arguments */
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
|
||||
|
@ -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")));
|
||||
|
@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.323 2005/12/20 02:30:35 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.324 2005/12/28 01:29:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -862,10 +862,8 @@ _copySubLink(SubLink *from)
|
||||
SubLink *newnode = makeNode(SubLink);
|
||||
|
||||
COPY_SCALAR_FIELD(subLinkType);
|
||||
COPY_SCALAR_FIELD(useOr);
|
||||
COPY_NODE_FIELD(lefthand);
|
||||
COPY_NODE_FIELD(testexpr);
|
||||
COPY_NODE_FIELD(operName);
|
||||
COPY_NODE_FIELD(operOids);
|
||||
COPY_NODE_FIELD(subselect);
|
||||
|
||||
return newnode;
|
||||
@ -880,8 +878,7 @@ _copySubPlan(SubPlan *from)
|
||||
SubPlan *newnode = makeNode(SubPlan);
|
||||
|
||||
COPY_SCALAR_FIELD(subLinkType);
|
||||
COPY_SCALAR_FIELD(useOr);
|
||||
COPY_NODE_FIELD(exprs);
|
||||
COPY_NODE_FIELD(testexpr);
|
||||
COPY_NODE_FIELD(paramIds);
|
||||
COPY_NODE_FIELD(plan);
|
||||
COPY_SCALAR_FIELD(plan_id);
|
||||
@ -1033,6 +1030,23 @@ _copyRowExpr(RowExpr *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyRowCompareExpr
|
||||
*/
|
||||
static RowCompareExpr *
|
||||
_copyRowCompareExpr(RowCompareExpr *from)
|
||||
{
|
||||
RowCompareExpr *newnode = makeNode(RowCompareExpr);
|
||||
|
||||
COPY_SCALAR_FIELD(rctype);
|
||||
COPY_NODE_FIELD(opnos);
|
||||
COPY_NODE_FIELD(opclasses);
|
||||
COPY_NODE_FIELD(largs);
|
||||
COPY_NODE_FIELD(rargs);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* _copyCoalesceExpr
|
||||
*/
|
||||
@ -2876,6 +2890,9 @@ copyObject(void *from)
|
||||
case T_RowExpr:
|
||||
retval = _copyRowExpr(from);
|
||||
break;
|
||||
case T_RowCompareExpr:
|
||||
retval = _copyRowCompareExpr(from);
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
retval = _copyCoalesceExpr(from);
|
||||
break;
|
||||
|
@ -18,7 +18,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.259 2005/12/20 02:30:35 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.260 2005/12/28 01:29:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -156,6 +156,7 @@ _equalParam(Param *a, Param *b)
|
||||
break;
|
||||
case PARAM_NUM:
|
||||
case PARAM_EXEC:
|
||||
case PARAM_SUBLINK:
|
||||
COMPARE_SCALAR_FIELD(paramid);
|
||||
break;
|
||||
default:
|
||||
@ -295,10 +296,8 @@ static bool
|
||||
_equalSubLink(SubLink *a, SubLink *b)
|
||||
{
|
||||
COMPARE_SCALAR_FIELD(subLinkType);
|
||||
COMPARE_SCALAR_FIELD(useOr);
|
||||
COMPARE_NODE_FIELD(lefthand);
|
||||
COMPARE_NODE_FIELD(testexpr);
|
||||
COMPARE_NODE_FIELD(operName);
|
||||
COMPARE_NODE_FIELD(operOids);
|
||||
COMPARE_NODE_FIELD(subselect);
|
||||
|
||||
return true;
|
||||
@ -308,8 +307,7 @@ static bool
|
||||
_equalSubPlan(SubPlan *a, SubPlan *b)
|
||||
{
|
||||
COMPARE_SCALAR_FIELD(subLinkType);
|
||||
COMPARE_SCALAR_FIELD(useOr);
|
||||
COMPARE_NODE_FIELD(exprs);
|
||||
COMPARE_NODE_FIELD(testexpr);
|
||||
COMPARE_NODE_FIELD(paramIds);
|
||||
/* should compare plans, but have to settle for comparing plan IDs */
|
||||
COMPARE_SCALAR_FIELD(plan_id);
|
||||
@ -440,6 +438,18 @@ _equalRowExpr(RowExpr *a, RowExpr *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b)
|
||||
{
|
||||
COMPARE_SCALAR_FIELD(rctype);
|
||||
COMPARE_NODE_FIELD(opnos);
|
||||
COMPARE_NODE_FIELD(opclasses);
|
||||
COMPARE_NODE_FIELD(largs);
|
||||
COMPARE_NODE_FIELD(rargs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
|
||||
{
|
||||
@ -1919,6 +1929,9 @@ equal(void *a, void *b)
|
||||
case T_RowExpr:
|
||||
retval = _equalRowExpr(a, b);
|
||||
break;
|
||||
case T_RowCompareExpr:
|
||||
retval = _equalRowCompareExpr(a, b);
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
retval = _equalCoalesceExpr(a, b);
|
||||
break;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.265 2005/12/20 02:30:35 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.266 2005/12/28 01:29:59 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Every node type that can appear in stored rules' parsetrees *must*
|
||||
@ -736,10 +736,8 @@ _outSubLink(StringInfo str, SubLink *node)
|
||||
WRITE_NODE_TYPE("SUBLINK");
|
||||
|
||||
WRITE_ENUM_FIELD(subLinkType, SubLinkType);
|
||||
WRITE_BOOL_FIELD(useOr);
|
||||
WRITE_NODE_FIELD(lefthand);
|
||||
WRITE_NODE_FIELD(testexpr);
|
||||
WRITE_NODE_FIELD(operName);
|
||||
WRITE_NODE_FIELD(operOids);
|
||||
WRITE_NODE_FIELD(subselect);
|
||||
}
|
||||
|
||||
@ -749,8 +747,7 @@ _outSubPlan(StringInfo str, SubPlan *node)
|
||||
WRITE_NODE_TYPE("SUBPLAN");
|
||||
|
||||
WRITE_ENUM_FIELD(subLinkType, SubLinkType);
|
||||
WRITE_BOOL_FIELD(useOr);
|
||||
WRITE_NODE_FIELD(exprs);
|
||||
WRITE_NODE_FIELD(testexpr);
|
||||
WRITE_NODE_FIELD(paramIds);
|
||||
WRITE_NODE_FIELD(plan);
|
||||
WRITE_INT_FIELD(plan_id);
|
||||
@ -855,6 +852,18 @@ _outRowExpr(StringInfo str, RowExpr *node)
|
||||
WRITE_ENUM_FIELD(row_format, CoercionForm);
|
||||
}
|
||||
|
||||
static void
|
||||
_outRowCompareExpr(StringInfo str, RowCompareExpr *node)
|
||||
{
|
||||
WRITE_NODE_TYPE("ROWCOMPARE");
|
||||
|
||||
WRITE_ENUM_FIELD(rctype, RowCompareType);
|
||||
WRITE_NODE_FIELD(opnos);
|
||||
WRITE_NODE_FIELD(opclasses);
|
||||
WRITE_NODE_FIELD(largs);
|
||||
WRITE_NODE_FIELD(rargs);
|
||||
}
|
||||
|
||||
static void
|
||||
_outCoalesceExpr(StringInfo str, CoalesceExpr *node)
|
||||
{
|
||||
@ -1936,6 +1945,9 @@ _outNode(StringInfo str, void *obj)
|
||||
case T_RowExpr:
|
||||
_outRowExpr(str, obj);
|
||||
break;
|
||||
case T_RowCompareExpr:
|
||||
_outRowCompareExpr(str, obj);
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
_outCoalesceExpr(str, obj);
|
||||
break;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.182 2005/10/15 02:49:19 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.183 2005/12/28 01:29:59 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Path and Plan nodes do not have any readfuncs support, because we
|
||||
@ -494,10 +494,8 @@ _readSubLink(void)
|
||||
READ_LOCALS(SubLink);
|
||||
|
||||
READ_ENUM_FIELD(subLinkType, SubLinkType);
|
||||
READ_BOOL_FIELD(useOr);
|
||||
READ_NODE_FIELD(lefthand);
|
||||
READ_NODE_FIELD(testexpr);
|
||||
READ_NODE_FIELD(operName);
|
||||
READ_NODE_FIELD(operOids);
|
||||
READ_NODE_FIELD(subselect);
|
||||
|
||||
READ_DONE();
|
||||
@ -645,6 +643,23 @@ _readRowExpr(void)
|
||||
READ_DONE();
|
||||
}
|
||||
|
||||
/*
|
||||
* _readRowCompareExpr
|
||||
*/
|
||||
static RowCompareExpr *
|
||||
_readRowCompareExpr(void)
|
||||
{
|
||||
READ_LOCALS(RowCompareExpr);
|
||||
|
||||
READ_ENUM_FIELD(rctype, RowCompareType);
|
||||
READ_NODE_FIELD(opnos);
|
||||
READ_NODE_FIELD(opclasses);
|
||||
READ_NODE_FIELD(largs);
|
||||
READ_NODE_FIELD(rargs);
|
||||
|
||||
READ_DONE();
|
||||
}
|
||||
|
||||
/*
|
||||
* _readCoalesceExpr
|
||||
*/
|
||||
@ -996,6 +1011,8 @@ parseNodeString(void)
|
||||
return_value = _readArrayExpr();
|
||||
else if (MATCH("ROW", 3))
|
||||
return_value = _readRowExpr();
|
||||
else if (MATCH("ROWCOMPARE", 10))
|
||||
return_value = _readRowCompareExpr();
|
||||
else if (MATCH("COALESCE", 8))
|
||||
return_value = _readCoalesceExpr();
|
||||
else if (MATCH("MINMAX", 6))
|
||||
|
@ -49,7 +49,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.151 2005/11/26 22:14:56 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.152 2005/12/28 01:29:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1609,6 +1609,13 @@ cost_qual_eval_walker(Node *node, QualCost *total)
|
||||
total->per_tuple +=
|
||||
cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
|
||||
}
|
||||
else if (IsA(node, RowCompareExpr))
|
||||
{
|
||||
/* Conservatively assume we will check all the columns */
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
|
||||
total->per_tuple += cpu_operator_cost * list_length(rcexpr->opnos);
|
||||
}
|
||||
else if (IsA(node, SubLink))
|
||||
{
|
||||
/* This routine should not be applied to un-planned expressions */
|
||||
@ -1624,7 +1631,6 @@ cost_qual_eval_walker(Node *node, QualCost *total)
|
||||
*
|
||||
* An exception occurs when we have decided we can implement the
|
||||
* subplan by hashing.
|
||||
*
|
||||
*/
|
||||
SubPlan *subplan = (SubPlan *) node;
|
||||
Plan *plan = subplan->plan;
|
||||
@ -1643,7 +1649,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
|
||||
/*
|
||||
* The per-tuple costs include the cost of evaluating the lefthand
|
||||
* expressions, plus the cost of probing the hashtable. Recursion
|
||||
* into the exprs list will handle the lefthand expressions
|
||||
* into the testexpr will handle the lefthand expressions
|
||||
* properly, and will count one cpu_operator_cost for each
|
||||
* comparison operator. That is probably too low for the probing
|
||||
* cost, but it's hard to make a better estimate, so live with it
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.102 2005/11/26 22:14:57 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.103 2005/12/28 01:29:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -19,14 +19,12 @@
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/params.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/planner.h"
|
||||
#include "optimizer/subselect.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_oper.h"
|
||||
#include "parser/parse_relation.h"
|
||||
#include "rewrite/rewriteManip.h"
|
||||
#include "utils/builtins.h"
|
||||
@ -74,6 +72,12 @@ typedef struct PlannerParamItem
|
||||
} PlannerParamItem;
|
||||
|
||||
|
||||
typedef struct convert_testexpr_context
|
||||
{
|
||||
int rtindex; /* RT index for Vars, or 0 for Params */
|
||||
List *righthandIds; /* accumulated list of Vars or Param IDs */
|
||||
} convert_testexpr_context;
|
||||
|
||||
typedef struct finalize_primnode_context
|
||||
{
|
||||
Bitmapset *paramids; /* Set of PARAM_EXEC paramids found */
|
||||
@ -81,10 +85,13 @@ typedef struct finalize_primnode_context
|
||||
} finalize_primnode_context;
|
||||
|
||||
|
||||
static List *convert_sublink_opers(List *lefthand, List *operOids,
|
||||
List *targetlist, int rtindex,
|
||||
List **righthandIds);
|
||||
static Node *convert_testexpr(Node *testexpr,
|
||||
int rtindex,
|
||||
List **righthandIds);
|
||||
static Node *convert_testexpr_mutator(Node *node,
|
||||
convert_testexpr_context *context);
|
||||
static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
|
||||
static bool hash_ok_operator(OpExpr *expr);
|
||||
static Node *replace_correlation_vars_mutator(Node *node, void *context);
|
||||
static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
|
||||
static Bitmapset *finalize_plan(Plan *plan, List *rtable,
|
||||
@ -228,20 +235,20 @@ generate_new_param(Oid paramtype, int32 paramtypmod)
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a bare SubLink (as created by the parser) into a SubPlan.
|
||||
* Convert a SubLink (as created by the parser) into a SubPlan.
|
||||
*
|
||||
* We are given the raw SubLink and the already-processed lefthand argument
|
||||
* list (use this instead of the SubLink's own field). We are also told if
|
||||
* We are given the original SubLink and the already-processed testexpr
|
||||
* (use this instead of the SubLink's own field). We are also told if
|
||||
* this expression appears at top level of a WHERE/HAVING qual.
|
||||
*
|
||||
* The result is whatever we need to substitute in place of the SubLink
|
||||
* node in the executable expression. This will be either the SubPlan
|
||||
* node (if we have to do the subplan as a subplan), or a Param node
|
||||
* representing the result of an InitPlan, or possibly an AND or OR tree
|
||||
* containing InitPlan Param nodes.
|
||||
* representing the result of an InitPlan, or a row comparison expression
|
||||
* tree containing InitPlan Param nodes.
|
||||
*/
|
||||
static Node *
|
||||
make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||
make_subplan(SubLink *slink, Node *testexpr, bool isTopQual)
|
||||
{
|
||||
SubPlan *node = makeNode(SubPlan);
|
||||
Query *subquery = (Query *) (slink->subselect);
|
||||
@ -264,7 +271,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||
* first tuple will be retrieved. For ALL and ANY subplans, we will be
|
||||
* able to stop evaluating if the test condition fails, so very often not
|
||||
* all the tuples will be retrieved; for lack of a better idea, specify
|
||||
* 50% retrieval. For EXPR and MULTIEXPR subplans, use default behavior
|
||||
* 50% retrieval. For EXPR and ROWCOMPARE subplans, use default behavior
|
||||
* (we're only expecting one row out, anyway).
|
||||
*
|
||||
* NOTE: if you change these numbers, also change cost_qual_eval_walker()
|
||||
@ -300,8 +307,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||
* Initialize other fields of the SubPlan node.
|
||||
*/
|
||||
node->subLinkType = slink->subLinkType;
|
||||
node->useOr = slink->useOr;
|
||||
node->exprs = NIL;
|
||||
node->testexpr = NULL;
|
||||
node->paramIds = NIL;
|
||||
node->useHashTable = false;
|
||||
/* At top level of a qual, can treat UNKNOWN the same as FALSE */
|
||||
@ -326,11 +332,11 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||
|
||||
/*
|
||||
* Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY, or
|
||||
* MULTIEXPR types can be used as initPlans. For EXISTS, EXPR, or ARRAY,
|
||||
* ROWCOMPARE types can be used as initPlans. For EXISTS, EXPR, or ARRAY,
|
||||
* we just produce a Param referring to the result of evaluating the
|
||||
* initPlan. For MULTIEXPR, we must build an AND or OR-clause of the
|
||||
* individual comparison operators, using the appropriate lefthand side
|
||||
* expressions and Params for the initPlan's target items.
|
||||
* initPlan. For ROWCOMPARE, we must modify the testexpr tree to contain
|
||||
* PARAM_EXEC Params instead of the PARAM_SUBLINK Params emitted by the
|
||||
* parser.
|
||||
*/
|
||||
if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
|
||||
{
|
||||
@ -369,34 +375,30 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||
PlannerInitPlan = lappend(PlannerInitPlan, node);
|
||||
result = (Node *) prm;
|
||||
}
|
||||
else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
|
||||
else if (node->parParam == NIL && slink->subLinkType == ROWCOMPARE_SUBLINK)
|
||||
{
|
||||
List *exprs;
|
||||
|
||||
/* Convert the lefthand exprs and oper OIDs into executable exprs */
|
||||
exprs = convert_sublink_opers(lefthand,
|
||||
slink->operOids,
|
||||
plan->targetlist,
|
||||
0,
|
||||
&node->paramIds);
|
||||
/* Adjust the Params */
|
||||
result = convert_testexpr(testexpr,
|
||||
0,
|
||||
&node->paramIds);
|
||||
node->setParam = list_copy(node->paramIds);
|
||||
PlannerInitPlan = lappend(PlannerInitPlan, node);
|
||||
|
||||
/*
|
||||
* The executable expressions are returned to become part of the outer
|
||||
* plan's expression tree; they are not kept in the initplan node.
|
||||
* The executable expression is returned to become part of the outer
|
||||
* plan's expression tree; it is not kept in the initplan node.
|
||||
*/
|
||||
if (list_length(exprs) > 1)
|
||||
result = (Node *) (node->useOr ? make_orclause(exprs) :
|
||||
make_andclause(exprs));
|
||||
else
|
||||
result = (Node *) linitial(exprs);
|
||||
}
|
||||
else
|
||||
{
|
||||
List *args;
|
||||
ListCell *l;
|
||||
|
||||
/* Adjust the Params */
|
||||
node->testexpr = convert_testexpr(testexpr,
|
||||
0,
|
||||
&node->paramIds);
|
||||
|
||||
/*
|
||||
* We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types to
|
||||
* initPlans, even when they are uncorrelated or undirect correlated,
|
||||
@ -434,13 +436,6 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||
node->plan = plan = materialize_finished_plan(plan);
|
||||
}
|
||||
|
||||
/* Convert the lefthand exprs and oper OIDs into executable exprs */
|
||||
node->exprs = convert_sublink_opers(lefthand,
|
||||
slink->operOids,
|
||||
plan->targetlist,
|
||||
0,
|
||||
&node->paramIds);
|
||||
|
||||
/*
|
||||
* Make node->args from parParam.
|
||||
*/
|
||||
@ -465,10 +460,9 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||
}
|
||||
|
||||
/*
|
||||
* convert_sublink_opers: given a lefthand-expressions list and a list of
|
||||
* operator OIDs, build a list of actually executable expressions. The
|
||||
* righthand sides of the expressions are Params or Vars representing the
|
||||
* results of the sub-select.
|
||||
* convert_testexpr: convert the testexpr given by the parser into
|
||||
* actually executable form. This entails replacing PARAM_SUBLINK Params
|
||||
* with Params or Vars representing the results of the sub-select:
|
||||
*
|
||||
* If rtindex is 0, we build Params to represent the sub-select outputs.
|
||||
* The paramids of the Params created are returned in the *righthandIds list.
|
||||
@ -476,90 +470,84 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||
* If rtindex is not 0, we build Vars using that rtindex as varno. Copies
|
||||
* of the Var nodes are returned in *righthandIds (this is a bit of a type
|
||||
* cheat, but we can get away with it).
|
||||
*
|
||||
* The given testexpr has already been recursively processed by
|
||||
* process_sublinks_mutator. Hence it can no longer contain any
|
||||
* PARAM_SUBLINK Params for lower SubLink nodes; we can safely assume that
|
||||
* any we find are for our own level of SubLink.
|
||||
*/
|
||||
static List *
|
||||
convert_sublink_opers(List *lefthand, List *operOids,
|
||||
List *targetlist, int rtindex,
|
||||
List **righthandIds)
|
||||
static Node *
|
||||
convert_testexpr(Node *testexpr,
|
||||
int rtindex,
|
||||
List **righthandIds)
|
||||
{
|
||||
List *result = NIL;
|
||||
ListCell *l,
|
||||
*lefthand_item,
|
||||
*tlist_item;
|
||||
|
||||
*righthandIds = NIL;
|
||||
lefthand_item = list_head(lefthand);
|
||||
tlist_item = list_head(targetlist);
|
||||
|
||||
foreach(l, operOids)
|
||||
{
|
||||
Oid opid = lfirst_oid(l);
|
||||
Node *leftop = (Node *) lfirst(lefthand_item);
|
||||
TargetEntry *te = (TargetEntry *) lfirst(tlist_item);
|
||||
Node *rightop;
|
||||
Operator tup;
|
||||
|
||||
Assert(!te->resjunk);
|
||||
|
||||
if (rtindex)
|
||||
{
|
||||
/* Make the Var node representing the subplan's result */
|
||||
rightop = (Node *) makeVar(rtindex,
|
||||
te->resno,
|
||||
exprType((Node *) te->expr),
|
||||
exprTypmod((Node *) te->expr),
|
||||
0);
|
||||
|
||||
/*
|
||||
* Copy it for caller. NB: we need a copy to avoid having
|
||||
* doubly-linked substructure in the modified parse tree.
|
||||
*/
|
||||
*righthandIds = lappend(*righthandIds, copyObject(rightop));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Make the Param node representing the subplan's result */
|
||||
Param *prm;
|
||||
|
||||
prm = generate_new_param(exprType((Node *) te->expr),
|
||||
exprTypmod((Node *) te->expr));
|
||||
/* Record its ID */
|
||||
*righthandIds = lappend_int(*righthandIds, prm->paramid);
|
||||
rightop = (Node *) prm;
|
||||
}
|
||||
|
||||
/* Look up the operator to pass to make_op_expr */
|
||||
tup = SearchSysCache(OPEROID,
|
||||
ObjectIdGetDatum(opid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "cache lookup failed for operator %u", opid);
|
||||
|
||||
/*
|
||||
* Make the expression node.
|
||||
*
|
||||
* Note: we use make_op_expr in case runtime type conversion function
|
||||
* calls must be inserted for this operator! (But we are not
|
||||
* expecting to have to resolve unknown Params, so it's okay to pass a
|
||||
* null pstate.)
|
||||
*/
|
||||
result = lappend(result,
|
||||
make_op_expr(NULL,
|
||||
tup,
|
||||
leftop,
|
||||
rightop,
|
||||
exprType(leftop),
|
||||
exprType((Node *) te->expr)));
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
lefthand_item = lnext(lefthand_item);
|
||||
tlist_item = lnext(tlist_item);
|
||||
}
|
||||
Node *result;
|
||||
convert_testexpr_context context;
|
||||
|
||||
context.rtindex = rtindex;
|
||||
context.righthandIds = NIL;
|
||||
result = convert_testexpr_mutator(testexpr, &context);
|
||||
*righthandIds = context.righthandIds;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Node *
|
||||
convert_testexpr_mutator(Node *node,
|
||||
convert_testexpr_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
if (IsA(node, Param))
|
||||
{
|
||||
Param *param = (Param *) node;
|
||||
|
||||
if (param->paramkind == PARAM_SUBLINK)
|
||||
{
|
||||
/*
|
||||
* We expect to encounter the Params in column-number sequence.
|
||||
* We could handle non-sequential order if necessary, but for now
|
||||
* there's no need. (This is also a useful cross-check that we
|
||||
* aren't finding any unexpected Params.)
|
||||
*/
|
||||
if (param->paramid != list_length(context->righthandIds) + 1)
|
||||
elog(ERROR, "unexpected PARAM_SUBLINK ID: %d", param->paramid);
|
||||
|
||||
if (context->rtindex)
|
||||
{
|
||||
/* Make the Var node representing the subplan's result */
|
||||
Var *newvar;
|
||||
|
||||
newvar = makeVar(context->rtindex,
|
||||
param->paramid,
|
||||
param->paramtype,
|
||||
-1,
|
||||
0);
|
||||
/*
|
||||
* Copy it for caller. NB: we need a copy to avoid having
|
||||
* doubly-linked substructure in the modified parse tree.
|
||||
*/
|
||||
context->righthandIds = lappend(context->righthandIds,
|
||||
copyObject(newvar));
|
||||
return (Node *) newvar;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Make the Param node representing the subplan's result */
|
||||
Param *newparam;
|
||||
|
||||
newparam = generate_new_param(param->paramtype, -1);
|
||||
/* Record its ID */
|
||||
context->righthandIds = lappend_int(context->righthandIds,
|
||||
newparam->paramid);
|
||||
return (Node *) newparam;
|
||||
}
|
||||
}
|
||||
}
|
||||
return expression_tree_mutator(node,
|
||||
convert_testexpr_mutator,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
/*
|
||||
* subplan_is_hashable: decide whether we can implement a subplan by hashing
|
||||
*
|
||||
@ -573,15 +561,19 @@ subplan_is_hashable(SubLink *slink, SubPlan *node)
|
||||
ListCell *l;
|
||||
|
||||
/*
|
||||
* The sublink type must be "= ANY" --- that is, an IN operator. (We
|
||||
* require the operator name to be unqualified, which may be overly
|
||||
* paranoid, or may not be.) XXX since we also check that the operators
|
||||
* are hashable, the test on operator name may be redundant?
|
||||
* The sublink type must be "= ANY" --- that is, an IN operator. We
|
||||
* expect that the test expression will be either a single OpExpr, or an
|
||||
* AND-clause containing OpExprs. (If it's anything else then the parser
|
||||
* must have determined that the operators have non-equality-like
|
||||
* semantics. In the OpExpr case we can't be sure what the operator's
|
||||
* semantics are like, but the test below for hashability will reject
|
||||
* anything that's not equality.)
|
||||
*/
|
||||
if (slink->subLinkType != ANY_SUBLINK)
|
||||
return false;
|
||||
if (list_length(slink->operName) != 1 ||
|
||||
strcmp(strVal(linitial(slink->operName)), "=") != 0)
|
||||
if (slink->testexpr == NULL ||
|
||||
(!IsA(slink->testexpr, OpExpr) &&
|
||||
!and_clause(slink->testexpr)))
|
||||
return false;
|
||||
|
||||
/*
|
||||
@ -614,26 +606,47 @@ subplan_is_hashable(SubLink *slink, SubPlan *node)
|
||||
* could be relaxed by using two different sets of operators with the hash
|
||||
* table, but there is no obvious usefulness to that at present.)
|
||||
*/
|
||||
foreach(l, slink->operOids)
|
||||
if (IsA(slink->testexpr, OpExpr))
|
||||
{
|
||||
Oid opid = lfirst_oid(l);
|
||||
HeapTuple tup;
|
||||
Form_pg_operator optup;
|
||||
|
||||
tup = SearchSysCache(OPEROID,
|
||||
ObjectIdGetDatum(opid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "cache lookup failed for operator %u", opid);
|
||||
optup = (Form_pg_operator) GETSTRUCT(tup);
|
||||
if (!optup->oprcanhash || optup->oprcom != opid ||
|
||||
!func_strict(optup->oprcode))
|
||||
{
|
||||
ReleaseSysCache(tup);
|
||||
if (!hash_ok_operator((OpExpr *) slink->testexpr))
|
||||
return false;
|
||||
}
|
||||
ReleaseSysCache(tup);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(l, ((BoolExpr *) slink->testexpr)->args)
|
||||
{
|
||||
Node *andarg = (Node *) lfirst(l);
|
||||
|
||||
if (!IsA(andarg, OpExpr))
|
||||
return false; /* probably can't happen */
|
||||
if (!hash_ok_operator((OpExpr *) andarg))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
hash_ok_operator(OpExpr *expr)
|
||||
{
|
||||
Oid opid = expr->opno;
|
||||
HeapTuple tup;
|
||||
Form_pg_operator optup;
|
||||
|
||||
tup = SearchSysCache(OPEROID,
|
||||
ObjectIdGetDatum(opid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "cache lookup failed for operator %u", opid);
|
||||
optup = (Form_pg_operator) GETSTRUCT(tup);
|
||||
if (!optup->oprcanhash || optup->oprcom != opid ||
|
||||
!func_strict(optup->oprcode))
|
||||
{
|
||||
ReleaseSysCache(tup);
|
||||
return false;
|
||||
}
|
||||
ReleaseSysCache(tup);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -659,17 +672,28 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
|
||||
RangeTblEntry *rte;
|
||||
RangeTblRef *rtr;
|
||||
InClauseInfo *ininfo;
|
||||
List *exprs;
|
||||
|
||||
/*
|
||||
* The sublink type must be "= ANY" --- that is, an IN operator. (We
|
||||
* require the operator name to be unqualified, which may be overly
|
||||
* paranoid, or may not be.)
|
||||
* The sublink type must be "= ANY" --- that is, an IN operator. We
|
||||
* expect that the test expression will be either a single OpExpr, or an
|
||||
* AND-clause containing OpExprs. (If it's anything else then the parser
|
||||
* must have determined that the operators have non-equality-like
|
||||
* semantics. In the OpExpr case we can't be sure what the operator's
|
||||
* semantics are like, and must check for ourselves.)
|
||||
*/
|
||||
if (sublink->subLinkType != ANY_SUBLINK)
|
||||
return NULL;
|
||||
if (list_length(sublink->operName) != 1 ||
|
||||
strcmp(strVal(linitial(sublink->operName)), "=") != 0)
|
||||
if (sublink->testexpr && IsA(sublink->testexpr, OpExpr))
|
||||
{
|
||||
List *opclasses;
|
||||
List *opstrats;
|
||||
|
||||
get_op_btree_interpretation(((OpExpr *) sublink->testexpr)->opno,
|
||||
&opclasses, &opstrats);
|
||||
if (!list_member_int(opstrats, ROWCOMPARE_EQ))
|
||||
return NULL;
|
||||
}
|
||||
else if (!and_clause(sublink->testexpr))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
@ -683,16 +707,14 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
|
||||
* The left-hand expressions must contain some Vars of the current query,
|
||||
* else it's not gonna be a join.
|
||||
*/
|
||||
left_varnos = pull_varnos((Node *) sublink->lefthand);
|
||||
left_varnos = pull_varnos(sublink->testexpr);
|
||||
if (bms_is_empty(left_varnos))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* The left-hand expressions mustn't be volatile. (Perhaps we should test
|
||||
* the combining operators, too? We'd only need to point the function
|
||||
* directly at the sublink ...)
|
||||
* The combining operators and left-hand expressions mustn't be volatile.
|
||||
*/
|
||||
if (contain_volatile_functions((Node *) sublink->lefthand))
|
||||
if (contain_volatile_functions(sublink->testexpr))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
@ -722,16 +744,13 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
|
||||
root->in_info_list = lappend(root->in_info_list, ininfo);
|
||||
|
||||
/*
|
||||
* Build the result qual expressions. As a side effect,
|
||||
* Build the result qual expression. As a side effect,
|
||||
* ininfo->sub_targetlist is filled with a list of Vars representing the
|
||||
* subselect outputs.
|
||||
*/
|
||||
exprs = convert_sublink_opers(sublink->lefthand,
|
||||
sublink->operOids,
|
||||
subselect->targetList,
|
||||
rtindex,
|
||||
&ininfo->sub_targetlist);
|
||||
return (Node *) make_ands_explicit(exprs);
|
||||
return convert_testexpr(sublink->testexpr,
|
||||
rtindex,
|
||||
&ininfo->sub_targetlist);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -802,19 +821,18 @@ process_sublinks_mutator(Node *node, bool *isTopQual)
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
List *lefthand;
|
||||
Node *testexpr;
|
||||
|
||||
/*
|
||||
* First, recursively process the lefthand-side expressions, if any.
|
||||
*/
|
||||
locTopQual = false;
|
||||
lefthand = (List *)
|
||||
process_sublinks_mutator((Node *) sublink->lefthand, &locTopQual);
|
||||
testexpr = process_sublinks_mutator(sublink->testexpr, &locTopQual);
|
||||
|
||||
/*
|
||||
* Now build the SubPlan node and make the expr to return.
|
||||
*/
|
||||
return make_subplan(sublink, lefthand, *isTopQual);
|
||||
return make_subplan(sublink, testexpr, *isTopQual);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.204 2005/12/20 02:30:36 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.205 2005/12/28 01:30:00 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -540,6 +540,8 @@ expression_returns_set_walker(Node *node, void *context)
|
||||
return false;
|
||||
if (IsA(node, RowExpr))
|
||||
return false;
|
||||
if (IsA(node, RowCompareExpr))
|
||||
return false;
|
||||
if (IsA(node, CoalesceExpr))
|
||||
return false;
|
||||
if (IsA(node, MinMaxExpr))
|
||||
@ -651,12 +653,12 @@ contain_mutable_functions_walker(Node *node, void *context)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
if (IsA(node, RowCompareExpr))
|
||||
{
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
ListCell *opid;
|
||||
|
||||
foreach(opid, sublink->operOids)
|
||||
foreach(opid, rcexpr->opnos)
|
||||
{
|
||||
if (op_volatile(lfirst_oid(opid)) != PROVOLATILE_IMMUTABLE)
|
||||
return true;
|
||||
@ -734,12 +736,13 @@ contain_volatile_functions_walker(Node *node, void *context)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
if (IsA(node, RowCompareExpr))
|
||||
{
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
/* RowCompare probably can't have volatile ops, but check anyway */
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
ListCell *opid;
|
||||
|
||||
foreach(opid, sublink->operOids)
|
||||
foreach(opid, rcexpr->opnos)
|
||||
{
|
||||
if (op_volatile(lfirst_oid(opid)) == PROVOLATILE_VOLATILE)
|
||||
return true;
|
||||
@ -847,6 +850,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
|
||||
return true;
|
||||
if (IsA(node, RowExpr))
|
||||
return true;
|
||||
if (IsA(node, RowCompareExpr))
|
||||
return true;
|
||||
if (IsA(node, CoalesceExpr))
|
||||
return true;
|
||||
if (IsA(node, MinMaxExpr))
|
||||
@ -2857,8 +2862,8 @@ evaluate_expr(Expr *expr, Oid result_type)
|
||||
* FromExpr, JoinExpr, and SetOperationStmt nodes are handled, so that query
|
||||
* jointrees and setOperation trees can be processed without additional code.
|
||||
*
|
||||
* expression_tree_walker will handle SubLink nodes by recursing normally into
|
||||
* the "lefthand" arguments (which are expressions belonging to the outer
|
||||
* expression_tree_walker will handle SubLink nodes by recursing normally
|
||||
* into the "testexpr" subtree (which is an expression belonging to the outer
|
||||
* plan). It will also call the walker on the sub-Query node; however, when
|
||||
* expression_tree_walker itself is called on a Query node, it does nothing
|
||||
* and returns "false". The net effect is that unless the walker does
|
||||
@ -2882,7 +2887,7 @@ evaluate_expr(Expr *expr, Oid result_type)
|
||||
* walker on all the expression subtrees of the given Query node.
|
||||
*
|
||||
* expression_tree_walker will handle SubPlan nodes by recursing normally
|
||||
* into the "exprs" and "args" lists (which are expressions belonging to
|
||||
* into the "testexpr" and the "args" list (which are expressions belonging to
|
||||
* the outer plan). It will not touch the completed subplan, however. Since
|
||||
* there is no link to the original Query, it is not possible to recurse into
|
||||
* subselects of an already-planned expression tree. This is OK for current
|
||||
@ -2992,7 +2997,7 @@ expression_tree_walker(Node *node,
|
||||
{
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
|
||||
if (expression_tree_walker((Node *) sublink->lefthand,
|
||||
if (expression_tree_walker(sublink->testexpr,
|
||||
walker, context))
|
||||
return true;
|
||||
|
||||
@ -3007,8 +3012,8 @@ expression_tree_walker(Node *node,
|
||||
{
|
||||
SubPlan *subplan = (SubPlan *) node;
|
||||
|
||||
/* recurse into the exprs list, but not into the Plan */
|
||||
if (expression_tree_walker((Node *) subplan->exprs,
|
||||
/* recurse into the testexpr, but not into the Plan */
|
||||
if (expression_tree_walker(subplan->testexpr,
|
||||
walker, context))
|
||||
return true;
|
||||
/* also examine args list */
|
||||
@ -3058,6 +3063,16 @@ expression_tree_walker(Node *node,
|
||||
return walker(((ArrayExpr *) node)->elements, context);
|
||||
case T_RowExpr:
|
||||
return walker(((RowExpr *) node)->args, context);
|
||||
case T_RowCompareExpr:
|
||||
{
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
|
||||
if (walker(rcexpr->largs, context))
|
||||
return true;
|
||||
if (walker(rcexpr->rargs, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
return walker(((CoalesceExpr *) node)->args, context);
|
||||
case T_MinMaxExpr:
|
||||
@ -3263,7 +3278,7 @@ range_table_walker(List *rtable,
|
||||
* and qualifier clauses during the planning stage.
|
||||
*
|
||||
* expression_tree_mutator will handle SubLink nodes by recursing normally
|
||||
* into the "lefthand" arguments (which are expressions belonging to the outer
|
||||
* into the "testexpr" subtree (which is an expression belonging to the outer
|
||||
* plan). It will also call the mutator on the sub-Query node; however, when
|
||||
* expression_tree_mutator itself is called on a Query node, it does nothing
|
||||
* and returns the unmodified Query node. The net effect is that unless the
|
||||
@ -3272,8 +3287,8 @@ range_table_walker(List *rtable,
|
||||
* SubLink node. Mutators that want to descend into sub-selects will usually
|
||||
* do so by recognizing Query nodes and calling query_tree_mutator (below).
|
||||
*
|
||||
* expression_tree_mutator will handle a SubPlan node by recursing into
|
||||
* the "exprs" and "args" lists (which belong to the outer plan), but it
|
||||
* expression_tree_mutator will handle a SubPlan node by recursing into the
|
||||
* "testexpr" and the "args" list (which belong to the outer plan), but it
|
||||
* will simply copy the link to the inner plan, since that's typically what
|
||||
* expression tree mutators want. A mutator that wants to modify the subplan
|
||||
* can force appropriate behavior by recognizing SubPlan expression nodes
|
||||
@ -3404,7 +3419,7 @@ expression_tree_mutator(Node *node,
|
||||
SubLink *newnode;
|
||||
|
||||
FLATCOPY(newnode, sublink, SubLink);
|
||||
MUTATE(newnode->lefthand, sublink->lefthand, List *);
|
||||
MUTATE(newnode->testexpr, sublink->testexpr, Node *);
|
||||
|
||||
/*
|
||||
* Also invoke the mutator on the sublink's Query node, so it
|
||||
@ -3420,8 +3435,8 @@ expression_tree_mutator(Node *node,
|
||||
SubPlan *newnode;
|
||||
|
||||
FLATCOPY(newnode, subplan, SubPlan);
|
||||
/* transform exprs list */
|
||||
MUTATE(newnode->exprs, subplan->exprs, List *);
|
||||
/* transform testexpr */
|
||||
MUTATE(newnode->testexpr, subplan->testexpr, Node *);
|
||||
/* transform args list (params to be passed to subplan) */
|
||||
MUTATE(newnode->args, subplan->args, List *);
|
||||
/* but not the sub-Plan itself, which is referenced as-is */
|
||||
@ -3513,6 +3528,17 @@ expression_tree_mutator(Node *node,
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_RowCompareExpr:
|
||||
{
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
RowCompareExpr *newnode;
|
||||
|
||||
FLATCOPY(newnode, rcexpr, RowCompareExpr);
|
||||
MUTATE(newnode->largs, rcexpr->largs, List *);
|
||||
MUTATE(newnode->rargs, rcexpr->rargs, List *);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
{
|
||||
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.519 2005/12/27 04:00:07 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.520 2005/12/28 01:30:00 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -6774,10 +6774,7 @@ a_expr: c_expr { $$ = $1; }
|
||||
/* generate foo = ANY (subquery) */
|
||||
SubLink *n = (SubLink *) $3;
|
||||
n->subLinkType = ANY_SUBLINK;
|
||||
if (IsA($1, RowExpr))
|
||||
n->lefthand = ((RowExpr *) $1)->args;
|
||||
else
|
||||
n->lefthand = list_make1($1);
|
||||
n->testexpr = $1;
|
||||
n->operName = list_make1(makeString("="));
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
@ -6796,10 +6793,7 @@ a_expr: c_expr { $$ = $1; }
|
||||
/* Make an = ANY node */
|
||||
SubLink *n = (SubLink *) $4;
|
||||
n->subLinkType = ANY_SUBLINK;
|
||||
if (IsA($1, RowExpr))
|
||||
n->lefthand = ((RowExpr *) $1)->args;
|
||||
else
|
||||
n->lefthand = list_make1($1);
|
||||
n->testexpr = $1;
|
||||
n->operName = list_make1(makeString("="));
|
||||
/* Stick a NOT on top */
|
||||
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n);
|
||||
@ -6814,10 +6808,7 @@ a_expr: c_expr { $$ = $1; }
|
||||
{
|
||||
SubLink *n = makeNode(SubLink);
|
||||
n->subLinkType = $3;
|
||||
if (IsA($1, RowExpr))
|
||||
n->lefthand = ((RowExpr *) $1)->args;
|
||||
else
|
||||
n->lefthand = list_make1($1);
|
||||
n->testexpr = $1;
|
||||
n->operName = $2;
|
||||
n->subselect = $4;
|
||||
$$ = (Node *)n;
|
||||
@ -6950,7 +6941,7 @@ c_expr: columnref { $$ = $1; }
|
||||
{
|
||||
SubLink *n = makeNode(SubLink);
|
||||
n->subLinkType = EXPR_SUBLINK;
|
||||
n->lefthand = NIL;
|
||||
n->testexpr = NULL;
|
||||
n->operName = NIL;
|
||||
n->subselect = $1;
|
||||
$$ = (Node *)n;
|
||||
@ -6959,7 +6950,7 @@ c_expr: columnref { $$ = $1; }
|
||||
{
|
||||
SubLink *n = makeNode(SubLink);
|
||||
n->subLinkType = EXISTS_SUBLINK;
|
||||
n->lefthand = NIL;
|
||||
n->testexpr = NULL;
|
||||
n->operName = NIL;
|
||||
n->subselect = $2;
|
||||
$$ = (Node *)n;
|
||||
@ -6968,7 +6959,7 @@ c_expr: columnref { $$ = $1; }
|
||||
{
|
||||
SubLink *n = makeNode(SubLink);
|
||||
n->subLinkType = ARRAY_SUBLINK;
|
||||
n->lefthand = NIL;
|
||||
n->testexpr = NULL;
|
||||
n->operName = NIL;
|
||||
n->subselect = $2;
|
||||
$$ = (Node *)n;
|
||||
|
@ -8,21 +8,20 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.188 2005/11/28 04:35:31 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.189 2005/12/28 01:30:00 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "mb/pg_wchar.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/params.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/analyze.h"
|
||||
#include "parser/gramparse.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
@ -33,7 +32,7 @@
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
bool Transform_null_equals = false;
|
||||
|
||||
@ -64,8 +63,8 @@ static Node *transformIndirection(ParseState *pstate, Node *basenode,
|
||||
List *indirection);
|
||||
static Node *typecast_expression(ParseState *pstate, Node *expr,
|
||||
TypeName *typename);
|
||||
static Node *make_row_op(ParseState *pstate, List *opname,
|
||||
RowExpr *lrow, RowExpr *rrow);
|
||||
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
|
||||
List *largs, List *rargs);
|
||||
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
|
||||
RowExpr *lrow, RowExpr *rrow);
|
||||
static Expr *make_distinct_op(ParseState *pstate, List *opname,
|
||||
@ -592,14 +591,14 @@ transformAExprOp(ParseState *pstate, A_Expr *a)
|
||||
((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK)
|
||||
{
|
||||
/*
|
||||
* Convert "row op subselect" into a MULTIEXPR sublink. Formerly the
|
||||
* Convert "row op subselect" into a ROWCOMPARE sublink. Formerly the
|
||||
* grammar did this, but now that a row construct is allowed anywhere
|
||||
* in expressions, it's easier to do it here.
|
||||
*/
|
||||
SubLink *s = (SubLink *) rexpr;
|
||||
|
||||
s->subLinkType = MULTIEXPR_SUBLINK;
|
||||
s->lefthand = ((RowExpr *) lexpr)->args;
|
||||
s->subLinkType = ROWCOMPARE_SUBLINK;
|
||||
s->testexpr = lexpr;
|
||||
s->operName = a->name;
|
||||
result = transformExpr(pstate, (Node *) s);
|
||||
}
|
||||
@ -612,10 +611,10 @@ transformAExprOp(ParseState *pstate, A_Expr *a)
|
||||
Assert(IsA(lexpr, RowExpr));
|
||||
Assert(IsA(rexpr, RowExpr));
|
||||
|
||||
result = make_row_op(pstate,
|
||||
a->name,
|
||||
(RowExpr *) lexpr,
|
||||
(RowExpr *) rexpr);
|
||||
result = make_row_comparison_op(pstate,
|
||||
a->name,
|
||||
((RowExpr *) lexpr)->args,
|
||||
((RowExpr *) rexpr)->args);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -885,10 +884,10 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("arguments of row IN must all be row expressions")));
|
||||
cmp = make_row_op(pstate,
|
||||
a->name,
|
||||
(RowExpr *) copyObject(lexpr),
|
||||
(RowExpr *) rexpr);
|
||||
cmp = make_row_comparison_op(pstate,
|
||||
a->name,
|
||||
(List *) copyObject(((RowExpr *) lexpr)->args),
|
||||
((RowExpr *) rexpr)->args);
|
||||
}
|
||||
else
|
||||
cmp = (Node *) make_op(pstate,
|
||||
@ -1080,13 +1079,11 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
|
||||
if (sublink->subLinkType == EXISTS_SUBLINK)
|
||||
{
|
||||
/*
|
||||
* EXISTS needs no lefthand or combining operator. These fields
|
||||
* should be NIL already, but make sure.
|
||||
* EXISTS needs no test expression or combining operator.
|
||||
* These fields should be null already, but make sure.
|
||||
*/
|
||||
sublink->lefthand = NIL;
|
||||
sublink->testexpr = NULL;
|
||||
sublink->operName = NIL;
|
||||
sublink->operOids = NIL;
|
||||
sublink->useOr = FALSE;
|
||||
}
|
||||
else if (sublink->subLinkType == EXPR_SUBLINK ||
|
||||
sublink->subLinkType == ARRAY_SUBLINK)
|
||||
@ -1111,128 +1108,72 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
|
||||
}
|
||||
|
||||
/*
|
||||
* EXPR and ARRAY need no lefthand or combining operator. These fields
|
||||
* should be NIL already, but make sure.
|
||||
* EXPR and ARRAY need no test expression or combining operator.
|
||||
* These fields should be null already, but make sure.
|
||||
*/
|
||||
sublink->lefthand = NIL;
|
||||
sublink->testexpr = NULL;
|
||||
sublink->operName = NIL;
|
||||
sublink->operOids = NIL;
|
||||
sublink->useOr = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ALL, ANY, or MULTIEXPR: generate operator list */
|
||||
List *left_list = sublink->lefthand;
|
||||
List *right_list = qtree->targetList;
|
||||
int row_length = list_length(left_list);
|
||||
bool needNot = false;
|
||||
List *op = sublink->operName;
|
||||
char *opname = strVal(llast(op));
|
||||
/* ALL, ANY, or ROWCOMPARE: generate row-comparing expression */
|
||||
Node *lefthand;
|
||||
List *left_list;
|
||||
List *right_list;
|
||||
ListCell *l;
|
||||
ListCell *ll_item;
|
||||
|
||||
/* transform lefthand expressions */
|
||||
foreach(l, left_list)
|
||||
lfirst(l) = transformExpr(pstate, lfirst(l));
|
||||
|
||||
/*
|
||||
* If the expression is "<> ALL" (with unqualified opname) then
|
||||
* convert it to "NOT IN". This is a hack to improve efficiency of
|
||||
* expressions output by pre-7.4 Postgres.
|
||||
* Transform lefthand expression, and convert to a list
|
||||
*/
|
||||
if (sublink->subLinkType == ALL_SUBLINK &&
|
||||
list_length(op) == 1 && strcmp(opname, "<>") == 0)
|
||||
{
|
||||
sublink->subLinkType = ANY_SUBLINK;
|
||||
opname = pstrdup("=");
|
||||
op = list_make1(makeString(opname));
|
||||
sublink->operName = op;
|
||||
needNot = true;
|
||||
}
|
||||
|
||||
/* Set useOr if op is "<>" (possibly qualified) */
|
||||
if (strcmp(opname, "<>") == 0)
|
||||
sublink->useOr = TRUE;
|
||||
lefthand = transformExpr(pstate, sublink->testexpr);
|
||||
if (lefthand && IsA(lefthand, RowExpr))
|
||||
left_list = ((RowExpr *) lefthand)->args;
|
||||
else
|
||||
sublink->useOr = FALSE;
|
||||
|
||||
/* Combining operators other than =/<> is dubious... */
|
||||
if (row_length != 1 &&
|
||||
strcmp(opname, "=") != 0 &&
|
||||
strcmp(opname, "<>") != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("row comparison cannot use operator %s",
|
||||
opname)));
|
||||
left_list = list_make1(lefthand);
|
||||
|
||||
/*
|
||||
* To build the list of combining operator OIDs, we must scan
|
||||
* subquery's targetlist to find values that will be matched against
|
||||
* lefthand values. We need to ignore resjunk targets, so doing the
|
||||
* outer iteration over right_list is easier than doing it over
|
||||
* left_list.
|
||||
* Build a list of PARAM_SUBLINK nodes representing the
|
||||
* output columns of the subquery.
|
||||
*/
|
||||
sublink->operOids = NIL;
|
||||
|
||||
ll_item = list_head(left_list);
|
||||
foreach(l, right_list)
|
||||
right_list = NIL;
|
||||
foreach(l, qtree->targetList)
|
||||
{
|
||||
TargetEntry *tent = (TargetEntry *) lfirst(l);
|
||||
Node *lexpr;
|
||||
Operator optup;
|
||||
Form_pg_operator opform;
|
||||
Param *param;
|
||||
|
||||
if (tent->resjunk)
|
||||
continue;
|
||||
|
||||
if (ll_item == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("subquery has too many columns")));
|
||||
lexpr = lfirst(ll_item);
|
||||
ll_item = lnext(ll_item);
|
||||
param = makeNode(Param);
|
||||
param->paramkind = PARAM_SUBLINK;
|
||||
param->paramid = (AttrNumber) tent->resno;
|
||||
param->paramtype = exprType((Node *) tent->expr);
|
||||
|
||||
/*
|
||||
* It's OK to use oper() not compatible_oper() here, because
|
||||
* make_subplan() will insert type coercion calls if needed.
|
||||
*/
|
||||
optup = oper(op,
|
||||
exprType(lexpr),
|
||||
exprType((Node *) tent->expr),
|
||||
false);
|
||||
opform = (Form_pg_operator) GETSTRUCT(optup);
|
||||
|
||||
if (opform->oprresult != BOOLOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("operator %s must return type boolean, not type %s",
|
||||
opname,
|
||||
format_type_be(opform->oprresult)),
|
||||
errhint("The operator of a quantified predicate subquery must return type boolean.")));
|
||||
|
||||
if (get_func_retset(opform->oprcode))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("operator %s must not return a set",
|
||||
opname),
|
||||
errhint("The operator of a quantified predicate subquery must return type boolean.")));
|
||||
|
||||
sublink->operOids = lappend_oid(sublink->operOids,
|
||||
oprid(optup));
|
||||
|
||||
ReleaseSysCache(optup);
|
||||
right_list = lappend(right_list, param);
|
||||
}
|
||||
if (ll_item != NULL)
|
||||
|
||||
/*
|
||||
* We could rely on make_row_comparison_op to complain if the
|
||||
* list lengths differ, but we prefer to generate a more specific
|
||||
* error message.
|
||||
*/
|
||||
if (list_length(left_list) < list_length(right_list))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("subquery has too many columns")));
|
||||
if (list_length(left_list) > list_length(right_list))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("subquery has too few columns")));
|
||||
|
||||
if (needNot)
|
||||
{
|
||||
result = coerce_to_boolean(pstate, result, "NOT");
|
||||
result = (Node *) makeBoolExpr(NOT_EXPR,
|
||||
list_make1(result));
|
||||
}
|
||||
/*
|
||||
* Identify the combining operator(s) and generate a suitable
|
||||
* row-comparison expression.
|
||||
*/
|
||||
sublink->testexpr = make_row_comparison_op(pstate,
|
||||
sublink->operName,
|
||||
left_list,
|
||||
right_list);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -1673,6 +1614,9 @@ exprType(Node *expr)
|
||||
case T_RowExpr:
|
||||
type = ((RowExpr *) expr)->row_typeid;
|
||||
break;
|
||||
case T_RowCompareExpr:
|
||||
type = BOOLOID;
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
type = ((CoalesceExpr *) expr)->coalescetype;
|
||||
break;
|
||||
@ -1953,76 +1897,258 @@ typecast_expression(ParseState *pstate, Node *expr, TypeName *typename)
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform a "row op row" construct
|
||||
* Transform a "row compare-op row" construct
|
||||
*
|
||||
* The input RowExprs are already transformed
|
||||
* The inputs are lists of already-transformed expressions.
|
||||
* As with coerce_type, pstate may be NULL if no special unknown-Param
|
||||
* processing is wanted.
|
||||
*
|
||||
* The output may be a single OpExpr, an AND or OR combination of OpExprs,
|
||||
* or a RowCompareExpr. In all cases it is guaranteed to return boolean.
|
||||
* The AND, OR, and RowCompareExpr cases further imply things about the
|
||||
* behavior of the operators (ie, they behave as =, <>, or < <= > >=).
|
||||
*/
|
||||
static Node *
|
||||
make_row_op(ParseState *pstate, List *opname,
|
||||
RowExpr *lrow, RowExpr *rrow)
|
||||
make_row_comparison_op(ParseState *pstate, List *opname,
|
||||
List *largs, List *rargs)
|
||||
{
|
||||
Node *result = NULL;
|
||||
List *largs = lrow->args;
|
||||
List *rargs = rrow->args;
|
||||
RowCompareExpr *rcexpr;
|
||||
RowCompareType rctype;
|
||||
List *opexprs;
|
||||
List *opnos;
|
||||
List *opclasses;
|
||||
ListCell *l,
|
||||
*r;
|
||||
char *oprname;
|
||||
BoolExprType boolop;
|
||||
List **opclass_lists;
|
||||
List **opstrat_lists;
|
||||
Bitmapset *strats;
|
||||
int nopers;
|
||||
int i;
|
||||
|
||||
if (list_length(largs) != list_length(rargs))
|
||||
nopers = list_length(largs);
|
||||
if (nopers != list_length(rargs))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("unequal number of entries in row expressions")));
|
||||
|
||||
/*
|
||||
* XXX it's really wrong to generate a simple AND combination for < <= >
|
||||
* >=. We probably need to invent a new runtime node type to handle those
|
||||
* correctly. For the moment, though, keep on doing this ...
|
||||
* We can't compare zero-length rows because there is no principled
|
||||
* basis for figuring out what the operator is.
|
||||
*/
|
||||
oprname = strVal(llast(opname));
|
||||
|
||||
if ((strcmp(oprname, "=") == 0) ||
|
||||
(strcmp(oprname, "<") == 0) ||
|
||||
(strcmp(oprname, "<=") == 0) ||
|
||||
(strcmp(oprname, ">") == 0) ||
|
||||
(strcmp(oprname, ">=") == 0))
|
||||
boolop = AND_EXPR;
|
||||
else if (strcmp(oprname, "<>") == 0)
|
||||
boolop = OR_EXPR;
|
||||
else
|
||||
{
|
||||
if (nopers == 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("operator %s is not supported for row expressions",
|
||||
oprname)));
|
||||
boolop = 0; /* keep compiler quiet */
|
||||
}
|
||||
errmsg("cannot compare rows of zero length")));
|
||||
|
||||
/*
|
||||
* Identify all the pairwise operators, using make_op so that
|
||||
* behavior is the same as in the simple scalar case.
|
||||
*/
|
||||
opexprs = NIL;
|
||||
forboth(l, largs, r, rargs)
|
||||
{
|
||||
Node *larg = (Node *) lfirst(l);
|
||||
Node *rarg = (Node *) lfirst(r);
|
||||
Node *cmp;
|
||||
OpExpr *cmp;
|
||||
|
||||
cmp = (Node *) make_op(pstate, opname, larg, rarg);
|
||||
cmp = coerce_to_boolean(pstate, cmp, "row comparison");
|
||||
if (result == NULL)
|
||||
result = cmp;
|
||||
else
|
||||
result = (Node *) makeBoolExpr(boolop,
|
||||
list_make2(result, cmp));
|
||||
cmp = (OpExpr *) make_op(pstate, opname, larg, rarg);
|
||||
Assert(IsA(cmp, OpExpr));
|
||||
|
||||
/*
|
||||
* We don't use coerce_to_boolean here because we insist on the
|
||||
* operator yielding boolean directly, not via coercion. If it
|
||||
* doesn't yield bool it won't be in any index opclasses...
|
||||
*/
|
||||
if (cmp->opresulttype != BOOLOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("row comparison operator must yield type boolean, "
|
||||
"not type %s",
|
||||
format_type_be(cmp->opresulttype))));
|
||||
if (expression_returns_set((Node *) cmp))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("row comparison operator must not return a set")));
|
||||
opexprs = lappend(opexprs, cmp);
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
/*
|
||||
* If rows are length 1, just return the single operator. In this
|
||||
* case we don't insist on identifying btree semantics for the operator
|
||||
* (but we still require it to return boolean).
|
||||
*/
|
||||
if (nopers == 1)
|
||||
return (Node *) linitial(opexprs);
|
||||
|
||||
/*
|
||||
* Now we must determine which row comparison semantics (= <> < <= > >=)
|
||||
* apply to this set of operators. We look for btree opclasses containing
|
||||
* the operators, and see which interpretations (strategy numbers) exist
|
||||
* for each operator.
|
||||
*/
|
||||
opclass_lists = (List **) palloc(nopers * sizeof(List *));
|
||||
opstrat_lists = (List **) palloc(nopers * sizeof(List *));
|
||||
strats = NULL;
|
||||
i = 0;
|
||||
foreach(l, opexprs)
|
||||
{
|
||||
/* zero-length rows? Generate constant TRUE or FALSE */
|
||||
if (boolop == AND_EXPR)
|
||||
result = makeBoolConst(true, false);
|
||||
Bitmapset *this_strats;
|
||||
ListCell *j;
|
||||
|
||||
get_op_btree_interpretation(((OpExpr *) lfirst(l))->opno,
|
||||
&opclass_lists[i], &opstrat_lists[i]);
|
||||
/*
|
||||
* convert strategy number list to a Bitmapset to make the intersection
|
||||
* calculation easy.
|
||||
*/
|
||||
this_strats = NULL;
|
||||
foreach(j, opstrat_lists[i])
|
||||
{
|
||||
this_strats = bms_add_member(this_strats, lfirst_int(j));
|
||||
}
|
||||
if (i == 0)
|
||||
strats = this_strats;
|
||||
else
|
||||
result = makeBoolConst(false, false);
|
||||
strats = bms_int_members(strats, this_strats);
|
||||
i++;
|
||||
}
|
||||
|
||||
return result;
|
||||
switch (bms_membership(strats))
|
||||
{
|
||||
case BMS_EMPTY_SET:
|
||||
/* No common interpretation, so fail */
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("could not determine interpretation of row comparison operator %s",
|
||||
strVal(llast(opname))),
|
||||
errhint("Row comparison operators must be associated with btree operator classes.")));
|
||||
rctype = 0; /* keep compiler quiet */
|
||||
break;
|
||||
case BMS_SINGLETON:
|
||||
/* Simple case: just one possible interpretation */
|
||||
rctype = bms_singleton_member(strats);
|
||||
break;
|
||||
case BMS_MULTIPLE:
|
||||
default: /* keep compiler quiet */
|
||||
{
|
||||
/*
|
||||
* Prefer the interpretation with the most default opclasses.
|
||||
*/
|
||||
int best_defaults = 0;
|
||||
bool multiple_best = false;
|
||||
int this_rctype;
|
||||
|
||||
rctype = 0; /* keep compiler quiet */
|
||||
while ((this_rctype = bms_first_member(strats)) >= 0)
|
||||
{
|
||||
int ndefaults = 0;
|
||||
|
||||
for (i = 0; i < nopers; i++)
|
||||
{
|
||||
forboth(l, opclass_lists[i], r, opstrat_lists[i])
|
||||
{
|
||||
Oid opclass = lfirst_oid(l);
|
||||
int opstrat = lfirst_int(r);
|
||||
|
||||
if (opstrat == this_rctype &&
|
||||
opclass_is_default(opclass))
|
||||
ndefaults++;
|
||||
}
|
||||
}
|
||||
if (ndefaults > best_defaults)
|
||||
{
|
||||
best_defaults = ndefaults;
|
||||
rctype = this_rctype;
|
||||
multiple_best = false;
|
||||
}
|
||||
else if (ndefaults == best_defaults)
|
||||
multiple_best = true;
|
||||
}
|
||||
if (best_defaults == 0 || multiple_best)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("could not determine interpretation of row comparison operator %s",
|
||||
strVal(llast(opname))),
|
||||
errdetail("There are multiple equally-plausible candidates.")));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For = and <> cases, we just combine the pairwise operators with
|
||||
* AND or OR respectively.
|
||||
*
|
||||
* Note: this is presently the only place where the parser generates
|
||||
* BoolExpr with more than two arguments. Should be OK since the
|
||||
* rest of the system thinks BoolExpr is N-argument anyway.
|
||||
*/
|
||||
if (rctype == ROWCOMPARE_EQ)
|
||||
return (Node *) makeBoolExpr(AND_EXPR, opexprs);
|
||||
if (rctype == ROWCOMPARE_NE)
|
||||
return (Node *) makeBoolExpr(OR_EXPR, opexprs);
|
||||
|
||||
/*
|
||||
* Otherwise we need to determine exactly which opclass to associate
|
||||
* with each operator.
|
||||
*/
|
||||
opclasses = NIL;
|
||||
for (i = 0; i < nopers; i++)
|
||||
{
|
||||
Oid best_opclass = 0;
|
||||
int ndefault = 0;
|
||||
int nmatch = 0;
|
||||
|
||||
forboth(l, opclass_lists[i], r, opstrat_lists[i])
|
||||
{
|
||||
Oid opclass = lfirst_oid(l);
|
||||
int opstrat = lfirst_int(r);
|
||||
|
||||
if (opstrat == rctype)
|
||||
{
|
||||
if (ndefault == 0)
|
||||
best_opclass = opclass;
|
||||
if (opclass_is_default(opclass))
|
||||
ndefault++;
|
||||
else
|
||||
nmatch++;
|
||||
}
|
||||
}
|
||||
if (ndefault == 1 || (ndefault == 0 && nmatch == 1))
|
||||
opclasses = lappend_oid(opclasses, best_opclass);
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("could not determine interpretation of row comparison operator %s",
|
||||
strVal(llast(opname))),
|
||||
errdetail("There are multiple equally-plausible candidates.")));
|
||||
}
|
||||
|
||||
/*
|
||||
* Now deconstruct the OpExprs and create a RowCompareExpr.
|
||||
*
|
||||
* Note: can't just reuse the passed largs/rargs lists, because of
|
||||
* possibility that make_op inserted coercion operations.
|
||||
*/
|
||||
opnos = NIL;
|
||||
largs = NIL;
|
||||
rargs = NIL;
|
||||
foreach(l, opexprs)
|
||||
{
|
||||
OpExpr *cmp = (OpExpr *) lfirst(l);
|
||||
|
||||
opnos = lappend_oid(opnos, cmp->opno);
|
||||
largs = lappend(largs, linitial(cmp->args));
|
||||
rargs = lappend(rargs, lsecond(cmp->args));
|
||||
}
|
||||
|
||||
rcexpr = makeNode(RowCompareExpr);
|
||||
rcexpr->rctype = rctype;
|
||||
rcexpr->opnos = opnos;
|
||||
rcexpr->opclasses = opclasses;
|
||||
rcexpr->largs = largs;
|
||||
rcexpr->rargs = rargs;
|
||||
|
||||
return (Node *) rcexpr;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.83 2005/11/22 18:17:16 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.84 2005/12/28 01:30:00 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -39,6 +39,9 @@ static const char *op_signature_string(List *op, char oprkind,
|
||||
Oid arg1, Oid arg2);
|
||||
static void op_error(List *op, char oprkind, Oid arg1, Oid arg2,
|
||||
FuncDetailCode fdresult);
|
||||
static Expr *make_op_expr(ParseState *pstate, Operator op,
|
||||
Node *ltree, Node *rtree,
|
||||
Oid ltypeId, Oid rtypeId);
|
||||
|
||||
|
||||
/*
|
||||
@ -942,7 +945,7 @@ make_scalar_array_op(ParseState *pstate, List *opname,
|
||||
* As with coerce_type, pstate may be NULL if no special unknown-Param
|
||||
* processing is wanted.
|
||||
*/
|
||||
Expr *
|
||||
static Expr *
|
||||
make_op_expr(ParseState *pstate, Operator op,
|
||||
Node *ltree, Node *rtree,
|
||||
Oid ltypeId, Oid rtypeId)
|
||||
|
@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.210 2005/12/10 19:21:03 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.211 2005/12/28 01:30:00 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@ -215,7 +215,6 @@ static void printSubscripts(ArrayRef *aref, deparse_context *context);
|
||||
static char *generate_relation_name(Oid relid);
|
||||
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
|
||||
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
|
||||
static void print_operator_name(StringInfo buf, List *opname);
|
||||
static text *string_to_text(char *str);
|
||||
|
||||
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
|
||||
@ -3106,6 +3105,7 @@ get_rule_expr(Node *node, deparse_context *context,
|
||||
break;
|
||||
case PARAM_NUM:
|
||||
case PARAM_EXEC:
|
||||
case PARAM_SUBLINK:
|
||||
appendStringInfo(buf, "$%d", param->paramid);
|
||||
break;
|
||||
default:
|
||||
@ -3514,6 +3514,50 @@ get_rule_expr(Node *node, deparse_context *context,
|
||||
}
|
||||
break;
|
||||
|
||||
case T_RowCompareExpr:
|
||||
{
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
ListCell *arg;
|
||||
char *sep;
|
||||
|
||||
/*
|
||||
* SQL99 allows "ROW" to be omitted when there is more than
|
||||
* one column, but for simplicity we always print it.
|
||||
*/
|
||||
appendStringInfo(buf, "(ROW(");
|
||||
sep = "";
|
||||
foreach(arg, rcexpr->largs)
|
||||
{
|
||||
Node *e = (Node *) lfirst(arg);
|
||||
|
||||
appendStringInfoString(buf, sep);
|
||||
get_rule_expr(e, context, true);
|
||||
sep = ", ";
|
||||
}
|
||||
/*
|
||||
* We assume that the name of the first-column operator
|
||||
* will do for all the rest too. This is definitely
|
||||
* open to failure, eg if some but not all operators
|
||||
* were renamed since the construct was parsed, but there
|
||||
* seems no way to be perfect.
|
||||
*/
|
||||
appendStringInfo(buf, ") %s ROW(",
|
||||
generate_operator_name(linitial_oid(rcexpr->opnos),
|
||||
exprType(linitial(rcexpr->largs)),
|
||||
exprType(linitial(rcexpr->rargs))));
|
||||
sep = "";
|
||||
foreach(arg, rcexpr->rargs)
|
||||
{
|
||||
Node *e = (Node *) lfirst(arg);
|
||||
|
||||
appendStringInfoString(buf, sep);
|
||||
get_rule_expr(e, context, true);
|
||||
sep = ", ";
|
||||
}
|
||||
appendStringInfo(buf, "))");
|
||||
}
|
||||
break;
|
||||
|
||||
case T_CoalesceExpr:
|
||||
{
|
||||
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
|
||||
@ -3967,6 +4011,7 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
|
||||
{
|
||||
StringInfo buf = context->buf;
|
||||
Query *query = (Query *) (sublink->subselect);
|
||||
char *opname = NULL;
|
||||
bool need_paren;
|
||||
|
||||
if (sublink->subLinkType == ARRAY_SUBLINK)
|
||||
@ -3974,25 +4019,67 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
|
||||
else
|
||||
appendStringInfoChar(buf, '(');
|
||||
|
||||
if (sublink->lefthand != NIL)
|
||||
/*
|
||||
* Note that we print the name of only the first operator, when there
|
||||
* are multiple combining operators. This is an approximation that
|
||||
* could go wrong in various scenarios (operators in different schemas,
|
||||
* renamed operators, etc) but there is not a whole lot we can do about
|
||||
* it, since the syntax allows only one operator to be shown.
|
||||
*/
|
||||
if (sublink->testexpr)
|
||||
{
|
||||
need_paren = (list_length(sublink->lefthand) > 1);
|
||||
if (need_paren)
|
||||
if (IsA(sublink->testexpr, OpExpr))
|
||||
{
|
||||
/* single combining operator */
|
||||
OpExpr *opexpr = (OpExpr *) sublink->testexpr;
|
||||
|
||||
get_rule_expr(linitial(opexpr->args), context, true);
|
||||
opname = generate_operator_name(opexpr->opno,
|
||||
exprType(linitial(opexpr->args)),
|
||||
exprType(lsecond(opexpr->args)));
|
||||
}
|
||||
else if (IsA(sublink->testexpr, BoolExpr))
|
||||
{
|
||||
/* multiple combining operators, = or <> cases */
|
||||
char *sep;
|
||||
ListCell *l;
|
||||
|
||||
appendStringInfoChar(buf, '(');
|
||||
get_rule_expr((Node *) sublink->lefthand, context, true);
|
||||
if (need_paren)
|
||||
sep = "";
|
||||
foreach(l, ((BoolExpr *) sublink->testexpr)->args)
|
||||
{
|
||||
OpExpr *opexpr = (OpExpr *) lfirst(l);
|
||||
|
||||
Assert(IsA(opexpr, OpExpr));
|
||||
appendStringInfoString(buf, sep);
|
||||
get_rule_expr(linitial(opexpr->args), context, true);
|
||||
if (!opname)
|
||||
opname = generate_operator_name(opexpr->opno,
|
||||
exprType(linitial(opexpr->args)),
|
||||
exprType(lsecond(opexpr->args)));
|
||||
sep = ", ";
|
||||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
appendStringInfoChar(buf, ' ');
|
||||
}
|
||||
else if (IsA(sublink->testexpr, RowCompareExpr))
|
||||
{
|
||||
/* multiple combining operators, < <= > >= cases */
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
|
||||
|
||||
appendStringInfoChar(buf, '(');
|
||||
get_rule_expr((Node *) rcexpr->largs, context, true);
|
||||
opname = generate_operator_name(linitial_oid(rcexpr->opnos),
|
||||
exprType(linitial(rcexpr->largs)),
|
||||
exprType(linitial(rcexpr->rargs)));
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
else
|
||||
elog(ERROR, "unrecognized testexpr type: %d",
|
||||
(int) nodeTag(sublink->testexpr));
|
||||
}
|
||||
|
||||
need_paren = true;
|
||||
|
||||
/*
|
||||
* XXX we regurgitate the originally given operator name, with or without
|
||||
* schema qualification. This is not necessarily 100% right but it's the
|
||||
* best we can do, since the operators actually used might not all be in
|
||||
* the same schema.
|
||||
*/
|
||||
switch (sublink->subLinkType)
|
||||
{
|
||||
case EXISTS_SUBLINK:
|
||||
@ -4000,27 +4087,18 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
|
||||
break;
|
||||
|
||||
case ANY_SUBLINK:
|
||||
if (list_length(sublink->operName) == 1 &&
|
||||
strcmp(strVal(linitial(sublink->operName)), "=") == 0)
|
||||
{
|
||||
/* Represent = ANY as IN */
|
||||
appendStringInfo(buf, "IN ");
|
||||
}
|
||||
if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
|
||||
appendStringInfo(buf, " IN ");
|
||||
else
|
||||
{
|
||||
print_operator_name(buf, sublink->operName);
|
||||
appendStringInfo(buf, " ANY ");
|
||||
}
|
||||
appendStringInfo(buf, " %s ANY ", opname);
|
||||
break;
|
||||
|
||||
case ALL_SUBLINK:
|
||||
print_operator_name(buf, sublink->operName);
|
||||
appendStringInfo(buf, " ALL ");
|
||||
appendStringInfo(buf, " %s ALL ", opname);
|
||||
break;
|
||||
|
||||
case MULTIEXPR_SUBLINK:
|
||||
print_operator_name(buf, sublink->operName);
|
||||
appendStringInfoChar(buf, ' ');
|
||||
case ROWCOMPARE_SUBLINK:
|
||||
appendStringInfo(buf, " %s ", opname);
|
||||
break;
|
||||
|
||||
case EXPR_SUBLINK:
|
||||
@ -4812,30 +4890,6 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
|
||||
return buf.data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print out a possibly-qualified operator name
|
||||
*/
|
||||
static void
|
||||
print_operator_name(StringInfo buf, List *opname)
|
||||
{
|
||||
ListCell *op = list_head(opname);
|
||||
int nnames = list_length(opname);
|
||||
|
||||
if (nnames == 1)
|
||||
appendStringInfoString(buf, strVal(lfirst(op)));
|
||||
else
|
||||
{
|
||||
appendStringInfo(buf, "OPERATOR(");
|
||||
while (nnames-- > 1)
|
||||
{
|
||||
appendStringInfo(buf, "%s.",
|
||||
quote_identifier(strVal(lfirst(op))));
|
||||
op = lnext(op);
|
||||
}
|
||||
appendStringInfo(buf, "%s)", strVal(lfirst(op)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a C string, produce a TEXT datum.
|
||||
*
|
||||
|
144
src/backend/utils/cache/lsyscache.c
vendored
144
src/backend/utils/cache/lsyscache.c
vendored
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.130 2005/11/17 22:14:53 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.131 2005/12/28 01:30:01 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Eventually, the index information should go through here, too.
|
||||
@ -183,6 +183,99 @@ get_op_hash_function(Oid opno)
|
||||
return InvalidOid;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_op_btree_interpretation
|
||||
* Given an operator's OID, find out which btree opclasses it belongs to,
|
||||
* and what strategy number it has within each one. The results are
|
||||
* returned as an OID list and a parallel integer list.
|
||||
*
|
||||
* In addition to the normal btree operators, we consider a <> operator to be
|
||||
* a "member" of an opclass if its negator is the opclass' equality operator.
|
||||
* ROWCOMPARE_NE is returned as the strategy number for this case.
|
||||
*/
|
||||
void
|
||||
get_op_btree_interpretation(Oid opno, List **opclasses, List **opstrats)
|
||||
{
|
||||
Oid lefttype,
|
||||
righttype;
|
||||
CatCList *catlist;
|
||||
bool op_negated;
|
||||
int i;
|
||||
|
||||
*opclasses = NIL;
|
||||
*opstrats = NIL;
|
||||
|
||||
/*
|
||||
* Get the nominal left-hand input type of the operator; we will ignore
|
||||
* opclasses that don't have that as the expected input datatype. This
|
||||
* is a kluge to avoid being confused by binary-compatible opclasses
|
||||
* (such as text_ops and varchar_ops, which share the same operators).
|
||||
*/
|
||||
op_input_types(opno, &lefttype, &righttype);
|
||||
Assert(OidIsValid(lefttype));
|
||||
|
||||
/*
|
||||
* Find all the pg_amop entries containing the operator.
|
||||
*/
|
||||
catlist = SearchSysCacheList(AMOPOPID, 1,
|
||||
ObjectIdGetDatum(opno),
|
||||
0, 0, 0);
|
||||
/*
|
||||
* If we can't find any opclass containing the op, perhaps it is a
|
||||
* <> operator. See if it has a negator that is in an opclass.
|
||||
*/
|
||||
op_negated = false;
|
||||
if (catlist->n_members == 0)
|
||||
{
|
||||
Oid op_negator = get_negator(opno);
|
||||
|
||||
if (OidIsValid(op_negator))
|
||||
{
|
||||
op_negated = true;
|
||||
ReleaseSysCacheList(catlist);
|
||||
catlist = SearchSysCacheList(AMOPOPID, 1,
|
||||
ObjectIdGetDatum(op_negator),
|
||||
0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now search the opclasses */
|
||||
for (i = 0; i < catlist->n_members; i++)
|
||||
{
|
||||
HeapTuple op_tuple = &catlist->members[i]->tuple;
|
||||
Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
|
||||
Oid opclass_id;
|
||||
StrategyNumber op_strategy;
|
||||
|
||||
opclass_id = op_form->amopclaid;
|
||||
|
||||
/* must be btree */
|
||||
if (!opclass_is_btree(opclass_id))
|
||||
continue;
|
||||
|
||||
/* must match operator input type exactly */
|
||||
if (get_opclass_input_type(opclass_id) != lefttype)
|
||||
continue;
|
||||
|
||||
/* Get the operator's btree strategy number */
|
||||
op_strategy = (StrategyNumber) op_form->amopstrategy;
|
||||
Assert(op_strategy >= 1 && op_strategy <= 5);
|
||||
|
||||
if (op_negated)
|
||||
{
|
||||
/* Only consider negators that are = */
|
||||
if (op_strategy != BTEqualStrategyNumber)
|
||||
continue;
|
||||
op_strategy = ROWCOMPARE_NE;
|
||||
}
|
||||
|
||||
*opclasses = lappend_oid(*opclasses, opclass_id);
|
||||
*opstrats = lappend_int(*opstrats, op_strategy);
|
||||
}
|
||||
|
||||
ReleaseSysCacheList(catlist);
|
||||
}
|
||||
|
||||
|
||||
/* ---------- AMPROC CACHES ---------- */
|
||||
|
||||
@ -433,6 +526,55 @@ opclass_is_hash(Oid opclass)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* opclass_is_default
|
||||
*
|
||||
* Returns TRUE iff the specified opclass is the default for its
|
||||
* index access method and input data type.
|
||||
*/
|
||||
bool
|
||||
opclass_is_default(Oid opclass)
|
||||
{
|
||||
HeapTuple tp;
|
||||
Form_pg_opclass cla_tup;
|
||||
bool result;
|
||||
|
||||
tp = SearchSysCache(CLAOID,
|
||||
ObjectIdGetDatum(opclass),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "cache lookup failed for opclass %u", opclass);
|
||||
cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
|
||||
|
||||
result = cla_tup->opcdefault;
|
||||
ReleaseSysCache(tp);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_opclass_input_type
|
||||
*
|
||||
* Returns the OID of the datatype the opclass indexes.
|
||||
*/
|
||||
Oid
|
||||
get_opclass_input_type(Oid opclass)
|
||||
{
|
||||
HeapTuple tp;
|
||||
Form_pg_opclass cla_tup;
|
||||
Oid result;
|
||||
|
||||
tp = SearchSysCache(CLAOID,
|
||||
ObjectIdGetDatum(opclass),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "cache lookup failed for opclass %u", opclass);
|
||||
cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
|
||||
|
||||
result = cla_tup->opcintype;
|
||||
ReleaseSysCache(tp);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ---------- OPERATOR CACHE ---------- */
|
||||
|
||||
/*
|
||||
|
@ -37,7 +37,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.307 2005/11/17 22:14:54 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.308 2005/12/28 01:30:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200511171
|
||||
#define CATALOG_VERSION_NO 200512271
|
||||
|
||||
#endif
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.146 2005/12/02 20:03:42 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.147 2005/12/28 01:30:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -563,7 +563,7 @@ typedef struct SubPlanState
|
||||
ExprState xprstate;
|
||||
EState *sub_estate; /* subselect plan has its own EState */
|
||||
struct PlanState *planstate; /* subselect plan's state tree */
|
||||
List *exprs; /* states of combining expression(s) */
|
||||
ExprState *testexpr; /* state of combining expression */
|
||||
List *args; /* states of argument expression(s) */
|
||||
bool needShutdown; /* TRUE = need to shutdown subplan */
|
||||
HeapTuple curTuple; /* copy of most recent tuple from subplan */
|
||||
@ -671,6 +671,18 @@ typedef struct RowExprState
|
||||
TupleDesc tupdesc; /* descriptor for result tuples */
|
||||
} RowExprState;
|
||||
|
||||
/* ----------------
|
||||
* RowCompareExprState node
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct RowCompareExprState
|
||||
{
|
||||
ExprState xprstate;
|
||||
List *largs; /* the left-hand input arguments */
|
||||
List *rargs; /* the right-hand input arguments */
|
||||
FmgrInfo *funcs; /* array of comparison function info */
|
||||
} RowCompareExprState;
|
||||
|
||||
/* ----------------
|
||||
* CoalesceExprState node
|
||||
* ----------------
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.179 2005/12/20 02:30:36 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.180 2005/12/28 01:30:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -125,6 +125,7 @@ typedef enum NodeTag
|
||||
T_CaseTestExpr,
|
||||
T_ArrayExpr,
|
||||
T_RowExpr,
|
||||
T_RowCompareExpr,
|
||||
T_CoalesceExpr,
|
||||
T_MinMaxExpr,
|
||||
T_NullIfExpr,
|
||||
@ -159,6 +160,7 @@ typedef enum NodeTag
|
||||
T_CaseWhenState,
|
||||
T_ArrayExprState,
|
||||
T_RowExprState,
|
||||
T_RowCompareExprState,
|
||||
T_CoalesceExprState,
|
||||
T_MinMaxExprState,
|
||||
T_CoerceToDomainState,
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.28 2004/12/31 22:03:34 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.29 2005/12/28 01:30:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -32,6 +32,11 @@
|
||||
* PARAM_EXEC: The parameter is an internal executor parameter.
|
||||
* It has a number contained in the `paramid' field.
|
||||
*
|
||||
* PARAM_SUBLINK: The parameter represents an output column of a SubLink
|
||||
* node's sub-select. The column number is contained in the
|
||||
* `paramid' field. (This type of Param is converted to
|
||||
* PARAM_EXEC during planning.)
|
||||
*
|
||||
* PARAM_INVALID should never appear in a Param node; it's used to mark
|
||||
* the end of a ParamListInfo array.
|
||||
*
|
||||
@ -44,6 +49,7 @@
|
||||
#define PARAM_NAMED 11
|
||||
#define PARAM_NUM 12
|
||||
#define PARAM_EXEC 15
|
||||
#define PARAM_SUBLINK 16
|
||||
#define PARAM_INVALID 100
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.110 2005/12/20 02:30:36 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.111 2005/12/28 01:30:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -158,6 +158,11 @@ typedef struct Const
|
||||
*
|
||||
* PARAM_EXEC: The parameter is an internal executor parameter.
|
||||
* It has a number contained in the `paramid' field.
|
||||
*
|
||||
* PARAM_SUBLINK: The parameter represents an output column of a SubLink
|
||||
* node's sub-select. The column number is contained in the
|
||||
* `paramid' field. (This type of Param is converted to
|
||||
* PARAM_EXEC during planning.)
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct Param
|
||||
@ -329,7 +334,7 @@ typedef struct BoolExpr
|
||||
List *args; /* arguments to this expression */
|
||||
} BoolExpr;
|
||||
|
||||
/* ----------------
|
||||
/*
|
||||
* SubLink
|
||||
*
|
||||
* A SubLink represents a subselect appearing in an expression, and in some
|
||||
@ -338,46 +343,42 @@ typedef struct BoolExpr
|
||||
* EXISTS_SUBLINK EXISTS(SELECT ...)
|
||||
* ALL_SUBLINK (lefthand) op ALL (SELECT ...)
|
||||
* ANY_SUBLINK (lefthand) op ANY (SELECT ...)
|
||||
* MULTIEXPR_SUBLINK (lefthand) op (SELECT ...)
|
||||
* ROWCOMPARE_SUBLINK (lefthand) op (SELECT ...)
|
||||
* EXPR_SUBLINK (SELECT with single targetlist item ...)
|
||||
* ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...)
|
||||
* For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
|
||||
* same length as the subselect's targetlist. MULTIEXPR will *always* have
|
||||
* For ALL, ANY, and ROWCOMPARE, the lefthand is a list of expressions of the
|
||||
* same length as the subselect's targetlist. ROWCOMPARE will *always* have
|
||||
* a list with more than one entry; if the subselect has just one target
|
||||
* then the parser will create an EXPR_SUBLINK instead (and any operator
|
||||
* above the subselect will be represented separately). Note that both
|
||||
* MULTIEXPR and EXPR require the subselect to deliver only one row.
|
||||
* ROWCOMPARE and EXPR require the subselect to deliver only one row.
|
||||
* ALL, ANY, and ROWCOMPARE require the combining operators to deliver boolean
|
||||
* results. ALL and ANY combine the per-row results using AND and OR
|
||||
* semantics respectively.
|
||||
* ARRAY requires just one target column, and creates an array of the target
|
||||
* column's type using one or more rows resulting from the subselect.
|
||||
* ALL, ANY, and MULTIEXPR require the combining operators to deliver boolean
|
||||
* results. These are reduced to one result per row using OR or AND semantics
|
||||
* depending on the "useOr" flag. ALL and ANY combine the per-row results
|
||||
* using AND and OR semantics respectively.
|
||||
*
|
||||
* SubLink is classed as an Expr node, but it is not actually executable;
|
||||
* it must be replaced in the expression tree by a SubPlan node during
|
||||
* planning.
|
||||
*
|
||||
* NOTE: in the raw output of gram.y, lefthand contains a list of raw
|
||||
* expressions; useOr and operOids are not filled in yet. Also, subselect
|
||||
* is a raw parsetree. During parse analysis, the parser transforms the
|
||||
* lefthand expression list using normal expression transformation rules.
|
||||
* It fills operOids with the OIDs representing the specific operator(s)
|
||||
* to apply to each pair of lefthand and targetlist expressions.
|
||||
* And subselect is transformed to a Query. This is the representation
|
||||
* seen in saved rules and in the rewriter.
|
||||
* NOTE: in the raw output of gram.y, testexpr contains just the raw form
|
||||
* of the lefthand expression (if any), and operName is the String name of
|
||||
* the combining operator. Also, subselect is a raw parsetree. During parse
|
||||
* analysis, the parser transforms testexpr into a complete boolean expression
|
||||
* that compares the lefthand value(s) to PARAM_SUBLINK nodes representing the
|
||||
* output columns of the subselect. And subselect is transformed to a Query.
|
||||
* This is the representation seen in saved rules and in the rewriter.
|
||||
*
|
||||
* In EXISTS, EXPR, and ARRAY SubLinks, lefthand, operName, and operOids are
|
||||
* unused and are always NIL. useOr is not significant either for these
|
||||
* sublink types.
|
||||
* ----------------
|
||||
* In EXISTS, EXPR, and ARRAY SubLinks, testexpr and operName are unused and
|
||||
* are always null.
|
||||
*/
|
||||
typedef enum SubLinkType
|
||||
{
|
||||
EXISTS_SUBLINK,
|
||||
ALL_SUBLINK,
|
||||
ANY_SUBLINK,
|
||||
MULTIEXPR_SUBLINK,
|
||||
ROWCOMPARE_SUBLINK,
|
||||
EXPR_SUBLINK,
|
||||
ARRAY_SUBLINK
|
||||
} SubLinkType;
|
||||
@ -386,12 +387,9 @@ typedef enum SubLinkType
|
||||
typedef struct SubLink
|
||||
{
|
||||
Expr xpr;
|
||||
SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
|
||||
bool useOr; /* TRUE to combine column results with "OR"
|
||||
* not "AND" */
|
||||
List *lefthand; /* list of outer-query expressions on the left */
|
||||
SubLinkType subLinkType; /* see above */
|
||||
Node *testexpr; /* outer-query test for ALL/ANY/ROWCOMPARE */
|
||||
List *operName; /* originally specified operator name */
|
||||
List *operOids; /* OIDs of actual combining operators */
|
||||
Node *subselect; /* subselect as Query* or parsetree */
|
||||
} SubLink;
|
||||
|
||||
@ -402,14 +400,18 @@ typedef struct SubLink
|
||||
* nodes after it has finished planning the subquery. SubPlan contains
|
||||
* a sub-plantree and rtable instead of a sub-Query.
|
||||
*
|
||||
* In an ordinary subplan, "exprs" points to a list of executable expressions
|
||||
* (OpExpr trees) for the combining operators; their left-hand arguments are
|
||||
* the original lefthand expressions, and their right-hand arguments are
|
||||
* PARAM_EXEC Param nodes representing the outputs of the sub-select.
|
||||
* (NOTE: runtime coercion functions may be inserted as well.) But if the
|
||||
* sub-select becomes an initplan rather than a subplan, these executable
|
||||
* expressions are part of the outer plan's expression tree (and the SubPlan
|
||||
* node itself is not). In this case "exprs" is NIL to avoid duplication.
|
||||
* In an ordinary subplan, testexpr points to an executable expression
|
||||
* (OpExpr, an AND/OR tree of OpExprs, or RowCompareExpr) for the combining
|
||||
* operator(s); the left-hand arguments are the original lefthand expressions,
|
||||
* and the right-hand arguments are PARAM_EXEC Param nodes representing the
|
||||
* outputs of the sub-select. (NOTE: runtime coercion functions may be
|
||||
* inserted as well.) This is just the same expression tree as testexpr in
|
||||
* the original SubLink node, but the PARAM_SUBLINK nodes are replaced by
|
||||
* suitably numbered PARAM_EXEC nodes.
|
||||
*
|
||||
* If the sub-select becomes an initplan rather than a subplan, the executable
|
||||
* expression is part of the outer plan's expression tree (and the SubPlan
|
||||
* node itself is not). In this case testexpr is NULL to avoid duplication.
|
||||
*
|
||||
* The planner also derives lists of the values that need to be passed into
|
||||
* and out of the subplan. Input values are represented as a list "args" of
|
||||
@ -426,13 +428,10 @@ typedef struct SubPlan
|
||||
{
|
||||
Expr xpr;
|
||||
/* Fields copied from original SubLink: */
|
||||
SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
|
||||
bool useOr; /* TRUE to combine column results with "OR"
|
||||
* not "AND" */
|
||||
/* The combining operators, transformed to executable expressions: */
|
||||
List *exprs; /* list of OpExpr expression trees */
|
||||
SubLinkType subLinkType; /* see above */
|
||||
/* The combining operators, transformed to an executable expression: */
|
||||
Node *testexpr; /* OpExpr or RowCompareExpr expression tree */
|
||||
List *paramIds; /* IDs of Params embedded in the above */
|
||||
/* Note: paramIds has a one-to-one correspondence to the exprs list */
|
||||
/* The subselect, transformed to a Plan: */
|
||||
struct Plan *plan; /* subselect plan itself */
|
||||
int plan_id; /* dummy thing because of we haven't equal
|
||||
@ -642,6 +641,41 @@ typedef struct RowExpr
|
||||
CoercionForm row_format; /* how to display this node */
|
||||
} RowExpr;
|
||||
|
||||
/*
|
||||
* RowCompareExpr - row-wise comparison, such as (a, b) <= (1, 2)
|
||||
*
|
||||
* We support row comparison for any operator that can be determined to
|
||||
* act like =, <>, <, <=, >, or >= (we determine this by looking for the
|
||||
* operator in btree opclasses). Note that the same operator name might
|
||||
* map to a different operator for each pair of row elements, since the
|
||||
* element datatypes can vary.
|
||||
*
|
||||
* A RowCompareExpr node is only generated for the < <= > >= cases;
|
||||
* the = and <> cases are translated to simple AND or OR combinations
|
||||
* of the pairwise comparisons. However, we include = and <> in the
|
||||
* RowCompareType enum for the convenience of parser logic.
|
||||
*/
|
||||
typedef enum RowCompareType
|
||||
{
|
||||
/* Values of this enum are chosen to match btree strategy numbers */
|
||||
ROWCOMPARE_LT = 1, /* BTLessStrategyNumber */
|
||||
ROWCOMPARE_LE = 2, /* BTLessEqualStrategyNumber */
|
||||
ROWCOMPARE_EQ = 3, /* BTEqualStrategyNumber */
|
||||
ROWCOMPARE_GE = 4, /* BTGreaterEqualStrategyNumber */
|
||||
ROWCOMPARE_GT = 5, /* BTGreaterStrategyNumber */
|
||||
ROWCOMPARE_NE = 6 /* no such btree strategy */
|
||||
} RowCompareType;
|
||||
|
||||
typedef struct RowCompareExpr
|
||||
{
|
||||
Expr xpr;
|
||||
RowCompareType rctype; /* LT LE GE or GT, never EQ or NE */
|
||||
List *opnos; /* OID list of pairwise comparison ops */
|
||||
List *opclasses; /* OID list of containing operator classes */
|
||||
List *largs; /* the left-hand input arguments */
|
||||
List *rargs; /* the right-hand input arguments */
|
||||
} RowCompareExpr;
|
||||
|
||||
/*
|
||||
* CoalesceExpr - a COALESCE expression
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/parser/parse_oper.h,v 1.36 2004/12/31 22:03:38 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/include/parser/parse_oper.h,v 1.37 2005/12/28 01:30:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -59,8 +59,5 @@ extern Expr *make_op(ParseState *pstate, List *opname,
|
||||
extern Expr *make_scalar_array_op(ParseState *pstate, List *opname,
|
||||
bool useOr,
|
||||
Node *ltree, Node *rtree);
|
||||
extern Expr *make_op_expr(ParseState *pstate, Operator op,
|
||||
Node *ltree, Node *rtree,
|
||||
Oid ltypeId, Oid rtypeId);
|
||||
|
||||
#endif /* PARSE_OPER_H */
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.101 2005/10/15 02:49:46 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.102 2005/12/28 01:30:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -31,6 +31,8 @@ extern void get_op_opclass_properties(Oid opno, Oid opclass,
|
||||
bool *recheck);
|
||||
extern Oid get_opclass_member(Oid opclass, Oid subtype, int16 strategy);
|
||||
extern Oid get_op_hash_function(Oid opno);
|
||||
extern void get_op_btree_interpretation(Oid opno,
|
||||
List **opclasses, List **opstrats);
|
||||
extern Oid get_opclass_proc(Oid opclass, Oid subtype, int16 procnum);
|
||||
extern char *get_attname(Oid relid, AttrNumber attnum);
|
||||
extern char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
|
||||
@ -41,6 +43,8 @@ extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
|
||||
Oid *typid, int32 *typmod);
|
||||
extern bool opclass_is_btree(Oid opclass);
|
||||
extern bool opclass_is_hash(Oid opclass);
|
||||
extern bool opclass_is_default(Oid opclass);
|
||||
extern Oid get_opclass_input_type(Oid opclass);
|
||||
extern RegProcedure get_opcode(Oid opno);
|
||||
extern char *get_opname(Oid opno);
|
||||
extern void op_input_types(Oid opno, Oid *lefttype, Oid *righttype);
|
||||
|
@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.157 2005/11/22 18:17:33 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.158 2005/12/28 01:30:01 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@ -4283,6 +4283,18 @@ exec_simple_check_node(Node *node)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case T_RowCompareExpr:
|
||||
{
|
||||
RowCompareExpr *expr = (RowCompareExpr *) node;
|
||||
|
||||
if (!exec_simple_check_node((Node *) expr->largs))
|
||||
return FALSE;
|
||||
if (!exec_simple_check_node((Node *) expr->rargs))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case T_CoalesceExpr:
|
||||
{
|
||||
CoalesceExpr *expr = (CoalesceExpr *) node;
|
||||
|
@ -117,3 +117,125 @@ select (fn).first, substr((fn).last, 1, 20), length((fn).last) from people;
|
||||
Jim | abcdefghijklabcdefgh | 1200000
|
||||
(2 rows)
|
||||
|
||||
-- Test row comparison semantics. Prior to PG 8.2 we did this in a totally
|
||||
-- non-spec-compliant way.
|
||||
select ROW(1,2) < ROW(1,3) as true;
|
||||
true
|
||||
------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
select ROW(1,2) < ROW(1,1) as false;
|
||||
false
|
||||
-------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
select ROW(1,2) < ROW(1,NULL) as null;
|
||||
null
|
||||
------
|
||||
|
||||
(1 row)
|
||||
|
||||
select ROW(1,2,3) < ROW(1,3,NULL) as true; -- the NULL is not examined
|
||||
true
|
||||
------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
select ROW(11,'ABC') < ROW(11,'DEF') as true;
|
||||
true
|
||||
------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
select ROW(11,'ABC') > ROW(11,'DEF') as false;
|
||||
false
|
||||
-------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
select ROW(12,'ABC') > ROW(11,'DEF') as true;
|
||||
true
|
||||
------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- = and <> have different NULL-behavior than < etc
|
||||
select ROW(1,2,3) < ROW(1,NULL,4) as null;
|
||||
null
|
||||
------
|
||||
|
||||
(1 row)
|
||||
|
||||
select ROW(1,2,3) = ROW(1,NULL,4) as false;
|
||||
false
|
||||
-------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
select ROW(1,2,3) <> ROW(1,NULL,4) as true;
|
||||
true
|
||||
------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
-- We allow operators beyond the six standard ones, if they have btree
|
||||
-- operator classes.
|
||||
select ROW('ABC','DEF') ~<=~ ROW('DEF','ABC') as true;
|
||||
true
|
||||
------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
select ROW('ABC','DEF') ~>=~ ROW('DEF','ABC') as false;
|
||||
false
|
||||
-------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail;
|
||||
ERROR: could not determine interpretation of row comparison operator ~~
|
||||
HINT: Row comparison operators must be associated with btree operator classes.
|
||||
-- Check row comparison with a subselect
|
||||
select unique1, unique2 from tenk1
|
||||
where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3);
|
||||
unique1 | unique2
|
||||
---------+---------
|
||||
1 | 2838
|
||||
0 | 9998
|
||||
(2 rows)
|
||||
|
||||
-- Also check row comparison with an indexable condition
|
||||
select thousand, tenthous from tenk1
|
||||
where (thousand, tenthous) >= (997, 5000)
|
||||
order by thousand, tenthous;
|
||||
thousand | tenthous
|
||||
----------+----------
|
||||
997 | 5997
|
||||
997 | 6997
|
||||
997 | 7997
|
||||
997 | 8997
|
||||
997 | 9997
|
||||
998 | 998
|
||||
998 | 1998
|
||||
998 | 2998
|
||||
998 | 3998
|
||||
998 | 4998
|
||||
998 | 5998
|
||||
998 | 6998
|
||||
998 | 7998
|
||||
998 | 8998
|
||||
998 | 9998
|
||||
999 | 999
|
||||
999 | 1999
|
||||
999 | 2999
|
||||
999 | 3999
|
||||
999 | 4999
|
||||
999 | 5999
|
||||
999 | 6999
|
||||
999 | 7999
|
||||
999 | 8999
|
||||
999 | 9999
|
||||
(25 rows)
|
||||
|
||||
|
@ -72,3 +72,35 @@ insert into pp values (repeat('abcdefghijkl', 100000));
|
||||
insert into people select ('Jim', f1, null)::fullname, current_date from pp;
|
||||
|
||||
select (fn).first, substr((fn).last, 1, 20), length((fn).last) from people;
|
||||
|
||||
-- Test row comparison semantics. Prior to PG 8.2 we did this in a totally
|
||||
-- non-spec-compliant way.
|
||||
|
||||
select ROW(1,2) < ROW(1,3) as true;
|
||||
select ROW(1,2) < ROW(1,1) as false;
|
||||
select ROW(1,2) < ROW(1,NULL) as null;
|
||||
select ROW(1,2,3) < ROW(1,3,NULL) as true; -- the NULL is not examined
|
||||
select ROW(11,'ABC') < ROW(11,'DEF') as true;
|
||||
select ROW(11,'ABC') > ROW(11,'DEF') as false;
|
||||
select ROW(12,'ABC') > ROW(11,'DEF') as true;
|
||||
|
||||
-- = and <> have different NULL-behavior than < etc
|
||||
select ROW(1,2,3) < ROW(1,NULL,4) as null;
|
||||
select ROW(1,2,3) = ROW(1,NULL,4) as false;
|
||||
select ROW(1,2,3) <> ROW(1,NULL,4) as true;
|
||||
|
||||
-- We allow operators beyond the six standard ones, if they have btree
|
||||
-- operator classes.
|
||||
select ROW('ABC','DEF') ~<=~ ROW('DEF','ABC') as true;
|
||||
select ROW('ABC','DEF') ~>=~ ROW('DEF','ABC') as false;
|
||||
select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail;
|
||||
|
||||
-- Check row comparison with a subselect
|
||||
select unique1, unique2 from tenk1
|
||||
where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3);
|
||||
|
||||
-- Also check row comparison with an indexable condition
|
||||
select thousand, tenthous from tenk1
|
||||
where (thousand, tenthous) >= (997, 5000)
|
||||
order by thousand, tenthous;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user