Support expressions of the form 'scalar op ANY (array)' and

'scalar op ALL (array)', where the operator is applied between the
lefthand scalar and each element of the array.  The operator must
yield boolean; the result of the construct is the OR or AND of the
per-element results, respectively.

Original coding by Joe Conway, after an idea of Peter's.  Rewritten
by Tom to keep the implementation strictly separate from subqueries.
This commit is contained in:
Tom Lane 2003-06-29 00:33:44 +00:00
parent df7618020b
commit bee217924d
28 changed files with 875 additions and 157 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.157 2003/06/27 00:33:25 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.158 2003/06/29 00:33:42 tgl Exp $
PostgreSQL documentation
-->
@ -7457,52 +7457,14 @@ SELECT col1 FROM tab1
</sect2>
<sect2>
<title><literal>IN</literal> (scalar form)</title>
<synopsis>
<replaceable>expression</replaceable> IN (<replaceable>value</replaceable><optional>, ...</optional>)
</synopsis>
<para>
The right-hand side of this form of <token>IN</token> is a parenthesized list
of scalar expressions. The result is <quote>true</> if the left-hand expression's
result is equal to any of the right-hand expressions. This is a shorthand
notation for
<synopsis>
<replaceable>expression</replaceable> = <replaceable>value1</replaceable>
OR
<replaceable>expression</replaceable> = <replaceable>value2</replaceable>
OR
...
</synopsis>
</para>
<para>
Note that if the left-hand expression yields null, or if there are
no equal right-hand values and at least one right-hand expression yields
null, the result of the <token>IN</token> construct will be null, not false.
This is in accordance with SQL's normal rules for Boolean combinations
of null values.
</para>
<note>
<para>
This form of <token>IN</token> is not truly a subquery expression, but it
seems best to document it in the same place as subquery <token>IN</token>.
</para>
</note>
</sect2>
<sect2>
<title><literal>IN</literal> (subquery form)</title>
<title><literal>IN</literal></title>
<synopsis>
<replaceable>expression</replaceable> IN (<replaceable>subquery</replaceable>)
</synopsis>
<para>
The right-hand side of this form of <token>IN</token> is a parenthesized
The right-hand side is a parenthesized
subquery, which must return exactly one column. The left-hand expression
is evaluated and compared to each row of the subquery result.
The result of <token>IN</token> is <quote>true</> if any equal subquery row is found.
@ -7538,7 +7500,7 @@ OR
</para>
<para>
As usual, null values in the expressions or subquery rows are combined per
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;
@ -7549,55 +7511,14 @@ OR
</sect2>
<sect2>
<title><literal>NOT IN</literal> (scalar form)</title>
<synopsis>
<replaceable>expression</replaceable> NOT IN (<replaceable>value</replaceable><optional>, ...</optional>)
</synopsis>
<para>
The right-hand side of this form of <token>NOT IN</token> is a parenthesized list
of scalar expressions. The result is <quote>true</quote> if the left-hand expression's
result is unequal to all of the right-hand expressions. This is a shorthand
notation for
<synopsis>
<replaceable>expression</replaceable> &lt;&gt; <replaceable>value1</replaceable>
AND
<replaceable>expression</replaceable> &lt;&gt; <replaceable>value2</replaceable>
AND
...
</synopsis>
</para>
<para>
Note that if the left-hand expression yields null, or if there are
no equal right-hand values and at least one right-hand expression yields
null, the result of the <token>NOT IN</token> construct will be null, not true
as one might naively expect.
This is in accordance with SQL's normal rules for Boolean combinations
of null values.
</para>
<tip>
<para>
<literal>x NOT IN y</literal> is equivalent to <literal>NOT (x IN y)</literal> in all
cases. However, null values are much more likely to trip up the novice when
working with <token>NOT IN</token> than when working with <token>IN</token>.
It's best to express your condition positively if possible.
</para>
</tip>
</sect2>
<sect2>
<title><literal>NOT IN </literal>(subquery form)</title>
<title><literal>NOT IN </literal></title>
<synopsis>
<replaceable>expression</replaceable> NOT IN (<replaceable>subquery</replaceable>)
</synopsis>
<para>
The right-hand side of this form of <token>NOT IN</token> is a parenthesized
The right-hand side is a parenthesized
subquery, which must return exactly one column. The left-hand expression
is evaluated and compared to each row of the subquery result.
The result of <token>NOT IN</token> is <quote>true</> if only unequal subquery rows
@ -7633,7 +7554,7 @@ AND
</para>
<para>
As usual, null values in the expressions or subquery rows are combined per
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;
@ -7652,7 +7573,7 @@ AND
</synopsis>
<para>
The right-hand side of this form of <token>ANY</token> is a parenthesized
The right-hand side is a parenthesized
subquery, which must return exactly one column. The left-hand expression
is evaluated and compared to each row of the subquery result using the
given <replaceable>operator</replaceable>, which must yield a Boolean
@ -7700,7 +7621,7 @@ AND
</para>
<para>
As usual, null values in the expressions or subquery rows are combined per
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;
@ -7718,7 +7639,7 @@ AND
</synopsis>
<para>
The right-hand side of this form of <token>ALL</token> is a parenthesized
The right-hand side is a parenthesized
subquery, which must return exactly one column. The left-hand expression
is evaluated and compared to each row of the subquery result using the
given <replaceable>operator</replaceable>, which must yield a Boolean
@ -7765,7 +7686,7 @@ AND
</para>
<para>
As usual, null values in the expressions or subquery rows are combined per
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;
@ -7780,24 +7701,201 @@ AND
<synopsis>
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</replaceable> (<replaceable>subquery</replaceable>)
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</replaceable> (<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>)
</synopsis>
<para>
The left-hand side is a list of scalar expressions. The right-hand side
can be either a list of scalar expressions of the same length, or a
parenthesized subquery, which must return exactly as many columns as there
The left-hand side is a list of scalar expressions. The right-hand side is
a parenthesized subquery, which must return exactly as many columns as there
are expressions on the left-hand side. Furthermore, 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, or to the right-hand expression list.
single subquery result row.
Presently, only <literal>=</literal> and <literal>&lt;&gt;</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 expressions or subquery rows are combined per
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).
</para>
</sect2>
</sect1>
<sect1 id="functions-comparisons">
<title>Row and Array Comparisons</title>
<indexterm>
<primary>in</primary>
</indexterm>
<indexterm>
<primary>not in</primary>
</indexterm>
<indexterm>
<primary>any</primary>
</indexterm>
<indexterm>
<primary>all</primary>
</indexterm>
<indexterm>
<primary>some</primary>
</indexterm>
<para>
This section describes several specialized constructs for making
multiple comparisons between groups of values. These forms are
syntactically related to the subquery forms of the previous section,
but do not involve subqueries.
The forms involving array subexpressions are
<productname>PostgreSQL</productname> extensions; the rest are
<acronym>SQL</acronym>-compliant.
All of the expression forms documented in this section return
Boolean (true/false) results.
</para>
<sect2>
<title><literal>IN</literal></title>
<synopsis>
<replaceable>expression</replaceable> IN (<replaceable>value</replaceable><optional>, ...</optional>)
</synopsis>
<para>
The right-hand side is a parenthesized list
of scalar expressions. The result is <quote>true</> if the left-hand expression's
result is equal to any of the right-hand expressions. This is a shorthand
notation for
<synopsis>
<replaceable>expression</replaceable> = <replaceable>value1</replaceable>
OR
<replaceable>expression</replaceable> = <replaceable>value2</replaceable>
OR
...
</synopsis>
</para>
<para>
Note that if the left-hand expression yields null, or if there are
no equal right-hand values and at least one right-hand expression yields
null, the result of the <token>IN</token> construct will be null, not false.
This is in accordance with SQL's normal rules for Boolean combinations
of null values.
</para>
</sect2>
<sect2>
<title><literal>NOT IN</literal></title>
<synopsis>
<replaceable>expression</replaceable> NOT IN (<replaceable>value</replaceable><optional>, ...</optional>)
</synopsis>
<para>
The right-hand side is a parenthesized list
of scalar expressions. The result is <quote>true</quote> if the left-hand expression's
result is unequal to all of the right-hand expressions. This is a shorthand
notation for
<synopsis>
<replaceable>expression</replaceable> &lt;&gt; <replaceable>value1</replaceable>
AND
<replaceable>expression</replaceable> &lt;&gt; <replaceable>value2</replaceable>
AND
...
</synopsis>
</para>
<para>
Note that if the left-hand expression yields null, or if there are
no equal right-hand values and at least one right-hand expression yields
null, the result of the <token>NOT IN</token> construct will be null, not true
as one might naively expect.
This is in accordance with SQL's normal rules for Boolean combinations
of null values.
</para>
<tip>
<para>
<literal>x NOT IN y</literal> is equivalent to <literal>NOT (x IN y)</literal> in all
cases. However, null values are much more likely to trip up the novice when
working with <token>NOT IN</token> than when working with <token>IN</token>.
It's best to express your condition positively if possible.
</para>
</tip>
</sect2>
<sect2>
<title><literal>ANY</literal>/<literal>SOME</literal> (array)</title>
<synopsis>
<replaceable>expression</replaceable> <replaceable>operator</replaceable> ANY (<replaceable>array expression</replaceable>)
<replaceable>expression</replaceable> <replaceable>operator</replaceable> SOME (<replaceable>array expression</replaceable>)
</synopsis>
<para>
The right-hand side is a parenthesized expression, which must yield an
array value.
The left-hand expression
is evaluated and compared to each element of the array using the
given <replaceable>operator</replaceable>, which must yield a Boolean
result.
The result of <token>ANY</token> is <quote>true</> if any true result is obtained.
The result is <quote>false</> if no true result is found (including the special
case where the array has zero elements).
</para>
<para>
<token>SOME</token> is a synonym for <token>ANY</token>.
</para>
</sect2>
<sect2>
<title><literal>ALL</literal> (array)</title>
<synopsis>
<replaceable>expression</replaceable> <replaceable>operator</replaceable> ALL (<replaceable>array expression</replaceable>)
</synopsis>
<para>
The right-hand side is a parenthesized expression, which must yield an
array value.
The left-hand expression
is evaluated and compared to each element of the array using the
given <replaceable>operator</replaceable>, which must yield a Boolean
result.
The result of <token>ALL</token> is <quote>true</> if all comparisons yield true
(including the special case where the array has zero elements).
The result is <quote>false</> if any false result is found.
</para>
</sect2>
<sect2>
<title>Row-wise Comparison</title>
<synopsis>
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</replaceable> (<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>)
</synopsis>
<para>
Each side is a list of scalar expressions; the two lists must be
of the same length. Each side is evaluated and they are compared
row-wise.
Presently, only <literal>=</literal> and <literal>&lt;&gt;</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;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.25 2003/05/28 16:03:55 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.26 2003/06/29 00:33:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1025,6 +1025,14 @@ find_expr_references_walker(Node *node,
&context->addrs);
/* fall through to examine arguments */
}
if (IsA(node, ScalarArrayOpExpr))
{
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
&context->addrs);
/* fall through to examine arguments */
}
if (IsA(node, NullIfExpr))
{
NullIfExpr *nullifexpr = (NullIfExpr *) node;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.133 2003/06/27 00:33:25 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.134 2003/06/29 00:33:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -65,6 +65,8 @@ static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
bool *isNull);
static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
ExprContext *econtext, bool *isNull);
static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList, ExprContext *econtext);
static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
@ -1121,7 +1123,6 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
/* ----------------------------------------------------------------
* ExecEvalFunc
* ExecEvalOper
* ExecEvalDistinct
*
* Evaluate the functional result of a list of arguments by calling the
* function manager.
@ -1241,6 +1242,149 @@ ExecEvalDistinct(FuncExprState *fcache,
return result;
}
/*
* ExecEvalScalarArrayOp
*
* Evaluate "scalar op ANY/ALL (array)". The operator always yields boolean,
* and we combine the results across all array elements using OR and AND
* (for ANY and ALL respectively). Of course we short-circuit as soon as
* the result is known.
*/
static Datum
ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
ExprContext *econtext, bool *isNull)
{
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr;
bool useOr = opexpr->useOr;
ArrayType *arr;
int nitems;
Datum result;
bool resultnull;
FunctionCallInfoData fcinfo;
ExprDoneCond argDone;
int i;
int16 typlen;
bool typbyval;
char typalign;
char *s;
/*
* Initialize function cache if first time through
*/
if (sstate->fxprstate.func.fn_oid == InvalidOid)
{
init_fcache(opexpr->opfuncid, &sstate->fxprstate,
econtext->ecxt_per_query_memory);
Assert(!sstate->fxprstate.func.fn_retset);
}
/* Need to prep callinfo structure */
MemSet(&fcinfo, 0, sizeof(fcinfo));
fcinfo.flinfo = &(sstate->fxprstate.func);
argDone = ExecEvalFuncArgs(&fcinfo, sstate->fxprstate.args, econtext);
if (argDone != ExprSingleResult)
elog(ERROR, "op ANY/ALL (array) does not support set arguments");
Assert(fcinfo.nargs == 2);
/*
* If the array is NULL then we return NULL --- it's not very meaningful
* to do anything else, even if the operator isn't strict.
*/
if (fcinfo.argnull[1])
{
*isNull = true;
return (Datum) 0;
}
/* Else okay to fetch and detoast the array */
arr = DatumGetArrayTypeP(fcinfo.arg[1]);
/*
* If the array is empty, we return either FALSE or TRUE per the useOr
* flag. This is correct even if the scalar is NULL; since we would
* evaluate the operator zero times, it matters not whether it would
* want to return NULL.
*/
nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
if (nitems <= 0)
return BoolGetDatum(!useOr);
/*
* If the scalar is NULL, and the function is strict, return NULL.
* This is just to avoid having to test for strictness inside the
* loop. (XXX but if arrays could have null elements, we'd need a
* test anyway.)
*/
if (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict)
{
*isNull = true;
return (Datum) 0;
}
/*
* We arrange to look up info about the element type only
* once per series of calls, assuming the element type doesn't change
* underneath us.
*/
if (sstate->element_type != ARR_ELEMTYPE(arr))
{
get_typlenbyvalalign(ARR_ELEMTYPE(arr),
&sstate->typlen,
&sstate->typbyval,
&sstate->typalign);
sstate->element_type = ARR_ELEMTYPE(arr);
}
typlen = sstate->typlen;
typbyval = sstate->typbyval;
typalign = sstate->typalign;
result = BoolGetDatum(!useOr);
resultnull = false;
/* Loop over the array elements */
s = (char *) ARR_DATA_PTR(arr);
for (i = 0; i < nitems; i++)
{
Datum elt;
Datum thisresult;
/* Get array element */
elt = fetch_att(s, typbyval, typlen);
s = att_addlength(s, typlen, PointerGetDatum(s));
s = (char *) att_align(s, typalign);
/* Call comparison function */
fcinfo.arg[1] = elt;
fcinfo.argnull[1] = false;
fcinfo.isnull = false;
thisresult = FunctionCallInvoke(&fcinfo);
/* Combine results per OR or AND semantics */
if (fcinfo.isnull)
resultnull = true;
else if (useOr)
{
if (DatumGetBool(thisresult))
{
result = BoolGetDatum(true);
resultnull = false;
break; /* needn't look at any more elements */
}
}
else
{
if (!DatumGetBool(thisresult))
{
result = BoolGetDatum(false);
resultnull = false;
break; /* needn't look at any more elements */
}
}
}
*isNull = resultnull;
return result;
}
/* ----------------------------------------------------------------
* ExecEvalNot
* ExecEvalOr
@ -2018,6 +2162,10 @@ ExecEvalExpr(ExprState *expression,
retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
isNull);
break;
case T_ScalarArrayOpExpr:
retDatum = ExecEvalScalarArrayOp((ScalarArrayOpExprState *) expression,
econtext, isNull);
break;
case T_BoolExpr:
{
BoolExprState *state = (BoolExprState *) expression;
@ -2263,6 +2411,18 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) fstate;
}
break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
ScalarArrayOpExprState *sstate = makeNode(ScalarArrayOpExprState);
sstate->fxprstate.args = (List *)
ExecInitExpr((Expr *) opexpr->args, parent);
sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */
sstate->element_type = InvalidOid; /* ditto */
state = (ExprState *) sstate;
}
break;
case T_BoolExpr:
{
BoolExpr *boolexpr = (BoolExpr *) node;

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.257 2003/06/27 14:45:28 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.258 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -802,6 +802,22 @@ _copyDistinctExpr(DistinctExpr *from)
return newnode;
}
/*
* _copyScalarArrayOpExpr
*/
static ScalarArrayOpExpr *
_copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
{
ScalarArrayOpExpr *newnode = makeNode(ScalarArrayOpExpr);
COPY_SCALAR_FIELD(opno);
COPY_SCALAR_FIELD(opfuncid);
COPY_SCALAR_FIELD(useOr);
COPY_NODE_FIELD(args);
return newnode;
}
/*
* _copyBoolExpr
*/
@ -2546,6 +2562,9 @@ copyObject(void *from)
case T_DistinctExpr:
retval = _copyDistinctExpr(from);
break;
case T_ScalarArrayOpExpr:
retval = _copyScalarArrayOpExpr(from);
break;
case T_BoolExpr:
retval = _copyBoolExpr(from);
break;

