Fix array coercion expressions to ensure that the correct volatility is
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 27 Mar 2007 23:21:12 +0000 (23:21 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 27 Mar 2007 23:21:12 +0000 (23:21 +0000)
seen by code inspecting the expression.  The best way to do this seems
to be to drop the original representation as a function invocation, and
instead make a special expression node type that represents applying
the element-type coercion function to each array element.  In this way
the element function is exposed and will be checked for volatility.
Per report from Guillaume Smet.

24 files changed:
src/backend/catalog/dependency.c
src/backend/executor/execQual.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/adt/ri_triggers.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/fmgr/fmgr.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/primnodes.h
src/include/parser/parse_coerce.h
src/include/utils/array.h
src/pl/plpgsql/src/pl_exec.c

index 40591fd368097099e70a324d656eac859851a985..e1d5101ae1e5ceb1ffd19d0efe7d0de2442aa3dc 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.64 2007/02/14 01:58:56 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.65 2007/03/27 23:21:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1315,6 +1315,17 @@ find_expr_references_walker(Node *node,
                add_object_address(OCLASS_TYPE, relab->resulttype, 0,
                                                   context->addrs);
        }
+       if (IsA(node, ArrayCoerceExpr))
+       {
+               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+
+               if (OidIsValid(acoerce->elemfuncid))
+                       add_object_address(OCLASS_PROC, acoerce->elemfuncid, 0,
+                                                          context->addrs);
+               add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
+                                                  context->addrs);
+               /* fall through to examine arguments */
+       }
        if (IsA(node, ConvertRowtypeExpr))
        {
                ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;
index 784bbac231f127272171a762f36330f7a2229a57..94e6829f544cf7827110efa484de127b77f2ae6b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.215 2007/02/27 23:48:07 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.216 2007/03/27 23:21:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -145,6 +145,9 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate,
 static Datum ExecEvalRelabelType(GenericExprState *exprstate,
                                        ExprContext *econtext,
                                        bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
+                                                                        ExprContext *econtext,
+                                                                        bool *isNull, ExprDoneCond *isDone);
 
 
 /* ----------------------------------------------------------------
@@ -3501,6 +3504,83 @@ ExecEvalRelabelType(GenericExprState *exprstate,
        return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
 }
 
+/* ----------------------------------------------------------------
+ *             ExecEvalArrayCoerceExpr
+ *
+ *             Evaluate an ArrayCoerceExpr node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
+                                               ExprContext *econtext,
+                                               bool *isNull, ExprDoneCond *isDone)
+{
+       ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr;
+       Datum           result;
+       ArrayType  *array;
+       FunctionCallInfoData locfcinfo;
+
+       result = ExecEvalExpr(astate->arg, econtext, isNull, isDone);
+
+       if (isDone && *isDone == ExprEndResult)
+               return result;                  /* nothing to do */
+       if (*isNull)
+               return result;                  /* nothing to do */
+
+       /*
+        * If it's binary-compatible, modify the element type in the array header,
+        * but otherwise leave the array as we received it.
+        */
+       if (!OidIsValid(acoerce->elemfuncid))
+       {
+               /* Detoast input array if necessary, and copy in any case */
+               array = DatumGetArrayTypePCopy(result);
+               ARR_ELEMTYPE(array) = astate->resultelemtype;
+               PG_RETURN_ARRAYTYPE_P(array);
+       }
+
+       /* Detoast input array if necessary, but don't make a useless copy */
+       array = DatumGetArrayTypeP(result);
+
+       /* Initialize function cache if first time through */
+       if (astate->elemfunc.fn_oid == InvalidOid)
+       {
+               AclResult       aclresult;
+
+               /* Check permission to call function */
+               aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
+                                                                        ACL_EXECUTE);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error(aclresult, ACL_KIND_PROC,
+                                                  get_func_name(acoerce->elemfuncid));
+
+               /* Set up the primary fmgr lookup information */
+               fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
+                                         econtext->ecxt_per_query_memory);
+
+               /* Initialize additional info */
+               astate->elemfunc.fn_expr = (Node *) acoerce;
+       }
+
+       /*
+        * Use array_map to apply the function to each array element.
+        *
+        * We pass on the desttypmod and isExplicit flags whether or not the
+        * function wants them.
+        */
+       InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3,
+                                                        NULL, NULL);
+       locfcinfo.arg[0] = PointerGetDatum(array);
+       locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
+       locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
+       locfcinfo.argnull[0] = false;
+       locfcinfo.argnull[1] = false;
+       locfcinfo.argnull[2] = false;
+
+       return array_map(&locfcinfo, ARR_ELEMTYPE(array), astate->resultelemtype,
+                                        astate->amstate);
+}
+
 
 /*
  * ExecEvalExprSwitchContext
@@ -3770,6 +3850,26 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) gstate;
                        }
                        break;
+               case T_ArrayCoerceExpr:
+                       {
+                               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+                               ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
+
+                               astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
+                               astate->arg = ExecInitExpr(acoerce->arg, parent);
+                               astate->resultelemtype = get_element_type(acoerce->resulttype);
+                               if (astate->resultelemtype == InvalidOid)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                        errmsg("target type is not an array")));
+                               /* Arrays over domains aren't supported yet */
+                               Assert(getBaseType(astate->resultelemtype) ==
+                                          astate->resultelemtype);
+                               astate->elemfunc.fn_oid = InvalidOid;   /* not initialized */
+                               astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
+                               state = (ExprState *) astate;
+                       }
+                       break;
                case T_ConvertRowtypeExpr:
                        {
                                ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
index f5dcc5b274e4a2ea35ea653979a69f8aace32a9f..198a583f88f4570fd7a0f0be4b2c7b21495f9ef6 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.371 2007/03/17 00:11:03 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.372 2007/03/27 23:21:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1020,6 +1020,24 @@ _copyRelabelType(RelabelType *from)
        return newnode;
 }
 
+/*
+ * _copyArrayCoerceExpr
+ */
+static ArrayCoerceExpr *
+_copyArrayCoerceExpr(ArrayCoerceExpr *from)
+{
+       ArrayCoerceExpr   *newnode = makeNode(ArrayCoerceExpr);
+
+       COPY_NODE_FIELD(arg);
+       COPY_SCALAR_FIELD(elemfuncid);
+       COPY_SCALAR_FIELD(resulttype);
+       COPY_SCALAR_FIELD(resulttypmod);
+       COPY_SCALAR_FIELD(isExplicit);
+       COPY_SCALAR_FIELD(coerceformat);
+
+       return newnode;
+}
+
 /*
  * _copyConvertRowtypeExpr
  */
@@ -3067,6 +3085,9 @@ copyObject(void *from)
                case T_RelabelType:
                        retval = _copyRelabelType(from);
                        break;
+               case T_ArrayCoerceExpr:
+                       retval = _copyArrayCoerceExpr(from);
+                       break;
                case T_ConvertRowtypeExpr:
                        retval = _copyConvertRowtypeExpr(from);
                        break;
index 586397761191f74a7333463479b0b542dff760e9..977f121bc42ecf5f1e3c42c7b99943c851133bcc 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.302 2007/03/17 00:11:03 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.303 2007/03/27 23:21:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -359,6 +359,27 @@ _equalRelabelType(RelabelType *a, RelabelType *b)
        return true;
 }
 
