aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
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;
}