@@ -874,7 +874,8 @@ copy_plpgsql_datum(PLpgSQL_datum *datum)
874874
875875 /*
876876 * These datum records are read-only at runtime, so no need to
877- * copy them
877+ * copy them (well, ARRAYELEM contains some cached type data,
878+ * but we'd just as soon centralize the caching anyway)
878879 */
879880 result = datum ;
880881 break ;
@@ -3986,20 +3987,16 @@ exec_assign_value(PLpgSQL_execstate *estate,
39863987 /*
39873988 * Target is an element of an array
39883989 */
3990+ PLpgSQL_arrayelem * arrayelem ;
39893991 int nsubscripts ;
39903992 int i ;
39913993 PLpgSQL_expr * subscripts [MAXDIM ];
39923994 int subscriptvals [MAXDIM ];
3993- bool oldarrayisnull ;
3994- Oid arraytypeid ,
3995- arrayelemtypeid ;
3996- int32 arraytypmod ;
3997- int16 arraytyplen ,
3998- elemtyplen ;
3999- bool elemtypbyval ;
4000- char elemtypalign ;
40013995 Datum oldarraydatum ,
40023996 coerced_value ;
3997+ bool oldarrayisnull ;
3998+ Oid parenttypoid ;
3999+ int32 parenttypmod ;
40034000 ArrayType * oldarrayval ;
40044001 ArrayType * newarrayval ;
40054002 SPITupleTable * save_eval_tuptable ;
@@ -4020,13 +4017,14 @@ exec_assign_value(PLpgSQL_execstate *estate,
40204017 * back to find the base array datum, and save the subscript
40214018 * expressions as we go. (We are scanning right to left here,
40224019 * but want to evaluate the subscripts left-to-right to
4023- * minimize surprises.)
4020+ * minimize surprises.) Note that arrayelem is left pointing
4021+ * to the leftmost arrayelem datum, where we will cache the
4022+ * array element type data.
40244023 */
40254024 nsubscripts = 0 ;
40264025 do
40274026 {
4028- PLpgSQL_arrayelem * arrayelem = (PLpgSQL_arrayelem * ) target ;
4029-
4027+ arrayelem = (PLpgSQL_arrayelem * ) target ;
40304028 if (nsubscripts >= MAXDIM )
40314029 ereport (ERROR ,
40324030 (errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
@@ -4038,24 +4036,51 @@ exec_assign_value(PLpgSQL_execstate *estate,
40384036
40394037 /* Fetch current value of array datum */
40404038 exec_eval_datum (estate , target ,
4041- & arraytypeid , & arraytypmod ,
4039+ & parenttypoid , & parenttypmod ,
40424040 & oldarraydatum , & oldarrayisnull );
40434041
4044- /* If target is domain over array, reduce to base type */
4045- arraytypeid = getBaseTypeAndTypmod (arraytypeid , & arraytypmod );
4046-
4047- /* ... and identify the element type */
4048- arrayelemtypeid = get_element_type (arraytypeid );
4049- if (!OidIsValid (arrayelemtypeid ))
4050- ereport (ERROR ,
4051- (errcode (ERRCODE_DATATYPE_MISMATCH ),
4052- errmsg ("subscripted object is not an array" )));
4053-
4054- get_typlenbyvalalign (arrayelemtypeid ,
4055- & elemtyplen ,
4056- & elemtypbyval ,
4057- & elemtypalign );
4058- arraytyplen = get_typlen (arraytypeid );
4042+ /* Update cached type data if necessary */
4043+ if (arrayelem -> parenttypoid != parenttypoid ||
4044+ arrayelem -> parenttypmod != parenttypmod )
4045+ {
4046+ Oid arraytypoid ;
4047+ int32 arraytypmod = parenttypmod ;
4048+ int16 arraytyplen ;
4049+ Oid elemtypoid ;
4050+ int16 elemtyplen ;
4051+ bool elemtypbyval ;
4052+ char elemtypalign ;
4053+
4054+ /* If target is domain over array, reduce to base type */
4055+ arraytypoid = getBaseTypeAndTypmod (parenttypoid ,
4056+ & arraytypmod );
4057+
4058+ /* ... and identify the element type */
4059+ elemtypoid = get_element_type (arraytypoid );
4060+ if (!OidIsValid (elemtypoid ))
4061+ ereport (ERROR ,
4062+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
4063+ errmsg ("subscripted object is not an array" )));
4064+
4065+ /* Collect needed data about the types */
4066+ arraytyplen = get_typlen (arraytypoid );
4067+
4068+ get_typlenbyvalalign (elemtypoid ,
4069+ & elemtyplen ,
4070+ & elemtypbyval ,
4071+ & elemtypalign );
4072+
4073+ /* Now safe to update the cached data */
4074+ arrayelem -> parenttypoid = parenttypoid ;
4075+ arrayelem -> parenttypmod = parenttypmod ;
4076+ arrayelem -> arraytypoid = arraytypoid ;
4077+ arrayelem -> arraytypmod = arraytypmod ;
4078+ arrayelem -> arraytyplen = arraytyplen ;
4079+ arrayelem -> elemtypoid = elemtypoid ;
4080+ arrayelem -> elemtyplen = elemtyplen ;
4081+ arrayelem -> elemtypbyval = elemtypbyval ;
4082+ arrayelem -> elemtypalign = elemtypalign ;
4083+ }
40594084
40604085 /*
40614086 * Evaluate the subscripts, switch into left-to-right order.
@@ -4093,8 +4118,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
40934118 /* Coerce source value to match array element type. */
40944119 coerced_value = exec_simple_cast_value (value ,
40954120 valtype ,
4096- arrayelemtypeid ,
4097- arraytypmod ,
4121+ arrayelem -> elemtypoid ,
4122+ arrayelem -> arraytypmod ,
40984123 * isNull );
40994124
41004125 /*
@@ -4107,12 +4132,12 @@ exec_assign_value(PLpgSQL_execstate *estate,
41074132 * array, either, so that's a no-op too. This is all ugly but
41084133 * corresponds to the current behavior of ExecEvalArrayRef().
41094134 */
4110- if (arraytyplen > 0 && /* fixed-length array? */
4135+ if (arrayelem -> arraytyplen > 0 && /* fixed-length array? */
41114136 (oldarrayisnull || * isNull ))
41124137 return ;
41134138
41144139 if (oldarrayisnull )
4115- oldarrayval = construct_empty_array (arrayelemtypeid );
4140+ oldarrayval = construct_empty_array (arrayelem -> elemtypoid );
41164141 else
41174142 oldarrayval = (ArrayType * ) DatumGetPointer (oldarraydatum );
41184143
@@ -4124,16 +4149,17 @@ exec_assign_value(PLpgSQL_execstate *estate,
41244149 subscriptvals ,
41254150 coerced_value ,
41264151 * isNull ,
4127- arraytyplen ,
4128- elemtyplen ,
4129- elemtypbyval ,
4130- elemtypalign );
4152+ arrayelem -> arraytyplen ,
4153+ arrayelem -> elemtyplen ,
4154+ arrayelem -> elemtypbyval ,
4155+ arrayelem -> elemtypalign );
41314156
41324157 /*
41334158 * Avoid leaking the result of exec_simple_cast_value, if it
41344159 * performed a conversion to a pass-by-ref type.
41354160 */
4136- if (!* isNull && coerced_value != value && !elemtypbyval )
4161+ if (!* isNull && coerced_value != value &&
4162+ !arrayelem -> elemtypbyval )
41374163 pfree (DatumGetPointer (coerced_value ));
41384164
41394165 /*
@@ -4145,7 +4171,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
41454171 * isNull = false;
41464172 exec_assign_value (estate , target ,
41474173 PointerGetDatum (newarrayval ),
4148- arraytypeid , isNull );
4174+ arrayelem -> arraytypoid , isNull );
41494175
41504176 /*
41514177 * Avoid leaking the modified array value, too.
0 commit comments