+static bool
+_equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b)
+{
+       COMPARE_NODE_FIELD(arg);
+       COMPARE_SCALAR_FIELD(elemfuncid);
+       COMPARE_SCALAR_FIELD(resulttype);
+       COMPARE_SCALAR_FIELD(resulttypmod);
+       COMPARE_SCALAR_FIELD(isExplicit);
+
+       /*
+        * Special-case COERCE_DONTCARE, so that planner can build coercion nodes
+        * that are equal() to both explicit and implicit coercions.
+        */
+       if (a->coerceformat != b->coerceformat &&
+               a->coerceformat != COERCE_DONTCARE &&
+               b->coerceformat != COERCE_DONTCARE)
+               return false;
+
+       return true;
+}
+
 static bool
 _equalConvertRowtypeExpr(ConvertRowtypeExpr *a, ConvertRowtypeExpr *b)
 {
@@ -2013,6 +2034,9 @@ equal(void *a, void *b)
                case T_RelabelType:
                        retval = _equalRelabelType(a, b);
                        break;
+               case T_ArrayCoerceExpr:
+                       retval = _equalArrayCoerceExpr(a, b);
+                       break;
                case T_ConvertRowtypeExpr:
                        retval = _equalConvertRowtypeExpr(a, b);
                        break;
index 083f016cf8fb8893ce948f7a6028b74584446759..49202f8664a3df2805ab403a9e44caa7cbbfa89d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.304 2007/03/17 00:11:03 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.305 2007/03/27 23:21:09 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -869,6 +869,19 @@ _outRelabelType(StringInfo str, RelabelType *node)
        WRITE_ENUM_FIELD(relabelformat, CoercionForm);
 }
 
+static void
+_outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node)
+{
+       WRITE_NODE_TYPE("ARRAYCOERCEEXPR");
+
+       WRITE_NODE_FIELD(arg);
+       WRITE_OID_FIELD(elemfuncid);
+       WRITE_OID_FIELD(resulttype);
+       WRITE_INT_FIELD(resulttypmod);
+       WRITE_BOOL_FIELD(isExplicit);
+       WRITE_ENUM_FIELD(coerceformat, CoercionForm);
+}
+
 static void
 _outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node)
 {
@@ -2149,6 +2162,9 @@ _outNode(StringInfo str, void *obj)
                        case T_RelabelType:
                                _outRelabelType(str, obj);
                                break;
+                       case T_ArrayCoerceExpr:
+                               _outArrayCoerceExpr(str, obj);
+                               break;
                        case T_ConvertRowtypeExpr:
                                _outConvertRowtypeExpr(str, obj);
                                break;
index 70612c864f757471439dd4f34d95c2afd425fa5e..1f3b81e275f335b1117bee7d4be8c3b975db574f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.204 2007/03/17 00:11:04 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.205 2007/03/27 23:21:09 tgl Exp $
  *
  * NOTES
  *       Path and Plan nodes do not have any readfuncs support, because we
@@ -584,6 +584,24 @@ _readRelabelType(void)
        READ_DONE();
 }
 
+/*
+ * _readArrayCoerceExpr
+ */
+static ArrayCoerceExpr *
+_readArrayCoerceExpr(void)
+{
+       READ_LOCALS(ArrayCoerceExpr);
+
+       READ_NODE_FIELD(arg);
+       READ_OID_FIELD(elemfuncid);
+       READ_OID_FIELD(resulttype);
+       READ_INT_FIELD(resulttypmod);
+       READ_BOOL_FIELD(isExplicit);
+       READ_ENUM_FIELD(coerceformat, CoercionForm);
+
+       READ_DONE();
+}
+
 /*
  * _readConvertRowtypeExpr
  */
@@ -1024,6 +1042,8 @@ parseNodeString(void)
                return_value = _readFieldStore();
        else if (MATCH("RELABELTYPE", 11))
                return_value = _readRelabelType();
+       else if (MATCH("ARRAYCOERCEEXPR", 15))
+               return_value = _readArrayCoerceExpr();
        else if (MATCH("CONVERTROWTYPEEXPR", 18))
                return_value = _readConvertRowtypeExpr();
        else if (MATCH("CASE", 4))
index 3dbb3bd802dbd8cd7cef7b14d27c8eb62f97a604..ff5bb7833727a95eb2eb28b9f35bd74b66a433d5 100644 (file)
@@ -54,7 +54,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.178 2007/02/22 22:00:24 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.179 2007/03/27 23:21:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1891,6 +1891,15 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
                context->total.per_tuple += get_func_cost(saop->opfuncid) *
                        cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
        }
+       else if (IsA(node, ArrayCoerceExpr))
+       {
+               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+               Node       *arraynode = (Node *) acoerce->arg;
+
+               if (OidIsValid(acoerce->elemfuncid))
+                       context->total.per_tuple += get_func_cost(acoerce->elemfuncid) *
+                               cpu_operator_cost * estimate_array_length(arraynode);
+       }
        else if (IsA(node, RowCompareExpr))
        {
                /* Conservatively assume we will check all the columns */
index 1fa45e02a9c2b4f10b1ea9ba9590ae9afada5b0e..67652fbfdecb5bbbb476fbd7454778aee58109c5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.239 2007/03/17 00:11:04 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.240 2007/03/27 23:21:09 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -710,7 +710,7 @@ contain_mutable_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, OpExpr))
+       else if (IsA(node, OpExpr))
        {
                OpExpr     *expr = (OpExpr *) node;
 
@@ -718,7 +718,7 @@ contain_mutable_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, DistinctExpr))
+       else if (IsA(node, DistinctExpr))
        {
                DistinctExpr *expr = (DistinctExpr *) node;
 
@@ -726,7 +726,7 @@ contain_mutable_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, ScalarArrayOpExpr))
+       else if (IsA(node, ScalarArrayOpExpr))
        {
                ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
 
@@ -734,7 +734,16 @@ contain_mutable_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, NullIfExpr))
+       else if (IsA(node, ArrayCoerceExpr))
+       {
+               ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
+
+               if (OidIsValid(expr->elemfuncid) &&
+                       func_volatile(expr->elemfuncid) != PROVOLATILE_IMMUTABLE)
+                       return true;
+               /* else fall through to check args */
+       }
+       else if (IsA(node, NullIfExpr))
        {
                NullIfExpr *expr = (NullIfExpr *) node;
 
@@ -742,7 +751,7 @@ contain_mutable_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, RowCompareExpr))
+       else if (IsA(node, RowCompareExpr))
        {
                RowCompareExpr *rcexpr = (RowCompareExpr *) node;
                ListCell   *opid;
@@ -793,7 +802,7 @@ contain_volatile_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, OpExpr))
+       else if (IsA(node, OpExpr))
        {
                OpExpr     *expr = (OpExpr *) node;
 
@@ -801,7 +810,7 @@ contain_volatile_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, DistinctExpr))
+       else if (IsA(node, DistinctExpr))
        {
                DistinctExpr *expr = (DistinctExpr *) node;
 
@@ -809,7 +818,7 @@ contain_volatile_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, ScalarArrayOpExpr))
+       else if (IsA(node, ScalarArrayOpExpr))
        {
                ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
 
@@ -817,7 +826,16 @@ contain_volatile_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, NullIfExpr))
+       else if (IsA(node, ArrayCoerceExpr))
+       {
+               ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
+
+               if (OidIsValid(expr->elemfuncid) &&
+                       func_volatile(expr->elemfuncid) == PROVOLATILE_VOLATILE)
+                       return true;
+               /* else fall through to check args */
+       }
+       else if (IsA(node, NullIfExpr))
        {
                NullIfExpr *expr = (NullIfExpr *) node;
 
@@ -825,7 +843,7 @@ contain_volatile_functions_walker(Node *node, void *context)
                        return true;
                /* else fall through to check args */
        }
-       if (IsA(node, RowCompareExpr))
+       else if (IsA(node, RowCompareExpr))
        {
                /* RowCompare probably can't have volatile ops, but check anyway */
                RowCompareExpr *rcexpr = (RowCompareExpr *) node;
@@ -932,6 +950,7 @@ contain_nonstrict_functions_walker(Node *node, void *context)
        }
        if (IsA(node, SubPlan))
                return true;
+       /* ArrayCoerceExpr is strict at the array level, regardless of elemfunc */
        if (IsA(node, FieldStore))
                return true;
        if (IsA(node, CaseExpr))
