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);