Re: [HACKERS] Arrays of domains

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: [HACKERS] Arrays of domains
Дата
Msg-id 10312.1505263823@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: [HACKERS] Arrays of domains  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: [HACKERS] Arrays of domains  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
I wrote:
> Attached is a patch series that allows us to create arrays of domain
> types.

Here's a rebased-up-to-HEAD version of this patch set.  The only
actual change is removal of a no-longer-needed hunk in pl_exec.c.

            regards, tom lane

diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 9d75e86..d7db32e 100644
*** a/src/backend/optimizer/prep/preptlist.c
--- b/src/backend/optimizer/prep/preptlist.c
*************** expand_targetlist(List *tlist, int comma
*** 306,314 ****
                          new_expr = coerce_to_domain(new_expr,
                                                      InvalidOid, -1,
                                                      atttype,
                                                      COERCE_IMPLICIT_CAST,
                                                      -1,
-                                                     false,
                                                      false);
                      }
                      else
--- 306,314 ----
                          new_expr = coerce_to_domain(new_expr,
                                                      InvalidOid, -1,
                                                      atttype,
+                                                     COERCION_IMPLICIT,
                                                      COERCE_IMPLICIT_CAST,
                                                      -1,
                                                      false);
                      }
                      else
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index e79ad26..5a241bd 100644
*** a/src/backend/parser/parse_coerce.c
--- b/src/backend/parser/parse_coerce.c
***************
*** 34,48 ****

  static Node *coerce_type_typmod(Node *node,
                     Oid targetTypeId, int32 targetTypMod,
!                    CoercionForm cformat, int location,
!                    bool isExplicit, bool hideInputCoercion);
  static void hide_coercion_node(Node *node);
  static Node *build_coercion_expression(Node *node,
                            CoercionPathType pathtype,
                            Oid funcId,
                            Oid targetTypeId, int32 targetTypMod,
!                           CoercionForm cformat, int location,
!                           bool isExplicit);
  static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
                           Oid targetTypeId,
                           CoercionContext ccontext,
--- 34,49 ----

  static Node *coerce_type_typmod(Node *node,
                     Oid targetTypeId, int32 targetTypMod,
!                    CoercionContext ccontext, CoercionForm cformat,
!                    int location,
!                    bool hideInputCoercion);
  static void hide_coercion_node(Node *node);
  static Node *build_coercion_expression(Node *node,
                            CoercionPathType pathtype,
                            Oid funcId,
                            Oid targetTypeId, int32 targetTypMod,
!                           CoercionContext ccontext, CoercionForm cformat,
!                           int location);
  static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
                           Oid targetTypeId,
                           CoercionContext ccontext,
*************** coerce_to_target_type(ParseState *pstate
*** 110,117 ****
       */
      result = coerce_type_typmod(result,
                                  targettype, targettypmod,
!                                 cformat, location,
!                                 (cformat != COERCE_IMPLICIT_CAST),
                                  (result != expr && !IsA(result, Const)));

      if (expr != origexpr)
--- 111,117 ----
       */
      result = coerce_type_typmod(result,
                                  targettype, targettypmod,
!                                 ccontext, cformat, location,
                                  (result != expr && !IsA(result, Const)));

      if (expr != origexpr)
*************** coerce_type(ParseState *pstate, Node *no
*** 355,361 ****
              result = coerce_to_domain(result,
                                        baseTypeId, baseTypeMod,
                                        targetTypeId,
!                                       cformat, location, false, false);

          ReleaseSysCache(baseType);

--- 355,362 ----
              result = coerce_to_domain(result,
                                        baseTypeId, baseTypeMod,
                                        targetTypeId,
!                                       ccontext, cformat, location,
!                                       false);

          ReleaseSysCache(baseType);

*************** coerce_type(ParseState *pstate, Node *no
*** 417,436 ****

              result = build_coercion_expression(node, pathtype, funcId,
                                                 baseTypeId, baseTypeMod,
!                                                cformat, location,
!                                                (cformat != COERCE_IMPLICIT_CAST));

              /*
               * If domain, coerce to the domain type and relabel with domain
!              * type ID.  We can skip the internal length-coercion step if the
!              * selected coercion function was a type-and-length coercion.
               */
              if (targetTypeId != baseTypeId)
                  result = coerce_to_domain(result, baseTypeId, baseTypeMod,
                                            targetTypeId,
!                                           cformat, location, true,
!                                           exprIsLengthCoercion(result,
!                                                                NULL));
          }
          else
          {
--- 418,434 ----

              result = build_coercion_expression(node, pathtype, funcId,
                                                 baseTypeId, baseTypeMod,
!                                                ccontext, cformat, location);

              /*
               * If domain, coerce to the domain type and relabel with domain
!              * type ID, hiding the previous coercion node.
               */
              if (targetTypeId != baseTypeId)
                  result = coerce_to_domain(result, baseTypeId, baseTypeMod,
                                            targetTypeId,
!                                           ccontext, cformat, location,
!                                           true);
          }
          else
          {
*************** coerce_type(ParseState *pstate, Node *no
*** 444,450 ****
               * then we won't need a RelabelType node.
               */
              result = coerce_to_domain(node, InvalidOid, -1, targetTypeId,
!                                       cformat, location, false, false);
              if (result == node)
              {
                  /*
--- 442,449 ----
               * then we won't need a RelabelType node.
               */
              result = coerce_to_domain(node, InvalidOid, -1, targetTypeId,
!                                       ccontext, cformat, location,
!                                       false);
              if (result == node)
              {
                  /*
*************** can_coerce_type(int nargs, Oid *input_ty
*** 636,654 ****
   * 'baseTypeMod': base type typmod of domain, if known (pass -1 if caller
   *        has not bothered to look this up)
   * 'typeId': target type to coerce to
!  * 'cformat': coercion format
   * 'location': coercion request location
   * 'hideInputCoercion': if true, hide the input coercion under this one.
-  * 'lengthCoercionDone': if true, caller already accounted for length,
-  *        ie the input is already of baseTypMod as well as baseTypeId.
   *
   * If the target type isn't a domain, the given 'arg' is returned as-is.
   */
  Node *
  coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
!                  CoercionForm cformat, int location,
!                  bool hideInputCoercion,
!                  bool lengthCoercionDone)
  {
      CoerceToDomain *result;

--- 635,651 ----
   * 'baseTypeMod': base type typmod of domain, if known (pass -1 if caller
   *        has not bothered to look this up)
   * 'typeId': target type to coerce to
!  * 'ccontext': context indicator to control coercions
!  * 'cformat': coercion display format
   * 'location': coercion request location
   * 'hideInputCoercion': if true, hide the input coercion under this one.
   *
   * If the target type isn't a domain, the given 'arg' is returned as-is.
   */
  Node *
  coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
!                  CoercionContext ccontext, CoercionForm cformat, int location,
!                  bool hideInputCoercion)
  {
      CoerceToDomain *result;

*************** coerce_to_domain(Node *arg, Oid baseType
*** 677,690 ****
       * would be safe to do anyway, without lots of knowledge about what the
       * base type thinks the typmod means.
       */
!     if (!lengthCoercionDone)
!     {
!         if (baseTypeMod >= 0)
!             arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod,
!                                      COERCE_IMPLICIT_CAST, location,
!                                      (cformat != COERCE_IMPLICIT_CAST),
!                                      false);
!     }

      /*
       * Now build the domain coercion node.  This represents run-time checking
--- 674,682 ----
       * would be safe to do anyway, without lots of knowledge about what the
       * base type thinks the typmod means.
       */
!     arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod,
!                              ccontext, COERCE_IMPLICIT_CAST, location,
!                              false);

      /*
       * Now build the domain coercion node.  This represents run-time checking
*************** coerce_to_domain(Node *arg, Oid baseType
*** 714,724 ****
   * The caller must have already ensured that the value is of the correct
   * type, typically by applying coerce_type.
   *
!  * cformat determines the display properties of the generated node (if any),
!  * while isExplicit may affect semantics.  If hideInputCoercion is true
!  * *and* we generate a node, the input node is forced to IMPLICIT display
!  * form, so that only the typmod coercion node will be visible when
!  * displaying the expression.
   *
   * NOTE: this does not need to work on domain types, because any typmod
   * coercion for a domain is considered to be part of the type coercion
--- 706,719 ----
   * The caller must have already ensured that the value is of the correct
   * type, typically by applying coerce_type.
   *
!  * ccontext may affect semantics, depending on whether the length coercion
!  * function pays attention to the isExplicit flag it's passed.
!  *
!  * cformat determines the display properties of the generated node (if any).
!  *
!  * If hideInputCoercion is true *and* we generate a node, the input node is
!  * forced to IMPLICIT display form, so that only the typmod coercion node will
!  * be visible when displaying the expression.
   *
   * NOTE: this does not need to work on domain types, because any typmod
   * coercion for a domain is considered to be part of the type coercion
*************** coerce_to_domain(Node *arg, Oid baseType
*** 726,733 ****
   */
  static Node *
  coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
!                    CoercionForm cformat, int location,
!                    bool isExplicit, bool hideInputCoercion)
  {
      CoercionPathType pathtype;
      Oid            funcId;
--- 721,729 ----
   */
  static Node *
  coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
!                    CoercionContext ccontext, CoercionForm cformat,
!                    int location,
!                    bool hideInputCoercion)
  {
      CoercionPathType pathtype;
      Oid            funcId;
*************** coerce_type_typmod(Node *node, Oid targe
*** 749,756 ****

          node = build_coercion_expression(node, pathtype, funcId,
                                           targetTypeId, targetTypMod,
!                                          cformat, location,
!                                          isExplicit);
      }

      return node;
--- 745,751 ----

          node = build_coercion_expression(node, pathtype, funcId,
                                           targetTypeId, targetTypMod,
!                                          ccontext, cformat, location);
      }

      return node;
*************** build_coercion_expression(Node *node,
*** 799,806 ****
                            CoercionPathType pathtype,
                            Oid funcId,
                            Oid targetTypeId, int32 targetTypMod,
!                           CoercionForm cformat, int location,
!                           bool isExplicit)
  {
      int            nargs = 0;

--- 794,801 ----
                            CoercionPathType pathtype,
                            Oid funcId,
                            Oid targetTypeId, int32 targetTypMod,
!                           CoercionContext ccontext, CoercionForm cformat,
!                           int location)
  {
      int            nargs = 0;

*************** build_coercion_expression(Node *node,
*** 865,871 ****
                               -1,
                               InvalidOid,
                               sizeof(bool),
!                              BoolGetDatum(isExplicit),
                               false,
                               true);

--- 860,866 ----
                               -1,
                               InvalidOid,
                               sizeof(bool),
!                              BoolGetDatum(ccontext == COERCION_EXPLICIT),
                               false,
                               true);

*************** build_coercion_expression(Node *node,
*** 893,899 ****
           */
          acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1;
          /* resultcollid will be set by parse_collate.c */
!         acoerce->isExplicit = isExplicit;
          acoerce->coerceformat = cformat;
          acoerce->location = location;

--- 888,894 ----
           */
          acoerce->resulttypmod = (nargs >= 2) ? targetTypMod : -1;
          /* resultcollid will be set by parse_collate.c */
!         acoerce->isExplicit = (ccontext == COERCION_EXPLICIT);
          acoerce->coerceformat = cformat;
          acoerce->location = location;

diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index ef52dd5..7054d4f 100644
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
*************** rewriteTargetListIU(List *targetList,
*** 875,883 ****
                      new_expr = coerce_to_domain(new_expr,
                                                  InvalidOid, -1,
                                                  att_tup->atttypid,
                                                  COERCE_IMPLICIT_CAST,
                                                  -1,
-                                                 false,
                                                  false);
                  }
              }
--- 875,883 ----
                      new_expr = coerce_to_domain(new_expr,
                                                  InvalidOid, -1,
                                                  att_tup->atttypid,
+                                                 COERCION_IMPLICIT,
                                                  COERCE_IMPLICIT_CAST,
                                                  -1,
                                                  false);
                  }
              }
*************** rewriteValuesRTE(RangeTblEntry *rte, Rel
*** 1271,1279 ****
                      new_expr = coerce_to_domain(new_expr,
                                                  InvalidOid, -1,
                                                  att_tup->atttypid,
                                                  COERCE_IMPLICIT_CAST,
                                                  -1,
-                                                 false,
                                                  false);
                  }
                  newList = lappend(newList, new_expr);
--- 1271,1279 ----
                      new_expr = coerce_to_domain(new_expr,
                                                  InvalidOid, -1,
                                                  att_tup->atttypid,
+                                                 COERCION_IMPLICIT,
                                                  COERCE_IMPLICIT_CAST,
                                                  -1,
                                                  false);
                  }
                  newList = lappend(newList, new_expr);
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 5c17213..c5773ef 100644
*** a/src/backend/rewrite/rewriteManip.c
--- b/src/backend/rewrite/rewriteManip.c
*************** ReplaceVarsFromTargetList_callback(Var *
*** 1429,1437 ****
                                                                 var->varcollid),
                                          InvalidOid, -1,
                                          var->vartype,
                                          COERCE_IMPLICIT_CAST,
                                          -1,
-                                         false,
                                          false);
          }
          elog(ERROR, "could not find replacement targetlist entry for attno %d",
--- 1429,1437 ----
                                                                 var->varcollid),
                                          InvalidOid, -1,
                                          var->vartype,