@@ -1105,6 +1124,13 @@ find_nonnullable_rels_walker(Node *node, bool top_level)
 
                result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);
        }
+       else if (IsA(node, ArrayCoerceExpr))
+       {
+               /* ArrayCoerceExpr is strict at the array level */
+               ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
+
+               result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);
+       }
        else if (IsA(node, ConvertRowtypeExpr))
        {
                /* not clear this is useful, but it can't hurt */
@@ -1460,6 +1486,13 @@ strip_implicit_coercions(Node *node)
                if (r->relabelformat == COERCE_IMPLICIT_CAST)
                        return strip_implicit_coercions((Node *) r->arg);
        }
+       else if (IsA(node, ArrayCoerceExpr))
+       {
+               ArrayCoerceExpr *c = (ArrayCoerceExpr *) node;
+
+               if (c->coerceformat == COERCE_IMPLICIT_CAST)
+                       return strip_implicit_coercions((Node *) c->arg);
+       }
        else if (IsA(node, ConvertRowtypeExpr))
        {
                ConvertRowtypeExpr *c = (ConvertRowtypeExpr *) node;
@@ -1504,6 +1537,8 @@ set_coercionform_dontcare_walker(Node *node, void *context)
                ((FuncExpr *) node)->funcformat = COERCE_DONTCARE;
        else if (IsA(node, RelabelType))
                ((RelabelType *) node)->relabelformat = COERCE_DONTCARE;
+       else if (IsA(node, ArrayCoerceExpr))
+               ((ArrayCoerceExpr *) node)->coerceformat = COERCE_DONTCARE;
        else if (IsA(node, ConvertRowtypeExpr))
                ((ConvertRowtypeExpr *) node)->convertformat = COERCE_DONTCARE;
        else if (IsA(node, RowExpr))
@@ -3436,6 +3471,8 @@ expression_tree_walker(Node *node,
                        break;
                case T_RelabelType:
                        return walker(((RelabelType *) node)->arg, context);
+               case T_ArrayCoerceExpr:
+                       return walker(((ArrayCoerceExpr *) node)->arg, context);
                case T_ConvertRowtypeExpr:
                        return walker(((ConvertRowtypeExpr *) node)->arg, context);
                case T_CaseExpr:
@@ -3901,6 +3938,16 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_ArrayCoerceExpr:
+                       {
+                               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+                               ArrayCoerceExpr *newnode;
+
+                               FLATCOPY(newnode, acoerce, ArrayCoerceExpr);
+                               MUTATE(newnode->arg, acoerce->arg, Expr *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_ConvertRowtypeExpr:
                        {
                                ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node;
index 00ce2a9927f433598a4b0b8e9438e55982d6537d..fbc83870a5c43815731c6ffa68c06e1a0e8320da 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.151 2007/03/17 00:11:04 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.152 2007/03/27 23:21:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,8 @@ static Node *coerce_type_typmod(Node *node,
                                   CoercionForm cformat, bool isExplicit,
                                   bool hideInputCoercion);
 static void hide_coercion_node(Node *node);
-static Node *build_coercion_expression(Node *node, Oid funcId,
+static Node *build_coercion_expression(Node *node,
+                                                 Oid funcId, bool arrayCoerce,
                                                  Oid targetTypeId, int32 targetTypMod,
                                                  CoercionForm cformat, bool isExplicit);
 static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
@@ -121,6 +122,7 @@ coerce_type(ParseState *pstate, Node *node,
 {
        Node       *result;
        Oid                     funcId;
+       bool            arrayCoerce;
 
        if (targetTypeId == inputTypeId ||
                node == NULL)
@@ -277,9 +279,9 @@ coerce_type(ParseState *pstate, Node *node,
                return (Node *) param;
        }
        if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
-                                                         &funcId))
+                                                         &funcId, &arrayCoerce))
        {
-               if (OidIsValid(funcId))
+               if (OidIsValid(funcId) || arrayCoerce)
                {
                        /*
                         * Generate an expression tree representing run-time application
@@ -294,7 +296,7 @@ coerce_type(ParseState *pstate, Node *node,
                        baseTypeMod = targetTypeMod;
                        baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
 
-                       result = build_coercion_expression(node, funcId,
+                       result = build_coercion_expression(node, funcId, arrayCoerce,
                                                                                           baseTypeId, baseTypeMod,
                                                                                           cformat,
                                                                                  (cformat != COERCE_IMPLICIT_CAST));
@@ -394,6 +396,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
                Oid                     inputTypeId = input_typeids[i];
                Oid                     targetTypeId = target_typeids[i];
                Oid                     funcId;
+               bool            arrayCoerce;
 
                /* no problem if same type */
                if (inputTypeId == targetTypeId)
@@ -423,7 +426,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
                 * both binary-compatible and coercion-function cases.
                 */
                if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
-                                                                 &funcId))
+                                                                 &funcId, &arrayCoerce))
                        continue;
 
                /*
@@ -564,6 +567,7 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
                                   bool hideInputCoercion)
 {
        Oid                     funcId;
+       bool            arrayCoerce;
 
        /*
         * A negative typmod is assumed to mean that no coercion is wanted. Also,
@@ -572,15 +576,14 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
        if (targetTypMod < 0 || targetTypMod == exprTypmod(node))
                return node;
 
-       funcId = find_typmod_coercion_function(targetTypeId);
-
-       if (OidIsValid(funcId))
+       if (find_typmod_coercion_function(targetTypeId,
+                                                                         &funcId, &arrayCoerce))
        {
                /* Suppress display of nested coercion steps */
                if (hideInputCoercion)
                        hide_coercion_node(node);
 
-               node = build_coercion_expression(node, funcId,
+               node = build_coercion_expression(node, funcId, arrayCoerce,
                                                                                 targetTypeId, targetTypMod,
                                                                                 cformat, isExplicit);
        }
@@ -605,6 +608,8 @@ hide_coercion_node(Node *node)
                ((FuncExpr *) node)->funcformat = COERCE_IMPLICIT_CAST;
        else if (IsA(node, RelabelType))
                ((RelabelType *) node)->relabelformat = COERCE_IMPLICIT_CAST;
+       else if (IsA(node, ArrayCoerceExpr))
+               ((ArrayCoerceExpr *) node)->coerceformat = COERCE_IMPLICIT_CAST;
        else if (IsA(node, ConvertRowtypeExpr))
                ((ConvertRowtypeExpr *) node)->convertformat = COERCE_IMPLICIT_CAST;
        else if (IsA(node, RowExpr))
@@ -617,74 +622,106 @@ hide_coercion_node(Node *node)
 
 /*
  * build_coercion_expression()
- *             Construct a function-call expression for applying a pg_cast entry.
+ *             Construct an expression tree for applying a pg_cast entry.
  *
- * This is used for both type-coercion and length-coercion functions,
+ * This is used for both type-coercion and length-coercion operations,
  * since there is no difference in terms of the calling convention.
  */
 static Node *
-build_coercion_expression(Node *node, Oid funcId,
+build_coercion_expression(Node *node,
+                                                 Oid funcId, bool arrayCoerce,
                                                  Oid targetTypeId, int32 targetTypMod,
                                                  CoercionForm cformat, bool isExplicit)
 {
-       HeapTuple       tp;
-       Form_pg_proc procstruct;
-       int                     nargs;
-       List       *args;
-       Const      *cons;
-
-       tp = SearchSysCache(PROCOID,
-                                               ObjectIdGetDatum(funcId),
-                                               0, 0, 0);
-       if (!HeapTupleIsValid(tp))
-               elog(ERROR, "cache lookup failed for function %u", funcId);
-       procstruct = (Form_pg_proc) GETSTRUCT(tp);
-
-       /*
-        * Asserts essentially check that function is a legal coercion function.
-        * We can't make the seemingly obvious tests on prorettype and
-        * proargtypes[0], because of various binary-compatibility cases.
-        */
-       /* Assert(targetTypeId == procstruct->prorettype); */
-       Assert(!procstruct->proretset);
-       Assert(!procstruct->proisagg);
-       nargs = procstruct->pronargs;
-       Assert(nargs >= 1 && nargs <= 3);
-       /* Assert(procstruct->proargtypes.values[0] == exprType(node)); */
-       Assert(nargs < 2 || procstruct->proargtypes.values[1] == INT4OID);
-       Assert(nargs < 3 || procstruct->proargtypes.values[2] == BOOLOID);
+       int                     nargs = 0;
 
-       ReleaseSysCache(tp);
+       if (OidIsValid(funcId))
+       {
+               HeapTuple       tp;
+               Form_pg_proc procstruct;
 
-       args = list_make1(node);
+               tp = SearchSysCache(PROCOID,
+                                                       ObjectIdGetDatum(funcId),
+                                                       0, 0, 0);
+               if (!HeapTupleIsValid(tp))
+                       elog(ERROR, "cache lookup failed for function %u", funcId);
+               procstruct = (Form_pg_proc) GETSTRUCT(tp);
 
-       if (nargs >= 2)
-       {
-               /* Pass target typmod as an int4 constant */
-               cons = makeConst(INT4OID,
-                                                -1,
-                                                sizeof(int32),
-                                                Int32GetDatum(targetTypMod),
-                                                false,
-                                                true);
-
-               args = lappend(args, cons);
+               /*
+                * These Asserts essentially check that function is a legal coercion
+                * function.  We can't make the seemingly obvious tests on prorettype
+                * and proargtypes[0], even in the non-arrayCoerce case, because of
+                * various binary-compatibility cases.
+                */
+               /* Assert(targetTypeId == procstruct->prorettype); */
+               Assert(!procstruct->proretset);
+               Assert(!procstruct->proisagg);
+               nargs = procstruct->pronargs;
+               Assert(nargs >= 1 && nargs <= 3);
+               /* Assert(procstruct->proargtypes.values[0] == exprType(node)); */
+               Assert(nargs < 2 || procstruct->proargtypes.values[1] == INT4OID);
+               Assert(nargs < 3 || procstruct->proargtypes.values[2] == BOOLOID);
+
+               ReleaseSysCache(tp);
        }
 
-       if (nargs == 3)
+       if (arrayCoerce)
        {
-               /* Pass it a boolean isExplicit parameter, too */
-               cons = makeConst(BOOLOID,
-                                                -1,
-                                                sizeof(bool),
-                                                BoolGetDatum(isExplicit),
-                                                false,
-                                                true);
-
-               args = lappend(args, cons);
+               /* We need to build an ArrayCoerceExpr */
+               ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr);
+
+               acoerce->arg = (Expr *) node;
+               acoerce->elemfuncid = funcId;
+               acoerce->resulttype = targetTypeId;
+               /*
+                * Label the output as having a particular typmod only if we are
+                * really invoking a length-coercion function, ie one with more
+                * than one argument.
+                */
+               acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1;
+               acoerce->isExplicit = isExplicit;
+               acoerce->coerceformat = cformat;
+
+               return (Node *) acoerce;
        }
+       else
+       {
+               /* We build an ordinary FuncExpr with special arguments */
+               List       *args;
+               Const      *cons;
+
+               Assert(OidIsValid(funcId));
+
+               args = list_make1(node);
 
-       return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat);
+               if (nargs >= 2)
+               {
+                       /* Pass target typmod as an int4 constant */
+                       cons = makeConst(INT4OID,
+                                                        -1,
+                                                        sizeof(int32),
+                                                        Int32GetDatum(targetTypMod),
+                                                        false,
+                                                        true);
+
+                       args = lappend(args, cons);
+               }
+
+               if (nargs == 3)
+               {
+                       /* Pass it a boolean isExplicit parameter, too */
+                       cons = makeConst(BOOLOID,
+                                                        -1,
+                                                        sizeof(bool),
+                                                        BoolGetDatum(isExplicit),
+                                                        false,
+                                                        true);
+
+                       args = lappend(args, cons);
+               }
+
+               return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat);
+       }
 }
 
 
@@ -1636,7 +1673,9 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
  *
  * If we find a suitable entry in pg_cast, return TRUE, and set *funcid
  * to the castfunc value, which may be InvalidOid for a binary-compatible
- * coercion.
+ * coercion.  Also, arrayCoerce is set to indicate whether this is a plain
+ * or array coercion (if true, funcid actually shows how to coerce the
+ * array elements).
  *
  * NOTE: *funcid == InvalidOid does not necessarily mean that no work is
  * needed to do the coercion; if the target is a domain then we may need to
@@ -1646,12 +1685,13 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
 bool
 find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
                                          CoercionContext ccontext,
-                                         Oid *funcid)
+                                         Oid *funcid, bool *arrayCoerce)
 {
        bool            result = false;
        HeapTuple       tuple;
 
        *funcid = InvalidOid;
+       *arrayCoerce = false;
 
        /* Perhaps the types are domains; if so, look at their base types */
        if (OidIsValid(sourceTypeId))
@@ -1707,18 +1747,19 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
                /*
                 * If there's no pg_cast entry, perhaps we are dealing with a pair of
                 * array types.  If so, and if the element types have a suitable cast,
-                * use array_type_coerce() or array_type_length_coerce().
+                * report that with arrayCoerce = true.
                 *
                 * Hack: disallow coercions to oidvector and int2vector, which
                 * otherwise tend to capture coercions that should go to "real" array
                 * types.  We want those types to be considered "real" arrays for many
-                * purposes, but not this one.  (Also, array_type_coerce isn't
+                * purposes, but not this one.  (Also, ArrayCoerceExpr isn't
                 * guaranteed to produce an output that meets the restrictions of
                 * these datatypes, such as being 1-dimensional.)
                 */
                Oid                     targetElemType;
                Oid                     sourceElemType;
                Oid                     elemfuncid;
+               bool            elemarraycoerce;
 
                if (targetTypeId == OIDVECTOROID || targetTypeId == INT2VECTOROID)
                        return false;
@@ -1727,21 +1768,12 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
                        (sourceElemType = get_element_type(sourceTypeId)) != InvalidOid)
                {
                        if (find_coercion_pathway(targetElemType, sourceElemType,
-                                                                         ccontext, &elemfuncid))
+                                                                         ccontext,
+                                                                         &elemfuncid, &elemarraycoerce) &&
+                               !elemarraycoerce)
                        {
-                               if (!OidIsValid(elemfuncid))
-                               {
-                                       /* binary-compatible element type conversion */
-                                       *funcid = F_ARRAY_TYPE_COERCE;
-                               }
-                               else
-                               {
-                                       /* does the function take a typmod arg? */
-                                       if (get_func_nargs(elemfuncid) > 1)
-                                               *funcid = F_ARRAY_TYPE_LENGTH_COERCE;
-                                       else
-                                               *funcid = F_ARRAY_TYPE_COERCE;
-                               }
+                               *funcid = elemfuncid;
+                               *arrayCoerce = true;
                                result = true;
                        }
                }
@@ -1761,18 +1793,20 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
  *
  * If the given type is a varlena array type, we do not look for a coercion
  * function associated directly with the array type, but instead look for
- * one associated with the element type.  If one exists, we report
- * array_length_coerce() as the coercion function to use.
+ * one associated with the element type.  If one exists, we report it with
+ * *arrayCoerce set to true.
  */
-Oid
-find_typmod_coercion_function(Oid typeId)
+bool
+find_typmod_coercion_function(Oid typeId,
+                                                         Oid *funcid, bool *arrayCoerce)
 {
-       Oid                     funcid = InvalidOid;
-       bool            isArray = false;
        Type            targetType;
        Form_pg_type typeForm;
        HeapTuple       tuple;
 
+       *funcid = InvalidOid;
+       *arrayCoerce = false;
+
        targetType = typeidType(typeId);
        typeForm = (Form_pg_type) GETSTRUCT(targetType);
 
@@ -1783,7 +1817,7 @@ find_typmod_coercion_function(Oid typeId)
        {
                /* Yes, switch our attention to the element type */
                typeId = typeForm->typelem;
-               isArray = true;
+               *arrayCoerce = true;
        }
        ReleaseSysCache(targetType);
 
@@ -1797,16 +1831,9 @@ find_typmod_coercion_function(Oid typeId)
        {
                Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
 
-               funcid = castForm->castfunc;
+               *funcid = castForm->castfunc;
                ReleaseSysCache(tuple);
        }
 
-       /*
-        * Now, if we did find a coercion function for an array element type,
-        * report array_length_coerce() as the function to use.
-        */
-       if (isArray && OidIsValid(funcid))
-               funcid = F_ARRAY_LENGTH_COERCE;
-
-       return funcid;
+       return OidIsValid(*funcid);
 }
