Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit16762b5

Browse files
committed
Speed up array element assignment in plpgsql by caching type information.
Cache assorted data in the PLpgSQL_arrayelem struct to avoid repetitivecatalog lookups over multiple executions of the same statement.Pavel Stehule
1 parent821fd90 commit16762b5

File tree

5 files changed

+179
-38
lines changed

5 files changed

+179
-38
lines changed

‎src/pl/plpgsql/src/gram.y

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,8 @@ assign_var: T_DATUM
998998
new->dtype= PLPGSQL_DTYPE_ARRAYELEM;
999999
new->subscript= $3;
10001000
new->arrayparentno = $1;
1001+
/* initialize cached type data to "not valid"*/
1002+
new->parenttypoid = InvalidOid;
10011003

10021004
plpgsql_adddatum((PLpgSQL_datum *)new);
10031005

‎src/pl/plpgsql/src/pl_exec.c

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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
intnsubscripts;
39903992
inti;
39913993
PLpgSQL_expr*subscripts[MAXDIM];
39923994
intsubscriptvals[MAXDIM];
3993-
boololdarrayisnull;
3994-
Oidarraytypeid,
3995-
arrayelemtypeid;
3996-
int32arraytypmod;
3997-
int16arraytyplen,
3998-
elemtyplen;
3999-
boolelemtypbyval;
4000-
charelemtypalign;
40013995
Datumoldarraydatum,
40023996
coerced_value;
3997+
boololdarrayisnull;
3998+
Oidparenttypoid;
3999+
int32parenttypmod;
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+
Oidarraytypoid;
4047+
int32arraytypmod=parenttypmod;
4048+
int16arraytyplen;
4049+
Oidelemtypoid;
4050+
int16elemtyplen;
4051+
boolelemtypbyval;
4052+
charelemtypalign;
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.

‎src/pl/plpgsql/src/plpgsql.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,16 @@ typedef struct
299299
intdno;
300300
PLpgSQL_expr*subscript;
301301
intarrayparentno;/* dno of parent array variable */
302+
/* Remaining fields are cached info about the array variable's type */
303+
Oidparenttypoid;/* type of array variable; 0 if not yet set */
304+
int32parenttypmod;/* typmod of array variable */
305+
Oidarraytypoid;/* OID of actual array type */
306+
int32arraytypmod;/* typmod of array (and its elements too) */
307+
int16arraytyplen;/* typlen of array type */
308+
Oidelemtypoid;/* OID of array element type */
309+
int16elemtyplen;/* typlen of element type */
310+
boolelemtypbyval;/* element type is pass-by-value? */
311+
charelemtypalign;/* typalign of element type */
302312
}PLpgSQL_arrayelem;
303313

304314

‎src/test/regress/expected/plpgsql.out

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4509,3 +4509,65 @@ NOTICE: {"(35,78)","(88,76)"}
45094509

45104510
drop function foreach_test(anyarray);
45114511
drop type xy_tuple;
4512+
--
4513+
-- Assorted tests for array subscript assignment
4514+
--
4515+
create temp table rtype (id int, ar text[]);
4516+
create function arrayassign1() returns text[] language plpgsql as $$
4517+
declare
4518+
r record;
4519+
begin
4520+
r := row(12, '{foo,bar,baz}')::rtype;
4521+
r.ar[2] := 'replace';
4522+
return r.ar;
4523+
end$$;
4524+
select arrayassign1();
4525+
arrayassign1
4526+
-------------------
4527+
{foo,replace,baz}
4528+
(1 row)
4529+
4530+
select arrayassign1(); -- try again to exercise internal caching
4531+
arrayassign1
4532+
-------------------
4533+
{foo,replace,baz}
4534+
(1 row)
4535+
4536+
create domain orderedarray as int[2]
4537+
constraint sorted check (value[1] < value[2]);
4538+
select '{1,2}'::orderedarray;
4539+
orderedarray
4540+
--------------
4541+
{1,2}
4542+
(1 row)
4543+
4544+
select '{2,1}'::orderedarray; -- fail
4545+
ERROR: value for domain orderedarray violates check constraint "sorted"
4546+
create function testoa(x1 int, x2 int, x3 int) returns orderedarray
4547+
language plpgsql as $$
4548+
declare res orderedarray;
4549+
begin
4550+
res := array[x1, x2];
4551+
res[2] := x3;
4552+
return res;
4553+
end$$;
4554+
select testoa(1,2,3);
4555+
testoa
4556+
--------
4557+
{1,3}
4558+
(1 row)
4559+
4560+
select testoa(1,2,3); -- try again to exercise internal caching
4561+
testoa
4562+
--------
4563+
{1,3}
4564+
(1 row)
4565+
4566+
select testoa(2,1,3); -- fail at initial assign
4567+
ERROR: value for domain orderedarray violates check constraint "sorted"
4568+
CONTEXT: PL/pgSQL function "testoa" line 4 at assignment
4569+
select testoa(1,2,1); -- fail at update
4570+
ERROR: value for domain orderedarray violates check constraint "sorted"
4571+
CONTEXT: PL/pgSQL function "testoa" line 5 at assignment
4572+
drop function arrayassign1();
4573+
drop function testoa(x1 int, x2 int, x3 int);

‎src/test/regress/sql/plpgsql.sql

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3559,3 +3559,44 @@ select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
35593559
35603560
drop function foreach_test(anyarray);
35613561
drop type xy_tuple;
3562+
3563+
--
3564+
-- Assorted tests for array subscript assignment
3565+
--
3566+
3567+
create temp table rtype (id int, ar text[]);
3568+
3569+
create function arrayassign1() returns text[] language plpgsql as $$
3570+
declare
3571+
r record;
3572+
begin
3573+
r := row(12,'{foo,bar,baz}')::rtype;
3574+
r.ar[2] :='replace';
3575+
return r.ar;
3576+
end$$;
3577+
3578+
select arrayassign1();
3579+
select arrayassign1(); -- try again to exercise internal caching
3580+
3581+
create domain orderedarray as int[2]
3582+
constraint sorted check (value[1] < value[2]);
3583+
3584+
select'{1,2}'::orderedarray;
3585+
select'{2,1}'::orderedarray; -- fail
3586+
3587+
create function testoa(x1 int, x2 int, x3 int) returns orderedarray
3588+
language plpgsql as $$
3589+
declare res orderedarray;
3590+
begin
3591+
res := array[x1, x2];
3592+
res[2] := x3;
3593+
return res;
3594+
end$$;
3595+
3596+
select testoa(1,2,3);
3597+
select testoa(1,2,3); -- try again to exercise internal caching
3598+
select testoa(2,1,3); -- fail at initial assign
3599+
select testoa(1,2,1); -- fail at update
3600+
3601+
drop function arrayassign1();
3602+
drop function testoa(x1 int, x2 int, x3 int);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp