diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 8636a0a6b8..7364e89b94 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.87 2006/03/09 21:29:36 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.88 2006/03/23 04:22:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1714,6 +1714,44 @@ lno : %% +#define MAX_EXPR_PARAMS 1024 + +/* + * determine the expression parameter position to use for a plpgsql datum + * + * It is important that any given plpgsql datum map to just one parameter. + * We used to be sloppy and assign a separate parameter for each occurrence + * of a datum reference, but that fails for situations such as "select DATUM + * from ... group by DATUM". + * + * The params[] array must be of size MAX_EXPR_PARAMS. + */ +static int +assign_expr_param(int dno, int *params, int *nparams) +{ + int i; + + /* already have an instance of this dno? */ + for (i = 0; i < *nparams; i++) + { + if (params[i] == dno) + return i+1; + } + /* check for array overflow */ + if (*nparams >= MAX_EXPR_PARAMS) + { + plpgsql_error_lineno = plpgsql_scanner_lineno(); + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("too many variables specified in SQL statement"))); + } + /* add new parameter dno to array */ + params[*nparams] = dno; + (*nparams)++; + return *nparams; +} + + PLpgSQL_expr * plpgsql_read_expression(int until, const char *expected) { @@ -1752,7 +1790,7 @@ read_sql_construct(int until, PLpgSQL_dstring ds; int parenlevel = 0; int nparams = 0; - int params[1024]; + int params[MAX_EXPR_PARAMS]; char buf[32]; PLpgSQL_expr *expr; @@ -1804,32 +1842,26 @@ read_sql_construct(int until, if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); - /* Check for array overflow */ - if (nparams >= 1024) - { - plpgsql_error_lineno = lno; - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("too many variables specified in SQL statement"))); - } - switch (tok) { case T_SCALAR: - params[nparams] = yylval.scalar->dno; - snprintf(buf, sizeof(buf), " $%d ", ++nparams); + snprintf(buf, sizeof(buf), " $%d ", + assign_expr_param(yylval.scalar->dno, + params, &nparams)); plpgsql_dstring_append(&ds, buf); break; case T_ROW: - params[nparams] = yylval.row->rowno; - snprintf(buf, sizeof(buf), " $%d ", ++nparams); + snprintf(buf, sizeof(buf), " $%d ", + assign_expr_param(yylval.row->rowno, + params, &nparams)); plpgsql_dstring_append(&ds, buf); break; case T_RECORD: - params[nparams] = yylval.rec->recno; - snprintf(buf, sizeof(buf), " $%d ", ++nparams); + snprintf(buf, sizeof(buf), " $%d ", + assign_expr_param(yylval.rec->recno, + params, &nparams)); plpgsql_dstring_append(&ds, buf); break; @@ -1927,7 +1959,7 @@ make_select_stmt(void) { PLpgSQL_dstring ds; int nparams = 0; - int params[1024]; + int params[MAX_EXPR_PARAMS]; char buf[32]; PLpgSQL_expr *expr; PLpgSQL_row *row = NULL; @@ -1992,32 +2024,26 @@ make_select_stmt(void) if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); - /* Check for array overflow */ - if (nparams >= 1024) - { - plpgsql_error_lineno = plpgsql_scanner_lineno(); - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("too many parameters specified in SQL statement"))); - } - switch (tok) { case T_SCALAR: - params[nparams] = yylval.scalar->dno; - snprintf(buf, sizeof(buf), " $%d ", ++nparams); + snprintf(buf, sizeof(buf), " $%d ", + assign_expr_param(yylval.scalar->dno, + params, &nparams)); plpgsql_dstring_append(&ds, buf); break; case T_ROW: - params[nparams] = yylval.row->rowno; - snprintf(buf, sizeof(buf), " $%d ", ++nparams); + snprintf(buf, sizeof(buf), " $%d ", + assign_expr_param(yylval.row->rowno, + params, &nparams)); plpgsql_dstring_append(&ds, buf); break; case T_RECORD: - params[nparams] = yylval.rec->recno; - snprintf(buf, sizeof(buf), " $%d ", ++nparams); + snprintf(buf, sizeof(buf), " $%d ", + assign_expr_param(yylval.rec->recno, + params, &nparams)); plpgsql_dstring_append(&ds, buf); break; diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index e72383bbb7..6e6597dadb 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -2776,3 +2776,21 @@ NOTICE: 4 bb cc (1 row) +-- regression test: verify that multiple uses of same plpgsql datum within +-- a SQL command all get mapped to the same $n parameter. The return value +-- of the SELECT is not important, we only care that it doesn't fail with +-- a complaint about an ungrouped column reference. +create function multi_datum_use(p1 int) returns bool as $$ +declare + x int; + y int; +begin + select into x,y unique1/p1, unique1/$1 from tenk1 group by unique1/p1; + return x = y; +end$$ language plpgsql; +select multi_datum_use(42); + multi_datum_use +----------------- + t +(1 row) + diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index a2d65817b1..19e145be65 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2309,3 +2309,18 @@ end; $proc$ language plpgsql; select for_vect(); + +-- regression test: verify that multiple uses of same plpgsql datum within +-- a SQL command all get mapped to the same $n parameter. The return value +-- of the SELECT is not important, we only care that it doesn't fail with +-- a complaint about an ungrouped column reference. +create function multi_datum_use(p1 int) returns bool as $$ +declare + x int; + y int; +begin + select into x,y unique1/p1, unique1/$1 from tenk1 group by unique1/p1; + return x = y; +end$$ language plpgsql; + +select multi_datum_use(42);