index 9f0a501dc53096d0509c63fde8d031f2efc9366e..45e488e33f5822cad75fc4a2296807c673919e0d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.214 2007/03/17 01:15:55 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.215 2007/03/27 23:21:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -265,6 +265,7 @@ transformExpr(ParseState *pstate, Node *expr)
                case T_FieldSelect:
                case T_FieldStore:
                case T_RelabelType:
+               case T_ArrayCoerceExpr:
                case T_ConvertRowtypeExpr:
                case T_CaseTestExpr:
                case T_CoerceToDomain:
@@ -1804,6 +1805,9 @@ exprType(Node *expr)
                case T_RelabelType:
                        type = ((RelabelType *) expr)->resulttype;
                        break;
+               case T_ArrayCoerceExpr:
+                       type = ((ArrayCoerceExpr *) expr)->resulttype;
+                       break;
                case T_ConvertRowtypeExpr:
                        type = ((ConvertRowtypeExpr *) expr)->resulttype;
                        break;
@@ -1918,6 +1922,8 @@ exprTypmod(Node *expr)
                        return ((FieldSelect *) expr)->resulttypmod;
                case T_RelabelType:
                        return ((RelabelType *) expr)->resulttypmod;
+               case T_ArrayCoerceExpr:
+                       return ((ArrayCoerceExpr *) expr)->resulttypmod;
                case T_CaseExpr:
                        {
                                /*
@@ -2072,47 +2078,68 @@ exprTypmod(Node *expr)
 bool
 exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
 {
-       FuncExpr   *func;
-       int                     nargs;
-       Const      *second_arg;
-
        if (coercedTypmod != NULL)
                *coercedTypmod = -1;    /* default result on failure */
 
-       /* Is it a function-call at all? */
-       if (expr == NULL || !IsA(expr, FuncExpr))
-               return false;
-       func = (FuncExpr *) expr;
-
        /*
-        * If it didn't come from a coercion context, reject.
+        * Scalar-type length coercions are FuncExprs, array-type length
+        * coercions are ArrayCoerceExprs
         */
-       if (func->funcformat != COERCE_EXPLICIT_CAST &&
-               func->funcformat != COERCE_IMPLICIT_CAST)
-               return false;
+       if (expr && IsA(expr, FuncExpr))
+       {
+               FuncExpr   *func = (FuncExpr *) expr;
+               int                     nargs;
+               Const      *second_arg;
 
-       /*
-        * If it's not a two-argument or three-argument function with the second
-        * argument being an int4 constant, it can't have been created from a
-        * length coercion (it must be a type coercion, instead).
-        */
-       nargs = list_length(func->args);
-       if (nargs < 2 || nargs > 3)
-               return false;
+               /*
+                * If it didn't come from a coercion context, reject.
+                */
+               if (func->funcformat != COERCE_EXPLICIT_CAST &&
+                       func->funcformat != COERCE_IMPLICIT_CAST)
+                       return false;
 
-       second_arg = (Const *) lsecond(func->args);
-       if (!IsA(second_arg, Const) ||
-               second_arg->consttype != INT4OID ||
-               second_arg->constisnull)
-               return false;
+               /*
+                * If it's not a two-argument or three-argument function with the
+                * second argument being an int4 constant, it can't have been created
+                * from a length coercion (it must be a type coercion, instead).
+                */
+               nargs = list_length(func->args);
+               if (nargs < 2 || nargs > 3)
+                       return false;
 
-       /*
-        * OK, it is indeed a length-coercion function.
-        */
-       if (coercedTypmod != NULL)
-               *coercedTypmod = DatumGetInt32(second_arg->constvalue);
+               second_arg = (Const *) lsecond(func->args);
+               if (!IsA(second_arg, Const) ||
+                       second_arg->consttype != INT4OID ||
+                       second_arg->constisnull)
+                       return false;
+
+               /*
+                * OK, it is indeed a length-coercion function.
+                */
+               if (coercedTypmod != NULL)
+                       *coercedTypmod = DatumGetInt32(second_arg->constvalue);
+
+               return true;
+       }
+
+       if (expr && IsA(expr, ArrayCoerceExpr))
+       {
+               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) expr;
+
+               /* It's not a length coercion unless there's a nondefault typmod */
+               if (acoerce->resulttypmod < 0)
+                       return false;
+
+               /*
+                * OK, it is indeed a length-coercion expression.
+                */
+               if (coercedTypmod != NULL)
+                       *coercedTypmod = acoerce->resulttypmod;
+
+               return true;
+       }
 
-       return true;
+       return false;
 }
 
 /*
index 888dc200d1f74a23787f8c21723a851b4a63ca78..01593317b1fce722311e2381b4541450f6723998 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.194 2007/02/01 19:10:27 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.195 2007/03/27 23:21:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -756,13 +756,15 @@ func_get_detail(List *funcname,
                                Oid                     sourceType = argtypes[0];
                                Node       *arg1 = linitial(fargs);
                                Oid                     cfuncid;
+                               bool            arrayCoerce;
 
                                if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
                                        (find_coercion_pathway(targetType, sourceType,
-                                                                                  COERCION_EXPLICIT, &cfuncid) &&
-                                        cfuncid == InvalidOid))
+                                                                                  COERCION_EXPLICIT,
+                                                                                  &cfuncid, &arrayCoerce) &&
+                                        cfuncid == InvalidOid && !arrayCoerce))
                                {
-                                       /* Yup, it's a type coercion */
+                                       /* Yup, it's a trivial type coercion */
                                        *funcid = InvalidOid;
                                        *rettype = targetType;
                                        *retset = false;
index 43acdffcaf0b8b8cd829450891e5a194e6e80765..38a86452e3f4dd167559afeef707dcaccb7c64ec 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.137 2007/02/27 23:48:07 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.138 2007/03/27 23:21:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -95,10 +95,6 @@ static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
                                   int *st, int *endp,
                                   int typlen, bool typbyval, char typalign);
 static int     array_cmp(FunctionCallInfo fcinfo);
