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 (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers