git.postgresql.org Git - postgresql.git/commitdiff

git projects / postgresql.git / commitdiff
? search:
summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 19956e0)
Fix plpgsql to pass only one copy of any given plpgsql variable into a SQL
2006年3月23日 04:22:37 +0000 (04:22 +0000)
2006年3月23日 04:22:37 +0000 (04:22 +0000)
command or expression, rather than one copy for each textual occurrence as
it did before. This might result in some small performance improvement,
but the compelling reason to do it is that not doing so can result in
unexpected grouping failures because the main SQL parser won't see different
parameter numbers as equivalent. Add a regression test for the failure case.
Per report from Robert Davidson.


diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 8636a0a6b85628f6f3c6d99b99e8ed9c0db7d89c..7364e89b949ed109c6e6fcec13a63efcaea8ac14 100644 (file)
--- 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 e72383bbb72c27b9e5a471cb44ce23ffd718613b..6e6597dadb2f452a7880e74c9295101efb570810 100644 (file)
--- 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 a2d65817b1fc4066eb52a88f622ad6a071b57f2b..19e145be65f359bc3c3d22318cab92656e31c0f0 100644 (file)
--- 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);
This is the main PostgreSQL git repository.
RSS Atom

AltStyle によって変換されたページ (->オリジナル) /