]> granicus.if.org Git - postgresql/commitdiff
Support expressions of the form 'scalar op ANY (array)' and
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 29 Jun 2003 00:33:44 +0000 (00:33 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 29 Jun 2003 00:33:44 +0000 (00:33 +0000)
'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.

28 files changed:
doc/src/sgml/func.sgml
src/backend/catalog/dependency.c
src/backend/executor/execQual.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/clausesel.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/gram.y
src/backend/parser/parse_expr.c
src/backend/parser/parse_oper.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/fmgr/fmgr.c
src/include/catalog/catversion.h
src/include/nodes/execnodes.h
src/include/nodes/nodeFuncs.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/primnodes.h
src/include/optimizer/planmain.h
src/include/parser/parse_oper.h
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/arrays.out
src/test/regress/sql/arrays.sql

index df677df37a227432d47e8dd32fe0ba18d2d28aa3..e15a44570b1acdbe252b906e647096c8db1bc81e 100644 (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 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;
index d57f645b281a85dfb32e18e6c6a1d3b72c0a3c64..80a4cebf06721ea4f323164ee27182e72564c1aa 100644 (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;
index ee29c195ae72def772515ec820851b89b2842488..bba9a6e49c700b3494c193db9bae0422b058961c 100644 (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;
index b1884bb0d28988933d49ca609aa3dd62fd27887b..0caa7a55589ab8acc497aad9aa56fe56654095af 100644 (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;
index 6f30c8fabd8517e93db2946230d15c34b9bf446e..0f6fbaae2ee6c55f145370fb8cf206af5c6adf9c 100644 (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;
index 6c09574e13c060765ae7cfa9dbddb744af82d52f..84dd85ad5f88e0e520aee464b08dad75aae3a183 100644 (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);
-}
index e2b8cb789d95f1320e9cb7c5933bd1acf1558beb..3b4858ee16ed99b58bc98a973e55dd08702030a3 100644 (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;
index 1d2db3b37a9d3672e0bd16a3d0f68d07df9ad44b..b26a7a1ae8c56385fb27a7002a5c1edb1d1e385c 100644 (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))
index 846d52140cca3403244cdacc55b8899401771081..67900d9e40f809d6db20ce1852c71f916df4fd4b 100644 (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 */
index 21bc152ce6e7ec19d239937661e51b9bdc4f3903..283987b63d53275846f7896ed546d97f68297638 100644 (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 */
index 4e17a85eb4b2cfd7d1b4e22d591d46ea3089df74..1da186e0aaac6b89395f04a948893006225a257b 100644 (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);
+}
index 8b04066133c6eb7a86db4dad1ab613caac4d10ab..54f2d7bd69b2a4c0e330ac539e8e6ce80cd250f7 100644 (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;
index d3d9b4a5e30b5ee944805f92491913de381d3d4f..a8df7c65e9dfe376d82464e09cc0c41ff7dc4426 100644 (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
index 6a90ab9197d04b7288c890f2b5cbafd2fb3b714f..b985b190bac285bca72f5289bdb40767aaa43ea1 100644 (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;
index 983041eb45bfc806813356a5a1c97374febf01d7..72327243310244d4fdc4fb23172d5e56d3790f3d 100644 (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()
index 27259044c1944395b2e0e51feb23720444932376..63b174a39cf9183eedbc1f39099927356647c7a1 100644 (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;
 
index e161bd1e59365f2292d0ac14d2085ee8e5d91c08..0d69ac1083e600dcf5c35b24e376ec567ac86678 100644 (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;
 }
index e8ce5d131a5b6e9b6d374b9d120247bd18294b02..7d73598d55baaff5e34be8ce444812b233982ad4 100644 (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
index 47879296c0eb8e1b16c4f5c17bcadf51c77e71ba..c6f1342343816b3ef488e5402aae725f55f64cfc 100644 (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
  * ----------------
index 99e1a62542c965a453b9c0406ee0614795b3051e..f0805bfa1f9c6ce73de7c56854e7aae928ac0010 100644 (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 */
index af87482e42a3539d33cbada6108fa90df4e4e236..2592270c25848d673ebb7148ec343f571ec6c1ec 100644 (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,
index 527c56b9edae26767fad5a6ba25328232ea6c08d..9101ba1e1776d29a3f6453d45f81f84d300ccff5 100644 (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 "!=" */
index 23dff8ed622e06307ba507280b1c42a6445f0c43..57827ee76fe83e20770ba9704f419899c8809583 100644 (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
  *
index 35a85e311abf325185210153c7283bea5b116964..106e9d1ce4b549cc4fe7ef873660dabe2bb59b6d 100644 (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 */
index 66e1258364522c272e7872f5770a46f25b6e331f..3fd5eea2a8a8f60197b3660bfd8148d758d58b76 100644 (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);
index efb423541a9d64ae77bf51de3b38153a46f0088b..54f2b3a88037bc29e756fb44c2cdbf3439a69d2e 100644 (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;
index 9822294d19af9710131f64beaaaf579bfc12b8ff..a18d345475ba286fcc2eb41cf254a4d05c9ca057 100644 (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'
index 8b18ffb9eb83bc46df5c6876cb395b009313225c..c3bcdd5e3a66e6441ca0c3ac87519aeb105942c3 100644 (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}');