Support expressions of the form 'scalar op ANY (array)' and
authorTom Lane
Sun, 29 Jun 2003 00:33:44 +0000 (00:33 +0000)
committerTom Lane
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 @@
 
 
@@ -7457,52 +7457,14 @@ SELECT col1 FROM tab1
   
 
   
-   <literal>IN</literal> (scalar form)
-
-
-expression IN (value, ...)
-
-
-  
-   The right-hand side of this form of IN is a parenthesized list
-   of scalar expressions.  The result is true if the left-hand expression's
-   result is equal to any of the right-hand expressions.  This is a shorthand
-   notation for
-
-
-expression = value1
-OR
-expression = value2
-OR
-...
-
-  
-
-  
-   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 IN construct will be null, not false.
-   This is in accordance with SQL's normal rules for Boolean combinations
-   of null values.
-  
-
-  
-  
-   This form of IN is not truly a subquery expression, but it
-   seems best to document it in the same place as subquery IN.
-  
-  
-  
-
-  
-   <literal>IN</literal> (subquery form)
+   <literal>IN</literal>
 
 
 expression IN (subquery)
 
 
   
-   The right-hand side of this form of IN 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 IN is true if any equal subquery row is found.
@@ -7538,7 +7500,7 @@ OR
   
 
   
-   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
   
 
   
-   <literal>NOT IN</literal> (scalar form)
-
-
-expression NOT IN (value, ...)
-
-
-  
-   The right-hand side of this form of NOT IN is a parenthesized list
-   of scalar expressions.  The result is true if the left-hand expression's
-   result is unequal to all of the right-hand expressions.  This is a shorthand
-   notation for
-
-
-expression <> value1
-AND
-expression <> value2
-AND
-...
-
-  
-
-  
-   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 NOT IN 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.
-  
-
-  
-  
-   x NOT IN y is equivalent to NOT (x IN y) in all
-   cases.  However, null values are much more likely to trip up the novice when
-   working with NOT IN than when working with IN.
-   It's best to express your condition positively if possible.
-  
-  
-  
-
-  
-   <literal>NOT IN </literal>(subquery form)
+   <literal>NOT IN </literal>
 
 
 expression NOT IN (subquery)
 
 
   
-   The right-hand side of this form of NOT IN 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 NOT IN is true if only unequal subquery rows
@@ -7633,7 +7554,7 @@ AND
   
 
   
-   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
 
 
   
-   The right-hand side of this form of ANY 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 operator, which must yield a Boolean
@@ -7700,7 +7621,7 @@ AND
   
 
   
-   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
 
 
   
-   The right-hand side of this form of ALL 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 operator, which must yield a Boolean
@@ -7765,7 +7686,7 @@ AND
   
 
   
-   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
 
 
 (expression expression ...operator (subquery)
-(expression expression ...operator (expression expression ...)
 
 
   
-   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 = and <> operators are allowed
+   in row-wise comparisons.
+   The result is true if the two rows are equal or unequal, respectively.
+  
+
+  
+   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).
+  
+  
+
+
+  Row and Array Comparisons
+
+  
+   in
+  
+
+  
+   not in
+  
+
+  
+   any
+  
+
+  
+   all
+  
+
+  
+   some
+  
+
+  
+   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
+   PostgreSQL extensions; the rest are
+   SQL-compliant.
+   All of the expression forms documented in this section return
+   Boolean (true/false) results.
+  
+
+  
+   <literal>IN</literal>
+
+
+expression IN (value, ...)
+
+
+  
+   The right-hand side is a parenthesized list
+   of scalar expressions.  The result is true if the left-hand expression's
+   result is equal to any of the right-hand expressions.  This is a shorthand
+   notation for
+
+
+expression = value1
+OR
+expression = value2
+OR
+...
+
+  
+
+  
+   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 IN construct will be null, not false.
+   This is in accordance with SQL's normal rules for Boolean combinations
+   of null values.
+  
+  
+
+  
+   <literal>NOT IN</literal>
+
+
+expression NOT IN (value, ...)
+
+
+  
+   The right-hand side is a parenthesized list
+   of scalar expressions.  The result is true if the left-hand expression's
+   result is unequal to all of the right-hand expressions.  This is a shorthand
+   notation for
+
+
+expression <> value1
+AND
+expression <> value2
+AND
+...
+
+  
+
+  
+   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 NOT IN 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.
+  
+
+  
+  
+   x NOT IN y is equivalent to NOT (x IN y) in all
+   cases.  However, null values are much more likely to trip up the novice when
+   working with NOT IN than when working with IN.
+   It's best to express your condition positively if possible.
+  
+  
+  
+
+  
+   <literal>ANY</literal>/<literal>SOME</literal> (array)
+
+
+expression operator ANY (array expression)
+expression operator SOME (array expression)
+
+
+  
+   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 operator, which must yield a Boolean
+   result.
+   The result of ANY is true if any true result is obtained.
+   The result is false if no true result is found (including the special
+   case where the array has zero elements).
+  
+
+  
+   SOME is a synonym for ANY.
+  
+  
+
+  
+   <literal>ALL</literal> (array)
+
+
+expression operator ALL (array expression)
+
+
+  
+   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 operator, which must yield a Boolean
+   result.
+   The result of ALL is true if all comparisons yield true
+   (including the special case where the array has zero elements).
+   The result is false if any false result is found.
+  
+  
+
+  
+   Row-wise Comparison
+
+
+(expression expression ...operator (expression expression ...)
+
+
+  
+   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 = and <> operators are allowed
    in row-wise comparisons.
    The result is true if the two rows are equal or unequal, respectively.
   
 
   
-   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}');