Hi
I am sending rebased patch
Regards
Pavel
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 94f1f58593..4b6bf0b5bc 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -92,9 +92,10 @@ static char *NameOfDatum(PLwdatum *wdatum);
static void check_assignable(PLpgSQL_datum *datum, int location);
static void read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row,
bool *strict);
-static PLpgSQL_row *read_into_scalar_list(char *initial_name,
- PLpgSQL_datum *initial_datum,
- int initial_location);
+static void read_into_list(char *initial_name,
+ PLpgSQL_datum *initial_datum, int initial_location,
+ PLpgSQL_datum **scalar,
+ PLpgSQL_rec **rec, PLpgSQL_row **row);
static PLpgSQL_row *make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum,
int lineno, int location);
@@ -1558,33 +1559,9 @@ for_variable : T_DATUM
{
$$.name = NameOfDatum(&($1));
$$.lineno = plpgsql_location_to_lineno(@1);
- if ($1.datum->dtype == PLPGSQL_DTYPE_ROW)
- {
- $$.scalar = NULL;
- $$.rec = NULL;
- $$.row = (PLpgSQL_row *) $1.datum;
- }
- else if ($1.datum->dtype == PLPGSQL_DTYPE_REC)
- {
- $$.scalar = NULL;
- $$.rec = (PLpgSQL_rec *) $1.datum;
- $$.row = NULL;
- }
- else
- {
- int tok;
- $$.scalar = $1.datum;
- $$.rec = NULL;
- $$.row = NULL;
- /* check for comma-separated list */
- tok = yylex();
- plpgsql_push_back_token(tok);
- if (tok == ',')
- $$.row = read_into_scalar_list($$.name,
- $$.scalar,
- @1);
- }
+ read_into_list($$.name, $1.datum, @1,
+ &$$.scalar, &$$.rec, &$$.row);
}
| T_WORD
{
@@ -3337,89 +3314,21 @@ check_assignable(PLpgSQL_datum *datum, int location)
}
/*
- * Read the argument of an INTO clause. On entry, we have just read the
- * INTO keyword.
- */
-static void
-read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict)
-{
- int tok;
-
- /* Set default results */
- *rec = NULL;
- *row = NULL;
- if (strict)
- *strict = false;
-
- tok = yylex();
- if (strict && tok == K_STRICT)
- {
- *strict = true;
- tok = yylex();
- }
-
- /*
- * Currently, a row or record variable can be the single INTO target,
- * but not a member of a multi-target list. So we throw error if there
- * is a comma after it, because that probably means the user tried to
- * write a multi-target list. If this ever gets generalized, we should
- * probably refactor read_into_scalar_list so it handles all cases.
- */
- switch (tok)
- {
- case T_DATUM:
- if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW)
- {
- check_assignable(yylval.wdatum.datum, yylloc);
- *row = (PLpgSQL_row *) yylval.wdatum.datum;
-
- if ((tok = yylex()) == ',')
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("record or row variable cannot be part of multiple-item INTO list"),
- parser_errposition(yylloc)));
- plpgsql_push_back_token(tok);
- }
- else if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
- {
- check_assignable(yylval.wdatum.datum, yylloc);
- *rec = (PLpgSQL_rec *) yylval.wdatum.datum;
-
- if ((tok = yylex()) == ',')
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("record or row variable cannot be part of multiple-item INTO list"),
- parser_errposition(yylloc)));
- plpgsql_push_back_token(tok);
- }
- else
- {
- *row = read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
- yylval.wdatum.datum, yylloc);
- }
- break;
-
- default:
- /* just to give a better message than "syntax error" */
- current_token_is_not_variable(tok);
- }
-}
-
-/*
* Given the first datum and name in the INTO list, continue to read
- * comma-separated scalar variables until we run out. Then construct
+ * comma-separated variables until we run out. Then construct
* and return a fake "row" variable that represents the list of
- * scalars.
+ * fields. When there is only one rec or row field, then return
+ * this variable without nesting.
*/
-static PLpgSQL_row *
-read_into_scalar_list(char *initial_name,
- PLpgSQL_datum *initial_datum,
- int initial_location)
+static void
+read_into_list(char *initial_name,
+ PLpgSQL_datum *initial_datum, int initial_location,
+ PLpgSQL_datum **scalar, PLpgSQL_rec **rec, PLpgSQL_row **row)
{
int nfields;
char *fieldnames[1024];
int varnos[1024];
- PLpgSQL_row *row;
+ PLpgSQL_row *auxrow;
int tok;
check_assignable(initial_datum, initial_location);
@@ -3427,6 +3336,21 @@ read_into_scalar_list(char *initial_name,
varnos[0] = initial_datum->dno;
nfields = 1;
+ *rec = NULL;
+ *row = NULL;
+ if (scalar)
+ *scalar = NULL;
+
+ /*
+ * save row or rec if list has only one field.
+ */
+ if (initial_datum->dtype == PLPGSQL_DTYPE_ROW)
+ *row = (PLpgSQL_row *) initial_datum;
+ else if (initial_datum->dtype == PLPGSQL_DTYPE_REC)
+ *rec = (PLpgSQL_rec *) initial_datum;
+ else if (scalar != NULL)
+ *scalar = initial_datum;
+
while ((tok = yylex()) == ',')
{
/* Check for array overflow */
@@ -3441,13 +3365,6 @@ read_into_scalar_list(char *initial_name,
{
case T_DATUM:
check_assignable(yylval.wdatum.datum, yylloc);
- if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
- yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("\"%s\" is not a scalar variable",
- NameOfDatum(&(yylval.wdatum))),
- parser_errposition(yylloc)));
fieldnames[nfields] = NameOfDatum(&(yylval.wdatum));
varnos[nfields++] = yylval.wdatum.datum->dno;
break;
@@ -3464,23 +3381,64 @@ read_into_scalar_list(char *initial_name,
*/
plpgsql_push_back_token(tok);
- row = palloc(sizeof(PLpgSQL_row));
- row->dtype = PLPGSQL_DTYPE_ROW;
- row->refname = pstrdup("*internal*");
- row->lineno = plpgsql_location_to_lineno(initial_location);
- row->rowtupdesc = NULL;
- row->nfields = nfields;
- row->fieldnames = palloc(sizeof(char *) * nfields);
- row->varnos = palloc(sizeof(int) * nfields);
+ /* leave when new row var is not necessary */
+ if (nfields == 1 && (*row != NULL || *rec != NULL || scalar != NULL))
+ return;
+
+ auxrow = palloc(sizeof(PLpgSQL_row));
+ auxrow->dtype = PLPGSQL_DTYPE_ROW;
+ auxrow->refname = pstrdup("*internal*");
+ auxrow->lineno = plpgsql_location_to_lineno(initial_location);
+ auxrow->rowtupdesc = NULL;
+ auxrow->nfields = nfields;
+ auxrow->fieldnames = palloc(sizeof(char *) * nfields);
+ auxrow->varnos = palloc(sizeof(int) * nfields);
while (--nfields >= 0)
{
- row->fieldnames[nfields] = fieldnames[nfields];
- row->varnos[nfields] = varnos[nfields];
+ auxrow->fieldnames[nfields] = fieldnames[nfields];
+ auxrow->varnos[nfields] = varnos[nfields];
}
- plpgsql_adddatum((PLpgSQL_datum *)row);
+ plpgsql_adddatum((PLpgSQL_datum *)auxrow);
- return row;
+ /* result should not be rec */
+ *rec = NULL;
+ *row = auxrow;
+}
+
+
+/*
+ * Read the argument of an INTO clause. On entry, we have just read the
+ * INTO keyword.
+ */
+static void
+read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict)
+{
+ int tok;
+
+ /* Set default results */
+ if (strict)
+ *strict = false;
+
+ tok = yylex();
+ if (strict && tok == K_STRICT)
+ {
+ *strict = true;
+ tok = yylex();
+ }
+
+ switch (tok)
+ {
+ case T_DATUM:
+ read_into_list(NameOfDatum(&(yylval.wdatum)),
+ yylval.wdatum.datum, yylloc,
+ NULL, rec, row);
+ break;
+
+ default:
+ /* just to give a better message than "syntax error" */
+ current_token_is_not_variable(tok);
+ }
}
/*
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 7d3e9225bb..320fa68d99 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -6040,3 +6040,41 @@ END; $$ LANGUAGE plpgsql;
ERROR: "x" is not a scalar variable
LINE 3: GET DIAGNOSTICS x = ROW_COUNT;
^
+CREATE TYPE ct1 AS (a int, b numeric, c varchar);
+CREATE TYPE ct2 AS (a varchar, b int, c date);
+CREATE OR REPLACE FUNCTION multiout(IN id int, OUT v1 ct1, OUT v2 ct2, OUT v3 text)
+AS $$
+BEGIN
+ v1.a := 10;
+ v1.b := 3.14;
+ v1.c := 'ok';
+ v2.a := 'without any error';
+ v2.b := 45442;
+ v2.c := '20170514';
+ v3 := 'no error';
+END;
+$$ LANGUAGE plpgsql;
+DO $$
+DECLARE v1 ct1; v2 ct2; v3 text;
+BEGIN
+ SELECT * FROM multiout(10) INTO v1, v2, v3;
+ RAISE NOTICE 'v1 := %', v1;
+ RAISE NOTICE 'v2 := %', v2;
+ RAISE NOTICE 'v3 := %', v3;
+END;
+$$;
+NOTICE: v1 := (10,3.14,ok)
+NOTICE: v2 := ("without any error",45442,05-14-2017)
+NOTICE: v3 := no error
+-- should fail
+DO $$
+DECLARE v1 ct1; v2 ct2; v3 text;
+BEGIN
+ SELECT * FROM multiout(10) INTO v2, v1, v3;
+ RAISE NOTICE 'v1 := %', v1;
+ RAISE NOTICE 'v2 := %', v2;
+ RAISE NOTICE 'v3 := %', v3;
+END;
+$$;
+ERROR: invalid input syntax for type date: "ok"
+CONTEXT: PL/pgSQL function inline_code_block line 4 at SQL statement
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 6c9399696b..15da3d709f 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -4820,3 +4820,40 @@ BEGIN
GET DIAGNOSTICS x = ROW_COUNT;
RETURN;
END; $$ LANGUAGE plpgsql;
+
+CREATE TYPE ct1 AS (a int, b numeric, c varchar);
+CREATE TYPE ct2 AS (a varchar, b int, c date);
+
+CREATE OR REPLACE FUNCTION multiout(IN id int, OUT v1 ct1, OUT v2 ct2, OUT v3 text)
+AS $$
+BEGIN
+ v1.a := 10;
+ v1.b := 3.14;
+ v1.c := 'ok';
+ v2.a := 'without any error';
+ v2.b := 45442;
+ v2.c := '20170514';
+ v3 := 'no error';
+END;
+$$ LANGUAGE plpgsql;
+
+DO $$
+DECLARE v1 ct1; v2 ct2; v3 text;
+BEGIN
+ SELECT * FROM multiout(10) INTO v1, v2, v3;
+ RAISE NOTICE 'v1 := %', v1;
+ RAISE NOTICE 'v2 := %', v2;
+ RAISE NOTICE 'v3 := %', v3;
+END;
+$$;
+
+-- should fail
+DO $$
+DECLARE v1 ct1; v2 ct2; v3 text;
+BEGIN
+ SELECT * FROM multiout(10) INTO v2, v1, v3;
+ RAISE NOTICE 'v1 := %', v1;
+ RAISE NOTICE 'v2 := %', v2;
+ RAISE NOTICE 'v3 := %', v3;
+END;
+$$;
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers