aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-06-29 00:33:44 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-06-29 00:33:44 +0000
commitbee217924d51af8cf0411167bfc8a7eb55122577 (patch)
treeab6d3f311de5fd6b415e0bdf032205ed503ba95c /src/backend
parentdf7618020b3845a51d1ba80cb9abfc6df5dfeaff (diff)
downloadpostgresql-bee217924d51af8cf0411167bfc8a7eb55122577.tar.gz
postgresql-bee217924d51af8cf0411167bfc8a7eb55122577.zip
Support expressions of the form 'scalar op ANY (array)' and
'scalar op ALL (array)', where the operator is applied between the lefthand scalar and each element of the array. The operator must yield boolean; the result of the construct is the OR or AND of the per-element results, respectively. Original coding by Joe Conway, after an idea of Peter's. Rewritten by Tom to keep the implementation strictly separate from subqueries.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/dependency.c10
-rw-r--r--src/backend/executor/execQual.c164
-rw-r--r--src/backend/nodes/copyfuncs.c21
-rw-r--r--src/backend/nodes/equalfuncs.c26
-rw-r--r--src/backend/nodes/nodeFuncs.c21
-rw-r--r--src/backend/nodes/outfuncs.c26
-rw-r--r--src/backend/nodes/readfuncs.c30
-rw-r--r--src/backend/optimizer/path/clausesel.c8
-rw-r--r--src/backend/optimizer/path/costsize.c7
-rw-r--r--src/backend/optimizer/plan/setrefs.c37
-rw-r--r--src/backend/optimizer/util/clauses.c46
-rw-r--r--src/backend/parser/gram.y9
-rw-r--r--src/backend/parser/parse_expr.c34
-rw-r--r--src/backend/parser/parse_oper.c92
-rw-r--r--src/backend/utils/adt/ruleutils.c38
-rw-r--r--src/backend/utils/fmgr/fmgr.c21
16 files changed, 540 insertions, 50 deletions
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index d57f645b281..80a4cebf067 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.25 2003/05/28 16:03:55 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.26 2003/06/29 00:33:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1025,6 +1025,14 @@ find_expr_references_walker(Node *node,
&context->addrs);
/* fall through to examine arguments */
}
+ if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+
+ add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
+ &context->addrs);
+ /* fall through to examine arguments */
+ }
if (IsA(node, NullIfExpr))
{
NullIfExpr *nullifexpr = (NullIfExpr *) node;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index ee29c195ae7..bba9a6e49c7 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.133 2003/06/27 00:33:25 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.134 2003/06/29 00:33:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -65,6 +65,8 @@ static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
bool *isNull);
+static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
+ ExprContext *econtext, bool *isNull);
static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList, ExprContext *econtext);
static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
@@ -1121,7 +1123,6 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
/* ----------------------------------------------------------------
* ExecEvalFunc
* ExecEvalOper
- * ExecEvalDistinct
*
* Evaluate the functional result of a list of arguments by calling the
* function manager.
@@ -1241,6 +1242,149 @@ ExecEvalDistinct(FuncExprState *fcache,
return result;
}
+/*
+ * ExecEvalScalarArrayOp
+ *
+ * Evaluate "scalar op ANY/ALL (array)". The operator always yields boolean,
+ * and we combine the results across all array elements using OR and AND
+ * (for ANY and ALL respectively). Of course we short-circuit as soon as
+ * the result is known.
+ */
+static Datum
+ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
+ ExprContext *econtext, bool *isNull)
+{
+ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr;
+ bool useOr = opexpr->useOr;
+ ArrayType *arr;
+ int nitems;
+ Datum result;
+ bool resultnull;
+ FunctionCallInfoData fcinfo;
+ ExprDoneCond argDone;
+ int i;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char *s;
+
+ /*
+ * Initialize function cache if first time through
+ */
+ if (sstate->fxprstate.func.fn_oid == InvalidOid)
+ {
+ init_fcache(opexpr->opfuncid, &sstate->fxprstate,
+ econtext->ecxt_per_query_memory);
+ Assert(!sstate->fxprstate.func.fn_retset);
+ }
+
+ /* Need to prep callinfo structure */
+ MemSet(&fcinfo, 0, sizeof(fcinfo));
+ fcinfo.flinfo = &(sstate->fxprstate.func);
+ argDone = ExecEvalFuncArgs(&fcinfo, sstate->fxprstate.args, econtext);
+ if (argDone != ExprSingleResult)
+ elog(ERROR, "op ANY/ALL (array) does not support set arguments");
+ Assert(fcinfo.nargs == 2);
+
+ /*
+ * If the array is NULL then we return NULL --- it's not very meaningful
+ * to do anything else, even if the operator isn't strict.
+ */
+ if (fcinfo.argnull[1])
+ {
+ *isNull = true;
+ return (Datum) 0;
+ }
+ /* Else okay to fetch and detoast the array */
+ arr = DatumGetArrayTypeP(fcinfo.arg[1]);
+
+ /*
+ * If the array is empty, we return either FALSE or TRUE per the useOr
+ * flag. This is correct even if the scalar is NULL; since we would
+ * evaluate the operator zero times, it matters not whether it would
+ * want to return NULL.
+ */
+ nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+ if (nitems <= 0)
+ return BoolGetDatum(!useOr);
+ /*
+ * If the scalar is NULL, and the function is strict, return NULL.
+ * This is just to avoid having to test for strictness inside the
+ * loop. (XXX but if arrays could have null elements, we'd need a
+ * test anyway.)
+ */
+ if (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict)
+ {
+ *isNull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * We arrange to look up info about the element type only
+ * once per series of calls, assuming the element type doesn't change
+ * underneath us.
+ */
+ if (sstate->element_type != ARR_ELEMTYPE(arr))
+ {
+ get_typlenbyvalalign(ARR_ELEMTYPE(arr),
+ &sstate->typlen,
+ &sstate->typbyval,
+ &sstate->typalign);
+ sstate->element_type = ARR_ELEMTYPE(arr);
+ }
+ typlen = sstate->typlen;
+ typbyval = sstate->typbyval;
+ typalign = sstate->typalign;
+
+ result = BoolGetDatum(!useOr);
+ resultnull = false;
+
+ /* Loop over the array elements */
+ s = (char *) ARR_DATA_PTR(arr);
+ for (i = 0; i < nitems; i++)
+ {
+ Datum elt;
+ Datum thisresult;
+
+ /* Get array element */
+ elt = fetch_att(s, typbyval, typlen);
+
+ s = att_addlength(s, typlen, PointerGetDatum(s));
+ s = (char *) att_align(s, typalign);
+
+ /* Call comparison function */
+ fcinfo.arg[1] = elt;
+ fcinfo.argnull[1] = false;
+ fcinfo.isnull = false;
+ thisresult = FunctionCallInvoke(&fcinfo);
+
+ /* Combine results per OR or AND semantics */
+ if (fcinfo.isnull)
+ resultnull = true;
+ else if (useOr)
+ {
+ if (DatumGetBool(thisresult))
+ {
+ result = BoolGetDatum(true);
+ resultnull = false;
+ break; /* needn't look at any more elements */
+ }
+ }
+ else
+ {
+ if (!DatumGetBool(thisresult))
+ {
+ result = BoolGetDatum(false);
+ resultnull = false;
+ break; /* needn't look at any more elements */
+ }
+ }
+ }
+
+ *isNull = resultnull;
+ return result;
+}
+
/* ----------------------------------------------------------------
* ExecEvalNot
* ExecEvalOr
@@ -2018,6 +2162,10 @@ ExecEvalExpr(ExprState *expression,
retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
isNull);
break;
+ case T_ScalarArrayOpExpr:
+ retDatum = ExecEvalScalarArrayOp((ScalarArrayOpExprState *) expression,
+ econtext, isNull);
+ break;
case T_BoolExpr:
{
BoolExprState *state = (BoolExprState *) expression;
@@ -2263,6 +2411,18 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) fstate;
}
break;
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+ ScalarArrayOpExprState *sstate = makeNode(ScalarArrayOpExprState);
+
+ sstate->fxprstate.args = (List *)
+ ExecInitExpr((Expr *) opexpr->args, parent);
+ sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */
+ sstate->element_type = InvalidOid; /* ditto */
+ state = (ExprState *) sstate;
+ }
+ break;
case T_BoolExpr:
{
BoolExpr *boolexpr = (BoolExpr *) node;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index b1884bb0d28..0caa7a55589 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.257 2003/06/27 14:45:28 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.258 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -803,6 +803,22 @@ _copyDistinctExpr(DistinctExpr *from)
}
/*
+ * _copyScalarArrayOpExpr
+ */
+static ScalarArrayOpExpr *
+_copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
+{
+ ScalarArrayOpExpr *newnode = makeNode(ScalarArrayOpExpr);
+
+ COPY_SCALAR_FIELD(opno);
+ COPY_SCALAR_FIELD(opfuncid);
+ COPY_SCALAR_FIELD(useOr);
+ COPY_NODE_FIELD(args);
+
+ return newnode;
+}
+
+/*
* _copyBoolExpr
*/
static BoolExpr *
@@ -2546,6 +2562,9 @@ copyObject(void *from)
case T_DistinctExpr:
retval = _copyDistinctExpr(from);
break;
+ case T_ScalarArrayOpExpr:
+ retval = _copyScalarArrayOpExpr(from);
+ break;
case T_BoolExpr:
retval = _copyBoolExpr(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6f30c8fabd8..0f6fbaae2ee 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.200 2003/06/27 14:45:28 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.201 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -288,6 +288,27 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
}
static bool
+_equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
+{
+ COMPARE_SCALAR_FIELD(opno);
+ /*
+ * Special-case opfuncid: it is allowable for it to differ if one
+ * node contains zero and the other doesn't. This just means that the
+ * one node isn't as far along in the parse/plan pipeline and hasn't
+ * had the opfuncid cache filled yet.
+ */
+ if (a->opfuncid != b->opfuncid &&
+ a->opfuncid != 0 &&
+ b->opfuncid != 0)
+ return false;
+
+ COMPARE_SCALAR_FIELD(useOr);
+ COMPARE_NODE_FIELD(args);
+
+ return true;
+}
+
+static bool
_equalBoolExpr(BoolExpr *a, BoolExpr *b)
{
COMPARE_SCALAR_FIELD(boolop);
@@ -1661,6 +1682,9 @@ equal(void *a, void *b)
case T_DistinctExpr:
retval = _equalDistinctExpr(a, b);
break;
+ case T_ScalarArrayOpExpr:
+ retval = _equalScalarArrayOpExpr(a, b);
+ break;
case T_BoolExpr:
retval = _equalBoolExpr(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 6c09574e13c..84dd85ad5f8 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -8,14 +8,14 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/nodeFuncs.c,v 1.21 2002/12/13 19:45:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/nodeFuncs.c,v 1.22 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/nodeFuncs.h"
-#include "utils/lsyscache.h"
+
static bool var_is_inner(Var *var);
@@ -72,20 +72,3 @@ var_is_rel(Var *var)
return (bool)
!(var_is_inner(var) || var_is_outer(var));
}
-
-/*****************************************************************************
- * OPER nodes
- *****************************************************************************/
-
-/*
- * set_opfuncid -
- *
- * Set the opfuncid (procedure OID) in an OpExpr node,
- * if it hasn't been set already.
- */
-void
-set_opfuncid(OpExpr *opexpr)
-{
- if (opexpr->opfuncid == InvalidOid)
- opexpr->opfuncid = get_opcode(opexpr->opno);
-}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e2b8cb789d9..3b4858ee16e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.210 2003/06/25 21:30:29 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.211 2003/06/29 00:33:43 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@@ -669,6 +669,17 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
}
static void
+_outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
+{
+ WRITE_NODE_TYPE("SCALARARRAYOPEXPR");
+
+ WRITE_OID_FIELD(opno);
+ WRITE_OID_FIELD(opfuncid);
+ WRITE_BOOL_FIELD(useOr);
+ WRITE_NODE_FIELD(args);
+}
+
+static void
_outBoolExpr(StringInfo str, BoolExpr *node)
{
char *opstr = NULL;
@@ -1333,6 +1344,16 @@ _outAExpr(StringInfo str, A_Expr *node)
case AEXPR_NOT:
appendStringInfo(str, " NOT");
break;
+ case AEXPR_OP_ANY:
+ appendStringInfo(str, " ");
+ WRITE_NODE_FIELD(name);
+ appendStringInfo(str, " ANY ");
+ break;
+ case AEXPR_OP_ALL:
+ appendStringInfo(str, " ");
+ WRITE_NODE_FIELD(name);
+ appendStringInfo(str, " ALL ");
+ break;
case AEXPR_DISTINCT:
appendStringInfo(str, " DISTINCT ");
WRITE_NODE_FIELD(name);
@@ -1619,6 +1640,9 @@ _outNode(StringInfo str, void *obj)
case T_DistinctExpr:
_outDistinctExpr(str, obj);
break;
+ case T_ScalarArrayOpExpr:
+ _outScalarArrayOpExpr(str, obj);
+ break;
case T_BoolExpr:
_outBoolExpr(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 1d2db3b37a9..b26a7a1ae8c 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.156 2003/06/25 21:30:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.157 2003/06/29 00:33:43 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@@ -511,6 +511,32 @@ _readDistinctExpr(void)
}
/*
+ * _readScalarArrayOpExpr
+ */
+static ScalarArrayOpExpr *
+_readScalarArrayOpExpr(void)
+{
+ READ_LOCALS(ScalarArrayOpExpr);
+
+ READ_OID_FIELD(opno);
+ READ_OID_FIELD(opfuncid);
+ /*
+ * The opfuncid is stored in the textual format primarily for debugging
+ * and documentation reasons. We want to always read it as zero to force
+ * it to be re-looked-up in the pg_operator entry. This ensures that
+ * stored rules don't have hidden dependencies on operators' functions.
+ * (We don't currently support an ALTER OPERATOR command, but might
+ * someday.)
+ */
+ local_node->opfuncid = InvalidOid;
+
+ READ_BOOL_FIELD(useOr);
+ READ_NODE_FIELD(args);
+
+ READ_DONE();
+}
+
+/*
* _readBoolExpr
*/
static BoolExpr *
@@ -951,6 +977,8 @@ parseNodeString(void)
return_value = _readOpExpr();
else if (MATCH("DISTINCTEXPR", 12))
return_value = _readDistinctExpr();
+ else if (MATCH("SCALARARRAYOPEXPR", 17))
+ return_value = _readScalarArrayOpExpr();
else if (MATCH("BOOLEXPR", 8))
return_value = _readBoolExpr();
else if (MATCH("SUBLINK", 7))
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 846d52140cc..67900d9e40f 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.58 2003/05/27 17:49:46 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.59 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -515,6 +515,12 @@ clause_selectivity(Query *root,
*/
s1 = (Selectivity) 0.5;
}
+ else if (IsA(clause, DistinctExpr) ||
+ IsA(clause, ScalarArrayOpExpr))
+ {
+ /* can we do better? */
+ s1 = (Selectivity) 0.5;
+ }
else if (IsA(clause, NullTest))
{
/* Use node specific selectivity calculation function */
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 21bc152ce6e..283987b63d5 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -49,7 +49,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.107 2003/02/16 02:30:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.108 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1473,6 +1473,11 @@ cost_qual_eval_walker(Node *node, QualCost *total)
{
total->per_tuple += cpu_operator_cost;
}
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ /* should charge more than 1 op cost, but how many? */
+ total->per_tuple += cpu_operator_cost * 10;
+ }
else if (IsA(node, SubLink))
{
/* This routine should not be applied to un-planned expressions */
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 4e17a85eb4b..1da186e0aaa 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,20 +9,19 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.92 2003/02/16 02:30:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.93 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-
#include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
+#include "utils/lsyscache.h"
typedef struct
@@ -61,6 +60,8 @@ static Node *replace_vars_with_subplan_refs(Node *node,
static Node *replace_vars_with_subplan_refs_mutator(Node *node,
replace_vars_with_subplan_refs_context *context);
static bool fix_opfuncids_walker(Node *node, void *context);
+static void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
+
/*****************************************************************************
*
@@ -284,6 +285,8 @@ fix_expr_references_walker(Node *node, void *context)
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ else if (IsA(node, ScalarArrayOpExpr))
+ set_sa_opfuncid((ScalarArrayOpExpr *) node);
else if (IsA(node, NullIfExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
else if (IsA(node, SubPlan))
@@ -738,7 +741,35 @@ fix_opfuncids_walker(Node *node, void *context)
set_opfuncid((OpExpr *) node);
else if (IsA(node, DistinctExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
+ else if (IsA(node, ScalarArrayOpExpr))
+ set_sa_opfuncid((ScalarArrayOpExpr *) node);
else if (IsA(node, NullIfExpr))
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
return expression_tree_walker(node, fix_opfuncids_walker, context);
}
+
+/*
+ * set_opfuncid
+ * Set the opfuncid (procedure OID) in an OpExpr node,
+ * if it hasn't been set already.
+ *
+ * Because of struct equivalence, this can also be used for
+ * DistinctExpr and NullIfExpr nodes.
+ */
+void
+set_opfuncid(OpExpr *opexpr)
+{
+ if (opexpr->opfuncid == InvalidOid)
+ opexpr->opfuncid = get_opcode(opexpr->opno);
+}
+
+/*
+ * set_sa_opfuncid
+ * As above, for ScalarArrayOpExpr nodes.
+ */
+static void
+set_sa_opfuncid(ScalarArrayOpExpr *opexpr)
+{
+ if (opexpr->opfuncid == InvalidOid)
+ opexpr->opfuncid = get_opcode(opexpr->opno);
+}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8b04066133c..54f2d7bd69b 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.141 2003/06/25 21:30:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.142 2003/06/29 00:33:43 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -25,8 +25,8 @@
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
@@ -461,6 +461,8 @@ expression_returns_set_walker(Node *node, void *context)
return false;
if (IsA(node, DistinctExpr))
return false;
+ if (IsA(node, ScalarArrayOpExpr))
+ return false;
if (IsA(node, BoolExpr))
return false;
if (IsA(node, SubLink))
@@ -563,6 +565,14 @@ contain_mutable_functions_walker(Node *node, void *context)
return true;
/* else fall through to check args */
}
+ if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ if (op_volatile(expr->opno) != PROVOLATILE_IMMUTABLE)
+ return true;
+ /* else fall through to check args */
+ }
if (IsA(node, NullIfExpr))
{
NullIfExpr *expr = (NullIfExpr *) node;
@@ -638,6 +648,14 @@ contain_volatile_functions_walker(Node *node, void *context)
return true;
/* else fall through to check args */
}
+ if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ if (op_volatile(expr->opno) == PROVOLATILE_VOLATILE)
+ return true;
+ /* else fall through to check args */
+ }
if (IsA(node, NullIfExpr))
{
NullIfExpr *expr = (NullIfExpr *) node;
@@ -711,6 +729,11 @@ contain_nonstrict_functions_walker(Node *node, void *context)
/* IS DISTINCT FROM is inherently non-strict */
return true;
}
+ if (IsA(node, ScalarArrayOpExpr))
+ {
+ /* inherently non-strict, consider null scalar and empty array */
+ return true;
+ }
if (IsA(node, BoolExpr))
{
BoolExpr *expr = (BoolExpr *) node;
@@ -2152,6 +2175,15 @@ expression_tree_walker(Node *node,
return true;
}
break;
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ if (expression_tree_walker((Node *) expr->args,
+ walker, context))
+ return true;
+ }
+ break;
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;
@@ -2510,6 +2542,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+ ScalarArrayOpExpr *newnode;
+
+ FLATCOPY(newnode, expr, ScalarArrayOpExpr);
+ MUTATE(newnode->args, expr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
case T_BoolExpr:
{
BoolExpr *expr = (BoolExpr *) node;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d3d9b4a5e30..a8df7c65e9d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.422 2003/06/27 14:45:28 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.423 2003/06/29 00:33:43 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -6051,6 +6051,13 @@ a_expr: c_expr { $$ = $1; }
n->subselect = $4;
$$ = (Node *)n;
}
+ | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
+ {
+ if ($3 == ANY_SUBLINK)
+ $$ = (Node *) makeA_Expr(AEXPR_OP_ANY, $2, $1, $5);
+ else
+ $$ = (Node *) makeA_Expr(AEXPR_OP_ALL, $2, $1, $5);
+ }
| UNIQUE select_with_parens %prec Op
{
/* Not sure how to get rid of the parentheses
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 6a90ab9197d..b985b190bac 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.153 2003/06/27 17:04:53 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.154 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -300,6 +300,34 @@ transformExpr(ParseState *pstate, Node *expr)
makeList1(rexpr));
}
break;
+ case AEXPR_OP_ANY:
+ {
+ Node *lexpr = transformExpr(pstate,
+ a->lexpr);
+ Node *rexpr = transformExpr(pstate,
+ a->rexpr);
+
+ result = (Node *) make_scalar_array_op(pstate,
+ a->name,
+ true,
+ lexpr,
+ rexpr);
+ }
+ break;
+ case AEXPR_OP_ALL:
+ {
+ Node *lexpr = transformExpr(pstate,
+ a->lexpr);
+ Node *rexpr = transformExpr(pstate,
+ a->rexpr);
+
+ result = (Node *) make_scalar_array_op(pstate,
+ a->name,
+ false,
+ lexpr,
+ rexpr);
+ }
+ break;
case AEXPR_DISTINCT:
{
Node *lexpr = transformExpr(pstate,
@@ -879,6 +907,7 @@ transformExpr(ParseState *pstate, Node *expr)
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
+ case T_ScalarArrayOpExpr:
case T_NullIfExpr:
case T_BoolExpr:
case T_FieldSelect:
@@ -1155,6 +1184,9 @@ exprType(Node *expr)
case T_DistinctExpr:
type = ((DistinctExpr *) expr)->opresulttype;
break;
+ case T_ScalarArrayOpExpr:
+ type = BOOLOID;
+ break;
case T_BoolExpr:
type = BOOLOID;
break;
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 983041eb45b..72327243310 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.67 2003/06/27 00:33:25 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.68 2003/06/29 00:33:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -737,6 +737,96 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree)
return result;
}
+/*
+ * make_scalar_array_op()
+ * Build expression tree for "scalar op ANY/ALL (array)" construct.
+ */
+Expr *
+make_scalar_array_op(ParseState *pstate, List *opname,
+ bool useOr,
+ Node *ltree, Node *rtree)
+{
+ Oid ltypeId,
+ rtypeId,
+ atypeId,
+ res_atypeId;
+ Operator tup;
+ Form_pg_operator opform;
+ Oid actual_arg_types[2];
+ Oid declared_arg_types[2];
+ List *args;
+ Oid rettype;
+ ScalarArrayOpExpr *result;
+
+ ltypeId = exprType(ltree);
+ atypeId = exprType(rtree);
+ /*
+ * The right-hand input of the operator will be the element type of
+ * the array. However, if we currently have just an untyped literal
+ * on the right, stay with that and hope we can resolve the operator.
+ */
+ if (atypeId == UNKNOWNOID)
+ rtypeId = UNKNOWNOID;
+ else
+ {
+ rtypeId = get_element_type(atypeId);
+ if (!OidIsValid(rtypeId))
+ elog(ERROR, "op ANY/ALL (array) requires array on right side");
+ }
+
+ /* Now resolve the operator */
+ tup = oper(opname, ltypeId, rtypeId, false);
+ opform = (Form_pg_operator) GETSTRUCT(tup);
+
+ args = makeList2(ltree, rtree);
+ actual_arg_types[0] = ltypeId;
+ actual_arg_types[1] = rtypeId;
+ declared_arg_types[0] = opform->oprleft;
+ declared_arg_types[1] = opform->oprright;
+
+ /*
+ * enforce consistency with ANYARRAY and ANYELEMENT argument and
+ * return types, possibly adjusting return type or declared_arg_types
+ * (which will be used as the cast destination by make_fn_arguments)
+ */
+ rettype = enforce_generic_type_consistency(actual_arg_types,
+ declared_arg_types,
+ 2,
+ opform->oprresult);
+
+ /*
+ * Check that operator result is boolean
+ */
+ if (rettype != BOOLOID)
+ elog(ERROR, "op ANY/ALL (array) requires operator to yield boolean");
+ if (get_func_retset(opform->oprcode))
+ elog(ERROR, "op ANY/ALL (array) requires operator not to return a set");
+
+ /*
+ * Now switch back to the array type on the right, arranging for
+ * any needed cast to be applied.
+ */
+ res_atypeId = get_array_type(declared_arg_types[1]);
+ if (!OidIsValid(res_atypeId))
+ elog(ERROR, "unable to find datatype for array of %s",
+ format_type_be(declared_arg_types[1]));
+ actual_arg_types[1] = atypeId;
+ declared_arg_types[1] = res_atypeId;
+
+ /* perform the necessary typecasting of arguments */
+ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
+
+ /* and build the expression node */
+ result = makeNode(ScalarArrayOpExpr);
+ result->opno = oprid(tup);
+ result->opfuncid = InvalidOid;
+ result->useOr = useOr;
+ result->args = args;
+
+ ReleaseSysCache(tup);
+
+ return (Expr *) result;
+}
/*
* make_op_expr()
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 27259044c19..63b174a39cf 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.142 2003/06/25 03:56:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.143 2003/06/29 00:33:44 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -2292,19 +2292,33 @@ get_rule_expr(Node *node, deparse_context *context,
{
DistinctExpr *expr = (DistinctExpr *) node;
List *args = expr->args;
+ Node *arg1 = (Node *) lfirst(args);
+ Node *arg2 = (Node *) lsecond(args);
- Assert(length(args) == 2);
- {
- /* binary operator */
- Node *arg1 = (Node *) lfirst(args);
- Node *arg2 = (Node *) lsecond(args);
+ appendStringInfoChar(buf, '(');
+ get_rule_expr(arg1, context, true);
+ appendStringInfo(buf, " IS DISTINCT FROM ");
+ get_rule_expr(arg2, context, true);
+ appendStringInfoChar(buf, ')');
+ }
+ break;
- appendStringInfoChar(buf, '(');
- get_rule_expr(arg1, context, true);
- appendStringInfo(buf, " IS DISTINCT FROM ");
- get_rule_expr(arg2, context, true);
- appendStringInfoChar(buf, ')');
- }
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+ List *args = expr->args;
+ Node *arg1 = (Node *) lfirst(args);
+ Node *arg2 = (Node *) lsecond(args);
+
+ appendStringInfoChar(buf, '(');
+ get_rule_expr(arg1, context, true);
+ appendStringInfo(buf, " %s %s (",
+ generate_operator_name(expr->opno,
+ exprType(arg1),
+ get_element_type(exprType(arg2))),
+ expr->useOr ? "ANY" : "ALL");
+ get_rule_expr(arg2, context, true);
+ appendStringInfo(buf, "))");
}
break;
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index e161bd1e593..0d69ac1083e 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.70 2003/06/25 21:30:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.71 2003/06/29 00:33:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1651,6 +1651,7 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
{
Node *expr;
List *args;
+ Oid argtype;
/*
* can't return anything useful if we have no FmgrInfo or if
@@ -1665,11 +1666,27 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
args = ((FuncExpr *) expr)->args;
else if (IsA(expr, OpExpr))
args = ((OpExpr *) expr)->args;
+ else if (IsA(expr, DistinctExpr))
+ args = ((DistinctExpr *) expr)->args;
+ else if (IsA(expr, ScalarArrayOpExpr))
+ args = ((ScalarArrayOpExpr *) expr)->args;
+ else if (IsA(expr, NullIfExpr))
+ args = ((NullIfExpr *) expr)->args;
else
return InvalidOid;
if (argnum < 0 || argnum >= length(args))
return InvalidOid;
- return exprType((Node *) nth(argnum, args));
+ argtype = exprType((Node *) nth(argnum, args));
+
+ /*
+ * special hack for ScalarArrayOpExpr: what the underlying function
+ * will actually get passed is the element type of the array.
+ */
+ if (IsA(expr, ScalarArrayOpExpr) &&
+ argnum == 1)
+ argtype = get_element_type(argtype);
+
+ return argtype;
}