diff -ruN ecpg/ChangeLog ecpg.mm/ChangeLog --- ecpg/ChangeLog Fri Oct 16 07:45:12 1998 +++ ecpg.mm/ChangeLog Tue Dec 22 14:16:50 1998 @@ -356,3 +356,13 @@ - Synced preproc.y with gram.y yet again. - Set version to 2.4.4 + +Wed Dec 9 11:24:54 MEZ 1998 + + - Synced preproc.y with gram.y and the keywords.c files to add CASE + statement. + +Tue Dec 22 14:16:11 CET 1998 + + - Synced preproc.y with gram.y for locking statements. + - Set version to 2.4.5 diff -ruN ecpg/preproc/Makefile ecpg.mm/preproc/Makefile --- ecpg/preproc/Makefile Fri Oct 16 07:45:12 1998 +++ ecpg.mm/preproc/Makefile Wed Dec 9 11:25:43 1998 @@ -3,7 +3,7 @@ MAJOR_VERSION=2 MINOR_VERSION=4 -PATCHLEVEL=4 +PATCHLEVEL=5 CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \ -DMINOR_VERSION=$(MINOR_VERSION) -DPATCHLEVEL=$(PATCHLEVEL) \ diff -ruN ecpg/preproc/ecpg.c ecpg.mm/preproc/ecpg.c --- ecpg/preproc/ecpg.c Tue Sep 1 06:46:11 1998 +++ ecpg.mm/preproc/ecpg.c Sat Nov 28 15:05:51 1998 @@ -181,7 +181,7 @@ /* initialize lex */ lex_init(); - /* we need two includes and a constant */ + /* we need two includes */ fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/* These two include files are added by the preprocessor */\n#include \n#include \n\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL); /* and parse the source */ diff -ruN ecpg/preproc/keywords.c ecpg.mm/preproc/keywords.c --- ecpg/preproc/keywords.c Sat Oct 3 07:47:12 1998 +++ ecpg.mm/preproc/keywords.c Wed Dec 9 11:24:41 1998 @@ -52,12 +52,14 @@ {"by", BY}, {"cache", CACHE}, {"cascade", CASCADE}, + {"case", CASE}, {"cast", CAST}, {"char", CHAR}, {"character", CHARACTER}, {"check", CHECK}, {"close", CLOSE}, {"cluster", CLUSTER}, + {"coalesce", COALESCE}, {"collate", COLLATE}, {"column", COLUMN}, {"commit", COMMIT}, @@ -89,6 +91,7 @@ {"double", DOUBLE}, {"drop", DROP}, {"each", EACH}, + {"else", ELSE}, {"encoding", ENCODING}, {"end", END_TRANS}, {"execute", EXECUTE}, @@ -115,8 +118,8 @@ {"index", INDEX}, {"inherits", INHERITS}, {"inner", INNER_P}, - {"insert", INSERT}, {"insensitive", INSENSITIVE}, + {"insert", INSERT}, {"instead", INSTEAD}, {"interval", INTERVAL}, {"into", INTO}, @@ -155,6 +158,7 @@ {"notify", NOTIFY}, {"notnull", NOTNULL}, {"null", NULL_P}, + {"nullif", NULLIF}, {"numeric", NUMERIC}, {"of", OF}, {"oids", OIDS}, @@ -202,6 +206,7 @@ {"stdout", STDOUT}, {"substring", SUBSTRING}, {"table", TABLE}, + {"then", THEN}, {"time", TIME}, {"timestamp", TIMESTAMP}, {"timezone_hour", TIMEZONE_HOUR}, @@ -229,6 +234,7 @@ {"verbose", VERBOSE}, {"version", VERSION}, {"view", VIEW}, + {"when", WHEN}, {"where", WHERE}, {"with", WITH}, {"work", WORK}, diff -ruN ecpg/preproc/preproc.y ecpg.mm/preproc/preproc.y --- ecpg/preproc/preproc.y Fri Oct 16 07:45:12 1998 +++ ecpg.mm/preproc/preproc.y Tue Dec 22 14:14:54 1998 @@ -530,26 +530,27 @@ /* Keywords (in SQL92 reserved words) */ %token ABSOLUTE, ACTION, ADD, ALL, ALTER, AND, ANY, AS, ASC, BEGIN_TRANS, BETWEEN, BOTH, BY, - CASCADE, CAST, CHAR, CHARACTER, CHECK, CLOSE, COLLATE, COLUMN, COMMIT, + CASCADE, CASE, CAST, CHAR, CHARACTER, CHECK, CLOSE, + COALESCE, COLLATE, COLUMN, COMMIT, CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP, - END_TRANS, EXECUTE, EXISTS, EXTRACT, + ELSE, END_TRANS, EXECUTE, EXISTS, EXTRACT, FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL, GRANT, GROUP, HAVING, HOUR_P, - IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS, - JOIN, KEY, LANGUAGE, LEADING, LEFT, LIKE, LOCAL, + IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS, ISOLATION, + JOIN, KEY, LANGUAGE, LEADING, LEFT, LEVEL, LIKE, LOCAL, MATCH, MINUTE_P, MONTH_P, NAMES, - NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULL_P, NUMERIC, + NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULLIF, NULL_P, NUMERIC, OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P, PARTIAL, POSITION, PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PUBLIC, READ, REFERENCES, RELATIVE, REVOKE, RIGHT, ROLLBACK, SCROLL, SECOND_P, SELECT, SET, SUBSTRING, - TABLE, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, + TABLE, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, TO, TRAILING, TRANSACTION, TRIM, TRUE_P, UNION, UNIQUE, UPDATE, USER, USING, VALUES, VARCHAR, VARYING, VIEW, - WHERE, WITH, WORK, YEAR_P, ZONE + WHEN, WHERE, WITH, WORK, YEAR_P, ZONE /* Keywords (in SQL3 reserved words) */ %token TRIGGER @@ -662,7 +663,8 @@ %type ViewStmt LoadStmt CreatedbStmt opt_database1 opt_database2 location %type DestroydbStmt ClusterStmt grantee RevokeStmt encoding %type GrantStmt privileges operation_commalist operation -%type cursor_clause, opt_cursor, opt_readonly, opt_of +%type cursor_clause opt_cursor opt_readonly opt_of opt_lmode +%type case_expr when_clause_list case_default case_arg when_clause %type ECPGWhenever ECPGConnect connection_target ECPGOpen open_opts %type indicator ECPGExecute ecpg_expr dotext @@ -914,6 +916,26 @@ { $$ = cat2_str(make1_str("set time zone"), $4); } + | SET TRANSACTION ISOLATION LEVEL READ ColId + { + if (strcasecmp($6, "COMMITTED")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $6); + yyerror(errortext); + } + + $$ = cat2_str(make1_str("set transaction isolation level read"), $6); + } + | SET TRANSACTION ISOLATION LEVEL ColId + { + if (strcasecmp($5, "SERIALIZABLE")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $5); + yyerror(errortext); + } + + $$ = cat2_str(make1_str("set transaction isolation level read"), $5); + } | SET NAMES encoding { #ifdef MB @@ -941,6 +963,10 @@ { $$ = make1_str("show time zone"); } + | SHOW TRANSACTION ISOLATION LEVEL + { + $$ = make1_str("show transaction isolation level"); + } ; VariableResetStmt: RESET ColId @@ -951,6 +977,10 @@ { $$ = make1_str("reset time zone"); } + | RESET TRANSACTION ISOLATION LEVEL + { + $$ = make1_str("reset transaction isolation level"); + } ; @@ -2413,17 +2443,84 @@ } ; -/* - * Total hack to just lock a table inside a transaction. - * Is it worth making this a separate command, with - * its own node type and file. I don't think so. bjm 1998/1/22 - */ LockStmt: LOCK_P opt_table relation_name { $$ = cat3_str(make1_str("lock"), $2, $3); } + | LOCK_P opt_table relation_name IN opt_lmode ROW IDENT IDENT + { + if (strcasecmp($8, "MODE")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $8); + yyerror(errortext); + } + if ($5 != NULL) + { + if (strcasecmp($5, "SHARE")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $5); + yyerror(errortext); + } + if (strcasecmp($7, "EXCLUSIVE")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $7); + yyerror(errortext); + } + } + else + { + if (strcasecmp($7, "SHARE") && strcasecmp($7, "EXCLUSIVE")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $7); + yyerror(errortext); + } + } + + $$=cat4_str(cat5_str(make1_str("lock"), $2, $3, make1_str("in"), $5), make1_str("row"), $7, $8); + } + | LOCK_P opt_table relation_name IN IDENT IDENT IDENT + { + if (strcasecmp($7, "MODE")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $7); + yyerror(errortext); + } + if (strcasecmp($5, "ACCESS")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $5); + yyerror(errortext); + } + if (strcasecmp($6, "SHARE") && strcasecmp($6, "EXCLUSIVE")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $6); + yyerror(errortext); + } + + $$=cat3_str(cat5_str(make1_str("lock"), $2, $3, make1_str("in"), $5), $6, $7); + } + | LOCK_P opt_table relation_name IN IDENT IDENT + { + if (strcasecmp($6, "MODE")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $6); + yyerror(errortext); + } + if (strcasecmp($5, "SHARE") && strcasecmp($5, "EXCLUSIVE")) + { + sprintf(errortext, "syntax error at or near \"%s\"", $5); + yyerror(errortext); + } + + $$=cat2_str(cat5_str(make1_str("lock"), $2, $3, make1_str("in"), $5), $6); + } ; +opt_lmode: IDENT { $$ = $1; } + | /*EMPTY*/ { $$ = make1_str(""); } + ; + + + /***************************************************************************** * @@ -3074,12 +3171,13 @@ } ; -/* +/* General expressions * This is the heart of the expression syntax. * Note that the BETWEEN clause looks similar to a boolean expression * and so we must define b_expr which is almost the same as a_expr * but without the boolean expressions. - * All operations are allowed in a BETWEEN clause if surrounded by parens. + * All operations/expressions are allowed in a BETWEEN clause + * if surrounded by parens. */ a_expr: attr opt_indirection @@ -3362,16 +3460,17 @@ { $$ = cat3_str($1, make1_str("or"), $3); } | NOT a_expr { $$ = cat2_str(make1_str("not"), $2); } + | case_expr + { $$ = $1; } | cinputvariable { $$ = make1_str(";;"); } ; -/* +/* Restricted expressions * b_expr is a subset of the complete expression syntax * defined by a_expr. b_expr is used in BETWEEN clauses * to eliminate parser ambiguities stemming from the AND keyword. */ - b_expr: attr opt_indirection { $$ = cat2_str($1, $2); @@ -3656,6 +3755,65 @@ { $$ = cat3_str($1, make1_str(","), $3);} ; +/* Case clause + * Define SQL92-style case clause. + * Allow all four forms described in the standard: + * - Full specification + * CASE WHEN a = b THEN c ... ELSE d END + * - Implicit argument + * CASE a WHEN b THEN c ... ELSE d END + * - Conditional NULL + * NULLIF(x,y) + * same as CASE WHEN x = y THEN NULL ELSE x END + * - Conditional substitution from list, use first non-null argument + * COALESCE(a,b,...) + * same as CASE WHEN a IS NOT NULL THEN a WHEN b IS NOT NULL THEN b ... END + * - thomas 1998-11-09 + */ +case_expr: CASE case_arg when_clause_list case_default END_TRANS + { $$ = cat5_str(make1_str("case"), $2, $3, $4, make1_str("end")); } + | NULLIF '(' a_expr ',' a_expr ')' + { + $$ = cat5_str(make1_str("nullif("), $3, make1_str(","), $5, make1_str(")")); + + fprintf(stderr, "NULLIF() not yet fully implemented"); + } + | COALESCE '(' expr_list ')' + { + $$ = cat3_str(make1_str("coalesce("), $3, make1_str(")")); + + fprintf(stderr, "COALESCE() not yet fully implemented"); + } + ; + +when_clause_list: when_clause_list when_clause + { $$ = cat2_str($1, $2); } + | when_clause + { $$ = $1; } + ; + +when_clause: WHEN a_expr THEN a_expr_or_null + { + $$ = cat4_str(make1_str("when"), $2, make1_str("then"), $4); + } + ; + +case_default: ELSE a_expr_or_null { $$ = cat2_str(make1_str("else"), $2); } + | /*EMPTY*/ { $$ = make1_str(""); } + ; + +case_arg: attr opt_indirection + { + $$ = cat2_str($1, $2); + } + | ColId + { + $$ = $1; + } + | /*EMPTY*/ + { $$ = make1_str(""); } + ; + attr: relation_name '.' attrs { $$ = make3_str($1, make1_str("."), $3); @@ -3924,12 +4082,16 @@ | ABORT_TRANS { $$ = make1_str("abort"); } | ANALYZE { $$ = make1_str("analyze"); } | BINARY { $$ = make1_str("binary"); } + | CASE { $$ = make1_str("case"); } | CLUSTER { $$ = make1_str("cluster"); } + | COALESCE { $$ = make1_str("coalesce"); } | CONSTRAINT { $$ = make1_str("constraint"); } | COPY { $$ = make1_str("copy"); } | CROSS { $$ = make1_str("cross"); } | CURRENT { $$ = make1_str("current"); } | DO { $$ = make1_str("do"); } + | ELSE { $$ = make1_str("else"); } + | END_TRANS { $$ = make1_str("end"); } | EXPLAIN { $$ = make1_str("explain"); } | EXTEND { $$ = make1_str("extend"); } | FALSE_P { $$ = make1_str("false"); } @@ -3941,6 +4103,7 @@ | MOVE { $$ = make1_str("move"); } | NEW { $$ = make1_str("new"); } | NONE { $$ = make1_str("none"); } + | NULLIF { $$ = make1_str("nullif"); } | ORDER { $$ = make1_str("order"); } | POSITION { $$ = make1_str("position"); } | PRECISION { $$ = make1_str("precision"); } @@ -3948,10 +4111,12 @@ | SETOF { $$ = make1_str("setof"); } | SHOW { $$ = make1_str("show"); } | TABLE { $$ = make1_str("table"); } + | THEN { $$ = make1_str("then"); } | TRANSACTION { $$ = make1_str("transaction"); } | TRUE_P { $$ = make1_str("true"); } | VACUUM { $$ = make1_str("vacuum"); } | VERBOSE { $$ = make1_str("verbose"); } + | WHEN { $$ = make1_str("when"); } ; SpecialRuleRelation: CURRENT diff -ruN ecpg/preproc/type.h ecpg.mm/preproc/type.h --- ecpg/preproc/type.h Tue Sep 1 13:02:52 1998 +++ ecpg.mm/preproc/type.h Wed Dec 9 11:32:33 1998 @@ -57,7 +57,7 @@ extern const char *ECPGtype_name(enum ECPGttype typ); /* some stuff for whenever statements */ -enum WHEN +enum WHEN_TYPE { W_NOTHING, W_CONTINUE, @@ -70,7 +70,7 @@ struct when { - enum WHEN code; + enum WHEN_TYPE code; char *command; char *str; };