-static Datum array_type_length_coerce_internal(ArrayType *src,
-                                                                 int32 desttypmod,
-                                                                 bool isExplicit,
-                                                                 FmgrInfo *fmgr_info);
 
 
 /*
@@ -4093,222 +4089,6 @@ array_insert_slice(ArrayType *destArray,
                                                  orignitems - orig_offset);
 }
 
-/*
- * array_type_coerce -- allow explicit or assignment coercion from
- * one array type to another.
- *
- * array_type_length_coerce -- the same, for cases where both type and length
- * coercion are done by a single function on the element type.
- *
- * Caller should have already verified that the source element type can be
- * coerced into the target element type.
- */
-Datum
-array_type_coerce(PG_FUNCTION_ARGS)
-{
-       ArrayType  *src = PG_GETARG_ARRAYTYPE_P(0);
-       FmgrInfo   *fmgr_info = fcinfo->flinfo;
-
-       return array_type_length_coerce_internal(src, -1, false, fmgr_info);
-}
-
-Datum
-array_type_length_coerce(PG_FUNCTION_ARGS)
-{
-       ArrayType  *src = PG_GETARG_ARRAYTYPE_P(0);
-       int32           desttypmod = PG_GETARG_INT32(1);
-       bool            isExplicit = PG_GETARG_BOOL(2);
-       FmgrInfo   *fmgr_info = fcinfo->flinfo;
-
-       return array_type_length_coerce_internal(src, desttypmod,
-                                                                                        isExplicit, fmgr_info);
-}
-
-static Datum
-array_type_length_coerce_internal(ArrayType *src,
-                                                                 int32 desttypmod,
-                                                                 bool isExplicit,
-                                                                 FmgrInfo *fmgr_info)
-{
-       Oid                     src_elem_type = ARR_ELEMTYPE(src);
-       typedef struct
-       {
-               Oid                     srctype;
-               Oid                     desttype;
-               FmgrInfo        coerce_finfo;
-               ArrayMapState amstate;
-       } atc_extra;
-       atc_extra  *my_extra;
-       FunctionCallInfoData locfcinfo;
-
-       /*
-        * We arrange to look up the coercion function only once per series of
-        * calls, assuming the input data type doesn't change underneath us.
-        * (Output type can't change.)
-        */
-       my_extra = (atc_extra *) fmgr_info->fn_extra;
-       if (my_extra == NULL)
-       {
-               fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt,
-                                                                                                        sizeof(atc_extra));
-               my_extra = (atc_extra *) fmgr_info->fn_extra;
-       }
-
-       if (my_extra->srctype != src_elem_type)
-       {
-               Oid                     tgt_type = get_fn_expr_rettype(fmgr_info);
-               Oid                     tgt_elem_type;
-               Oid                     funcId;
-
-               if (tgt_type == InvalidOid)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("could not determine target array type")));
-
-               tgt_elem_type = get_element_type(tgt_type);
-               if (tgt_elem_type == InvalidOid)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("target type is not an array")));
-
-               /*
-                * We don't deal with domain constraints yet, so bail out. This isn't
-                * currently a problem, because we also don't support arrays of domain
-                * type elements either. But in the future we might. At that point
-                * consideration should be given to removing the check below and
-                * adding a domain constraints check to the coercion.
-                */
-               if (getBaseType(tgt_elem_type) != tgt_elem_type)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("array coercion to domain type elements not "
-                                                       "currently supported")));
-
-               if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
-                                                                  COERCION_EXPLICIT, &funcId))
-               {
-                       /* should never happen, but check anyway */
-                       elog(ERROR, "no conversion function from %s to %s",
-                                format_type_be(src_elem_type),
-                                format_type_be(tgt_elem_type));
-               }
-               if (OidIsValid(funcId))
-                       fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
-               else
-                       my_extra->coerce_finfo.fn_oid = InvalidOid;
-               my_extra->srctype = src_elem_type;
-               my_extra->desttype = tgt_elem_type;
-       }
-
-       /*
-        * If it's binary-compatible, modify the element type in the array header,
-        * but otherwise leave the array as we received it.
-        */
-       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
-       {
-               ArrayType  *result;
-
-               result = (ArrayType *) DatumGetPointer(datumCopy(PointerGetDatum(src),
-                                                                                                                false, -1));
-               ARR_ELEMTYPE(result) = my_extra->desttype;
-               PG_RETURN_ARRAYTYPE_P(result);
-       }
-
-       /*
-        * Use array_map to apply the function to each array element.
-        *
-        * We pass on the desttypmod and isExplicit flags whether or not the
-        * function wants them.
-        */
-       InitFunctionCallInfoData(locfcinfo, &my_extra->coerce_finfo, 3,
-                                                        NULL, NULL);
-       locfcinfo.arg[0] = PointerGetDatum(src);
-       locfcinfo.arg[1] = Int32GetDatum(desttypmod);
-       locfcinfo.arg[2] = BoolGetDatum(isExplicit);
-       locfcinfo.argnull[0] = false;
-       locfcinfo.argnull[1] = false;
-       locfcinfo.argnull[2] = false;
-
-       return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype,
-                                        &my_extra->amstate);
-}
-
-/*
- * array_length_coerce -- apply the element type's length-coercion routine
- *             to each element of the given array.
- */
-Datum
-array_length_coerce(PG_FUNCTION_ARGS)
-{
-       ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
-       int32           desttypmod = PG_GETARG_INT32(1);
-       bool            isExplicit = PG_GETARG_BOOL(2);
-       FmgrInfo   *fmgr_info = fcinfo->flinfo;
-       typedef struct
-       {
-               Oid                     elemtype;
-               FmgrInfo        coerce_finfo;
-               ArrayMapState amstate;
-       } alc_extra;
-       alc_extra  *my_extra;
-       FunctionCallInfoData locfcinfo;
-
-       /* If no typmod is provided, shortcircuit the whole thing */
-       if (desttypmod < 0)
-               PG_RETURN_ARRAYTYPE_P(v);
-
-       /*
-        * We arrange to look up the element type's coercion function only once
-        * per series of calls, assuming the element type doesn't change
-        * underneath us.
-        */
-       my_extra = (alc_extra *) fmgr_info->fn_extra;
-       if (my_extra == NULL)
-       {
-               fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt,
-                                                                                                        sizeof(alc_extra));
-               my_extra = (alc_extra *) fmgr_info->fn_extra;
-       }
-
-       if (my_extra->elemtype != ARR_ELEMTYPE(v))
-       {
-               Oid                     funcId;
-
-               funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v));
-
-               if (OidIsValid(funcId))
-                       fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
-               else
-                       my_extra->coerce_finfo.fn_oid = InvalidOid;
-               my_extra->elemtype = ARR_ELEMTYPE(v);
-       }
-
-       /*
-        * If we didn't find a coercion function, return the array unmodified
-        * (this should not happen in the normal course of things, but might
-        * happen if this function is called manually).
-        */
-       if (my_extra->coerce_finfo.fn_oid == InvalidOid)
-               PG_RETURN_ARRAYTYPE_P(v);
-
-       /*
-        * Use array_map to apply the function to each array element.
-        *
-        * Note: we pass isExplicit whether or not the function wants it ...
-        */
-       InitFunctionCallInfoData(locfcinfo, &my_extra->coerce_finfo, 3,
-                                                        NULL, NULL);
-       locfcinfo.arg[0] = PointerGetDatum(v);
-       locfcinfo.arg[1] = Int32GetDatum(desttypmod);
-       locfcinfo.arg[2] = BoolGetDatum(isExplicit);
-       locfcinfo.argnull[0] = false;
-       locfcinfo.argnull[1] = false;
-       locfcinfo.argnull[2] = false;
-
-       return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v),
-                                        &my_extra->amstate);
-}
-
 /*
  * accumArrayResult - accumulate one (more) Datum for an array result
  *
index af363f4acff393b5c6ec795392d33625c087c82b..b9a026c7ea119c17b9197dd1fbaf0edb1d0a14d8 100644 (file)
@@ -15,7 +15,7 @@
  *
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.93 2007/03/25 19:45:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.94 2007/03/27 23:21:10 tgl Exp $
  *
  * ----------
  */
@@ -3871,6 +3871,7 @@ ri_HashCompareOp(Oid eq_opr, Oid typeid)
                Oid             lefttype,
                                righttype,
                                castfunc;
