diff --git a/src/bin/psql/psqlscan.l b/src/bin/psql/psqlscan.l index a736bbdb7..dd9a18085 100644 --- a/src/bin/psql/psqlscan.l +++ b/src/bin/psql/psqlscan.l @@ -2310,6 +2310,17 @@ analyze_state(const char* text,PsqlScanStateData* state) state->count_to_read = -1; break; case IDEN_PROCEDURE: + if (state->declare_encountered && state->begin_state == BEGIN_UNDEFINED) { + /* + * declare_encountered is true means we have met a 'declare' already, sql may like + * 'create trigger declare on declare execute procedure xx', 'declare' should be treat as an identifier + * not a keyword. PROCEDURE is a reserve keyword, so it must be a keyword not a identifier. + */ + state->declare_encountered = false; + state->gram_state[IDEN_PROCEDURE] = false; + state->gram_state[IDEN_DECLARE] = false; + break; + } state->gram_state[IDEN_AS] = true; state->gram_state[IDEN_IS] = true; state->include_ora_comment = true; @@ -2365,6 +2376,7 @@ analyze_state(const char* text,PsqlScanStateData* state) state->gram_state[IDEN_BINARY] = true; state->gram_state[IDEN_INSENSITIVE] = true; state->gram_state[IDEN_NO] = true; + state->gram_state[IDEN_PROCEDURE] = true; state->declare_encountered = true; break; case IDEN_BEGIN: diff --git a/src/common/backend/parser/parser.cpp b/src/common/backend/parser/parser.cpp index 718a71e26..b48b169eb 100644 --- a/src/common/backend/parser/parser.cpp +++ b/src/common/backend/parser/parser.cpp @@ -108,21 +108,25 @@ List* raw_parser(const char* str, List** query_string_locationlist) return yyextra.parsetree; } -#define GET_NEXT_TOKEN() \ +#define GET_NEXT_TOKEN_WITHOUT_YY() \ do { \ - cur_yylval = lvalp->core_yystype; \ - cur_yylloc = *llocp; \ if (yyextra->lookahead_num != 0) { \ next_token = yyextra->lookahead_token[yyextra->lookahead_num - 1]; \ lvalp->core_yystype = yyextra->lookahead_yylval[yyextra->lookahead_num - 1]; \ *llocp = yyextra->lookahead_yylloc[yyextra->lookahead_num - 1]; \ yyextra->lookahead_num--; \ - Assert(yyextra->lookahead_num == 0); \ } else { \ next_token = core_yylex(&(lvalp->core_yystype), llocp, yyscanner); \ } \ } while (0) +#define GET_NEXT_TOKEN() \ + do { \ + cur_yylval = lvalp->core_yystype; \ + cur_yylloc = *llocp; \ + GET_NEXT_TOKEN_WITHOUT_YY(); \ + } while (0) + #define SET_LOOKAHEAD_TOKEN() \ do { \ yyextra->lookahead_token[0] = next_token; \ @@ -428,17 +432,20 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) /* * DECLARE foo CUROSR must be looked ahead, and if determined as a DECLARE_CURSOR, we should set the yylaval * and yylloc back, letting the parser read the cursor name correctly. + * here we may still have token in lookahead_token, so use GET_NEXT_TOKEN to get */ - cur_yylval = lvalp->core_yystype; - cur_yylloc = *llocp; - next_token = core_yylex(&(lvalp->core_yystype), llocp, yyscanner); + GET_NEXT_TOKEN(); /* get first token after DECLARE. We don't care what it is */ yyextra->lookahead_token[1] = next_token; yyextra->lookahead_yylval[1] = lvalp->core_yystype; yyextra->lookahead_yylloc[1] = *llocp; - /* get the second token after DECLARE. If it is cursor grammer, we are sure that this is a cursr stmt */ - next_token = core_yylex(&(lvalp->core_yystype), llocp, yyscanner); + /* + * get the second token after DECLARE. If it is cursor grammer, we are sure that this is a cursr stmt + * in fact we don't have any lookahead_token here for sure, cause MAX_LOOKAHEAD_NUM is 2. + * but maybe someday MAX_LOOKAHEAD_NUM increase, so we still use GET_NEXT_TOKEN_WITHOUT_SET_CURYY + */ + GET_NEXT_TOKEN_WITHOUT_YY(); yyextra->lookahead_token[0] = next_token; yyextra->lookahead_yylval[0] = lvalp->core_yystype; yyextra->lookahead_yylloc[0] = *llocp; @@ -523,35 +530,38 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) } break; case ON: - cur_yylval = lvalp->core_yystype; - cur_yylloc = *llocp; - next_token = core_yylex(&(lvalp->core_yystype), llocp, yyscanner); + /* here we may still have token in lookahead_token, so use GET_NEXT_TOKEN to get */ + GET_NEXT_TOKEN(); /* get first token after ON (Normal UPDATE). We don't care what it is */ yyextra->lookahead_token[1] = next_token; yyextra->lookahead_yylval[1] = lvalp->core_yystype; yyextra->lookahead_yylloc[1] = *llocp; - /* get the second token after ON. */ - next_token = core_yylex(&(lvalp->core_yystype), llocp, yyscanner); + /* + * get the second token after ON. + * in fact we don't have any lookahead_token here for sure, cause MAX_LOOKAHEAD_NUM is 2. + * but maybe someday MAX_LOOKAHEAD_NUM increase, so we still use GET_NEXT_TOKEN_WITHOUT_SET_CURYY + */ + GET_NEXT_TOKEN_WITHOUT_YY(); yyextra->lookahead_token[0] = next_token; yyextra->lookahead_yylval[0] = lvalp->core_yystype; yyextra->lookahead_yylloc[0] = *llocp; yyextra->lookahead_num = 2; switch (next_token) { - case CURRENT_TIMESTAMP: - case CURRENT_TIME: - case CURRENT_DATE: - case LOCALTIME: - case LOCALTIMESTAMP: - cur_token = ON_UPDATE_TIME; - lvalp->core_yystype = cur_yylval; - *llocp = cur_yylloc; - break; - default: - /* and back up the output info to cur_token */ - lvalp->core_yystype = cur_yylval; - *llocp = cur_yylloc; - break; + case CURRENT_TIMESTAMP: + case CURRENT_TIME: + case CURRENT_DATE: + case LOCALTIME: + case LOCALTIMESTAMP: + cur_token = ON_UPDATE_TIME; + lvalp->core_yystype = cur_yylval; + *llocp = cur_yylloc; + break; + default: + /* and back up the output info to cur_token */ + lvalp->core_yystype = cur_yylval; + *llocp = cur_yylloc; + break; } break; default: diff --git a/src/test/regress/expected/single_node_triggers.out b/src/test/regress/expected/single_node_triggers.out index 3ea503adc..875b7c416 100644 --- a/src/test/regress/expected/single_node_triggers.out +++ b/src/test/regress/expected/single_node_triggers.out @@ -1594,3 +1594,72 @@ drop table depth_a, depth_b, depth_c; drop function depth_a_tf(); drop function depth_b_tf(); drop function depth_c_tf(); +-- test declare + on in one sql. +create schema trigger_declare_test; +set current_schema to trigger_declare_test; +create table declare_test(id int); +create index declare on declare_test(id); +\d declare_test +Table "trigger_declare_test.declare_test" + Column | Type | Modifiers +--------+---------+----------- + id | integer | +Indexes: + "declare" btree (id) TABLESPACE pg_default + +drop index declare; +create table declare(id int); +create index id_test on declare(id); +\d declare +Table "trigger_declare_test.declare" + Column | Type | Modifiers +--------+---------+----------- + id | integer | +Indexes: + "id_test" btree (id) TABLESPACE pg_default + +CREATE OR REPLACE FUNCTION declare() RETURNS TRIGGER AS +$$ +DECLARE +BEGIN + INSERT INTO declare_test VALUES(NEW.id); + RETURN NEW; +END +$$ LANGUAGE PLPGSQL; +CREATE TRIGGER declare AFTER INSERT ON declare FOR EACH ROW EXECUTE PROCEDURE declare(); +insert into declare values(1); +drop TRIGGER declare on declare cascade; +CREATE TRIGGER declare_test AFTER INSERT ON declare FOR EACH ROW EXECUTE PROCEDURE declare(); +insert into declare values(2); +drop TRIGGER declare_test on declare cascade; +CREATE OR REPLACE FUNCTION declare() RETURNS TRIGGER AS +$$ +DECLARE +BEGIN + INSERT INTO declare VALUES(NEW.id); + RETURN NEW; +END +$$ LANGUAGE PLPGSQL; +CREATE TRIGGER declare AFTER INSERT ON declare_test FOR EACH ROW EXECUTE PROCEDURE declare(); +insert into declare_test values(3); +drop TRIGGER declare on declare_test cascade; +select * from declare order by 1; + id +---- + 1 + 2 + 3 +(3 rows) + +select * from declare_test order by 1; + id +---- + 1 + 2 + 3 +(3 rows) + +drop table declare; +drop table declare_test; +drop schema trigger_declare_test cascade; +NOTICE: drop cascades to function declare() diff --git a/src/test/regress/sql/single_node_triggers.sql b/src/test/regress/sql/single_node_triggers.sql index c98d070a5..a51081986 100644 --- a/src/test/regress/sql/single_node_triggers.sql +++ b/src/test/regress/sql/single_node_triggers.sql @@ -1044,3 +1044,51 @@ drop table depth_a, depth_b, depth_c; drop function depth_a_tf(); drop function depth_b_tf(); drop function depth_c_tf(); + +-- test declare + on in one sql. +create schema trigger_declare_test; +set current_schema to trigger_declare_test; +create table declare_test(id int); +create index declare on declare_test(id); +\d declare_test +drop index declare; + +create table declare(id int); +create index id_test on declare(id); +\d declare + +CREATE OR REPLACE FUNCTION declare() RETURNS TRIGGER AS +$$ +DECLARE +BEGIN + INSERT INTO declare_test VALUES(NEW.id); + RETURN NEW; +END +$$ LANGUAGE PLPGSQL; + +CREATE TRIGGER declare AFTER INSERT ON declare FOR EACH ROW EXECUTE PROCEDURE declare(); +insert into declare values(1); +drop TRIGGER declare on declare cascade; + +CREATE TRIGGER declare_test AFTER INSERT ON declare FOR EACH ROW EXECUTE PROCEDURE declare(); +insert into declare values(2); +drop TRIGGER declare_test on declare cascade; + +CREATE OR REPLACE FUNCTION declare() RETURNS TRIGGER AS +$$ +DECLARE +BEGIN + INSERT INTO declare VALUES(NEW.id); + RETURN NEW; +END +$$ LANGUAGE PLPGSQL; + +CREATE TRIGGER declare AFTER INSERT ON declare_test FOR EACH ROW EXECUTE PROCEDURE declare(); +insert into declare_test values(3); +drop TRIGGER declare on declare_test cascade; +select * from declare order by 1; +select * from declare_test order by 1; + +drop table declare; +drop table declare_test; +drop schema trigger_declare_test cascade;