/*
             * These datum records are read-only at runtime, so no need to
-            * copy them
+            * copy them (well, ARRAYELEM contains some cached type data,
+            * but we'd just as soon centralize the caching anyway)
             */
            result = datum;
            break;
                /*
                 * Target is an element of an array
                 */
+               PLpgSQL_arrayelem *arrayelem;
                int         nsubscripts;
                int         i;
                PLpgSQL_expr *subscripts[MAXDIM];
                int         subscriptvals[MAXDIM];
-               bool        oldarrayisnull;
-               Oid         arraytypeid,
-                           arrayelemtypeid;
-               int32       arraytypmod;
-               int16       arraytyplen,
-                           elemtyplen;
-               bool        elemtypbyval;
-               char        elemtypalign;
                Datum       oldarraydatum,
                            coerced_value;
+               bool        oldarrayisnull;
+               Oid         parenttypoid;
+               int32       parenttypmod;
                ArrayType  *oldarrayval;
                ArrayType  *newarrayval;
                SPITupleTable *save_eval_tuptable;
                 * back to find the base array datum, and save the subscript
                 * expressions as we go.  (We are scanning right to left here,
                 * but want to evaluate the subscripts left-to-right to
-                * minimize surprises.)
+                * minimize surprises.)  Note that arrayelem is left pointing
+                * to the leftmost arrayelem datum, where we will cache the
+                * array element type data.
                 */
                nsubscripts = 0;
                do
                {
-                   PLpgSQL_arrayelem *arrayelem = (PLpgSQL_arrayelem *) target;
-
+                   arrayelem = (PLpgSQL_arrayelem *) target;
                    if (nsubscripts >= MAXDIM)
                        ereport(ERROR,
                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
                /* Fetch current value of array datum */
                exec_eval_datum(estate, target,
-                               &arraytypeid, &arraytypmod,
+                               &parenttypoid, &parenttypmod,
                                &oldarraydatum, &oldarrayisnull);
 
-               /* If target is domain over array, reduce to base type */
-               arraytypeid = getBaseTypeAndTypmod(arraytypeid, &arraytypmod);
-
-               /* ... and identify the element type */
-               arrayelemtypeid = get_element_type(arraytypeid);
-               if (!OidIsValid(arrayelemtypeid))
-                   ereport(ERROR,
-                           (errcode(ERRCODE_DATATYPE_MISMATCH),
-                            errmsg("subscripted object is not an array")));
-
-               get_typlenbyvalalign(arrayelemtypeid,
-                                    &elemtyplen,
-                                    &elemtypbyval,
-                                    &elemtypalign);
-               arraytyplen = get_typlen(arraytypeid);
+               /* Update cached type data if necessary */
+               if (arrayelem->parenttypoid != parenttypoid ||
+                   arrayelem->parenttypmod != parenttypmod)
+               {
+                   Oid         arraytypoid;
+                   int32       arraytypmod = parenttypmod;
+                   int16       arraytyplen;
+                   Oid         elemtypoid;
+                   int16       elemtyplen;
+                   bool        elemtypbyval;
+                   char        elemtypalign;
+
+                   /* If target is domain over array, reduce to base type */
+                   arraytypoid = getBaseTypeAndTypmod(parenttypoid,
+                                                      &arraytypmod);
+
+                   /* ... and identify the element type */
+                   elemtypoid = get_element_type(arraytypoid);
+                   if (!OidIsValid(elemtypoid))
+                       ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("subscripted object is not an array")));
+
+                   /* Collect needed data about the types */
+                   arraytyplen = get_typlen(arraytypoid);
+
+                   get_typlenbyvalalign(elemtypoid,
+                                        &elemtyplen,
+                                        &elemtypbyval,
+                                        &elemtypalign);
+
+                   /* Now safe to update the cached data */
+                   arrayelem->parenttypoid = parenttypoid;
+                   arrayelem->parenttypmod = parenttypmod;
+                   arrayelem->arraytypoid = arraytypoid;
+                   arrayelem->arraytypmod = arraytypmod;
+                   arrayelem->arraytyplen = arraytyplen;
+                   arrayelem->elemtypoid = elemtypoid;
+                   arrayelem->elemtyplen = elemtyplen;
+                   arrayelem->elemtypbyval = elemtypbyval;
+                   arrayelem->elemtypalign = elemtypalign;
+               }
 
                /*
                 * Evaluate the subscripts, switch into left-to-right order.
                /* Coerce source value to match array element type. */
                coerced_value = exec_simple_cast_value(value,
                                                       valtype,
-                                                      arrayelemtypeid,
-                                                      arraytypmod,
+                                                      arrayelem->elemtypoid,
+                                                      arrayelem->arraytypmod,
                                                       *isNull);
 
                /*
                 * array, either, so that's a no-op too.  This is all ugly but
                 * corresponds to the current behavior of ExecEvalArrayRef().
                 */
