diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 1300ea6a525..2d7844bd9da 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -2384,14 +2384,14 @@ plpgsql_finish_datums(PLpgSQL_function *function) /* ---------- * plpgsql_add_initdatums Make an array of the datum numbers of - * all the simple VAR datums created since the last call + * all the initializable datums created since the last call * to this function. * * If varnos is NULL, we just forget any datum entries created since the * last call. * - * This is used around a DECLARE section to create a list of the VARs - * that have to be initialized at block entry. Note that VARs can also + * This is used around a DECLARE section to create a list of the datums + * that have to be initialized at block entry. Note that datums can also * be created elsewhere than DECLARE, eg by a FOR-loop, but it is then * the responsibility of special-purpose code to initialize them. * ---------- @@ -2402,11 +2402,16 @@ plpgsql_add_initdatums(int **varnos) int i; int n = 0; + /* + * The set of dtypes recognized here must match what exec_stmt_block() + * cares about (re)initializing at block entry. + */ for (i = datums_last; i < plpgsql_nDatums; i++) { switch (plpgsql_Datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: + case PLPGSQL_DTYPE_REC: n++; break; @@ -2427,6 +2432,7 @@ plpgsql_add_initdatums(int **varnos) switch (plpgsql_Datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: + case PLPGSQL_DTYPE_REC: (*varnos)[n++] = plpgsql_Datums[i]->dno; default: diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 1959d6dc424..fa4d573e50c 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -1184,7 +1184,6 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) { volatile int rc = -1; int i; - int n; /* * First initialize all variables declared in this block @@ -1193,13 +1192,17 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) for (i = 0; i < block->n_initvars; i++) { - n = block->initvarnos[i]; + int n = block->initvarnos[i]; + PLpgSQL_datum *datum = estate->datums[n]; - switch (estate->datums[n]->dtype) + /* + * The set of dtypes handled here must match plpgsql_add_initdatums(). + */ + switch (datum->dtype) { case PLPGSQL_DTYPE_VAR: { - PLpgSQL_var *var = (PLpgSQL_var *) (estate->datums[n]); + PLpgSQL_var *var = (PLpgSQL_var *) datum; /* * Free any old value, in case re-entering block, and @@ -1241,7 +1244,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) case PLPGSQL_DTYPE_REC: { - PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[n]); + PLpgSQL_rec *rec = (PLpgSQL_rec *) datum; if (rec->freetup) { @@ -1258,13 +1261,8 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) } break; - case PLPGSQL_DTYPE_RECFIELD: - case PLPGSQL_DTYPE_ARRAYELEM: - break; - default: - elog(ERROR, "unrecognized dtype: %d", - estate->datums[n]->dtype); + elog(ERROR, "unrecognized dtype: %d", datum->dtype); } } diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 84485785370..39bd82acd19 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -407,8 +407,8 @@ typedef struct PLpgSQL_stmt_block int lineno; char *label; List *body; /* List of statements */ - int n_initvars; - int *initvarnos; + int n_initvars; /* Length of initvarnos[] */ + int *initvarnos; /* dnos of variables declared in this block */ PLpgSQL_exception_block *exceptions; } PLpgSQL_stmt_block; diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index d6e5bc33536..29f9e86d560 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -5025,6 +5025,33 @@ select scope_test(); (1 row) drop function scope_test(); +-- Check that variables are reinitialized on block re-entry. +do $$ +begin + for i in 1..3 loop + declare + x int; + y int := i; + r record; + begin + if i = 1 then + x := 42; + r := row(i, i+1); + end if; + raise notice 'x = %', x; + raise notice 'y = %', y; + raise notice 'r = %', r; + end; + end loop; +end$$; +NOTICE: x = 42 +NOTICE: y = 1 +NOTICE: r = (1,2) +NOTICE: x = +NOTICE: y = 2 +ERROR: record "r" is not assigned yet +DETAIL: The tuple structure of a not-yet-assigned record is indeterminate. +CONTEXT: PL/pgSQL function inline_code_block line 15 at RAISE -- Check handling of conflicts between plpgsql vars and table columns. set plpgsql.variable_conflict = error; create function conflict_test() returns setof int8_tbl as $$ diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 1c355132b77..07b6fc89716 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -4014,6 +4014,27 @@ select scope_test(); drop function scope_test(); +-- Check that variables are reinitialized on block re-entry. + +do $$ +begin + for i in 1..3 loop + declare + x int; + y int := i; + r record; + begin + if i = 1 then + x := 42; + r := row(i, i+1); + end if; + raise notice 'x = %', x; + raise notice 'y = %', y; + raise notice 'r = %', r; + end; + end loop; +end$$; + -- Check handling of conflicts between plpgsql vars and table columns. set plpgsql.variable_conflict = error;