View File

@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.200 2003/06/27 14:45:28 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.201 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -287,6 +287,27 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
return true;
}
static bool
_equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
{
COMPARE_SCALAR_FIELD(opno);
/*
* Special-case opfuncid: it is allowable for it to differ if one
* node contains zero and the other doesn't. This just means that the
* one node isn't as far along in the parse/plan pipeline and hasn't
* had the opfuncid cache filled yet.
*/
if (a->opfuncid != b->opfuncid &&
a->opfuncid != 0 &&
b->opfuncid != 0)
return false;
COMPARE_SCALAR_FIELD(useOr);
COMPARE_NODE_FIELD(args);
return true;
}
static bool
_equalBoolExpr(BoolExpr *a, BoolExpr *b)
{
@ -1661,6 +1682,9 @@ equal(void *a, void *b)
case T_DistinctExpr:
retval = _equalDistinctExpr(a, b);
break;
case T_ScalarArrayOpExpr:
retval = _equalScalarArrayOpExpr(a, b);
break;
case T_BoolExpr:
retval = _equalBoolExpr(a, b);
break;

View File

@ -8,14 +8,14 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/nodeFuncs.c,v 1.21 2002/12/13 19:45:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/nodeFuncs.c,v 1.22 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/nodeFuncs.h"
#include "utils/lsyscache.h"
static bool var_is_inner(Var *var);
@ -72,20 +72,3 @@ var_is_rel(Var *var)
return (bool)
!(var_is_inner(var) || var_is_outer(var));
}
/*****************************************************************************
* OPER nodes
*****************************************************************************/
/*
* set_opfuncid -
*
* Set the opfuncid (procedure OID) in an OpExpr node,
* if it hasn't been set already.
*/
void
set_opfuncid(OpExpr *opexpr)
{
if (opexpr->opfuncid == InvalidOid)
opexpr->opfuncid = get_opcode(opexpr->opno);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.210 2003/06/25 21:30:29 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.211 2003/06/29 00:33:43 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@ -668,6 +668,17 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
WRITE_NODE_FIELD(args);
}
static void
_outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
{
WRITE_NODE_TYPE("SCALARARRAYOPEXPR");
WRITE_OID_FIELD(opno);
WRITE_OID_FIELD(opfuncid);
WRITE_BOOL_FIELD(useOr);
WRITE_NODE_FIELD(args);
}
static void
_outBoolExpr(StringInfo str, BoolExpr *node)
{
@ -1333,6 +1344,16 @@ _outAExpr(StringInfo str, A_Expr *node)
case AEXPR_NOT:
appendStringInfo(str, " NOT");
break;
case AEXPR_OP_ANY:
appendStringInfo(str, " ");
WRITE_NODE_FIELD(name);
appendStringInfo(str, " ANY ");
break;
case AEXPR_OP_ALL:
appendStringInfo(str, " ");
WRITE_NODE_FIELD(name);
appendStringInfo(str, " ALL ");
break;
case AEXPR_DISTINCT:
appendStringInfo(str, " DISTINCT ");
WRITE_NODE_FIELD(name);
@ -1619,6 +1640,9 @@ _outNode(StringInfo str, void *obj)
case T_DistinctExpr:
_outDistinctExpr(str, obj);
break;
case T_ScalarArrayOpExpr:
_outScalarArrayOpExpr(str, obj);
break;
case T_BoolExpr:
_outBoolExpr(str, obj);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.156 2003/06/25 21:30:30 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.157 2003/06/29 00:33:43 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@ -510,6 +510,32 @@ _readDistinctExpr(void)
READ_DONE();
}
/*
* _readScalarArrayOpExpr
*/
static ScalarArrayOpExpr *
_readScalarArrayOpExpr(void)
{
READ_LOCALS(ScalarArrayOpExpr);
READ_OID_FIELD(opno);
READ_OID_FIELD(opfuncid);
/*
* The opfuncid is stored in the textual format primarily for debugging
* and documentation reasons. We want to always read it as zero to force
* it to be re-looked-up in the pg_operator entry. This ensures that
* stored rules don't have hidden dependencies on operators' functions.
* (We don't currently support an ALTER OPERATOR command, but might
* someday.)
*/
local_node->opfuncid = InvalidOid;
READ_BOOL_FIELD(useOr);
READ_NODE_FIELD(args);
READ_DONE();
}
/*
* _readBoolExpr
*/
@ -951,6 +977,8 @@ parseNodeString(void)
return_value = _readOpExpr();
else if (MATCH("DISTINCTEXPR", 12))
return_value = _readDistinctExpr();
else if (MATCH("SCALARARRAYOPEXPR", 17))
return_value = _readScalarArrayOpExpr();
else if (MATCH("BOOLEXPR", 8))
return_value = _readBoolExpr();
else if (MATCH("SUBLINK", 7))

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.58 2003/05/27 17:49:46 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.59 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -515,6 +515,12 @@ clause_selectivity(Query *root,
*/
s1 = (Selectivity) 0.5;
}
else if (IsA(clause, DistinctExpr) ||
IsA(clause, ScalarArrayOpExpr))
{
/* can we do better? */
s1 = (Selectivity) 0.5;
}
else if (IsA(clause, NullTest))
{
/* Use node specific selectivity calculation function */

View File

@ -49,7 +49,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.107 2003/02/16 02:30:38 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.108 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1473,6 +1473,11 @@ cost_qual_eval_walker(Node *node, QualCost *total)
{
total->per_tuple += cpu_operator_cost;
}
else if (IsA(node, ScalarArrayOpExpr))
{
/* should charge more than 1 op cost, but how many? */
total->per_tuple += cpu_operator_cost * 10;
}
else if (IsA(node, SubLink))
{
/* This routine should not be applied to un-planned expressions */

View File

@ -9,20 +9,19 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.92 2003/02/16 02:30:38 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.93 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
typedef struct
@ -61,6 +60,8 @@ static Node *replace_vars_with_subplan_refs(Node *node,
static Node *replace_vars_with_subplan_refs_mutator(Node *node,
replace_vars_with_subplan_refs_context *context);
static bool fix_opfuncids_walker(Node *node, void *context);
static void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
/*****************************************************************************
*
@ -284,6 +285,8 @@ fix_expr_references_walker(Node *node, void *context)
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
else if (IsA(node, ScalarArrayOpExpr))
set_sa_opfuncid((ScalarArrayOpExpr *) node);
else if (IsA(node, NullIfExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
else if (IsA(node, SubPlan))
@ -738,7 +741,35 @@ fix_opfuncids_walker(Node *node, void *context)
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
else if (IsA(node, ScalarArrayOpExpr))
set_sa_opfuncid((ScalarArrayOpExpr *) node);
else if (IsA(node, NullIfExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
return expression_tree_walker(node, fix_opfuncids_walker, context);
}
/*
* set_opfuncid
* Set the opfuncid (procedure OID) in an OpExpr node,
* if it hasn't been set already.
*
* Because of struct equivalence, this can also be used for
* DistinctExpr and NullIfExpr nodes.
*/
void
set_opfuncid(OpExpr *opexpr)
{
if (opexpr->opfuncid == InvalidOid)
opexpr->opfuncid = get_opcode(opexpr->opno);
}
/*
* set_sa_opfuncid
* As above, for ScalarArrayOpExpr nodes.
*/
static void
set_sa_opfuncid(ScalarArrayOpExpr *opexpr)
{
if (opexpr->opfuncid == InvalidOid)
opexpr->opfuncid = get_opcode(opexpr->opno);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.141 2003/06/25 21:30:30 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.142 2003/06/29 00:33:43 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -25,8 +25,8 @@
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
@ -461,6 +461,8 @@ expression_returns_set_walker(Node *node, void *context)
return false;
if (IsA(node, DistinctExpr))
return false;
if (IsA(node, ScalarArrayOpExpr))
return false;
if (IsA(node, BoolExpr))
return false;
if (IsA(node, SubLink))
@ -563,6 +565,14 @@ contain_mutable_functions_walker(Node *node, void *context)
return true;
/* else fall through to check args */
}
if (IsA(node, ScalarArrayOpExpr))
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
return true;
/* else fall through to check args */
}
if (IsA(node, NullIfExpr))
{
NullIfExpr *expr = (NullIfExpr *) node;
@ -638,6 +648,14 @@ contain_volatile_functions_walker(Node *node, void *context)
return true;
/* else fall through to check args */
}
if (IsA(node, ScalarArrayOpExpr))
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
return true;
/* else fall through to check args */
}
if (IsA(node, NullIfExpr))
{
NullIfExpr *expr = (NullIfExpr *) node;
@ -711,6 +729,11 @@ contain_nonstrict_functions_walker(Node *node, void *context)
/* IS DISTINCT FROM is inherently non-strict */
return true;
}
if (IsA(node, ScalarArrayOpExpr))
{
/* inherently non-strict, consider null scalar and empty array */
return true;
}
if (IsA(node, BoolExpr))
{
BoolExpr *expr = (BoolExpr *) node;
@ -2152,6 +2175,15 @@ expression_tree_walker(Node *node,
return true;
}
break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
if (expression_tree_walker((Node *) expr->args,
walker, context))
return true;
}
break;
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;
@ -2510,6 +2542,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
ScalarArrayOpExpr *newnode;
FLATCOPY(newnode, expr, ScalarArrayOpExpr);
MUTATE(newnode->args, expr->args, List *);
return (Node *) newnode;
}
break;
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.422 2003/06/27 14:45:28 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.423 2003/06/29 00:33:43 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -6051,6 +6051,13 @@ a_expr: c_expr { $$ = $1; }
n->subselect = $4;
$$ = (Node *)n;
}
| a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
{
if ($3 == ANY_SUBLINK)
$$ = (Node *) makeA_Expr(AEXPR_OP_ANY, $2, $1, $5);
else
$$ = (Node *) makeA_Expr(AEXPR_OP_ALL, $2, $1, $5);
}
| UNIQUE select_with_parens %prec Op
{
/* Not sure how to get rid of the parentheses

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.153 2003/06/27 17:04:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.154 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -300,6 +300,34 @@ transformExpr(ParseState *pstate, Node *expr)
makeList1(rexpr));
}
break;
case AEXPR_OP_ANY:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
Node *rexpr = transformExpr(pstate,
a->rexpr);
result = (Node *) make_scalar_array_op(pstate,
a->name,
true,
lexpr,
rexpr);
}
break;
case AEXPR_OP_ALL:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
Node *rexpr = transformExpr(pstate,
a->rexpr);
result = (Node *) make_scalar_array_op(pstate,
a->name,
false,
lexpr,
rexpr);
}
break;
case AEXPR_DISTINCT:
{
Node *lexpr = transformExpr(pstate,
@ -879,6 +907,7 @@ transformExpr(ParseState *pstate, Node *expr)
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
case T_ScalarArrayOpExpr:
case T_NullIfExpr:
case T_BoolExpr:
case T_FieldSelect:
@ -1155,6 +1184,9 @@ exprType(Node *expr)
case T_DistinctExpr:
type = ((DistinctExpr *) expr)->opresulttype;
break;
case T_ScalarArrayOpExpr:
type = BOOLOID;
break;
case T_BoolExpr:
type = BOOLOID;
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.67 2003/06/27 00:33:25 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.68 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -737,6 +737,96 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree)
return result;
}
/*
* make_scalar_array_op()
* Build expression tree for "scalar op ANY/ALL (array)" construct.
*/
Expr *
make_scalar_array_op(ParseState *pstate, List *opname,
bool useOr,
Node *ltree, Node *rtree)
{
Oid ltypeId,
rtypeId,
atypeId,
res_atypeId;
Operator tup;
Form_pg_operator opform;
Oid actual_arg_types[2];
Oid declared_arg_types[2];
List *args;
Oid rettype;
ScalarArrayOpExpr *result;
ltypeId = exprType(ltree);
atypeId = exprType(rtree);
/*
* The right-hand input of the operator will be the element type of
* the array. However, if we currently have just an untyped literal
* on the right, stay with that and hope we can resolve the operator.
*/
if (atypeId == UNKNOWNOID)
rtypeId = UNKNOWNOID;
else
{
rtypeId = get_element_type(atypeId);
if (!OidIsValid(rtypeId))
elog(ERROR, "op ANY/ALL (array) requires array on right side");
}
/* Now resolve the operator */
tup = oper(opname, ltypeId, rtypeId, false);
opform = (Form_pg_operator) GETSTRUCT(tup);
args = makeList2(ltree, rtree);
actual_arg_types[0] = ltypeId;
actual_arg_types[1] = rtypeId;
declared_arg_types[0] = opform->oprleft;
declared_arg_types[1] = opform->oprright;
/*
* enforce consistency with ANYARRAY and ANYELEMENT argument and
* return types, possibly adjusting return type or declared_arg_types
* (which will be used as the cast destination by make_fn_arguments)
*/
rettype = enforce_generic_type_consistency(actual_arg_types,
declared_arg_types,
2,
opform->oprresult);
/*
* Check that operator result is boolean
*/
if (rettype != BOOLOID)
elog(ERROR, "op ANY/ALL (array) requires operator to yield boolean");
if (get_func_retset(opform->oprcode))
elog(ERROR, "op ANY/ALL (array) requires operator not to return a set");
/*
* Now switch back to the array type on the right, arranging for
* any needed cast to be applied.
*/
res_atypeId = get_array_type(declared_arg_types[1]);
if (!OidIsValid(res_atypeId))
elog(ERROR, "unable to find datatype for array of %s",
format_type_be(declared_arg_types[1]));
actual_arg_types[1] = atypeId;
declared_arg_types[1] = res_atypeId;
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
/* and build the expression node */
result = makeNode(ScalarArrayOpExpr);
result->opno = oprid(tup);
result->opfuncid = InvalidOid;
result->useOr = useOr;
result->args = args;
ReleaseSysCache(tup);
return (Expr *) result;
}
/*
* make_op_expr()

View File

@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.142 2003/06/25 03:56:30 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.143 2003/06/29 00:33:44 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -2292,19 +2292,33 @@ get_rule_expr(Node *node, deparse_context *context,
{
DistinctExpr *expr = (DistinctExpr *) node;
List *args = expr->args;
Node *arg1 = (Node *) lfirst(args);
Node *arg2 = (Node *) lsecond(args);
Assert(length(args) == 2);
{
/* binary operator */
Node *arg1 = (Node *) lfirst(args);
Node *arg2 = (Node *) lsecond(args);
appendStringInfoChar(buf, '(');
get_rule_expr(arg1, context, true);
appendStringInfo(buf, " IS DISTINCT FROM ");
get_rule_expr(arg2, context, true);
appendStringInfoChar(buf, ')');
}
break;
appendStringInfoChar(buf, '(');
get_rule_expr(arg1, context, true);
appendStringInfo(buf, " IS DISTINCT FROM ");
get_rule_expr(arg2, context, true);
appendStringInfoChar(buf, ')');
}
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
List *args = expr->args;
Node *arg1 = (Node *) lfirst(args);
Node *arg2 = (Node *) lsecond(args);
appendStringInfoChar(buf, '(');
get_rule_expr(arg1, context, true);
appendStringInfo(buf, " %s %s (",
generate_operator_name(expr->opno,
exprType(arg1),
get_element_type(exprType(arg2))),
expr->useOr ? "ANY" : "ALL");
get_rule_expr(arg2, context, true);
appendStringInfo(buf, "))");
}
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.70 2003/06/25 21:30:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.71 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1651,6 +1651,7 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
{
Node *expr;
List *args;
Oid argtype;
/*
* can't return anything useful if we have no FmgrInfo or if
@ -1665,11 +1666,27 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
args = ((FuncExpr *) expr)->args;
else if (IsA(expr, OpExpr))
args = ((OpExpr *) expr)->args;
else if (IsA(expr, DistinctExpr))
args = ((DistinctExpr *) expr)->args;
else if (IsA(expr, ScalarArrayOpExpr))
args = ((ScalarArrayOpExpr *) expr)->args;
else if (IsA(expr, NullIfExpr))
args = ((NullIfExpr *) expr)->args;
else
return InvalidOid;
if (argnum < 0 || argnum >= length(args))
return InvalidOid;
return exprType((Node *) nth(argnum, args));
argtype = exprType((Node *) nth(argnum, args));
/*
* special hack for ScalarArrayOpExpr: what the underlying function
* will actually get passed is the element type of the array.
*/
if (IsA(expr, ScalarArrayOpExpr) &&
argnum == 1)
argtype = get_element_type(argtype);
return argtype;
}

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.201 2003/06/27 00:33:25 tgl Exp $
* $Id: catversion.h,v 1.202 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200306261
#define CATALOG_VERSION_NO 200306281
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: execnodes.h,v 1.99 2003/06/22 22:04:55 tgl Exp $
* $Id: execnodes.h,v 1.100 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -488,6 +488,22 @@ typedef struct FuncExprState
FunctionCallInfoData setArgs;
} FuncExprState;
/* ----------------
* ScalarArrayOpExprState node
*
* This is a FuncExprState plus some additional data.
* ----------------
*/
typedef struct ScalarArrayOpExprState
{
FuncExprState fxprstate;
/* Cached info about array element type */
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
} ScalarArrayOpExprState;
/* ----------------
* BoolExprState node
* ----------------

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodeFuncs.h,v 1.18 2002/12/12 15:49:40 tgl Exp $
* $Id: nodeFuncs.h,v 1.19 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,6 +19,5 @@
extern bool single_node(Node *node);
extern bool var_is_outer(Var *var);
extern bool var_is_rel(Var *var);
extern void set_opfuncid(OpExpr *opexpr);
#endif /* NODEFUNCS_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodes.h,v 1.142 2003/06/25 04:19:24 momjian Exp $
* $Id: nodes.h,v 1.143 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -105,6 +105,7 @@ typedef enum NodeTag
T_FuncExpr,
T_OpExpr,
T_DistinctExpr,
T_ScalarArrayOpExpr,
T_BoolExpr,
T_SubLink,
T_SubPlan,
@ -135,6 +136,7 @@ typedef enum NodeTag
T_AggrefExprState,
T_ArrayRefExprState,
T_FuncExprState,
T_ScalarArrayOpExprState,
T_BoolExprState,
T_SubPlanState,
T_CaseExprState,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.241 2003/06/27 14:45:31 petere Exp $
* $Id: parsenodes.h,v 1.242 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -170,6 +170,8 @@ typedef enum A_Expr_Kind
AEXPR_AND, /* booleans - name field is unused */
AEXPR_OR,
AEXPR_NOT,
AEXPR_OP_ANY, /* scalar op ANY (array) */
AEXPR_OP_ALL, /* scalar op ALL (array) */
AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */
AEXPR_NULLIF, /* NULLIF - name must be "=" */
AEXPR_OF /* IS (not) OF - name must be "=" or "!=" */

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: primnodes.h,v 1.85 2003/06/25 21:30:33 momjian Exp $
* $Id: primnodes.h,v 1.86 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -334,6 +334,25 @@ typedef struct OpExpr
*/
typedef OpExpr DistinctExpr;
/*
* ScalarArrayOpExpr - expression node for "scalar op ANY/ALL (array)"
*
* The operator must yield boolean. It is applied to the left operand
* and each element of the righthand array, and the results are combined
* with OR or AND (for ANY or ALL respectively). The node representation
* is almost the same as for the underlying operator, but we need a useOr
* flag to remember whether it's ANY or ALL, and we don't have to store
* the result type because it must be boolean.
*/
typedef struct ScalarArrayOpExpr
{
Expr xpr;
Oid opno; /* PG_OPERATOR OID of the operator */
Oid opfuncid; /* PG_PROC OID of underlying function */
bool useOr; /* true for ANY, false for ALL */
List *args; /* the scalar and array operands */
} ScalarArrayOpExpr;
/*
* BoolExpr - expression node for the basic Boolean operators AND, OR, NOT
*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: planmain.h,v 1.70 2003/05/06 00:20:33 tgl Exp $
* $Id: planmain.h,v 1.71 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -69,5 +69,6 @@ extern void process_implied_equality(Query *root,
*/
extern void set_plan_references(Plan *plan, List *rtable);
extern void fix_opfuncids(Node *node);
extern void set_opfuncid(OpExpr *opexpr);
#endif /* PLANMAIN_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_oper.h,v 1.28 2003/06/27 00:33:26 tgl Exp $
* $Id: parse_oper.h,v 1.29 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -54,6 +54,9 @@ extern Oid oprfuncid(Operator op);
/* Build expression tree for an operator invocation */
extern Expr *make_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree);
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);

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.86 2003/04/24 21:16:44 tgl Exp $
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.87 2003/06/29 00:33:44 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -3520,6 +3520,16 @@ exec_simple_check_node(Node *node)
return TRUE;
}
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
if (!exec_simple_check_node((Node *) expr->args))
return FALSE;
return TRUE;
}
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;

View File

@ -294,6 +294,68 @@ SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"
{{{{{{a,bb,ccc}}}}}}
(1 row)
-- scalar op any/all (array)
select 33 = any ('{1,2,3}');
?column?
----------
f
(1 row)
select 33 = any ('{1,2,33}');
?column?
----------
t
(1 row)
select 33 = all ('{1,2,33}');
?column?
----------
f
(1 row)
select 33 >= all ('{1,2,33}');
?column?
----------
t
(1 row)
-- boundary cases
select null::int >= all ('{1,2,33}');
?column?
----------
(1 row)
select null::int >= all ('{}');
?column?
----------
t
(1 row)
select null::int >= any ('{}');
?column?
----------
f
(1 row)
-- cross-datatype
select 33.4 = any (array[1,2,3]);
?column?
----------
f
(1 row)
select 33.4 > all (array[1,2,3]);
?column?
----------
t
(1 row)
-- errors
select 33 * any ('{1,2,3}');
ERROR: op ANY/ALL (array) requires operator to yield boolean
select 33 * any (44);
ERROR: op ANY/ALL (array) requires array on right side
-- test indexes on arrays
create temp table arr_tbl (f1 int[] unique);
NOTICE: CREATE TABLE / UNIQUE will create implicit index 'arr_tbl_f1_key' for table 'arr_tbl'

View File

@ -154,6 +154,22 @@ SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE";
SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}";
-- scalar op any/all (array)
select 33 = any ('{1,2,3}');
select 33 = any ('{1,2,33}');
select 33 = all ('{1,2,33}');
select 33 >= all ('{1,2,33}');
-- boundary cases
select null::int >= all ('{1,2,33}');
select null::int >= all ('{}');
select null::int >= any ('{}');
-- cross-datatype
select 33.4 = any (array[1,2,3]);
select 33.4 > all (array[1,2,3]);
-- errors
select 33 * any ('{1,2,3}');
select 33 * any (44);
-- test indexes on arrays
create temp table arr_tbl (f1 int[] unique);
insert into arr_tbl values ('{1,2,3}');