diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index 688ca7940d22b27e2177de0dfbeda8ed9e24cb7d..464f5cea0b8634230d26d17947e2eac88dc73920 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -371,4 +371,9 @@ Tue Jan 7 15:19:34 CET 1999 - Synced preproc.y with gram.y for for-update clause and changes in handling of numerics + +Mon Jan 18 11:22:44 CET 1999 + + - Added INTERSECT, EXCEPT and UNION for Select statements + - Put keywords.c in sync again after forgettimg it the last time. - Set version to 2.4.6 diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index 1c907a673cf0e29c9107dd98c2daf624290a8a2a..730753efcdae65be721b920e2929cf8ff6bc41d7 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -1,4 +1,4 @@ -#include <c.h> +#include <postgres.h> #ifdef __cplusplus extern "C" diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index cd4b989f7ba532e14356a93c3a1ccea665c1535b..2a3f5b26335be9f50f772ede6230fc6bbdb11919 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -3,7 +3,7 @@ include $(SRCDIR)/Makefile.global MAJOR_VERSION=2 MINOR_VERSION=4 -PATCHLEVEL=5 +PATCHLEVEL=6 CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \ -DMINOR_VERSION=$(MINOR_VERSION) -DPATCHLEVEL=$(PATCHLEVEL) \ diff --git a/src/interfaces/ecpg/preproc/keywords.c b/src/interfaces/ecpg/preproc/keywords.c index 5b280e534cd09da073fd8c822324695ea965486a..49f936aa8f95472c962998a05ea1a3e7710e06c0 100644 --- a/src/interfaces/ecpg/preproc/keywords.c +++ b/src/interfaces/ecpg/preproc/keywords.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.7 1998/12/22 18:50:55 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.8 1999/01/18 17:17:01 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -94,6 +94,8 @@ static ScanKeyword ScanKeywords[] = { {"else", ELSE}, {"encoding", ENCODING}, {"end", END_TRANS}, + /***S*I***/ + {"except", EXCEPT}, {"execute", EXECUTE}, {"exists", EXISTS}, {"explain", EXPLAIN}, @@ -121,16 +123,20 @@ static ScanKeyword ScanKeywords[] = { {"insensitive", INSENSITIVE}, {"insert", INSERT}, {"instead", INSTEAD}, + /***S*I***/ + {"intersect", INTERSECT}, {"interval", INTERVAL}, {"into", INTO}, {"is", IS}, {"isnull", ISNULL}, + {"isolation", ISOLATION}, {"join", JOIN}, {"key", KEY}, {"lancompiler", LANCOMPILER}, {"language", LANGUAGE}, {"leading", LEADING}, {"left", LEFT}, + {"level", LEVEL}, {"like", LIKE}, {"listen", LISTEN}, {"load", LOAD}, diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 1342ab3da5a97d4a773564721d92d511c83037bb..7f2949373f7d672669d0d49adc2159aaa1f89553 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -20,7 +20,7 @@ */ static int struct_level = 0; static char errortext[128]; -static int QueryIsRule = 0; +static int QueryIsRule = 0, ForUpdateNotAllowed = 0; static enum ECPGttype actual_type[STRUCT_DEPTH]; static char *actual_storage[STRUCT_DEPTH]; @@ -536,11 +536,11 @@ output_statement(char * stmt, int mode) CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP, - ELSE, END_TRANS, EXECUTE, EXISTS, EXTRACT, + ELSE, END_TRANS, EXCEPT, EXECUTE, EXISTS, EXTRACT, FALSE_P, FETCH, FLOAT, FOR, FOREIGN, FROM, FULL, GRANT, GROUP, HAVING, HOUR_P, - IN, INNER_P, INSENSITIVE, INSERT, INTERVAL, INTO, IS, ISOLATION, - JOIN, KEY, LANGUAGE, LEADING, LEFT, LEVEL, LIKE, LOCAL, + IN, INNER_P, INSENSITIVE, INSERT, INTERSECT, INTERVAL, INTO, IS, + ISOLATION, JOIN, KEY, LANGUAGE, LEADING, LEFT, LEVEL, LIKE, LOCAL, MATCH, MINUTE_P, MONTH_P, NAMES, NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULLIF, NULL_P, NUMERIC, OF, ON, ONLY, OPTION, OR, ORDER, OUTER_P, @@ -608,7 +608,7 @@ output_statement(char * stmt, int mode) %left '.' %left '[' ']' %nonassoc TYPECAST -%left UNION +%left UNION INTERSECT EXCEPT %type <str> Iconst Fconst Sconst TransactionStmt CreateStmt UserId %type <str> CreateAsElement OptCreateAs CreateAsList CreateAsStmt @@ -630,7 +630,7 @@ output_statement(char * stmt, int mode) %type <str> opt_decimal Character character opt_varying opt_charset %type <str> opt_collate Datetime datetime opt_timezone opt_interval %type <str> numeric a_expr_or_null row_expr row_descriptor row_list -%type <str> SelectStmt union_clause select_list SubSelect result +%type <str> SelectStmt SubSelect result %type <str> opt_table opt_union opt_unique sort_clause sortby_list %type <str> sortby OptUseOp opt_inh_star relation_name_list name_list %type <str> group_clause having_clause from_clause c_list @@ -638,7 +638,7 @@ output_statement(char * stmt, int mode) %type <str> join_using where_clause relation_expr row_op sub_type %type <str> opt_column_list insert_rest InsertStmt OptimizableStmt %type <str> columnList DeleteStmt LockStmt UpdateStmt CursorStmt -%type <str> NotifyStmt columnElem copy_dirn SubUnion c_expr UnlistenStmt +%type <str> NotifyStmt columnElem copy_dirn c_expr UnlistenStmt %type <str> copy_delimiter ListenStmt CopyStmt copy_file_name opt_binary %type <str> opt_with_copy FetchStmt opt_direction fetch_how_many opt_portal_name %type <str> ClosePortalStmt DestroyStmt VacuumStmt opt_verbose @@ -666,6 +666,7 @@ output_statement(char * stmt, int mode) %type <str> GrantStmt privileges operation_commalist operation %type <str> cursor_clause opt_cursor opt_readonly opt_of opt_lmode %type <str> case_expr when_clause_list case_default case_arg when_clause +%type <str> select_w_o_sort %type <str> ECPGWhenever ECPGConnect connection_target ECPGOpen open_opts %type <str> indicator ECPGExecute ecpg_expr dotext @@ -2121,7 +2122,10 @@ RuleStmt: CREATE RULE name AS OptStmtList: NOTHING { $$ = make1_str("nothing"); } | OptimizableStmt { $$ = $1; } | '[' OptStmtBlock ']' { $$ = cat3_str(make1_str("["), $2, make1_str("]")); } - | '(' OptStmtBlock ')' { $$ = cat3_str(make1_str("("), $2, make1_str(")")); } +/***S*I*D***/ +/* We comment this out because it produces a shift / reduce conflict + * with the select_w_o_sort rule */ +/* | '(' OptStmtBlock ')' { $$ = cat3_str(make1_str("("), $2, make1_str(")")); }*/ ; OptStmtBlock: OptStmtMulti @@ -2132,8 +2136,13 @@ OptStmtBlock: OptStmtMulti OptStmtMulti: OptStmtMulti OptimizableStmt ';' { $$ = cat3_str($1, $2, make1_str(";")); } - | OptStmtMulti OptimizableStmt - { $$ = cat2_str($1, $2); } +/***S*I***/ +/* We comment the next rule because it seems to be redundant + * and produces 16 shift/reduce conflicts with the new SelectStmt rule + * needed for EXCEPT and INTERSECT. So far I did not notice any + * violations by removing the rule! */ +/* | OptStmtMulti OptimizableStmt + { $$ = cat2_str($1, $2); }*/ | OptimizableStmt ';' { $$ = cat2_str($1, make1_str(";")); } ; @@ -2389,9 +2398,15 @@ OptimizableStmt: SelectStmt * *****************************************************************************/ -InsertStmt: INSERT INTO relation_name opt_column_list insert_rest +/***S*I***/ +/* This rule used 'opt_column_list' between 'relation_name' and 'insert_rest' + * originally. When the second rule of 'insert_rest' was changed to use + * the new 'SelectStmt' rule (for INTERSECT and EXCEPT) it produced a shift/red uce + * conflict. So I just changed the rules 'InsertStmt' and 'insert_rest' to accept + * the same statements without any shift/reduce conflicts */ +InsertStmt: INSERT INTO relation_name insert_rest { - $$ = cat4_str(make1_str("insert into"), $3, $4, $5); + $$ = cat3_str(make1_str("insert into"), $3, $4); } ; @@ -2403,12 +2418,17 @@ insert_rest: VALUES '(' res_target_list2 ')' { $$ = make1_str("default values"); } - | SELECT opt_unique res_target_list2 - from_clause where_clause - group_clause having_clause - union_clause + | SelectStmt { - $$ = cat4_str(cat5_str(make1_str("select"), $2, $3, $4, $5), $6, $7, $8); + $$ = $1 + } + | '(' columnList ')' VALUES '(' res_target_list2 ')' + { + $$ = make5_str(make1_str("("), $2, make1_str(") values ("), $6, make1_str(")")); + } + | '(' columnList ')' SelectStmt + { + $$ = make4_str(make1_str("("), $2, make1_str(")"), $4); } ; @@ -2546,12 +2566,7 @@ UpdateStmt: UPDATE relation_name * CURSOR STATEMENTS * *****************************************************************************/ -CursorStmt: DECLARE name opt_cursor CURSOR FOR - SELECT opt_unique res_target_list2 - from_clause where_clause - group_clause having_clause - union_clause sort_clause - cursor_clause +CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause { struct cursor *ptr, *this; @@ -2570,7 +2585,7 @@ CursorStmt: DECLARE name opt_cursor CURSOR FOR /* initial definition */ this->next = cur; this->name = $2; - this->command = cat4_str(cat5_str(cat5_str(make1_str("declare"), mm_strdup($2), $3, make1_str("cursor for select"), $7), $8, $9, $10, $11), $12, $13, $14); + this->command = cat2_str(cat5_str(make1_str("declare"), mm_strdup($2), $3, make1_str("cursor for"), $6), $7); this->argsinsert = argsinsert; this->argsresult = argsresult; argsinsert = argsresult = NULL; @@ -2609,54 +2624,60 @@ opt_of: OF columnList { $$ = make2_str(make1_str("of"), $2); } * *****************************************************************************/ -SelectStmt: SELECT opt_unique res_target_list2 - result from_clause where_clause - group_clause having_clause - union_clause sort_clause for_update_clause +/***S*I***/ +/* The new 'SelectStmt' rule adapted for the optional use of INTERSECT EXCEPT a nd UNION + * accepts the use of '(' and ')' to select an order of set operations. + */ +SelectStmt: select_w_o_sort sort_clause for_update_clause { - $$ = cat3_str(cat5_str(cat5_str(make1_str("select"), $2, $3, $4, $5), $6, $7, $8, $9), $10, $11); - if (strlen($11) > 0) - { - if (strlen($9) > 0) - yyerror("SELECT FOR UPDATE is not allowed with UNION clause"); - if (strlen($7) > 0) - yyerror("SELECT FOR UPDATE is not allowed with GROUP BY clause"); - if (strlen($6) > 0) - yyerror("SELECT FOR UPDATE is not allowed with HAVING clause"); - } - } - ; - -SubSelect: SELECT opt_unique res_target_list2 - from_clause where_clause - group_clause having_clause - union_clause - { - $$ =cat4_str(cat5_str(make1_str("select"), $2, $3, $4, $5), $6, $7, $8); - } - ; + if (strlen($3) > 0 && ForUpdateNotAllowed != 0) + yyerror("SELECT FOR UPDATE is not allowed in this context"); -union_clause: UNION opt_union select_list - { - $$ = cat3_str(make1_str("union"), $2, $3); + ForUpdateNotAllowed = 0; + $$ = cat3_str($1, $2, $3); } - | /*EMPTY*/ - { $$ = make1_str(""); } - ; -select_list: select_list UNION opt_union SubUnion - { - $$ = cat4_str($1, make1_str("union"), $3, $4); - } - | SubUnion - { $$ = $1; } +/***S*I***/ +/* This rule parses Select statements including UNION INTERSECT and EXCEPT. + * '(' and ')' can be used to specify the order of the operations + * (UNION EXCEPT INTERSECT). Without the use of '(' and ')' we want the + * operations to be left associative. + * + * The sort_clause is not handled here! + */ +select_w_o_sort: '(' select_w_o_sort ')' + { + $$ = make3_str(make1_str("("), $2, make1_str(")")); + } + | SubSelect + { + $$ = $1; + } + | select_w_o_sort EXCEPT select_w_o_sort + { + $$ = cat3_str($1, make1_str("except"), $3); + ForUpdateNotAllowed = 1; + } + | select_w_o_sort UNION opt_union select_w_o_sort + { + $$ = cat3_str($1, make1_str("union"), $3); + ForUpdateNotAllowed = 1; + } + | select_w_o_sort INTERSECT opt_union select_w_o_sort + { + $$ = cat3_str($1, make1_str("intersect"), $3); + ForUpdateNotAllowed = 1; + } ; -SubUnion: SELECT opt_unique res_target_list2 - from_clause where_clause - group_clause having_clause +/***S*I***/ +SubSelect: SELECT opt_unique res_target_list2 + result from_clause where_clause + group_clause having_clause { - $$ = cat3_str(cat5_str(make1_str("select"), $2, $3, $4, $5), $6, $7); + $$ = cat4_str(cat5_str(make1_str("select"), $2, $3, $4, $5), $6, $7, $8); + if (strlen($7) > 0 || strlen($8) > 0) + ForUpdateNotAllowed = 1; } ; @@ -4416,9 +4437,9 @@ connection_target: database_name opt_server opt_port yyerror(errortext); } - if (strncmp($1, "unix", 4) == 0 && strncmp($2, "localhost", 9) != 0) + if (strncmp($1, "unix", 4) == 0 && strncmp($2 + 3, "localhost", 9) != 0) { - sprintf(errortext, "unix domain sockets only work on 'localhost'"); + sprintf(errortext, "unix domain sockets only work on 'localhost' but not on '%9.9s'", $2); yyerror(errortext); } diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile index 4e19502fca9705f9b125fa2948968a8489bc26ac..45d65fb44c67c4c5ceaf584d569307cf64ec596f 100644 --- a/src/interfaces/ecpg/test/Makefile +++ b/src/interfaces/ecpg/test/Makefile @@ -1,18 +1,18 @@ all: test1 test2 perftest -LDFLAGS=-g -I ../include -I ../../libpq -L../lib -lecpg -L../../libpq -lpq -lcrypt +LDFLAGS=-g -I /usr/local/pgsql/include -L/usr/local/pgsql/lib -lecpg -lpq -lcrypt test1: test1.c test1.c: test1.pgc - ../preproc/ecpg $? + /usr/local/pgsql/bin/ecpg $? test2: test2.c test2.c: test2.pgc - ../preproc/ecpg $? + /usr/local/pgsql/bin/ecpg $? perftest: perftest.c perftest.c:perftest.pgc - ../preproc/ecpg $? + /usr/local/pgsql/bin/ecpg $? clean: /bin/rm test1 test2 perftest *.c log diff --git a/src/interfaces/ecpg/test/test2.pgc b/src/interfaces/ecpg/test/test2.pgc index 7d939426b97471af6183dbc66edccbc2feb7cdae..5b06c0390e578c80a29aaab3c76021b0dc20e022 100644 --- a/src/interfaces/ecpg/test/test2.pgc +++ b/src/interfaces/ecpg/test/test2.pgc @@ -30,14 +30,14 @@ exec sql declare cur cursor for ECPGdebug(1, dbgs); strcpy(msg, "connect"); - exec sql connect to tcp:postgresql://localhost:5432/mm; + exec sql connect to unix:postgresql://localhost:5432/mm; strcpy(msg, "create"); exec sql create table meskes(name char(8), born integer, age smallint, married char(8)); strcpy(msg, "insert"); - exec sql insert into meskes(name, born, age, married) values ('Petra', 19661202, 31, '19900404'); - exec sql insert into meskes(name, born, age, married) values ('Michael', 19660117, 32, '19900404'); + exec sql insert into meskes(name, born, age, married) values ('Petra', 19661202, 32, '19900404'); + exec sql insert into meskes(name, born, age, married) values ('Michael', 19660117, 33, '19900404'); exec sql insert into meskes(name, born, age) values ('Carsten', 19910103, 7); exec sql insert into meskes(name, born, age) values ('Marc', 19930907, 4); exec sql insert into meskes(name, born, age) values ('Chris', 19970923, 0);