From baebab3ace480477f210dadc4633d8d119dfa978 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Tue, 12 Jul 2016 18:06:50 -0400 Subject: [PATCH] Allow IMPORT FOREIGN SCHEMA within pl/pgsql. Since IMPORT FOREIGN SCHEMA has an INTO clause, pl/pgsql needs to be aware of that and avoid capturing the INTO as an INTO-variables clause. This isn't hard, though it's annoying to have to make IMPORT a plpgsql keyword just for this. (Fortunately, we have the infrastructure now to make it an unreserved keyword, so at least this shouldn't break any existing pl/pgsql code.) Per report from Merlin Moncure. Back-patch to 9.5 where IMPORT FOREIGN SCHEMA was introduced. Report: <CAHyXU0wpHf2bbtKGL1gtUEFATCY86r=VKxfcACVcTMQ70mCyig@mail.gmail.com> --- src/pl/plpgsql/src/pl_gram.y | 47 ++++++++++++++++++++++++++------- src/pl/plpgsql/src/pl_scanner.c | 1 + 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y index df09575e123..0b41e3acb6c 100644 --- a/src/pl/plpgsql/src/pl_gram.y +++ b/src/pl/plpgsql/src/pl_gram.y @@ -287,6 +287,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %token <keyword> K_GET %token <keyword> K_HINT %token <keyword> K_IF +%token <keyword> K_IMPORT %token <keyword> K_IN %token <keyword> K_INFO %token <keyword> K_INSERT @@ -1929,7 +1930,11 @@ loop_body : proc_sect K_END K_LOOP opt_label ';' * assignment. Give an appropriate complaint for that, instead of letting * the core parser throw an unhelpful "syntax error". */ -stmt_execsql : K_INSERT +stmt_execsql : K_IMPORT + { + $$ = make_execsql_stmt(K_IMPORT, @1); + } + | K_INSERT { $$ = make_execsql_stmt(K_INSERT, @1); } @@ -2418,6 +2423,7 @@ unreserved_keyword : | K_FORWARD | K_GET | K_HINT + | K_IMPORT | K_INFO | K_INSERT | K_IS @@ -2843,12 +2849,32 @@ make_execsql_stmt(int firsttoken, int location) plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR; /* - * We have to special-case the sequence INSERT INTO, because we don't want - * that to be taken as an INTO-variables clause. Fortunately, this is the - * only valid use of INTO in a pl/pgsql SQL command, and INTO is already a - * fully reserved word in the main grammar. We have to treat it that way - * anywhere in the string, not only at the start; consider CREATE RULE - * containing an INSERT statement. + * Scan to the end of the SQL command. Identify any INTO-variables + * clause lurking within it, and parse that via read_into_target(). + * + * Because INTO is sometimes used in the main SQL grammar, we have to be + * careful not to take any such usage of INTO as a pl/pgsql INTO clause. + * There are currently three such cases: + * + * 1. SELECT ... INTO. We don't care, we just override that with the + * pl/pgsql definition. + * + * 2. INSERT INTO. This is relatively easy to recognize since the words + * must appear adjacently; but we can't assume INSERT starts the command, + * because it can appear in CREATE RULE or WITH. Unfortunately, INSERT is + * *not* fully reserved, so that means there is a chance of a false match; + * but it's not very likely. + * + * 3. IMPORT FOREIGN SCHEMA ... INTO. This is not allowed in CREATE RULE + * or WITH, so we just check for IMPORT as the command's first token. + * (If IMPORT FOREIGN SCHEMA returned data someone might wish to capture + * with an INTO-variables clause, we'd have to work much harder here.) + * + * Fortunately, INTO is a fully reserved word in the main grammar, so + * at least we need not worry about it appearing as an identifier. + * + * Any future additional uses of INTO in the main grammar will doubtless + * break this logic again ... beware! */ tok = firsttoken; for (;;) @@ -2861,9 +2887,12 @@ make_execsql_stmt(int firsttoken, int location) break; if (tok == 0) yyerror("unexpected end of function definition"); - - if (tok == K_INTO && prev_tok != K_INSERT) + if (tok == K_INTO) { + if (prev_tok == K_INSERT) + continue; /* INSERT INTO is not an INTO-target */ + if (firsttoken == K_IMPORT) + continue; /* IMPORT ... INTO is not an INTO-target */ if (have_into) yyerror("INTO specified more than once"); have_into = true; diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c index bb0f25b18ce..2737fff3fbf 100644 --- a/src/pl/plpgsql/src/pl_scanner.c +++ b/src/pl/plpgsql/src/pl_scanner.c @@ -127,6 +127,7 @@ static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("forward", K_FORWARD, UNRESERVED_KEYWORD) PG_KEYWORD("get", K_GET, UNRESERVED_KEYWORD) PG_KEYWORD("hint", K_HINT, UNRESERVED_KEYWORD) + PG_KEYWORD("import", K_IMPORT, UNRESERVED_KEYWORD) PG_KEYWORD("info", K_INFO, UNRESERVED_KEYWORD) PG_KEYWORD("insert", K_INSERT, UNRESERVED_KEYWORD) PG_KEYWORD("is", K_IS, UNRESERVED_KEYWORD) -- GitLab