diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index df677df37a227432d47e8dd32fe0ba18d2d28aa3..e15a44570b1acdbe252b906e647096c8db1bc81e 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.157 2003/06/27 00:33:25 tgl Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.158 2003/06/29 00:33:42 tgl Exp $ PostgreSQL documentation --> @@ -7457,52 +7457,14 @@ SELECT col1 FROM tab1 </sect2> <sect2> - <title><literal>IN</literal> (scalar form)</title> - -<synopsis> -<replaceable>expression</replaceable> IN (<replaceable>value</replaceable><optional>, ...</optional>) -</synopsis> - - <para> - The right-hand side of this form of <token>IN</token> is a parenthesized list - of scalar expressions. The result is <quote>true</> if the left-hand expression's - result is equal to any of the right-hand expressions. This is a shorthand - notation for - -<synopsis> -<replaceable>expression</replaceable> = <replaceable>value1</replaceable> -OR -<replaceable>expression</replaceable> = <replaceable>value2</replaceable> -OR -... -</synopsis> - </para> - - <para> - Note that if the left-hand expression yields null, or if there are - no equal right-hand values and at least one right-hand expression yields - null, the result of the <token>IN</token> construct will be null, not false. - This is in accordance with SQL's normal rules for Boolean combinations - of null values. - </para> - - <note> - <para> - This form of <token>IN</token> is not truly a subquery expression, but it - seems best to document it in the same place as subquery <token>IN</token>. - </para> - </note> - </sect2> - - <sect2> - <title><literal>IN</literal> (subquery form)</title> + <title><literal>IN</literal></title> <synopsis> <replaceable>expression</replaceable> IN (<replaceable>subquery</replaceable>) </synopsis> <para> - The right-hand side of this form of <token>IN</token> is a parenthesized + The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result. The result of <token>IN</token> is <quote>true</> if any equal subquery row is found. @@ -7538,7 +7500,7 @@ OR </para> <para> - As usual, null values in the expressions or subquery rows are combined per + As usual, null values in the rows are combined per the normal rules of SQL Boolean expressions. Two rows are considered equal if all their corresponding members are non-null and equal; the rows are unequal if any corresponding members are non-null and unequal; @@ -7549,55 +7511,14 @@ OR </sect2> <sect2> - <title><literal>NOT IN</literal> (scalar form)</title> - -<synopsis> -<replaceable>expression</replaceable> NOT IN (<replaceable>value</replaceable><optional>, ...</optional>) -</synopsis> - - <para> - The right-hand side of this form of <token>NOT IN</token> is a parenthesized list - of scalar expressions. The result is <quote>true</quote> if the left-hand expression's - result is unequal to all of the right-hand expressions. This is a shorthand - notation for - -<synopsis> -<replaceable>expression</replaceable> <> <replaceable>value1</replaceable> -AND -<replaceable>expression</replaceable> <> <replaceable>value2</replaceable> -AND -... -</synopsis> - </para> - - <para> - Note that if the left-hand expression yields null, or if there are - no equal right-hand values and at least one right-hand expression yields - null, the result of the <token>NOT IN</token> construct will be null, not true - as one might naively expect. - This is in accordance with SQL's normal rules for Boolean combinations - of null values. - </para> - - <tip> - <para> - <literal>x NOT IN y</literal> is equivalent to <literal>NOT (x IN y)</literal> in all - cases. However, null values are much more likely to trip up the novice when - working with <token>NOT IN</token> than when working with <token>IN</token>. - It's best to express your condition positively if possible. - </para> - </tip> - </sect2> - - <sect2> - <title><literal>NOT IN </literal>(subquery form)</title> + <title><literal>NOT IN </literal></title> <synopsis> <replaceable>expression</replaceable> NOT IN (<replaceable>subquery</replaceable>) </synopsis> <para> - The right-hand side of this form of <token>NOT IN</token> is a parenthesized + The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result. The result of <token>NOT IN</token> is <quote>true</> if only unequal subquery rows @@ -7633,7 +7554,7 @@ AND </para> <para> - As usual, null values in the expressions or subquery rows are combined per + As usual, null values in the rows are combined per the normal rules of SQL Boolean expressions. Two rows are considered equal if all their corresponding members are non-null and equal; the rows are unequal if any corresponding members are non-null and unequal; @@ -7652,7 +7573,7 @@ AND </synopsis> <para> - The right-hand side of this form of <token>ANY</token> is a parenthesized + The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result using the given <replaceable>operator</replaceable>, which must yield a Boolean @@ -7700,7 +7621,7 @@ AND </para> <para> - As usual, null values in the expressions or subquery rows are combined per + As usual, null values in the rows are combined per the normal rules of SQL Boolean expressions. Two rows are considered equal if all their corresponding members are non-null and equal; the rows are unequal if any corresponding members are non-null and unequal; @@ -7718,7 +7639,7 @@ AND </synopsis> <para> - The right-hand side of this form of <token>ALL</token> is a parenthesized + The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result using the given <replaceable>operator</replaceable>, which must yield a Boolean @@ -7765,7 +7686,7 @@ AND </para> <para> - As usual, null values in the expressions or subquery rows are combined per + As usual, null values in the rows are combined per the normal rules of SQL Boolean expressions. Two rows are considered equal if all their corresponding members are non-null and equal; the rows are unequal if any corresponding members are non-null and unequal; @@ -7780,24 +7701,201 @@ AND <synopsis> (<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</replaceable> (<replaceable>subquery</replaceable>) -(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</replaceable> (<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) </synopsis> <para> - The left-hand side is a list of scalar expressions. The right-hand side - can be either a list of scalar expressions of the same length, or a - parenthesized subquery, which must return exactly as many columns as there + The left-hand side is a list of scalar expressions. The right-hand side is + a parenthesized subquery, which must return exactly as many columns as there are expressions on the left-hand side. Furthermore, the subquery cannot return more than one row. (If it returns zero rows, the result is taken to be null.) The left-hand side is evaluated and compared row-wise to the - single subquery result row, or to the right-hand expression list. + single subquery result row. + Presently, only <literal>=</literal> and <literal><></literal> operators are allowed + in row-wise comparisons. + The result is <quote>true</> if the two rows are equal or unequal, respectively. + </para> + + <para> + As usual, null values in the rows are combined per + the normal rules of SQL Boolean expressions. Two rows are considered + equal if all their corresponding members are non-null and equal; the rows + are unequal if any corresponding members are non-null and unequal; + otherwise the result of the row comparison is unknown (null). + </para> + </sect2> + </sect1> + + + <sect1 id="functions-comparisons"> + <title>Row and Array Comparisons</title> + + <indexterm> + <primary>in</primary> + </indexterm> + + <indexterm> + <primary>not in</primary> + </indexterm> + + <indexterm> + <primary>any</primary> + </indexterm> + + <indexterm> + <primary>all</primary> + </indexterm> + + <indexterm> + <primary>some</primary> + </indexterm> + + <para> + This section describes several specialized constructs for making + multiple comparisons between groups of values. These forms are + syntactically related to the subquery forms of the previous section, + but do not involve subqueries. + The forms involving array subexpressions are + <productname>PostgreSQL</productname> extensions; the rest are + <acronym>SQL</acronym>-compliant. + All of the expression forms documented in this section return + Boolean (true/false) results. + </para> + + <sect2> + <title><literal>IN</literal></title> + +<synopsis> +<replaceable>expression</replaceable> IN (<replaceable>value</replaceable><optional>, ...</optional>) +</synopsis> + + <para> + The right-hand side is a parenthesized list + of scalar expressions. The result is <quote>true</> if the left-hand expression's + result is equal to any of the right-hand expressions. This is a shorthand + notation for + +<synopsis> +<replaceable>expression</replaceable> = <replaceable>value1</replaceable> +OR +<replaceable>expression</replaceable> = <replaceable>value2</replaceable> +OR +... +</synopsis> + </para> + + <para> + Note that if the left-hand expression yields null, or if there are + no equal right-hand values and at least one right-hand expression yields + null, the result of the <token>IN</token> construct will be null, not false. + This is in accordance with SQL's normal rules for Boolean combinations + of null values. + </para> + </sect2> + + <sect2> + <title><literal>NOT IN</literal></title> + +<synopsis> +<replaceable>expression</replaceable> NOT IN (<replaceable>value</replaceable><optional>, ...</optional>) +</synopsis> + + <para> + The right-hand side is a parenthesized list + of scalar expressions. The result is <quote>true</quote> if the left-hand expression's + result is unequal to all of the right-hand expressions. This is a shorthand + notation for + +<synopsis> +<replaceable>expression</replaceable> <> <replaceable>value1</replaceable> +AND +<replaceable>expression</replaceable> <> <replaceable>value2</replaceable> +AND +... +</synopsis> + </para> + + <para> + Note that if the left-hand expression yields null, or if there are + no equal right-hand values and at least one right-hand expression yields + null, the result of the <token>NOT IN</token> construct will be null, not true + as one might naively expect. + This is in accordance with SQL's normal rules for Boolean combinations + of null values. + </para> + + <tip> + <para> + <literal>x NOT IN y</literal> is equivalent to <literal>NOT (x IN y)</literal> in all + cases. However, null values are much more likely to trip up the novice when + working with <token>NOT IN</token> than when working with <token>IN</token>. + It's best to express your condition positively if possible. + </para> + </tip> + </sect2> + + <sect2> + <title><literal>ANY</literal>/<literal>SOME</literal> (array)</title> + +<synopsis> +<replaceable>expression</replaceable> <replaceable>operator</replaceable> ANY (<replaceable>array expression</replaceable>) +<replaceable>expression</replaceable> <replaceable>operator</replaceable> SOME (<replaceable>array expression</replaceable>) +</synopsis> + + <para> + The right-hand side is a parenthesized expression, which must yield an + array value. + The left-hand expression + is evaluated and compared to each element of the array using the + given <replaceable>operator</replaceable>, which must yield a Boolean + result. + The result of <token>ANY</token> is <quote>true</> if any true result is obtained. + The result is <quote>false</> if no true result is found (including the special + case where the array has zero elements). + </para> + + <para> + <token>SOME</token> is a synonym for <token>ANY</token>. + </para> + </sect2> + + <sect2> + <title><literal>ALL</literal> (array)</title> + +<synopsis> +<replaceable>expression</replaceable> <replaceable>operator</replaceable> ALL (<replaceable>array expression</replaceable>) +</synopsis> + + <para> + The right-hand side is a parenthesized expression, which must yield an + array value. + The left-hand expression + is evaluated and compared to each element of the array using the + given <replaceable>operator</replaceable>, which must yield a Boolean + result. + The result of <token>ALL</token> is <quote>true</> if all comparisons yield true + (including the special case where the array has zero elements). + The result is <quote>false</> if any false result is found. + </para> + </sect2> + + <sect2> + <title>Row-wise Comparison</title> + +<synopsis> +(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</replaceable> (<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) +</synopsis> + + <para> + Each side is a list of scalar expressions; the two lists must be + of the same length. Each side is evaluated and they are compared + row-wise. Presently, only <literal>=</literal> and <literal><></literal> operators are allowed in row-wise comparisons. The result is <quote>true</> if the two rows are equal or unequal, respectively. </para> <para> - As usual, null values in the expressions or subquery rows are combined per + As usual, null values in the rows are combined per the normal rules of SQL Boolean expressions. Two rows are considered equal if all their corresponding members are non-null and equal; the rows are unequal if any corresponding members are non-null and unequal; diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index d57f645b281a85dfb32e18e6c6a1d3b72c0a3c64..80a4cebf06721ea4f323164ee27182e72564c1aa 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 ee29c195ae72def772515ec820851b89b2842488..bba9a6e49c700b3494c193db9bae0422b058961c 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 b1884bb0d28988933d49ca609aa3dd62fd27887b..0caa7a55589ab8acc497aad9aa56fe56654095af 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 $ * *------------------------------------------------------------------------- */ @@ -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; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 6f30c8fabd8517e93db2946230d15c34b9bf446e..0f6fbaae2ee6c55f145370fb8cf206af5c6adf9c 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 $ * *------------------------------------------------------------------------- */ @@ -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; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 6c09574e13c060765ae7cfa9dbddb744af82d52f..84dd85ad5f88e0e520aee464b08dad75aae3a183 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 e2b8cb789d95f1320e9cb7c5933bd1acf1558beb..3b4858ee16ed99b58bc98a973e55dd08702030a3 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* @@ -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; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 1d2db3b37a9d3672e0bd16a3d0f68d07df9ad44b..b26a7a1ae8c56385fb27a7002a5c1edb1d1e385c 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 @@ -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)) diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 846d52140cca3403244cdacc55b8899401771081..67900d9e40f809d6db20ce1852c71f916df4fd4b 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 21bc152ce6e7ec19d239937661e51b9bdc4f3903..283987b63d53275846f7896ed546d97f68297638 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 4e17a85eb4b2cfd7d1b4e22d591d46ea3089df74..1da186e0aaac6b89395f04a948893006225a257b 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 8b04066133c6eb7a86db4dad1ab613caac4d10ab..54f2d7bd69b2a4c0e330ac539e8e6ce80cd250f7 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 d3d9b4a5e30b5ee944805f92491913de381d3d4f..a8df7c65e9dfe376d82464e09cc0c41ff7dc4426 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 6a90ab9197d04b7288c890f2b5cbafd2fb3b714f..b985b190bac285bca72f5289bdb40767aaa43ea1 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 983041eb45bfc806813356a5a1c97374febf01d7..72327243310244d4fdc4fb23172d5e56d3790f3d 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 27259044c1944395b2e0e51feb23720444932376..63b174a39cf9183eedbc1f39099927356647c7a1 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 e161bd1e59365f2292d0ac14d2085ee8e5d91c08..0d69ac1083e600dcf5c35b24e376ec567ac86678 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.70 2003/06/25 21:30:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.71 2003/06/29 00:33:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1651,6 +1651,7 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum) { Node *expr; List *args; + Oid argtype; /* * can't return anything useful if we have no FmgrInfo or if @@ -1665,11 +1666,27 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum) args = ((FuncExpr *) expr)->args; else if (IsA(expr, OpExpr)) args = ((OpExpr *) expr)->args; + else if (IsA(expr, DistinctExpr)) + args = ((DistinctExpr *) expr)->args; + else if (IsA(expr, ScalarArrayOpExpr)) + args = ((ScalarArrayOpExpr *) expr)->args; + else if (IsA(expr, NullIfExpr)) + args = ((NullIfExpr *) expr)->args; else return InvalidOid; if (argnum < 0 || argnum >= length(args)) return InvalidOid; - return exprType((Node *) nth(argnum, args)); + argtype = exprType((Node *) nth(argnum, args)); + + /* + * special hack for ScalarArrayOpExpr: what the underlying function + * will actually get passed is the element type of the array. + */ + if (IsA(expr, ScalarArrayOpExpr) && + argnum == 1) + argtype = get_element_type(argtype); + + return argtype; } diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index e8ce5d131a5b6e9b6d374b9d120247bd18294b02..7d73598d55baaff5e34be8ce444812b233982ad4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.201 2003/06/27 00:33:25 tgl Exp $ + * $Id: catversion.h,v 1.202 2003/06/29 00:33:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200306261 +#define CATALOG_VERSION_NO 200306281 #endif diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 47879296c0eb8e1b16c4f5c17bcadf51c77e71ba..c6f1342343816b3ef488e5402aae725f55f64cfc 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execnodes.h,v 1.99 2003/06/22 22:04:55 tgl Exp $ + * $Id: execnodes.h,v 1.100 2003/06/29 00:33:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 * ---------------- diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h index 99e1a62542c965a453b9c0406ee0614795b3051e..f0805bfa1f9c6ce73de7c56854e7aae928ac0010 100644 --- a/src/include/nodes/nodeFuncs.h +++ b/src/include/nodes/nodeFuncs.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: nodeFuncs.h,v 1.18 2002/12/12 15:49:40 tgl Exp $ + * $Id: nodeFuncs.h,v 1.19 2003/06/29 00:33:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,5 @@ extern bool single_node(Node *node); extern bool var_is_outer(Var *var); extern bool var_is_rel(Var *var); -extern void set_opfuncid(OpExpr *opexpr); #endif /* NODEFUNCS_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index af87482e42a3539d33cbada6108fa90df4e4e236..2592270c25848d673ebb7148ec343f571ec6c1ec 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: nodes.h,v 1.142 2003/06/25 04:19:24 momjian Exp $ + * $Id: nodes.h,v 1.143 2003/06/29 00:33:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -105,6 +105,7 @@ typedef enum NodeTag T_FuncExpr, T_OpExpr, T_DistinctExpr, + T_ScalarArrayOpExpr, T_BoolExpr, T_SubLink, T_SubPlan, @@ -135,6 +136,7 @@ typedef enum NodeTag T_AggrefExprState, T_ArrayRefExprState, T_FuncExprState, + T_ScalarArrayOpExprState, T_BoolExprState, T_SubPlanState, T_CaseExprState, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 527c56b9edae26767fad5a6ba25328232ea6c08d..9101ba1e1776d29a3f6453d45f81f84d300ccff5 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.241 2003/06/27 14:45:31 petere Exp $ + * $Id: parsenodes.h,v 1.242 2003/06/29 00:33:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -170,6 +170,8 @@ typedef enum A_Expr_Kind AEXPR_AND, /* booleans - name field is unused */ AEXPR_OR, AEXPR_NOT, + AEXPR_OP_ANY, /* scalar op ANY (array) */ + AEXPR_OP_ALL, /* scalar op ALL (array) */ AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */ AEXPR_NULLIF, /* NULLIF - name must be "=" */ AEXPR_OF /* IS (not) OF - name must be "=" or "!=" */ diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 23dff8ed622e06307ba507280b1c42a6445f0c43..57827ee76fe83e20770ba9704f419899c8809583 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.85 2003/06/25 21:30:33 momjian Exp $ + * $Id: primnodes.h,v 1.86 2003/06/29 00:33:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 * diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 35a85e311abf325185210153c7283bea5b116964..106e9d1ce4b549cc4fe7ef873660dabe2bb59b6d 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: planmain.h,v 1.70 2003/05/06 00:20:33 tgl Exp $ + * $Id: planmain.h,v 1.71 2003/06/29 00:33:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -69,5 +69,6 @@ extern void process_implied_equality(Query *root, */ extern void set_plan_references(Plan *plan, List *rtable); extern void fix_opfuncids(Node *node); +extern void set_opfuncid(OpExpr *opexpr); #endif /* PLANMAIN_H */ diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h index 66e1258364522c272e7872f5770a46f25b6e331f..3fd5eea2a8a8f60197b3660bfd8148d758d58b76 100644 --- a/src/include/parser/parse_oper.h +++ b/src/include/parser/parse_oper.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_oper.h,v 1.28 2003/06/27 00:33:26 tgl Exp $ + * $Id: parse_oper.h,v 1.29 2003/06/29 00:33:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -54,6 +54,9 @@ extern Oid oprfuncid(Operator op); /* Build expression tree for an operator invocation */ extern Expr *make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree); +extern Expr *make_scalar_array_op(ParseState *pstate, List *opname, + bool useOr, + Node *ltree, Node *rtree); extern Expr *make_op_expr(ParseState *pstate, Operator op, Node *ltree, Node *rtree, Oid ltypeId, Oid rtypeId); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index efb423541a9d64ae77bf51de3b38153a46f0088b..54f2b3a88037bc29e756fb44c2cdbf3439a69d2e 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.86 2003/04/24 21:16:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.87 2003/06/29 00:33:44 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -3520,6 +3520,16 @@ exec_simple_check_node(Node *node) return TRUE; } + case T_ScalarArrayOpExpr: + { + ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; + + if (!exec_simple_check_node((Node *) expr->args)) + return FALSE; + + return TRUE; + } + case T_BoolExpr: { BoolExpr *expr = (BoolExpr *) node; diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 9822294d19af9710131f64beaaaf579bfc12b8ff..a18d345475ba286fcc2eb41cf254a4d05c9ca057 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -294,6 +294,68 @@ SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}" {{{{{{a,bb,ccc}}}}}} (1 row) +-- scalar op any/all (array) +select 33 = any ('{1,2,3}'); + ?column? +---------- + f +(1 row) + +select 33 = any ('{1,2,33}'); + ?column? +---------- + t +(1 row) + +select 33 = all ('{1,2,33}'); + ?column? +---------- + f +(1 row) + +select 33 >= all ('{1,2,33}'); + ?column? +---------- + t +(1 row) + +-- boundary cases +select null::int >= all ('{1,2,33}'); + ?column? +---------- + +(1 row) + +select null::int >= all ('{}'); + ?column? +---------- + t +(1 row) + +select null::int >= any ('{}'); + ?column? +---------- + f +(1 row) + +-- cross-datatype +select 33.4 = any (array[1,2,3]); + ?column? +---------- + f +(1 row) + +select 33.4 > all (array[1,2,3]); + ?column? +---------- + t +(1 row) + +-- errors +select 33 * any ('{1,2,3}'); +ERROR: op ANY/ALL (array) requires operator to yield boolean +select 33 * any (44); +ERROR: op ANY/ALL (array) requires array on right side -- test indexes on arrays create temp table arr_tbl (f1 int[] unique); NOTICE: CREATE TABLE / UNIQUE will create implicit index 'arr_tbl_f1_key' for table 'arr_tbl' diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index 8b18ffb9eb83bc46df5c6876cb395b009313225c..c3bcdd5e3a66e6441ca0c3ac87519aeb105942c3 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -154,6 +154,22 @@ SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE"; SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"; +-- scalar op any/all (array) +select 33 = any ('{1,2,3}'); +select 33 = any ('{1,2,33}'); +select 33 = all ('{1,2,33}'); +select 33 >= all ('{1,2,33}'); +-- boundary cases +select null::int >= all ('{1,2,33}'); +select null::int >= all ('{}'); +select null::int >= any ('{}'); +-- cross-datatype +select 33.4 = any (array[1,2,3]); +select 33.4 > all (array[1,2,3]); +-- errors +select 33 * any ('{1,2,3}'); +select 33 * any (44); + -- test indexes on arrays create temp table arr_tbl (f1 int[] unique); insert into arr_tbl values ('{1,2,3}');