aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/func.sgml290
-rw-r--r--src/backend/catalog/dependency.c10
-rw-r--r--src/backend/executor/execQual.c164
-rw-r--r--src/backend/nodes/copyfuncs.c21
-rw-r--r--src/backend/nodes/equalfuncs.c26
-rw-r--r--src/backend/nodes/nodeFuncs.c21
-rw-r--r--src/backend/nodes/outfuncs.c26
-rw-r--r--src/backend/nodes/readfuncs.c30
-rw-r--r--src/backend/optimizer/path/clausesel.c8
-rw-r--r--src/backend/optimizer/path/costsize.c7
-rw-r--r--src/backend/optimizer/plan/setrefs.c37
-rw-r--r--src/backend/optimizer/util/clauses.c46
-rw-r--r--src/backend/parser/gram.y9
-rw-r--r--src/backend/parser/parse_expr.c34
-rw-r--r--src/backend/parser/parse_oper.c92
-rw-r--r--src/backend/utils/adt/ruleutils.c38
-rw-r--r--src/backend/utils/fmgr/fmgr.c21
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/nodes/execnodes.h18
-rw-r--r--src/include/nodes/nodeFuncs.h3
-rw-r--r--src/include/nodes/nodes.h4
-rw-r--r--src/include/nodes/parsenodes.h4
-rw-r--r--src/include/nodes/primnodes.h21
-rw-r--r--src/include/optimizer/planmain.h3
-rw-r--r--src/include/parser/parse_oper.h5
-rw-r--r--src/pl/plpgsql/src/pl_exec.c12
-rw-r--r--src/test/regress/expected/arrays.out62
-rw-r--r--src/test/regress/sql/arrays.sql16
28 files changed, 875 insertions, 157 deletions
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index df677df37a2..e15a44570b1 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -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 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 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;
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index d57f645b281..80a4cebf067 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -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;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index ee29c195ae7..bba9a6e49c7 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -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;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index b1884bb0d28..0caa7a55589 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -803,6 +803,22 @@ _copyDistinctExpr(DistinctExpr *from)
}
/*
+ * _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
*/
static BoolExpr *
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6f30c8fabd8..0f6fbaae2ee 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -288,6 +288,27 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
}
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)
{
COMPARE_SCALAR_FIELD(boolop);
@@ -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;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 6c09574e13c..84dd85ad5f8 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -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);
-}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e2b8cb789d9..3b4858ee16e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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*
@@ -669,6 +669,17 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
}
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)
{
char *opstr = NULL;
@@ -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;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 1d2db3b37a9..b26a7a1ae8c 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -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
@@ -511,6 +511,32 @@ _readDistinctExpr(void)
}
/*
+ * _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
*/
static BoolExpr *
@@ -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))
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 846d52140cc..67900d9e40f 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -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 */
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 21bc152ce6e..283987b63d5 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -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 */
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 4e17a85eb4b..1da186e0aaa 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -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);
+}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8b04066133c..54f2d7bd69b 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d3d9b4a5e30..a8df7c65e9d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 6a90ab9197d..b985b190bac 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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;
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 983041eb45b..72327243310 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -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()
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 27259044c19..63b174a39cf 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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;
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index e161bd1e593..0d69ac1083e 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -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;
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index e8ce5d131a5..7d73598d55b 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -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
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 47879296c0e..c6f13423438 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -489,6 +489,22 @@ typedef struct FuncExprState
} 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
* ----------------
*/
diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h
index 99e1a62542c..f0805bfa1f9 100644
--- a/src/include/nodes/nodeFuncs.h
+++ b/src/include/nodes/nodeFuncs.h
@@ -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 */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index af87482e42a..2592270c258 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -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,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 527c56b9eda..9101ba1e177 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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 "!=" */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 23dff8ed622..57827ee76fe 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -335,6 +335,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
*
* Notice the arguments are given as a List. For NOT, of course the list
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 35a85e311ab..106e9d1ce4b 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -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 */
diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h
index 66e12583645..3fd5eea2a8a 100644
--- a/src/include/parser/parse_oper.h
+++ b/src/include/parser/parse_oper.h
@@ -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);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index efb423541a9..54f2b3a8803 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -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;
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 9822294d19a..a18d345475b 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -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'
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 8b18ffb9eb8..c3bcdd5e3a6 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -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}');