+               bool    arrayCoerce;
 
                /* We always need to know how to call the equality operator */
                fmgr_info_cxt(get_opcode(eq_opr), &entry->eq_opr_finfo,
@@ -3879,13 +3880,19 @@ ri_HashCompareOp(Oid eq_opr, Oid typeid)
                /*
                 * If we chose to use a cast from FK to PK type, we may have to
                 * apply the cast function to get to the operator's input type.
+                *
+                * XXX eventually it would be good to support array-coercion cases
+                * here and in ri_AttributesEqual().  At the moment there is no
+                * point because cases involving nonidentical array types will
+                * be rejected at constraint creation time.
                 */
                op_input_types(eq_opr, &lefttype, &righttype);
                Assert(lefttype == righttype);
                if (typeid == lefttype)
                        castfunc = InvalidOid;                          /* simplest case */
                else if (!find_coercion_pathway(lefttype, typeid, COERCION_IMPLICIT,
-                                                                               &castfunc))
+                                                                               &castfunc, &arrayCoerce)
+                                || arrayCoerce)                                /* XXX fixme */
                {
                        /* If target is ANYARRAY, assume it's OK, else punt. */
                        if (lefttype != ANYARRAYOID)
index da5ab61e84bfd164a8104a3052e04bbff589daca..46cf1dd45af6374aadcde36bd12a4fcc34430556 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.256 2007/03/18 16:50:42 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.257 2007/03/27 23:21:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3123,6 +3123,9 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
                case T_RelabelType:
                        return isSimpleNode((Node *) ((RelabelType *) node)->arg,
                                                                node, prettyFlags);
+               case T_ArrayCoerceExpr:
+                       return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
+                                                               node, prettyFlags);
                case T_ConvertRowtypeExpr:
                        return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
                                                                node, prettyFlags);
@@ -3588,6 +3591,27 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
+               case T_ArrayCoerceExpr:
+                       {
+                               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+                               Node       *arg = (Node *) acoerce->arg;
+
+                               if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
+                                       !showimplicit)
+                               {
+                                       /* don't show the implicit cast */
+                                       get_rule_expr_paren(arg, context, false, node);
+                               }
+                               else
+                               {
+                                       get_coercion_expr(arg, context,
+                                                                         acoerce->resulttype,
+                                                                         acoerce->resulttypmod,
+                                                                         node);
+                               }
+                       }
+                       break;
+
                case T_ConvertRowtypeExpr:
                        {
                                ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
index a92317aeac1a96bbc3617ae64c567878a91486f3..f596220d5a4418323e13878041cbdfb28145a185 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.230 2007/03/21 22:18:12 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.231 2007/03/27 23:21:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1454,53 +1454,25 @@ nulltestsel(PlannerInfo *root, NullTestType nulltesttype,
 /*
  * strip_array_coercion - strip binary-compatible relabeling from an array expr
  *
- * For array values, the parser doesn't generate simple RelabelType nodes,
- * but function calls of array_type_coerce() or array_type_length_coerce().
- * If we want to cope with binary-compatible situations we have to look
- * through these calls whenever the element-type coercion is binary-compatible.
+ * For array values, the parser normally generates ArrayCoerceExpr conversions,
+ * but it seems possible that RelabelType might show up.  Also, the planner
+ * is not currently tense about collapsing stacked ArrayCoerceExpr nodes,
+ * so we need to be ready to deal with more than one level.
  */
 static Node *
 strip_array_coercion(Node *node)
 {
-       /* could be more than one level, so loop */
        for (;;)
        {
-               if (node && IsA(node, RelabelType))
+               if (node && IsA(node, ArrayCoerceExpr) &&
+                       ((ArrayCoerceExpr *) node)->elemfuncid == InvalidOid)
                {
-                       /* We don't really expect this case, but may as well cope */
-                       node = (Node *) ((RelabelType *) node)->arg;
+                       node = (Node *) ((ArrayCoerceExpr *) node)->arg;
                }
-               else if (node && IsA(node, FuncExpr))
+               else if (node && IsA(node, RelabelType))
                {
-                       FuncExpr   *fexpr = (FuncExpr *) node;
-                       Node       *arg1;
-                       Oid                     src_elem_type;
-                       Oid                     tgt_elem_type;
-                       Oid                     funcId;
-
-                       /* must be the right function(s) */
-                       if (!(fexpr->funcid == F_ARRAY_TYPE_COERCE ||
-                                 fexpr->funcid == F_ARRAY_TYPE_LENGTH_COERCE))
-                               break;
-
-                       /* fetch source and destination array element types */
-                       arg1 = (Node *) linitial(fexpr->args);
-                       src_elem_type = get_element_type(exprType(arg1));
-                       if (src_elem_type == InvalidOid)
-                               break;                  /* probably shouldn't happen */
-                       tgt_elem_type = get_element_type(fexpr->funcresulttype);
-                       if (tgt_elem_type == InvalidOid)
-                               break;                  /* probably shouldn't happen */
-
-                       /* find out how to coerce */
-                       if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
-                                                                          COERCION_EXPLICIT, &funcId))
-                               break;                  /* definitely shouldn't happen */
-
-                       if (OidIsValid(funcId))
-                               break;                  /* non-binary-compatible coercion */
-
-                       node = arg1;            /* OK to look through the node */
+                       /* We don't really expect this case, but may as well cope */
+                       node = (Node *) ((RelabelType *) node)->arg;
                }
                else
                        break;
index c36d9fee13450e32f29bf9680e2a22ab801df3d7..a28acdc2ad8e71d9727b7fe404117831b7320905 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.104 2007/02/09 03:35:34 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.105 2007/03/27 23:21:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2061,6 +2061,8 @@ get_call_expr_argtype(Node *expr, int argnum)
                args = ((DistinctExpr *) expr)->args;
        else if (IsA(expr, ScalarArrayOpExpr))
                args = ((ScalarArrayOpExpr *) expr)->args;
+       else if (IsA(expr, ArrayCoerceExpr))
+               args = list_make1(((ArrayCoerceExpr *) expr)->arg);
        else if (IsA(expr, NullIfExpr))
                args = ((NullIfExpr *) expr)->args;
        else
@@ -2072,12 +2074,16 @@ get_call_expr_argtype(Node *expr, int argnum)
        argtype = exprType((Node *) list_nth(args, argnum));
 
        /*
-        * special hack for ScalarArrayOpExpr: what the underlying function will
-        * actually get passed is the element type of the array.
+        * special hack for ScalarArrayOpExpr and ArrayCoerceExpr: 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);
+       else if (IsA(expr, ArrayCoerceExpr) &&
+                        argnum == 0)
+               argtype = get_element_type(argtype);
 
        return argtype;
 }
index 79dd0cdb10e1c59da4f2432fe7ad62f6296242e1..270687b153e31ff004e93ede64843c21a5985f25 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.395 2007/03/26 16:58:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.396 2007/03/27 23:21:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200703261
+#define CATALOG_VERSION_NO     200703271
 
 #endif
index 0a096972edcdff7e12b569a5ff2fe4a91b39f07a..44e585e9c4e1db804326a4f79b5fb4c9e75088ff 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.450 2007/03/22 20:14:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.451 2007/03/27 23:21:11 tgl Exp $
  *
  * NOTES
  *       The script catalog/genbki.sh reads this file and generates .bki
@@ -1013,8 +1013,6 @@ DATA(insert OID = 379 (  array_prepend       PGNSP PGUID 12 1 0 f f f f i 2 2277 "2
 DESCR("prepend element onto front of array");
 DATA(insert OID = 383 (  array_cat                PGNSP PGUID 12 1 0 f f f f i 2 2277 "2277 2277" _null_ _null_ _null_ array_cat - _null_ ));
 DESCR("concatenate two arrays");
-DATA(insert OID = 384  (  array_coerce    PGNSP PGUID 12 1 0 f f t f s 1 2277 "2277" _null_ _null_ _null_ array_type_coerce - _null_ ));
-DESCR("coerce array to another array type");
 DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 1 0 f f t f i 2 1009 "25 25" _null_ _null_ _null_ text_to_array - _null_ ));
 DESCR("split delimited text into text[]");
 DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 1 0 f f t f i 2 25 "2277 25" _null_ _null_ _null_ array_to_text - _null_ ));
@@ -1622,9 +1620,6 @@ DESCR("convert int8 to text");
 DATA(insert OID = 1290 (  int8                    PGNSP PGUID 12 1 0 f f t f i 1 20 "25" _null_ _null_ _null_  text_int8 - _null_ ));
 DESCR("convert text to int8");
 
-DATA(insert OID = 1291 (  array_length_coerce  PGNSP PGUID 12 1 0 f f t f s 3 2277 "2277 23 16" _null_ _null_ _null_ array_length_coerce - _null_ ));
-DESCR("adjust any array to new element typmod");
-
 DATA(insert OID = 1292 ( tideq                    PGNSP PGUID 12 1 0 f f t f i 2 16 "27 27" _null_ _null_ _null_ tideq - _null_ ));
 DESCR("equal");
 DATA(insert OID = 1293 ( currtid                  PGNSP PGUID 12 1 0 f f t f v 2 27 "26 27" _null_ _null_ _null_ currtid_byreloid - _null_ ));
@@ -1786,10 +1781,6 @@ DATA(insert OID = 1370 (  interval                        PGNSP PGUID 12 1 0 f f t f i 1 1186 "1083"
 DESCR("convert time to interval");
 DATA(insert OID = 1372 (  char_length           PGNSP PGUID 12 1 0 f f t f i 1 23       "1042" _null_ _null_ _null_    bpcharlen - _null_ ));
 DESCR("character length");
-
-DATA(insert OID = 1373 (  array_type_length_coerce     PGNSP PGUID 12 1 0 f f t f s 3 2277 "2277 23 16" _null_ _null_ _null_ array_type_length_coerce - _null_ ));
-DESCR("coerce array to another type and adjust element typmod");
-
 DATA(insert OID = 1374 (  octet_length                  PGNSP PGUID 12 1 0 f f t f i 1 23       "25" _null_ _null_ _null_      textoctetlen - _null_ ));
 DESCR("octet length");
 DATA(insert OID = 1375 (  octet_length                  PGNSP PGUID 12 1 0 f f t f i 1 23       "1042" _null_ _null_ _null_    bpcharoctetlen - _null_ ));
index fe9ce9a4bf8db4e18d385028ea2bf32ae1306f6a..56bac9350f5205e7a496e8eafd356e0d9e47a150 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.170 2007/02/27 01:11:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.171 2007/03/27 23:21:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -625,6 +625,20 @@ typedef struct FieldStoreState
        TupleDesc       argdesc;                /* tupdesc for most recent input */
 } FieldStoreState;
 
+/* ----------------
+ *             ArrayCoerceExprState node
+ * ----------------
+ */
+typedef struct ArrayCoerceExprState
+{
+       ExprState       xprstate;
+       ExprState  *arg;                        /* input array value */
+       Oid                     resultelemtype; /* element type of result array */
+       FmgrInfo        elemfunc;               /* lookup info for element coercion function */
+       /* use struct pointer to avoid including array.h here */
+       struct ArrayMapState *amstate;  /* workspace for array_map */
+} ArrayCoerceExprState;
+
 /* ----------------
  *             ConvertRowtypeExprState node
  * ----------------
index 53bd13b5fbe58271eee4f1e824b0c99ecc87eac2..f344e3f5d1064d0a458b6c6ded89dda1ed95dd01 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.196 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.197 2007/03/27 23:21:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -121,6 +121,7 @@ typedef enum NodeTag
        T_FieldSelect,
        T_FieldStore,
        T_RelabelType,
+       T_ArrayCoerceExpr,
        T_ConvertRowtypeExpr,
        T_CaseExpr,
        T_CaseWhen,
@@ -159,6 +160,7 @@ typedef enum NodeTag
        T_SubPlanState,
        T_FieldSelectState,
        T_FieldStoreState,
+       T_ArrayCoerceExprState,
        T_ConvertRowtypeExprState,
        T_CaseExprState,
        T_CaseWhenState,
index 475bc149d7358338352502bc42f1eafc88583b49..ccad1329a6470a6e0bf083bb4ce5d15f33c23ebb 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.128 2007/03/17 00:11:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.129 2007/03/27 23:21:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -550,6 +550,29 @@ typedef struct RelabelType
        CoercionForm relabelformat; /* how to display this node */
 } RelabelType;
 