-               if (arraytyplen > 0 &&  /* fixed-length array? */
+               if (arrayelem->arraytyplen > 0 &&   /* fixed-length array? */
                    (oldarrayisnull || *isNull))
                    return;
 
                if (oldarrayisnull)
-                   oldarrayval = construct_empty_array(arrayelemtypeid);
+                   oldarrayval = construct_empty_array(arrayelem->elemtypoid);
                else
                    oldarrayval = (ArrayType *) DatumGetPointer(oldarraydatum);
 
                                        subscriptvals,
                                        coerced_value,
                                        *isNull,
-                                       arraytyplen,
-                                       elemtyplen,
-                                       elemtypbyval,
-                                       elemtypalign);
+                                       arrayelem->arraytyplen,
+                                       arrayelem->elemtyplen,
+                                       arrayelem->elemtypbyval,
+                                       arrayelem->elemtypalign);
 
                /*
                 * Avoid leaking the result of exec_simple_cast_value, if it
                 * performed a conversion to a pass-by-ref type.
                 */
-               if (!*isNull && coerced_value != value && !elemtypbyval)
+               if (!*isNull && coerced_value != value &&
+                   !arrayelem->elemtypbyval)
                    pfree(DatumGetPointer(coerced_value));
 
                /*
                *isNull = false;
                exec_assign_value(estate, target,
                                  PointerGetDatum(newarrayval),
-                                 arraytypeid, isNull);
+                                 arrayelem->arraytypoid, isNull);
 
                /*
                 * Avoid leaking the modified array value, too.
 
 
 drop function foreach_test(anyarray);
 drop type xy_tuple;
+--
+-- Assorted tests for array subscript assignment
+--
+create temp table rtype (id int, ar text[]);
+create function arrayassign1() returns text[] language plpgsql as $$
+declare
+ r record;
+begin
+  r := row(12, '{foo,bar,baz}')::rtype;
+  r.ar[2] := 'replace';
+  return r.ar;
+end$$;
+select arrayassign1();
+   arrayassign1    
+-------------------
+ {foo,replace,baz}
+(1 row)
+
+select arrayassign1(); -- try again to exercise internal caching
+   arrayassign1    
+-------------------
+ {foo,replace,baz}
+(1 row)
+
+create domain orderedarray as int[2]
+  constraint sorted check (value[1] < value[2]);
+select '{1,2}'::orderedarray;
+ orderedarray 
+--------------
+ {1,2}
+(1 row)
+
+select '{2,1}'::orderedarray;  -- fail
+ERROR:  value for domain orderedarray violates check constraint "sorted"
+create function testoa(x1 int, x2 int, x3 int) returns orderedarray
+language plpgsql as $$
+declare res orderedarray;
+begin
+  res := array[x1, x2];
+  res[2] := x3;
+  return res;
+end$$;
+select testoa(1,2,3);
+ testoa 
+--------
+ {1,3}
+(1 row)
+
+select testoa(1,2,3); -- try again to exercise internal caching
+ testoa 
+--------
+ {1,3}
+(1 row)
+
+select testoa(2,1,3); -- fail at initial assign
+ERROR:  value for domain orderedarray violates check constraint "sorted"
+CONTEXT:  PL/pgSQL function "testoa" line 4 at assignment
+select testoa(1,2,1); -- fail at update
+ERROR:  value for domain orderedarray violates check constraint "sorted"
+CONTEXT:  PL/pgSQL function "testoa" line 5 at assignment
+drop function arrayassign1();
+drop function testoa(x1 int, x2 int, x3 int);
 
 
 drop function foreach_test(anyarray);
 drop type xy_tuple;
+
+--
+-- Assorted tests for array subscript assignment
+--
+
+create temp table rtype (id int, ar text[]);
+
+create function arrayassign1() returns text[] language plpgsql as $$
+declare
+ r record;
+begin
+  r := row(12, '{foo,bar,baz}')::rtype;
+  r.ar[2] := 'replace';
+  return r.ar;
+end$$;
+
+select arrayassign1();
+select arrayassign1(); -- try again to exercise internal caching
+
+create domain orderedarray as int[2]
+  constraint sorted check (value[1] < value[2]);
+
+select '{1,2}'::orderedarray;
+select '{2,1}'::orderedarray;  -- fail
+
+create function testoa(x1 int, x2 int, x3 int) returns orderedarray
+language plpgsql as $$
+declare res orderedarray;
+begin
+  res := array[x1, x2];
+  res[2] := x3;
+  return res;
+end$$;
+
+select testoa(1,2,3);
+select testoa(1,2,3); -- try again to exercise internal caching
+select testoa(2,1,3); -- fail at initial assign
+select testoa(1,2,1); -- fail at update
+
+drop function arrayassign1();
+drop function testoa(x1 int, x2 int, x3 int);