+                                         COERCION_IMPLICIT,
                                          COERCE_IMPLICIT_CAST,
                                          -1,
                                          false);
          }
          elog(ERROR, "could not find replacement targetlist entry for attno %d",
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 06f6529..e560f0c 100644
*** a/src/include/parser/parse_coerce.h
--- b/src/include/parser/parse_coerce.h
*************** extern Node *coerce_type(ParseState *pst
*** 48,56 ****
              CoercionContext ccontext, CoercionForm cformat, int location);
  extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
                   Oid typeId,
!                  CoercionForm cformat, int location,
!                  bool hideInputCoercion,
!                  bool lengthCoercionDone);

  extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
                    const char *constructName);
--- 48,55 ----
              CoercionContext ccontext, CoercionForm cformat, int location);
  extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
                   Oid typeId,
!                  CoercionContext ccontext, CoercionForm cformat, int location,
!                  bool hideInputCoercion);

  extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
                    const char *constructName);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index fa409d7..1776730 100644
*** a/contrib/pg_stat_statements/pg_stat_statements.c
--- b/contrib/pg_stat_statements/pg_stat_statements.c
*************** JumbleExpr(pgssJumbleState *jstate, Node
*** 2631,2636 ****
--- 2631,2637 ----

                  APP_JUMB(acexpr->resulttype);
                  JumbleExpr(jstate, (Node *) acexpr->arg);
+                 JumbleExpr(jstate, (Node *) acexpr->elemexpr);
              }
              break;
          case T_ConvertRowtypeExpr:
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 6fffc29..2668650 100644
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
*************** find_expr_references_walker(Node *node,
*** 1738,1748 ****
      {
          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 */
      }
      else if (IsA(node, ConvertRowtypeExpr))
--- 1738,1751 ----
      {
          ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;

!         /* as above, depend on type */
          add_object_address(OCLASS_TYPE, acoerce->resulttype, 0,
                             context->addrs);
+         /* the collation might not be referenced anywhere else, either */
+         if (OidIsValid(acoerce->resultcollid) &&
+             acoerce->resultcollid != DEFAULT_COLLATION_OID)
+             add_object_address(OCLASS_COLLATION, acoerce->resultcollid, 0,
+                                context->addrs);
          /* fall through to examine arguments */
      }
      else if (IsA(node, ConvertRowtypeExpr))
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index be9d23b..e083961 100644
*** a/src/backend/executor/execExpr.c
--- b/src/backend/executor/execExpr.c
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1225,1230 ****
--- 1225,1231 ----
              {
                  ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
                  Oid            resultelemtype;
+                 ExprState  *elemstate;

                  /* evaluate argument into step's result area */
                  ExecInitExprRec(acoerce->arg, parent, state, resv, resnull);
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1234,1275 ****
                      ereport(ERROR,
                              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                               errmsg("target type is not an array")));
-                 /* Arrays over domains aren't supported yet */
-                 Assert(getBaseType(resultelemtype) == resultelemtype);

!                 scratch.opcode = EEOP_ARRAYCOERCE;
!                 scratch.d.arraycoerce.coerceexpr = acoerce;
!                 scratch.d.arraycoerce.resultelemtype = resultelemtype;

!                 if (OidIsValid(acoerce->elemfuncid))
!                 {
!                     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));
!                     InvokeFunctionExecuteHook(acoerce->elemfuncid);

!                     /* Set up the primary fmgr lookup information */
!                     scratch.d.arraycoerce.elemfunc =
!                         (FmgrInfo *) palloc0(sizeof(FmgrInfo));
!                     fmgr_info(acoerce->elemfuncid,
!                               scratch.d.arraycoerce.elemfunc);
!                     fmgr_info_set_expr((Node *) acoerce,
!                                        scratch.d.arraycoerce.elemfunc);

                      /* Set up workspace for array_map */
                      scratch.d.arraycoerce.amstate =
                          (ArrayMapState *) palloc0(sizeof(ArrayMapState));
                  }
                  else
                  {
!                     /* Don't need workspace if there's no conversion func */
!                     scratch.d.arraycoerce.elemfunc = NULL;
                      scratch.d.arraycoerce.amstate = NULL;
                  }

--- 1235,1283 ----
                      ereport(ERROR,
                              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                               errmsg("target type is not an array")));

!                 /*
!                  * Construct a sub-expression for the per-element expression;
!                  * but don't ready it until after we check it for triviality.
!                  * We assume it hasn't any Var references, but does have a
!                  * CaseTestExpr representing the source array element values.
!                  */
!                 elemstate = makeNode(ExprState);
!                 elemstate->expr = acoerce->elemexpr;
!                 elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
!                 elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));

!                 ExecInitExprRec(acoerce->elemexpr, parent, elemstate,
!                                 &elemstate->resvalue, &elemstate->resnull);

!                 if (elemstate->steps_len == 1 &&
!                     elemstate->steps[0].opcode == EEOP_CASE_TESTVAL)
!                 {
!                     /* Trivial, so we need no per-element work at runtime */
!                     elemstate = NULL;
!                 }
!                 else
!                 {
!                     /* Not trivial, so append a DONE step */
!                     scratch.opcode = EEOP_DONE;
!                     ExprEvalPushStep(elemstate, &scratch);
!                     /* and ready the subexpression */
!                     ExecReadyExpr(elemstate);
!                 }

!                 scratch.opcode = EEOP_ARRAYCOERCE;
!                 scratch.d.arraycoerce.elemexprstate = elemstate;
!                 scratch.d.arraycoerce.resultelemtype = resultelemtype;

+                 if (elemstate)
+                 {
                      /* Set up workspace for array_map */
                      scratch.d.arraycoerce.amstate =
                          (ArrayMapState *) palloc0(sizeof(ArrayMapState));
                  }
                  else
                  {
!                     /* Don't need workspace if there's no subexpression */
                      scratch.d.arraycoerce.amstate = NULL;
                  }

diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index bd8a15d..f9244d8 100644
*** a/src/backend/executor/execExprInterp.c
--- b/src/backend/executor/execExprInterp.c
*************** ExecInterpExpr(ExprState *state, ExprCon
*** 1252,1258 ****
          EEO_CASE(EEOP_ARRAYCOERCE)
          {
              /* too complex for an inline implementation */
!             ExecEvalArrayCoerce(state, op);

              EEO_NEXT();
          }
--- 1252,1258 ----
          EEO_CASE(EEOP_ARRAYCOERCE)
          {
              /* too complex for an inline implementation */
!             ExecEvalArrayCoerce(state, op, econtext);

              EEO_NEXT();
          }
*************** ExecEvalArrayExpr(ExprState *state, Expr
*** 2328,2338 ****
   * Source array is in step's result variable.
   */
  void
! ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
  {
-     ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr;
      Datum        arraydatum;
-     FunctionCallInfoData locfcinfo;

      /* NULL array -> NULL result */
      if (*op->resnull)
--- 2328,2336 ----
   * Source array is in step's result variable.
   */
  void
! ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
      Datum        arraydatum;

      /* NULL array -> NULL result */
      if (*op->resnull)
*************** ExecEvalArrayCoerce(ExprState *state, Ex
*** 2344,2350 ****
       * 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 */
          ArrayType  *array = DatumGetArrayTypePCopy(arraydatum);
--- 2342,2348 ----
       * If it's binary-compatible, modify the element type in the array header,
       * but otherwise leave the array as we received it.
       */
!     if (op->d.arraycoerce.elemexprstate == NULL)
      {
          /* Detoast input array if necessary, and copy in any case */
          ArrayType  *array = DatumGetArrayTypePCopy(arraydatum);
*************** ExecEvalArrayCoerce(ExprState *state, Ex
*** 2355,2377 ****
      }

      /*
!      * 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.
!      *
!      * Note: coercion functions are assumed to not use collation.
       */
!     InitFunctionCallInfoData(locfcinfo, op->d.arraycoerce.elemfunc, 3,
!                              InvalidOid, NULL, NULL);
!     locfcinfo.arg[0] = arraydatum;
!     locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
!     locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
!     locfcinfo.argnull[0] = false;
!     locfcinfo.argnull[1] = false;
!     locfcinfo.argnull[2] = false;
!
!     *op->resvalue = array_map(&locfcinfo, op->d.arraycoerce.resultelemtype,
                                op->d.arraycoerce.amstate);
  }

--- 2353,2364 ----
      }

      /*
!      * Use array_map to apply the sub-expression to each array element.
       */
!     *op->resvalue = array_map(arraydatum,
!                               op->d.arraycoerce.elemexprstate,
!                               econtext,
!                               op->d.arraycoerce.resultelemtype,
                                op->d.arraycoerce.amstate);
  }

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f1bed14..b274af2 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyArrayCoerceExpr(const ArrayCoerceEx
*** 1698,1708 ****
      ArrayCoerceExpr *newnode = makeNode(ArrayCoerceExpr);

      COPY_NODE_FIELD(arg);
!     COPY_SCALAR_FIELD(elemfuncid);
      COPY_SCALAR_FIELD(resulttype);
      COPY_SCALAR_FIELD(resulttypmod);
      COPY_SCALAR_FIELD(resultcollid);
-     COPY_SCALAR_FIELD(isExplicit);
      COPY_SCALAR_FIELD(coerceformat);
      COPY_LOCATION_FIELD(location);

--- 1698,1707 ----
      ArrayCoerceExpr *newnode = makeNode(ArrayCoerceExpr);

      COPY_NODE_FIELD(arg);
!     COPY_NODE_FIELD(elemexpr);
      COPY_SCALAR_FIELD(resulttype);
      COPY_SCALAR_FIELD(resulttypmod);
      COPY_SCALAR_FIELD(resultcollid);
      COPY_SCALAR_FIELD(coerceformat);
      COPY_LOCATION_FIELD(location);

diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8b56b91..5c839f4 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** static bool
*** 513,523 ****
  _equalArrayCoerceExpr(const ArrayCoerceExpr *a, const ArrayCoerceExpr *b)
  {
      COMPARE_NODE_FIELD(arg);
!     COMPARE_SCALAR_FIELD(elemfuncid);
      COMPARE_SCALAR_FIELD(resulttype);
      COMPARE_SCALAR_FIELD(resulttypmod);
      COMPARE_SCALAR_FIELD(resultcollid);
-     COMPARE_SCALAR_FIELD(isExplicit);
      COMPARE_COERCIONFORM_FIELD(coerceformat);
      COMPARE_LOCATION_FIELD(location);

--- 513,522 ----
  _equalArrayCoerceExpr(const ArrayCoerceExpr *a, const ArrayCoerceExpr *b)
  {
      COMPARE_NODE_FIELD(arg);
!     COMPARE_NODE_FIELD(elemexpr);
      COMPARE_SCALAR_FIELD(resulttype);
      COMPARE_SCALAR_FIELD(resulttypmod);
      COMPARE_SCALAR_FIELD(resultcollid);
      COMPARE_COERCIONFORM_FIELD(coerceformat);
      COMPARE_LOCATION_FIELD(location);

diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index e3eb0c5..8e6f27e 100644
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
*************** check_functions_in_node(Node *node, chec
*** 1717,1731 ****
                      return true;
              }
              break;
-         case T_ArrayCoerceExpr:
-             {
-                 ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
-
-                 if (OidIsValid(expr->elemfuncid) &&
-                     checker(expr->elemfuncid, context))
-                     return true;
-             }
-             break;
          case T_RowCompareExpr:
              {
                  RowCompareExpr *rcexpr = (RowCompareExpr *) node;
--- 1717,1722 ----
*************** expression_tree_walker(Node *node,
*** 2023,2029 ****
          case T_CoerceViaIO:
              return walker(((CoerceViaIO *) node)->arg, context);
          case T_ArrayCoerceExpr:
!             return walker(((ArrayCoerceExpr *) node)->arg, context);
          case T_ConvertRowtypeExpr:
              return walker(((ConvertRowtypeExpr *) node)->arg, context);
          case T_CollateExpr:
--- 2014,2028 ----
          case T_CoerceViaIO:
              return walker(((CoerceViaIO *) node)->arg, context);
          case T_ArrayCoerceExpr:
!             {
!                 ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
!
!                 if (walker(acoerce->arg, context))
!                     return true;
!                 if (walker(acoerce->elemexpr, context))
!                     return true;
!             }
!             break;
          case T_ConvertRowtypeExpr:
              return walker(((ConvertRowtypeExpr *) node)->arg, context);
          case T_CollateExpr:
*************** expression_tree_mutator(Node *node,
*** 2705,2710 ****
--- 2704,2710 ----

                  FLATCOPY(newnode, acoerce, ArrayCoerceExpr);
                  MUTATE(newnode->arg, acoerce->arg, Expr *);
+                 MUTATE(newnode->elemexpr, acoerce->elemexpr, Expr *);
                  return (Node *) newnode;
              }
              break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b83d919..2532edc 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outArrayCoerceExpr(StringInfo str, cons
*** 1394,1404 ****
      WRITE_NODE_TYPE("ARRAYCOERCEEXPR");

      WRITE_NODE_FIELD(arg);
!     WRITE_OID_FIELD(elemfuncid);
      WRITE_OID_FIELD(resulttype);
      WRITE_INT_FIELD(resulttypmod);
      WRITE_OID_FIELD(resultcollid);
-     WRITE_BOOL_FIELD(isExplicit);
      WRITE_ENUM_FIELD(coerceformat, CoercionForm);
      WRITE_LOCATION_FIELD(location);
  }
--- 1394,1403 ----
      WRITE_NODE_TYPE("ARRAYCOERCEEXPR");

      WRITE_NODE_FIELD(arg);
!     WRITE_NODE_FIELD(elemexpr);
      WRITE_OID_FIELD(resulttype);
      WRITE_INT_FIELD(resulttypmod);
      WRITE_OID_FIELD(resultcollid);
      WRITE_ENUM_FIELD(coerceformat, CoercionForm);
      WRITE_LOCATION_FIELD(location);
  }
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index fbf8330..07ba691 100644
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
*************** _readArrayCoerceExpr(void)
*** 892,902 ****
      READ_LOCALS(ArrayCoerceExpr);

      READ_NODE_FIELD(arg);
!     READ_OID_FIELD(elemfuncid);
      READ_OID_FIELD(resulttype);
      READ_INT_FIELD(resulttypmod);
      READ_OID_FIELD(resultcollid);
-     READ_BOOL_FIELD(isExplicit);
      READ_ENUM_FIELD(coerceformat, CoercionForm);
      READ_LOCATION_FIELD(location);

--- 892,901 ----
      READ_LOCALS(ArrayCoerceExpr);

      READ_NODE_FIELD(arg);
!     READ_NODE_FIELD(elemexpr);
      READ_OID_FIELD(resulttype);
      READ_INT_FIELD(resulttypmod);
      READ_OID_FIELD(resultcollid);
      READ_ENUM_FIELD(coerceformat, CoercionForm);
      READ_LOCATION_FIELD(location);

diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 051a854..8fd49d6 100644
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** cost_qual_eval_walker(Node *node, cost_q
*** 3632,3642 ****
      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))
      {
--- 3632,3645 ----
      else if (IsA(node, ArrayCoerceExpr))
      {
          ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
!         QualCost    perelemcost;

!         cost_qual_eval_node(&perelemcost, (Node *) acoerce->elemexpr,
!                             context->root);
!         context->total.startup += perelemcost.startup;
!         if (perelemcost.per_tuple > 0)
!             context->total.per_tuple += perelemcost.per_tuple *
!                 estimate_array_length((Node *) acoerce->arg);
      }
      else if (IsA(node, RowCompareExpr))
      {
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index b0c9e94..dee4414 100644
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
*************** fix_expr_common(PlannerInfo *root, Node
*** 1395,1406 ****
          record_plan_function_dependency(root,
                                          ((ScalarArrayOpExpr *) node)->opfuncid);
      }
-     else if (IsA(node, ArrayCoerceExpr))
-     {
-         if (OidIsValid(((ArrayCoerceExpr *) node)->elemfuncid))
-             record_plan_function_dependency(root,
-                                             ((ArrayCoerceExpr *) node)->elemfuncid);
-     }
      else if (IsA(node, Const))
      {
          Const       *con = (Const *) node;
--- 1395,1400 ----
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 93add27..7961362 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** contain_nonstrict_functions_walker(Node
*** 1361,1366 ****
--- 1361,1377 ----
          return true;
      if (IsA(node, FieldStore))
          return true;
+     if (IsA(node, ArrayCoerceExpr))
+     {
+         /*
+          * ArrayCoerceExpr is strict at the array level, regardless of what
+          * the per-element expression is; so we should ignore elemexpr and
+          * recurse only into the arg.
+          */
+         return expression_tree_walker((Node *) ((ArrayCoerceExpr *) node)->arg,
+                                       contain_nonstrict_functions_walker,
+                                       context);
+     }
      if (IsA(node, CaseExpr))
          return true;
      if (IsA(node, ArrayExpr))
*************** contain_nonstrict_functions_walker(Node
*** 1380,1393 ****
      if (IsA(node, BooleanTest))
          return true;

!     /*
!      * Check other function-containing nodes; but ArrayCoerceExpr is strict at
!      * the array level, regardless of elemfunc.
!      */
!     if (!IsA(node, ArrayCoerceExpr) &&
!         check_functions_in_node(node, contain_nonstrict_functions_checker,
                                  context))
          return true;
      return expression_tree_walker(node, contain_nonstrict_functions_walker,
                                    context);
  }
--- 1391,1401 ----
      if (IsA(node, BooleanTest))
          return true;

!     /* Check other function-containing nodes */
!     if (check_functions_in_node(node, contain_nonstrict_functions_checker,
                                  context))
          return true;
+
      return expression_tree_walker(node, contain_nonstrict_functions_walker,
                                    context);
  }
*************** find_nonnullable_rels_walker(Node *node,
*** 1757,1763 ****
      }
      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);
--- 1765,1771 ----
      }
      else if (IsA(node, ArrayCoerceExpr))
      {
!         /* ArrayCoerceExpr is strict at the array level; ignore elemexpr */
          ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;

          result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);
*************** find_nonnullable_vars_walker(Node *node,
*** 1965,1971 ****
      }
      else if (IsA(node, ArrayCoerceExpr))
      {
!         /* ArrayCoerceExpr is strict at the array level */
          ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;

          result = find_nonnullable_vars_walker((Node *) expr->arg, top_level);
--- 1973,1979 ----
      }
      else if (IsA(node, ArrayCoerceExpr))
      {
!         /* ArrayCoerceExpr is strict at the array level; ignore elemexpr */
          ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;

          result = find_nonnullable_vars_walker((Node *) expr->arg, top_level);
*************** eval_const_expressions_mutator(Node *nod
*** 3005,3036 ****
              {
                  ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
                  Expr       *arg;
                  ArrayCoerceExpr *newexpr;

                  /*
!                  * Reduce constants in the ArrayCoerceExpr's argument, then
!                  * build a new ArrayCoerceExpr.
                   */
                  arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
                                                                context);

                  newexpr = makeNode(ArrayCoerceExpr);
                  newexpr->arg = arg;
!                 newexpr->elemfuncid = expr->elemfuncid;
                  newexpr->resulttype = expr->resulttype;
                  newexpr->resulttypmod = expr->resulttypmod;
                  newexpr->resultcollid = expr->resultcollid;
-                 newexpr->isExplicit = expr->isExplicit;
                  newexpr->coerceformat = expr->coerceformat;
                  newexpr->location = expr->location;

                  /*
!                  * If constant argument and it's a binary-coercible or
!                  * immutable conversion, we can simplify it to a constant.
                   */
                  if (arg && IsA(arg, Const) &&
!                     (!OidIsValid(newexpr->elemfuncid) ||
!                      func_volatile(newexpr->elemfuncid) == PROVOLATILE_IMMUTABLE))
                      return (Node *) evaluate_expr((Expr *) newexpr,
                                                    newexpr->resulttype,
                                                    newexpr->resulttypmod,
--- 3013,3050 ----
              {
                  ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
                  Expr       *arg;
+                 Expr       *elemexpr;
                  ArrayCoerceExpr *newexpr;

                  /*
!                  * Reduce constants in the ArrayCoerceExpr's argument and
!                  * per-element expressions, then build a new ArrayCoerceExpr.
                   */
                  arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
                                                                context);
+                 elemexpr = (Expr *) eval_const_expressions_mutator((Node *) expr->elemexpr,
+                                                                    context);

                  newexpr = makeNode(ArrayCoerceExpr);
                  newexpr->arg = arg;
!                 newexpr->elemexpr = elemexpr;
                  newexpr->resulttype = expr->resulttype;
                  newexpr->resulttypmod = expr->resulttypmod;
                  newexpr->resultcollid = expr->resultcollid;
                  newexpr->coerceformat = expr->coerceformat;
                  newexpr->location = expr->location;

                  /*
!                  * If constant argument and per-element expression is
!                  * immutable, we can simplify the whole thing to a constant.
!                  * Exception: although contain_mutable_functions considers
!                  * CoerceToDomain immutable for historical reasons, let's not
!                  * do so here; this ensures coercion to an array-over-domain
!                  * does not apply the domain's constraints until runtime.
                   */
                  if (arg && IsA(arg, Const) &&
!                     elemexpr && !IsA(elemexpr, CoerceToDomain) &&
!                     !contain_mutable_functions((Node *) elemexpr))
                      return (Node *) evaluate_expr((Expr *) newexpr,
                                                    newexpr->resulttype,
                                                    newexpr->resulttypmod,
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 5a241bd..0355c02 100644
*** a/src/backend/parser/parse_coerce.c
--- b/src/backend/parser/parse_coerce.c
*************** build_coercion_expression(Node *node,
*** 876,894 ****
      {
          /* 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;
          /* resultcollid will be set by parse_collate.c */
-         acoerce->isExplicit = (ccontext == COERCION_EXPLICIT);
          acoerce->coerceformat = cformat;
          acoerce->location = location;

--- 876,927 ----
      {
          /* We need to build an ArrayCoerceExpr */
          ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr);
+         CaseTestExpr *ctest = makeNode(CaseTestExpr);
+         Oid            sourceBaseTypeId;
+         int32        sourceBaseTypeMod;
+         Oid            targetElementType;
+         Node       *elemexpr;
+
+         /*
+          * Look through any domain over the source array type.  Note we don't
+          * expect that the target type is a domain; it must be a plain array.
+          * (To get to a domain target type, we'll do coerce_to_domain later.)
+          */
+         sourceBaseTypeMod = exprTypmod(node);
+         sourceBaseTypeId = getBaseTypeAndTypmod(exprType(node),
+                                                 &sourceBaseTypeMod);
+
+         /* Set up CaseTestExpr representing one element of source array */
+         ctest->typeId = get_element_type(sourceBaseTypeId);
+         Assert(OidIsValid(ctest->typeId));
+         ctest->typeMod = sourceBaseTypeMod;
+         ctest->collation = InvalidOid;    /* Assume coercions don't care */
+
+         /* And coerce it to the target element type */
+         targetElementType = get_element_type(targetTypeId);
+         Assert(OidIsValid(targetElementType));
+
+         elemexpr = coerce_to_target_type(NULL,
+                                          (Node *) ctest,
+                                          ctest->typeId,
+                                          targetElementType,
+                                          targetTypMod,
+                                          ccontext,
+                                          cformat,
+                                          location);
+         if (elemexpr == NULL)    /* shouldn't happen */
+             elog(ERROR, "failed to coerce array element type as expected");

          acoerce->arg = (Expr *) node;
!         acoerce->elemexpr = (Expr *) elemexpr;
          acoerce->resulttype = targetTypeId;

          /*
!          * Label the output as having a particular element typmod only if we
!          * ended up with a per-element expression that is labeled that way.
           */
!         acoerce->resulttypmod = exprTypmod(elemexpr);
          /* resultcollid will be set by parse_collate.c */
          acoerce->coerceformat = cformat;
          acoerce->location = location;

*************** IsBinaryCoercible(Oid srctype, Oid targe
*** 2143,2150 ****
   *    COERCION_PATH_RELABELTYPE: binary-compatible cast, no function needed
   *                *funcid is set to InvalidOid
   *    COERCION_PATH_ARRAYCOERCE: need an ArrayCoerceExpr node
!  *                *funcid is set to the element cast function, or InvalidOid
!  *                if the array elements are binary-compatible
   *    COERCION_PATH_COERCEVIAIO: need a CoerceViaIO node
   *                *funcid is set to InvalidOid
   *
--- 2176,2182 ----
   *    COERCION_PATH_RELABELTYPE: binary-compatible cast, no function needed
   *                *funcid is set to InvalidOid
   *    COERCION_PATH_ARRAYCOERCE: need an ArrayCoerceExpr node
!  *                *funcid is set to InvalidOid
   *    COERCION_PATH_COERCEVIAIO: need a CoerceViaIO node
   *                *funcid is set to InvalidOid
   *
*************** find_coercion_pathway(Oid targetTypeId,
*** 2230,2240 ****
      {
          /*
           * 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,
!          * report that we can coerce with an ArrayCoerceExpr.
!          *
!          * Note that the source type can be a domain over array, but not the
!          * target, because ArrayCoerceExpr won't check domain constraints.
           *
           * Hack: disallow coercions to oidvector and int2vector, which
           * otherwise tend to capture coercions that should go to "real" array
--- 2262,2269 ----
      {
          /*
           * If there's no pg_cast entry, perhaps we are dealing with a pair of
!          * array types.  If so, and if their element types have a conversion
!          * pathway, report that we can coerce with an ArrayCoerceExpr.
           *
           * Hack: disallow coercions to oidvector and int2vector, which
           * otherwise tend to capture coercions that should go to "real" array
*************** find_coercion_pathway(Oid targetTypeId,
*** 2249,2255 ****
              Oid            sourceElem;

              if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
!                 (sourceElem = get_base_element_type(sourceTypeId)) != InvalidOid)
              {
                  CoercionPathType elempathtype;
                  Oid            elemfuncid;
--- 2278,2284 ----
              Oid            sourceElem;

              if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
!                 (sourceElem = get_element_type(sourceTypeId)) != InvalidOid)
              {
                  CoercionPathType elempathtype;
                  Oid            elemfuncid;
*************** find_coercion_pathway(Oid targetTypeId,
*** 2258,2271 ****
                                                       sourceElem,
                                                       ccontext,
                                                       &elemfuncid);
!                 if (elempathtype != COERCION_PATH_NONE &&
!                     elempathtype != COERCION_PATH_ARRAYCOERCE)
                  {
!                     *funcid = elemfuncid;
!                     if (elempathtype == COERCION_PATH_COERCEVIAIO)
!                         result = COERCION_PATH_COERCEVIAIO;
!                     else
!                         result = COERCION_PATH_ARRAYCOERCE;
                  }
              }
          }
--- 2287,2295 ----
                                                       sourceElem,
                                                       ccontext,
                                                       &elemfuncid);
!                 if (elempathtype != COERCION_PATH_NONE)
                  {
!                     result = COERCION_PATH_ARRAYCOERCE;
                  }
              }
          }
*************** find_coercion_pathway(Oid targetTypeId,
*** 2306,2312 ****
   * 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.  An ArrayCoerceExpr node must be
!  * used to apply such a function.
   *
   * We use the same result enum as find_coercion_pathway, but the only possible
   * result codes are:
--- 2330,2338 ----
   * 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.  An ArrayCoerceExpr node must be
!  * used to apply such a function.  (Note: currently, it's pointless to
!  * return the funcid in this case, because it'll just get looked up again
!  * in the recursive construction of the ArrayCoerceExpr's elemexpr.)
   *
   * We use the same result enum as find_coercion_pathway, but the only possible
   * result codes are:
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 2a4de41..4caff0b 100644
*** a/src/backend/utils/adt/arrayfuncs.c
--- b/src/backend/utils/adt/arrayfuncs.c
*************** array_set(ArrayType *array, int nSubscri
*** 3092,3112 ****
  /*
   * array_map()
   *
!  * Map an array through an arbitrary function.  Return a new array with
!  * same dimensions and each source element transformed by fn().  Each
!  * source element is passed as the first argument to fn(); additional
!  * arguments to be passed to fn() can be specified by the caller.
!  * The output array can have a different element type than the input.
   *
   * Parameters are:
!  * * fcinfo: a function-call data structure pre-constructed by the caller
!  *     to be ready to call the desired function, with everything except the
!  *     first argument position filled in.  In particular, flinfo identifies
!  *     the function fn(), and if nargs > 1 then argument positions after the
!  *     first must be preset to the additional values to be passed.  The
!  *     first argument position initially holds the input array value.
   * * retType: OID of element type of output array.  This must be the same as,
!  *     or binary-compatible with, the result type of fn().
   * * amstate: workspace for array_map.  Must be zeroed by caller before
   *     first call, and not touched after that.
   *
--- 3092,3109 ----
  /*
   * array_map()
   *
!  * Map an array through an arbitrary expression.  Return a new array with
!  * the same dimensions and each source element transformed by the given,
!  * already-compiled expression.  Each source element is placed in the
!  * innermost_caseval/innermost_casenull fields of the ExprState.
   *
   * Parameters are:
!  * * arrayd: Datum representing array argument.
!  * * exprstate: ExprState representing the per-element transformation.
!  * * econtext: context for expression evaluation.
   * * retType: OID of element type of output array.  This must be the same as,
!  *     or binary-compatible with, the result type of the expression.  It might
!  *     be different from the input array's element type.
   * * amstate: workspace for array_map.  Must be zeroed by caller before
   *     first call, and not touched after that.
   *
*************** array_set(ArrayType *array, int nSubscri
*** 3116,3126 ****
   *
   * NB: caller must assure that input array is not NULL.  NULL elements in
   * the array are OK however.
   */
  Datum
! array_map(FunctionCallInfo fcinfo, Oid retType, ArrayMapState *amstate)
  {
!     AnyArrayType *v;
      ArrayType  *result;
      Datum       *values;
      bool       *nulls;
--- 3113,3126 ----
   *
   * NB: caller must assure that input array is not NULL.  NULL elements in
   * the array are OK however.
+  * NB: caller should be running in econtext's per-tuple memory context.
   */
  Datum
! array_map(Datum arrayd,
!           ExprState *exprstate, ExprContext *econtext,
!           Oid retType, ArrayMapState *amstate)
  {
!     AnyArrayType *v = DatumGetAnyArray(arrayd);
      ArrayType  *result;
      Datum       *values;
      bool       *nulls;
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3141,3153 ****
      array_iter    iter;
      ArrayMetaState *inp_extra;
      ArrayMetaState *ret_extra;
!
!     /* Get input array */
!     if (fcinfo->nargs < 1)
!         elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
!     if (PG_ARGISNULL(0))
!         elog(ERROR, "null input array");
!     v = PG_GETARG_ANY_ARRAY(0);

      inpType = AARR_ELEMTYPE(v);
      ndim = AARR_NDIM(v);
--- 3141,3148 ----
      array_iter    iter;
      ArrayMetaState *inp_extra;
      ArrayMetaState *ret_extra;
!     Datum       *transform_source = exprstate->innermost_caseval;
!     bool       *transform_source_isnull = exprstate->innermost_casenull;

      inpType = AARR_ELEMTYPE(v);
      ndim = AARR_NDIM(v);
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3158,3164 ****
      if (nitems <= 0)
      {
          /* Return empty array */
!         PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
      }

      /*
--- 3153,3159 ----
      if (nitems <= 0)
      {
          /* Return empty array */
!         return PointerGetDatum(construct_empty_array(retType));
      }

      /*
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3203,3241 ****

      for (i = 0; i < nitems; i++)
      {
-         bool        callit = true;
-
          /* Get source element, checking for NULL */
!         fcinfo->arg[0] = array_iter_next(&iter, &fcinfo->argnull[0], i,
!                                          inp_typlen, inp_typbyval, inp_typalign);
!
!         /*
!          * Apply the given function to source elt and extra args.
!          */
!         if (fcinfo->flinfo->fn_strict)
!         {
!             int            j;
!
!             for (j = 0; j < fcinfo->nargs; j++)
!             {
!                 if (fcinfo->argnull[j])
!                 {
!                     callit = false;
!                     break;
!                 }
!             }
!         }

!         if (callit)
!         {
!             fcinfo->isnull = false;
!             values[i] = FunctionCallInvoke(fcinfo);
!         }
!         else
!             fcinfo->isnull = true;

!         nulls[i] = fcinfo->isnull;
!         if (fcinfo->isnull)
              hasnulls = true;
          else
          {
--- 3198,3212 ----

      for (i = 0; i < nitems; i++)
      {
          /* Get source element, checking for NULL */
!         *transform_source =
!             array_iter_next(&iter, transform_source_isnull, i,
!                             inp_typlen, inp_typbyval, inp_typalign);

!         /* Apply the given expression to source element */
!         values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);

!         if (nulls[i])
              hasnulls = true;
          else
          {
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3254,3260 ****
          }
      }

!     /* Allocate and initialize the result array */
      if (hasnulls)
      {
          dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
--- 3225,3231 ----
          }
      }

!     /* Allocate and fill the result array */
      if (hasnulls)
      {
          dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
*************** array_map(FunctionCallInfo fcinfo, Oid r
*** 3273,3290 ****
      memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
      memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));

-     /*
-      * Note: do not risk trying to pfree the results of the called function
-      */
      CopyArrayEls(result,
                   values, nulls, nitems,
                   typlen, typbyval, typalign,
                   false);

      pfree(values);
      pfree(nulls);

!     PG_RETURN_ARRAYTYPE_P(result);
  }

  /*
--- 3244,3261 ----
      memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
      memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));

      CopyArrayEls(result,
                   values, nulls, nitems,
                   typlen, typbyval, typalign,
                   false);

+     /*
+      * Note: do not risk trying to pfree the results of the called expression
+      */
      pfree(values);
      pfree(nulls);

!     return PointerGetDatum(result);
  }

  /*
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 81b0bc3..7b3fdc9 100644
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** strip_array_coercion(Node *node)
*** 1753,1762 ****
  {
      for (;;)
      {
!         if (node && IsA(node, ArrayCoerceExpr) &&
!             ((ArrayCoerceExpr *) node)->elemfuncid == InvalidOid)
          {
!             node = (Node *) ((ArrayCoerceExpr *) node)->arg;
          }
          else if (node && IsA(node, RelabelType))
          {
--- 1753,1771 ----
  {
      for (;;)
      {
!         if (node && IsA(node, ArrayCoerceExpr))
          {
!             ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
!
!             /*
!              * If the per-element expression is just a RelabelType on top of
!              * CaseTestExpr, then we know it's a binary-compatible relabeling.
!              */
!             if (IsA(acoerce->elemexpr, RelabelType) &&
!                 IsA(((RelabelType *) acoerce->elemexpr)->arg, CaseTestExpr))
!                 node = (Node *) acoerce->arg;
!             else
!                 break;
          }
          else if (node && IsA(node, RelabelType))
          {
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index a7b0782..9197335 100644
*** a/src/backend/utils/fmgr/fmgr.c
--- b/src/backend/utils/fmgr/fmgr.c
*************** get_call_expr_argtype(Node *expr, int ar
*** 1941,1948 ****
          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 if (IsA(expr, WindowFunc))
--- 1941,1946 ----
*************** get_call_expr_argtype(Node *expr, int ar
*** 1956,1971 ****
      argtype = exprType((Node *) list_nth(args, argnum));

      /*
!      * 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_base_element_type(argtype);
-     else if (IsA(expr, ArrayCoerceExpr) &&
-              argnum == 0)
-         argtype = get_base_element_type(argtype);

      return argtype;
  }
--- 1954,1965 ----
      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.
       */
      if (IsA(expr, ScalarArrayOpExpr) &&
          argnum == 1)
          argtype = get_base_element_type(argtype);

      return argtype;
  }
*************** get_call_expr_arg_stable(Node *expr, int
*** 2012,2019 ****
          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 if (IsA(expr, WindowFunc))
--- 2006,2011 ----
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 8ee0496..78d2247 100644
*** a/src/include/executor/execExpr.h
--- b/src/include/executor/execExpr.h
*************** typedef struct ExprEvalStep
*** 385,394 ****
          /* for EEOP_ARRAYCOERCE */
          struct
          {
!             ArrayCoerceExpr *coerceexpr;
              Oid            resultelemtype; /* element type of result array */
-             FmgrInfo   *elemfunc;    /* lookup info for element coercion
-                                      * function */
              struct ArrayMapState *amstate;    /* workspace for array_map */
          }            arraycoerce;

--- 385,392 ----
          /* for EEOP_ARRAYCOERCE */
          struct
          {
!             ExprState  *elemexprstate;    /* null if no per-element work */
              Oid            resultelemtype; /* element type of result array */
              struct ArrayMapState *amstate;    /* workspace for array_map */
          }            arraycoerce;

*************** extern void ExecEvalRowNull(ExprState *s
*** 621,627 ****
  extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op,
                     ExprContext *econtext);
  extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op,
--- 619,626 ----
  extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op,
                     ExprContext *econtext);
  extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op,
!                     ExprContext *econtext);
  extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
  extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 8c536a8..ccb5123 100644
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
*************** typedef struct CoerceViaIO
*** 820,830 ****
   * 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).
   * ----------------
   */

--- 820,831 ----
   * ArrayCoerceExpr
   *
   * ArrayCoerceExpr represents a type coercion from one array type to another,
!  * which is implemented by applying the per-element coercion expression
!  * "elemexpr" to each element of the source array.  Within elemexpr, the
!  * source element is represented by a CaseTestExpr node.  Note that even if
!  * elemexpr is a no-op (that is, just CaseTestExpr + RelabelType), the
!  * coercion still requires some effort: we have to fix the element type OID
!  * stored in the array header.
   * ----------------
   */

*************** typedef struct ArrayCoerceExpr
*** 832,842 ****
  {
      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) */
      Oid            resultcollid;    /* OID of collation, or InvalidOid if none */
-     bool        isExplicit;        /* conversion semantics flag to pass to func */
      CoercionForm coerceformat;    /* how to display this node */
      int            location;        /* token location, or -1 if unknown */
  } ArrayCoerceExpr;
--- 833,842 ----
  {
      Expr        xpr;
      Expr       *arg;            /* input expression (yields an array) */
!     Expr       *elemexpr;        /* expression representing per-element work */
      Oid            resulttype;        /* output type of coercion (an array type) */
      int32        resulttypmod;    /* output typmod (also element typmod) */
      Oid            resultcollid;    /* OID of collation, or InvalidOid if none */
      CoercionForm coerceformat;    /* how to display this node */
      int            location;        /* token location, or -1 if unknown */
  } ArrayCoerceExpr;
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 61a67a2..32833f0 100644
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
***************
*** 64,69 ****
--- 64,73 ----
  #include "fmgr.h"
  #include "utils/expandeddatum.h"

+ /* avoid including execnodes.h here */
+ struct ExprState;
+ struct ExprContext;
+

  /*
   * Arrays are varlena objects, so must meet the varlena convention that
*************** extern ArrayType *array_set(ArrayType *a
*** 360,367 ****
            Datum dataValue, bool isNull,
            int arraytyplen, int elmlen, bool elmbyval, char elmalign);

! extern Datum array_map(FunctionCallInfo fcinfo, Oid retType,
!           ArrayMapState *amstate);

  extern void array_bitmap_copy(bits8 *destbitmap, int destoffset,
                    const bits8 *srcbitmap, int srcoffset,
--- 364,372 ----
            Datum dataValue, bool isNull,
            int arraytyplen, int elmlen, bool elmbyval, char elmalign);

! extern Datum array_map(Datum arrayd,
!           struct ExprState *exprstate, struct ExprContext *econtext,
!           Oid retType, ArrayMapState *amstate);

  extern void array_bitmap_copy(bits8 *destbitmap, int destoffset,
                    const bits8 *srcbitmap, int srcoffset,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16ae..12d2cd6 100644
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** ObjectAddress
*** 729,734 ****
--- 729,735 ----
  DefineDomain(CreateDomainStmt *stmt)
  {
      char       *domainName;
+     char       *domainArrayName;
      Oid            domainNamespace;
      AclResult    aclresult;
      int16        internalLength;
*************** DefineDomain(CreateDomainStmt *stmt)
*** 757,762 ****
--- 758,764 ----
      Oid            basetypeoid;
      Oid            old_type_oid;
      Oid            domaincoll;
+     Oid            domainArrayOid;
      Form_pg_type baseType;
      int32        basetypeMod;
      Oid            baseColl;
*************** DefineDomain(CreateDomainStmt *stmt)
*** 1027,1032 ****
--- 1029,1037 ----
          }
      }

+     /* Allocate OID for array type */
+     domainArrayOid = AssignTypeArrayOid();
+
      /*
       * Have TypeCreate do all the real work.
       */
*************** DefineDomain(CreateDomainStmt *stmt)
*** 1051,1057 ****
                     analyzeProcedure,    /* analyze procedure */
                     InvalidOid,    /* no array element type */
                     false,        /* this isn't an array */
!                    InvalidOid,    /* no arrays for domains (yet) */
                     basetypeoid, /* base type ID */
                     defaultValue,    /* default type value (text) */
                     defaultValueBin, /* default type value (binary) */
--- 1056,1062 ----
                     analyzeProcedure,    /* analyze procedure */
                     InvalidOid,    /* no array element type */
                     false,        /* this isn't an array */
!                    domainArrayOid,    /* array type we are about to create */
                     basetypeoid, /* base type ID */
                     defaultValue,    /* default type value (text) */
                     defaultValueBin, /* default type value (binary) */
*************** DefineDomain(CreateDomainStmt *stmt)
*** 1064,1069 ****
--- 1069,1116 ----
                     domaincoll); /* type's collation */

      /*
+      * Create the array type that goes with it.
+      */
+     domainArrayName = makeArrayTypeName(domainName, domainNamespace);
+
+     /* alignment must be 'i' or 'd' for arrays */
+     alignment = (alignment == 'd') ? 'd' : 'i';
+
+     TypeCreate(domainArrayOid,    /* force assignment of this type OID */
+                domainArrayName, /* type name */
+                domainNamespace, /* namespace */
+                InvalidOid,        /* relation oid (n/a here) */
+                0,                /* relation kind (ditto) */
+                GetUserId(),        /* owner's ID */
+                -1,                /* internal size (always varlena) */
+                TYPTYPE_BASE,    /* type-type (base type) */
+                TYPCATEGORY_ARRAY,    /* type-category (array) */
+                false,            /* array types are never preferred */
+                delimiter,        /* array element delimiter */
+                F_ARRAY_IN,        /* input procedure */
+                F_ARRAY_OUT,        /* output procedure */
+                F_ARRAY_RECV,    /* receive procedure */
+                F_ARRAY_SEND,    /* send procedure */
+                InvalidOid,        /* typmodin procedure - none */
+                InvalidOid,        /* typmodout procedure - none */
+                F_ARRAY_TYPANALYZE,    /* analyze procedure */
+                address.objectId,    /* element type ID */
+                true,            /* yes this is an array type */
+                InvalidOid,        /* no further array type */
+                InvalidOid,        /* base type ID */
+                NULL,            /* never a default type value */
+                NULL,            /* binary default isn't sent either */
+                false,            /* never passed by value */
+                alignment,        /* see above */
+                'x',                /* ARRAY is always toastable */
+                -1,                /* typMod (Domains only) */
+                0,                /* Array dimensions of typbasetype */
+                false,            /* Type NOT NULL */
+                domaincoll);        /* type's collation */
+
+     pfree(domainArrayName);
+
+     /*
       * Process constraints which refer to the domain ID returned by TypeCreate
       */
      foreach(listptr, schema)
*************** DefineEnum(CreateEnumStmt *stmt)
*** 1139,1144 ****
--- 1186,1192 ----
                       errmsg("type \"%s\" already exists", enumName)));
      }

+     /* Allocate OID for array type */
      enumArrayOid = AssignTypeArrayOid();

      /* Create the pg_type entry */
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 3acc696..1e62c57 100644
*** a/src/test/regress/expected/domain.out
--- b/src/test/regress/expected/domain.out
*************** Rules:
*** 310,315 ****
--- 310,410 ----
  drop table dcomptable;
  drop type comptype cascade;
  NOTICE:  drop cascades to type dcomptypea
+ -- Test arrays over domains
+ create domain posint as int check (value > 0);
+ create table pitable (f1 posint[]);
+ insert into pitable values(array[42]);
+ insert into pitable values(array[-1]);  -- fail
+ ERROR:  value for domain posint violates check constraint "posint_check"
+ insert into pitable values('{0}');  -- fail
+ ERROR:  value for domain posint violates check constraint "posint_check"
+ LINE 1: insert into pitable values('{0}');
+                                    ^
+ update pitable set f1[1] = f1[1] + 1;
+ update pitable set f1[1] = 0;  -- fail
+ ERROR:  value for domain posint violates check constraint "posint_check"
+ select * from pitable;
+   f1
+ ------
+  {43}
+ (1 row)
+
+ drop table pitable;
+ create domain vc4 as varchar(4);
+ create table vc4table (f1 vc4[]);
+ insert into vc4table values(array['too long']);  -- fail
+ ERROR:  value too long for type character varying(4)
+ insert into vc4table values(array['too long']::vc4[]);  -- cast truncates
+ select * from vc4table;
+     f1
+ ----------
+  {"too "}
+ (1 row)
+
+ drop table vc4table;
+ drop type vc4;
+ -- You can sort of fake arrays-of-arrays by putting a domain in between
+ create domain dposinta as posint[];
+ create table dposintatable (f1 dposinta[]);
+ insert into dposintatable values(array[array[42]]);  -- fail
+ ERROR:  column "f1" is of type dposinta[] but expression is of type integer[]
+ LINE 1: insert into dposintatable values(array[array[42]]);
+                                          ^
+ HINT:  You will need to rewrite or cast the expression.
+ insert into dposintatable values(array[array[42]::posint[]]); -- still fail
+ ERROR:  column "f1" is of type dposinta[] but expression is of type posint[]
+ LINE 1: insert into dposintatable values(array[array[42]::posint[]])...
+                                          ^
+ HINT:  You will need to rewrite or cast the expression.
+ insert into dposintatable values(array[array[42]::dposinta]); -- but this works
+ select f1, f1[1], (f1[1])[1] from dposintatable;
+     f1    |  f1  | f1
+ ----------+------+----
+  {"{42}"} | {42} | 42
+ (1 row)
+
+ select pg_typeof(f1) from dposintatable;
+  pg_typeof
+ ------------
+  dposinta[]
+ (1 row)
+
+ select pg_typeof(f1[1]) from dposintatable;
+  pg_typeof
+ -----------
+  dposinta
+ (1 row)
+
+ select pg_typeof(f1[1][1]) from dposintatable;
+  pg_typeof
+ -----------
+  dposinta
+ (1 row)
+
+ select pg_typeof((f1[1])[1]) from dposintatable;
+  pg_typeof
+ -----------
+  posint
+ (1 row)
+
+ update dposintatable set f1[2] = array[99];
+ select f1, f1[1], (f1[2])[1] from dposintatable;
+        f1        |  f1  | f1
+ -----------------+------+----
+  {"{42}","{99}"} | {42} | 99
+ (1 row)
+
+ -- it'd be nice if you could do something like this, but for now you can't:
+ update dposintatable set f1[2][1] = array[97];
+ ERROR:  wrong number of array subscripts
+ -- maybe someday we can make this syntax work:
+ update dposintatable set (f1[2])[1] = array[98];
+ ERROR:  syntax error at or near "["
+ LINE 1: update dposintatable set (f1[2])[1] = array[98];
+                                         ^
+ drop table dposintatable;
+ drop domain posint cascade;
+ NOTICE:  drop cascades to type dposinta
  -- Test not-null restrictions
  create domain dnotnull varchar(15) NOT NULL;
  create domain dnull    varchar(15);
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index 0fd383e..8fb3e20 100644
*** a/src/test/regress/sql/domain.sql
--- b/src/test/regress/sql/domain.sql
*************** drop table dcomptable;
*** 166,171 ****
--- 166,214 ----
  drop type comptype cascade;


+ -- Test arrays over domains
+
+ create domain posint as int check (value > 0);
+
+ create table pitable (f1 posint[]);
+ insert into pitable values(array[42]);
+ insert into pitable values(array[-1]);  -- fail
+ insert into pitable values('{0}');  -- fail
+ update pitable set f1[1] = f1[1] + 1;
+ update pitable set f1[1] = 0;  -- fail
+ select * from pitable;
+ drop table pitable;
+
+ create domain vc4 as varchar(4);
+ create table vc4table (f1 vc4[]);
+ insert into vc4table values(array['too long']);  -- fail
+ insert into vc4table values(array['too long']::vc4[]);  -- cast truncates
+ select * from vc4table;
+ drop table vc4table;
+ drop type vc4;
+
+ -- You can sort of fake arrays-of-arrays by putting a domain in between
+ create domain dposinta as posint[];
+ create table dposintatable (f1 dposinta[]);
+ insert into dposintatable values(array[array[42]]);  -- fail
+ insert into dposintatable values(array[array[42]::posint[]]); -- still fail
+ insert into dposintatable values(array[array[42]::dposinta]); -- but this works
+ select f1, f1[1], (f1[1])[1] from dposintatable;
+ select pg_typeof(f1) from dposintatable;
+ select pg_typeof(f1[1]) from dposintatable;
+ select pg_typeof(f1[1][1]) from dposintatable;
+ select pg_typeof((f1[1])[1]) from dposintatable;
+ update dposintatable set f1[2] = array[99];
+ select f1, f1[1], (f1[2])[1] from dposintatable;
+ -- it'd be nice if you could do something like this, but for now you can't:
+ update dposintatable set f1[2][1] = array[97];
+ -- maybe someday we can make this syntax work:
+ update dposintatable set (f1[2])[1] = array[98];
+
+ drop table dposintatable;
+ drop domain posint cascade;
+
+
  -- Test not-null restrictions

  create domain dnotnull varchar(15) NOT NULL;

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Kyotaro HORIGUCHI
Дата:
Сообщение: Re: [HACKERS] Patches that don't apply or don't compile: 2017-09-12
Следующее
От: Amit Langote
Дата:
Сообщение: Re: [HACKERS] dropping partitioned tables without CASCADE