+/* ----------------
+ * ArrayCoerceExpr
+ *
+ * ArrayCoerceExpr represents a type coercion from one array type to another,
+ * which is implemented by applying the indicated element-type coercion
+ * function to each element of the source array.  If elemfuncid is InvalidOid
+ * then the element types are binary-compatible, but the coercion still
+ * requires some effort (we have to fix the element type ID stored in the
+ * array header).
+ * ----------------
+ */
+
+typedef struct ArrayCoerceExpr
+{
+       Expr            xpr;
+       Expr       *arg;                        /* input expression (yields an array) */
+       Oid                     elemfuncid;             /* OID of element coercion function, or 0 */
+       Oid                     resulttype;             /* output type of coercion (an array type) */
+       int32           resulttypmod;   /* output typmod (also element typmod) */
+       bool            isExplicit;             /* conversion semantics flag to pass to func */
+       CoercionForm coerceformat;      /* how to display this node */
+} ArrayCoerceExpr;
+
 /* ----------------
  * ConvertRowtypeExpr
  *
index 40ece05470b158f3addad1aaf6ac5f442b4810d9..23aaa87cfae584ac99498cd26f0e503925a31794 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.69 2007/01/05 22:19:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.70 2007/03/27 23:21:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,7 +77,8 @@ extern Oid resolve_generic_type(Oid declared_type,
 
 extern bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
                                          CoercionContext ccontext,
-                                         Oid *funcid);
-extern Oid     find_typmod_coercion_function(Oid typeId);
+                                         Oid *funcid, bool *arrayCoerce);
+extern bool find_typmod_coercion_function(Oid typeId,
+                                         Oid *funcid, bool *arrayCoerce);
 
 #endif   /* PARSE_COERCE_H */
index 9176a8b288d6c6c493d59ecddc5ae73dc16155ef..534adef53f6d36464f73008a3cfa4f8c70c43732 100644 (file)
@@ -49,7 +49,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.63 2007/02/27 23:48:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.64 2007/03/27 23:21:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -198,9 +198,6 @@ extern Datum arraycontained(PG_FUNCTION_ARGS);
 extern Datum array_dims(PG_FUNCTION_ARGS);
 extern Datum array_lower(PG_FUNCTION_ARGS);
 extern Datum array_upper(PG_FUNCTION_ARGS);
-extern Datum array_type_coerce(PG_FUNCTION_ARGS);
-extern Datum array_type_length_coerce(PG_FUNCTION_ARGS);
-extern Datum array_length_coerce(PG_FUNCTION_ARGS);
 extern Datum array_larger(PG_FUNCTION_ARGS);
 extern Datum array_smaller(PG_FUNCTION_ARGS);
 
index 165ce0426ec1784f34cd909cba65a8d07d28f2c5..41d2b3e54481eb2f0d0f957c67e6d650f3557a00 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.191 2007/03/25 23:27:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.192 2007/03/27 23:21:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4563,6 +4563,9 @@ exec_simple_check_node(Node *node)
                case T_RelabelType:
                        return exec_simple_check_node((Node *) ((RelabelType *) node)->arg);
 
+               case T_ArrayCoerceExpr:
+                       return exec_simple_check_node((Node *) ((ArrayCoerceExpr *) node)->arg);
+
                case T_ConvertRowtypeExpr:
                        return exec_simple_check_node((Node *) ((ConvertRowtypeExpr *) node)->arg);