diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile index 553fda257e1..3099f77ca64 100644 --- a/src/backend/parser/Makefile +++ b/src/backend/parser/Makefile @@ -2,7 +2,7 @@ # # Makefile for parser # -# $PostgreSQL: pgsql/src/backend/parser/Makefile,v 1.43 2006/03/07 01:00:17 tgl Exp $ +# $PostgreSQL: pgsql/src/backend/parser/Makefile,v 1.44 2006/05/27 17:38:45 tgl Exp $ # #------------------------------------------------------------------------- @@ -57,7 +57,7 @@ endif # Force these dependencies to be known even without dependency info built: -gram.o keywords.o: $(srcdir)/parse.h +gram.o keywords.o parser.o: $(srcdir)/parse.h # gram.c, parse.h, and scan.c are in the distribution tarball, so they diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 69e7a201425..d84f4034aba 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.544 2006/04/30 18:30:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.545 2006/05/27 17:38:45 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -70,6 +70,12 @@ (Current) = (Rhs)[0]; \ } while (0) +/* + * The %name-prefix option below will make bison call base_yylex, but we + * really want it to call filtered_base_yylex (see parser.c). + */ +#define base_yylex filtered_base_yylex + extern List *parsetree; /* final parse result is delivered here */ static bool QueryIsRule = FALSE; @@ -339,6 +345,7 @@ static void doNegateFloat(Value *v); %type constraints_set_list %type constraints_set_mode %type OptTableSpace OptConsTableSpace OptTableSpaceOwner +%type opt_check_option /* @@ -356,7 +363,7 @@ static void doNegateFloat(Value *v); BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT BOOLEAN_P BOTH BY - CACHE CALLED CASCADE CASE CAST CHAIN CHAR_P + CACHE CALLED CASCADE CASCADED CASE CAST CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT COMMITTED CONNECTION CONSTRAINT CONSTRAINTS CONVERSION_P CONVERT COPY CREATE CREATEDB @@ -431,6 +438,12 @@ static void doNegateFloat(Value *v); ZONE +/* The grammar thinks these are keywords, but they are not in the keywords.c + * list and so can never be entered directly. The filter in parser.c + * creates these tokens when required. + */ +%token WITH_CASCADED WITH_LOCAL WITH_CHECK + /* Special token types, not actually keywords - see the "lex" file */ %token IDENT FCONST SCONST BCONST XCONST Op %token ICONST PARAM @@ -4618,12 +4631,13 @@ transaction_mode_list_or_empty: /***************************************************************************** * * QUERY: - * CREATE [ OR REPLACE ] [ TEMP ] VIEW '('target-list ')' AS + * CREATE [ OR REPLACE ] [ TEMP ] VIEW '('target-list ')' + * AS [ WITH [ CASCADED | LOCAL ] CHECK OPTION ] * *****************************************************************************/ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list - AS SelectStmt + AS SelectStmt opt_check_option { ViewStmt *n = makeNode(ViewStmt); n->replace = false; @@ -4634,7 +4648,7 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list $$ = (Node *) n; } | CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list - AS SelectStmt + AS SelectStmt opt_check_option { ViewStmt *n = makeNode(ViewStmt); n->replace = true; @@ -4646,6 +4660,32 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list } ; +/* + * We use merged tokens here to avoid creating shift/reduce conflicts against + * a whole lot of other uses of WITH. + */ +opt_check_option: + WITH_CHECK OPTION + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH CHECK OPTION is not implemented"))); + } + | WITH_CASCADED CHECK OPTION + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH CHECK OPTION is not implemented"))); + } + | WITH_LOCAL CHECK OPTION + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH CHECK OPTION is not implemented"))); + } + | /* EMPTY */ { $$ = NIL; } + ; + /***************************************************************************** * * QUERY: @@ -8319,6 +8359,7 @@ unreserved_keyword: | CACHE | CALLED | CASCADE + | CASCADED | CHAIN | CHARACTERISTICS | CHECKPOINT @@ -9139,4 +9180,10 @@ doNegateFloat(Value *v) } } +/* + * Must undefine base_yylex before including scan.c, since we want it + * to create the function base_yylex not filtered_base_yylex. + */ +#undef base_yylex + #include "scan.c" diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c index 6c331ad338d..de40e64fa8a 100644 --- a/src/backend/parser/parser.c +++ b/src/backend/parser/parser.c @@ -14,7 +14,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parser.c,v 1.65 2006/03/07 01:00:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parser.c,v 1.66 2006/05/27 17:38:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,11 +22,15 @@ #include "postgres.h" #include "parser/gramparse.h" +#include "parser/parse.h" #include "parser/parser.h" List *parsetree; /* result of parsing is left here */ +static int lookahead_token; /* one-token lookahead */ +static bool have_lookahead; /* lookahead_token set? */ + /* * raw_parser @@ -40,6 +44,7 @@ raw_parser(const char *str) int yyresult; parsetree = NIL; /* in case grammar forgets to set it */ + have_lookahead = false; scanner_init(str); parser_init(); @@ -53,3 +58,70 @@ raw_parser(const char *str) return parsetree; } + + +/* + * Intermediate filter between parser and base lexer (base_yylex in scan.l). + * + * The filter is needed because in some cases the standard SQL grammar + * requires more than one token lookahead. We reduce these cases to one-token + * lookahead by combining tokens here, in order to keep the grammar LALR(1). + * + * Using a filter is simpler than trying to recognize multiword tokens + * directly in scan.l, because we'd have to allow for comments between the + * words. Furthermore it's not clear how to do it without re-introducing + * scanner backtrack, which would cost more performance than this filter + * layer does. + */ +int +filtered_base_yylex(void) +{ + int cur_token; + + /* Get next token --- we might already have it */ + if (have_lookahead) + { + cur_token = lookahead_token; + have_lookahead = false; + } + else + cur_token = base_yylex(); + + /* Do we need to look ahead for a possible multiword token? */ + switch (cur_token) + { + case WITH: + /* + * WITH CASCADED, LOCAL, or CHECK must be reduced to one token + * + * XXX an alternative way is to recognize just WITH_TIME and + * put the ugliness into the datetime datatype productions + * instead of WITH CHECK OPTION. However that requires promoting + * WITH to a fully reserved word. If we ever have to do that + * anyway (perhaps for SQL99 recursive queries), come back and + * simplify this code. + */ + lookahead_token = base_yylex(); + switch (lookahead_token) + { + case CASCADED: + cur_token = WITH_CASCADED; + break; + case LOCAL: + cur_token = WITH_LOCAL; + break; + case CHECK: + cur_token = WITH_CHECK; + break; + default: + have_lookahead = true; + break; + } + break; + + default: + break; + } + + return cur_token; +} diff --git a/src/include/parser/gramparse.h b/src/include/parser/gramparse.h index 38f4b26f94e..b61ceb0ac72 100644 --- a/src/include/parser/gramparse.h +++ b/src/include/parser/gramparse.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/gramparse.h,v 1.36 2006/05/21 20:10:42 tgl Exp $ + * $PostgreSQL: pgsql/src/include/parser/gramparse.h,v 1.37 2006/05/27 17:38:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,9 @@ extern bool escape_string_warning; extern bool standard_conforming_strings; +/* from parser.c */ +extern int filtered_base_yylex(void); + /* from scan.l */ extern void scanner_init(const char *str); extern void scanner_finish(void);