diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 117 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 360 | ||||
-rw-r--r-- | src/backend/parser/keywords.c | 8 |
3 files changed, 361 insertions, 124 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 3dc0cad8165..ea9cb5fa8c2 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.91 1998/12/14 06:50:32 scrappy Exp $ + * $Id: analyze.c,v 1.92 1999/01/18 00:09:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,6 +26,11 @@ #include "parser/parse_node.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" +/***S*I***/ +#include "parser/parse_expr.h" +#include "catalog/pg_type.h" +#include "parse.h" + #include "utils/builtins.h" #include "utils/mcxt.h" @@ -383,8 +388,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * The INSERT INTO ... SELECT ... could have a UNION in child, so * unionClause may be false */ - qry->unionall = stmt->unionall; - qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList); + qry->unionall = stmt->unionall; + + /***S*I***/ + /* Just hand through the unionClause and intersectClause. + * We will handle it in the function Except_Intersect_Rewrite() */ + qry->unionClause = stmt->unionClause; + qry->intersectClause = stmt->intersectClause; /* * If there is a havingQual but there are no aggregates, then there is @@ -942,7 +952,12 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) * unionClause may be false */ qry->unionall = stmt->unionall; - qry->unionClause = transformUnionClause(stmt->unionClause, qry->targetList); + + /***S*I***/ + /* Just hand through the unionClause and intersectClause. + * We will handle it in the function Except_Intersect_Rewrite() */ + qry->unionClause = stmt->unionClause; + qry->intersectClause = stmt->intersectClause; /* * If there is a havingQual but there are no aggregates, then there is @@ -1012,3 +1027,97 @@ transformCursorStmt(ParseState *pstate, SelectStmt *stmt) return qry; } + +/***S*I***/ +/* This function steps through the tree + * built up by the select_w_o_sort rule + * and builds a list of all SelectStmt Nodes found + * The built up list is handed back in **select_list. + * If one of the SelectStmt Nodes has the 'unionall' flag + * set to true *unionall_present hands back 'true' */ +void +create_select_list(Node *ptr, List **select_list, bool *unionall_present) +{ + if(IsA(ptr, SelectStmt)) { + *select_list = lappend(*select_list, ptr); + if(((SelectStmt *)ptr)->unionall == TRUE) *unionall_present = TRUE; + return; + } + + /* Recursively call for all arguments. A NOT expr has no lexpr! */ + if (((A_Expr *)ptr)->lexpr != NULL) + create_select_list(((A_Expr *)ptr)->lexpr, select_list, unionall_present); + create_select_list(((A_Expr *)ptr)->rexpr, select_list, unionall_present); +} + +/* Changes the A_Expr Nodes to Expr Nodes and exchanges ANDs and ORs. + * The reason for the exchange is easy: We implement INTERSECTs and EXCEPTs + * by rewriting these queries to semantically equivalent queries that use + * IN and NOT IN subselects. To be able to use all three operations + * (UNIONs INTERSECTs and EXCEPTs) in one complex query we have to + * translate the queries into Disjunctive Normal Form (DNF). Unfortunately + * there is no function 'dnfify' but there is a function 'cnfify' + * which produces DNF when we exchange ANDs and ORs before calling + * 'cnfify' and exchange them back in the result. + * + * If an EXCEPT or INTERSECT is present *intersect_present + * hands back 'true' */ +Node *A_Expr_to_Expr(Node *ptr, bool *intersect_present) +{ + Node *result; + + switch(nodeTag(ptr)) + { + case T_A_Expr: + { + A_Expr *a = (A_Expr *)ptr; + + switch (a->oper) + { + case AND: + { + Expr *expr = makeNode(Expr); + Node *lexpr = A_Expr_to_Expr(((A_Expr *)ptr)->lexpr, intersect_present); + Node *rexpr = A_Expr_to_Expr(((A_Expr *)ptr)->rexpr, intersect_present); + + *intersect_present = TRUE; + + expr->typeOid = BOOLOID; + expr->opType = OR_EXPR; + expr->args = makeList(lexpr, rexpr, -1); + result = (Node *) expr; + break; + } + case OR: + { + Expr *expr = makeNode(Expr); + Node *lexpr = A_Expr_to_Expr(((A_Expr *)ptr)->lexpr, intersect_present); + Node *rexpr = A_Expr_to_Expr(((A_Expr *)ptr)->rexpr, intersect_present); + + expr->typeOid = BOOLOID; + expr->opType = AND_EXPR; + expr->args = makeList(lexpr, rexpr, -1); + result = (Node *) expr; + break; + } + case NOT: + { + Expr *expr = makeNode(Expr); + Node *rexpr = A_Expr_to_Expr(((A_Expr *)ptr)->rexpr, intersect_present); + + expr->typeOid = BOOLOID; + expr->opType = NOT_EXPR; + expr->args = makeList(rexpr, -1); + result = (Node *) expr; + break; + } + } + break; + } + default: + { + result = ptr; + } + } + return result; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 76e9dc1d7fd..b3fb314a8f4 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.42 1999/01/05 15:46:25 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.43 1999/01/18 00:09:51 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -47,6 +47,7 @@ #include "access/xact.h" #include "storage/lmgr.h" #include "utils/numeric.h" +#include "parser/analyze.h" #ifdef MULTIBYTE #include "mb/pg_wchar.h" @@ -128,9 +129,9 @@ Oid param_type(int t); /* used in parse_expr.c */ ProcedureStmt, RecipeStmt, RemoveAggrStmt, RemoveOperStmt, RemoveFuncStmt, RemoveStmt, RenameStmt, RevokeStmt, RuleStmt, TransactionStmt, ViewStmt, LoadStmt, - CreatedbStmt, DestroydbStmt, VacuumStmt, CursorStmt, SubSelect, SubUnion, - UpdateStmt, InsertStmt, SelectStmt, NotifyStmt, DeleteStmt, ClusterStmt, - ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt, + CreatedbStmt, DestroydbStmt, VacuumStmt, CursorStmt, SubSelect, + UpdateStmt, InsertStmt, select_w_o_sort, SelectStmt, NotifyStmt, DeleteStmt, + ClusterStmt, ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt, CreateUserStmt, AlterUserStmt, DropUserStmt %type <str> opt_database1, opt_database2, location, encoding @@ -174,7 +175,7 @@ Oid param_type(int t); /* used in parse_expr.c */ %type <boolean> TriggerForOpt, TriggerForType -%type <list> union_clause, select_list, for_update_clause +%type <list> for_update_clause %type <list> join_list %type <joinusing> join_using @@ -271,11 +272,11 @@ Oid param_type(int t); /* used in parse_expr.c */ 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, @@ -343,7 +344,7 @@ Oid param_type(int t); /* used in parse_expr.c */ %left '.' %left '[' ']' %nonassoc TYPECAST -%left UNION +%left UNION INTERSECT EXCEPT %% stmtblock: stmtmulti @@ -354,8 +355,13 @@ stmtblock: stmtmulti stmtmulti: stmtmulti stmt ';' { $$ = lappend($1, $2); } - | stmtmulti stmt - { $$ = lappend($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 INTERSECTS. So far I did not notice any + * violations by removing the rule! */ +/* | stmtmulti stmt + { $$ = lappend($1, $2); } */ | stmt ';' { $$ = lcons($1,NIL); } ; @@ -2062,7 +2068,10 @@ RuleStmt: CREATE RULE name AS OptStmtList: NOTHING { $$ = NIL; } | OptimizableStmt { $$ = lcons($1, NIL); } | '[' OptStmtBlock ']' { $$ = $2; } - | '(' OptStmtBlock ')' { $$ = $2; } +/***S*I*D***/ +/* We comment this out because it produces a shift / reduce conflict + * with the select_w_o_sort rule */ +/* | '(' OptStmtBlock ')' { $$ = $2; } */ ; OptStmtBlock: OptStmtMulti @@ -2073,8 +2082,13 @@ OptStmtBlock: OptStmtMulti OptStmtMulti: OptStmtMulti OptimizableStmt ';' { $$ = lappend($1, $2); } - | OptStmtMulti OptimizableStmt - { $$ = lappend($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 + { $$ = lappend($1, $2); } */ | OptimizableStmt ';' { $$ = lcons($1, NIL); } ; @@ -2426,17 +2440,23 @@ 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/reduce + * 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 { - $5->relname = $3; - $5->cols = $4; - $$ = (Node *)$5; + $4->relname = $3; + $$ = (Node *)$4; } ; insert_rest: VALUES '(' res_target_list2 ')' { $$ = makeNode(InsertStmt); + $$->cols = NULL; $$->unique = NULL; $$->targetList = $3; $$->fromClause = NIL; @@ -2455,20 +2475,57 @@ insert_rest: VALUES '(' res_target_list2 ')' $$->groupClause = NIL; $$->havingClause = NULL; $$->unionClause = NIL; + /***S*I***/ + $$->intersectClause = NIL; } - | SELECT opt_unique res_target_list2 - from_clause where_clause - group_clause having_clause - union_clause + /***S*I***/ + /* We want the full power of SelectStatements including INTERSECT and EXCEPT + * for insertion */ + | SelectStmt { + SelectStmt *n; + + n = (SelectStmt *)$1; $$ = makeNode(InsertStmt); - $$->unique = $2; - $$->targetList = $3; - $$->fromClause = $4; - $$->whereClause = $5; - $$->groupClause = $6; - $$->havingClause = $7; - $$->unionClause = $8; + $$->cols = NULL; + $$->unique = n->unique; + $$->targetList = n->targetList; + $$->fromClause = n->fromClause; + $$->whereClause = n->whereClause; + $$->groupClause = n->groupClause; + $$->havingClause = n->havingClause; + $$->unionClause = n->unionClause; + $$->intersectClause = n->intersectClause; + } + | '(' columnList ')' VALUES '(' res_target_list2 ')' + { + $$ = makeNode(InsertStmt); + $$->cols = $2; + $$->unique = NULL; + $$->targetList = $6; + $$->fromClause = NIL; + $$->whereClause = NULL; + $$->groupClause = NIL; + $$->havingClause = NULL; + $$->unionClause = NIL; + /***S*I***/ + $$->intersectClause = NIL; + } + | '(' columnList ')' SelectStmt + { + SelectStmt *n; + + n = (SelectStmt *)$4; + $$ = makeNode(InsertStmt); + $$->cols = $2; + $$->unique = n->unique; + $$->targetList = n->targetList; + $$->fromClause = n->fromClause; + $$->whereClause = n->whereClause; + $$->groupClause = n->groupClause; + $$->havingClause = n->havingClause; + $$->unionClause = n->unionClause; + $$->intersectClause = n->intersectClause; } ; @@ -2610,18 +2667,15 @@ 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 - { - SelectStmt *n = makeNode(SelectStmt); - - /* from PORTAL name */ - /* - * 15 august 1991 -- since 3.0 postgres does locking +/***S*I***/ +CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt cursor_clause + { + SelectStmt *n; + + n= (SelectStmt *)$6; + /* from PORTAL name */ + /* + * 15 august 1991 -- since 3.0 postgres does locking * right, we discovered that portals were violating * locking protocol. portal locks cannot span xacts. * as a short-term fix, we installed the check here. @@ -2632,14 +2686,6 @@ CursorStmt: DECLARE name opt_cursor CURSOR FOR n->portalname = $2; n->binary = $3; - n->unique = $7; - n->targetList = $8; - n->fromClause = $9; - n->whereClause = $10; - n->groupClause = $11; - n->havingClause = $12; - n->unionClause = $13; - n->sortClause = $14; $$ = (Node *)n; } ; @@ -2675,88 +2721,164 @@ opt_of: OF columnList * SELECT STATEMENTS * *****************************************************************************/ +/***S*I***/ +/* The new 'SelectStmt' rule adapted for the optional use of INTERSECT EXCEPT and UNION + * accepts the use of '(' and ')' to select an order of set operations. + * + * The rule returns a SelectStmt Node having the set operations attached to + * unionClause and intersectClause (NIL if no set operations were present) */ +SelectStmt: select_w_o_sort sort_clause for_update_clause + { + /* There were no set operations, so just attach the sortClause */ + if IsA($1, SelectStmt) + { + SelectStmt *n = (SelectStmt *)$1; + n->sortClause = $2; + n->forUpdate = $3; + $$ = (Node *)n; + } + /* There were set operations: The root of the operator tree + * is delivered by $1 but we cannot hand back an A_Expr Node. + * So we search for the leftmost 'SelectStmt' in the operator + * tree $1 (which is the first Select Statement in the query + * typed in by the user or where ever it came from). + * + * Then we attach the whole operator tree to 'intersectClause', + * and a list of all 'SelectStmt' Nodes to 'unionClause' and + * hand back the leftmost 'SelectStmt' Node. (We do it this way + * because the following functions (e.g. parse_analyze etc.) + * excpect a SelectStmt node and not an operator tree! The whole + * tree attached to 'intersectClause' won't be touched by + * parse_analyze() etc. until the function + * Except_Intersect_Rewrite() (in rewriteHandler.c) which performs + * the necessary steps to be able create a plan!) */ + else + { + List *select_list = NIL; + SelectStmt *first_select; + Node *op = (Node *) $1; + bool intersect_present = FALSE, unionall_present = FALSE; + + /* Take the operator tree as an argument and + * create a list of all SelectStmt Nodes found in the tree. + * + * If one of the SelectStmt Nodes has the 'unionall' flag + * set to true the 'unionall_present' flag is also set to + * true */ + create_select_list((Node *)op, &select_list, &unionall_present); + + /* Replace all the A_Expr Nodes in the operator tree by + * Expr Nodes. + * + * If an INTERSECT or an EXCEPT is present, the + * 'intersect_present' flag is set to true */ + op = A_Expr_to_Expr(op, &intersect_present); + + /* If both flags are set to true we have a UNION ALL + * statement mixed up with INTERSECT or EXCEPT + * which can not be handled at the moment */ + if (intersect_present && unionall_present) + { + elog(ERROR,"UNION ALL not allowed in mixed set operations!"); + } + + /* Get the leftmost SeletStmt Node (which automatically + * represents the first Select Statement of the query!) */ + first_select = (SelectStmt *)lfirst(select_list); + + /* Attach the list of all SeletStmt Nodes to unionClause */ + first_select->unionClause = select_list; + + /* Attach the whole operator tree to intersectClause */ + first_select->intersectClause = (List *) op; + + /* finally attach the sort clause */ + first_select->sortClause = $2; + first_select>forUpdate = $3; + $$ = (Node *)first_select; + } + if ((SelectStmt *)$$)->forUpdate != NULL) + { + SelectStmt *n = (SelectStmt *)$1; + + if (n->unionClause != NULL) + elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION clause"); + if (n->unique != NULL) + elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause"); + if (n->groupClause != NULL) + elog(ERROR, "SELECT FOR UPDATE is not allowed with GROUP BY clause"); + if (n->havingClause != NULL) + elog(ERROR, "SELECT FOR UPDATE is not allowed with HAVING clause"); + } + } + ; + +/***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! + * + * The rule builds up an operator tree using A_Expr Nodes. AND Nodes represent + * INTERSECTs OR Nodes represent UNIONs and AND NOT nodes represent EXCEPTs. + * The SelectStatements to be connected are the left and right arguments to + * the A_Expr Nodes. + * If no set operations show up in the query the tree consists only of one + * SelectStmt Node */ +select_w_o_sort: '(' select_w_o_sort ')' + { + $$ = $2; + } + | SubSelect + { + $$ = $1; + } + | select_w_o_sort EXCEPT select_w_o_sort + { + $$ = (Node *)makeA_Expr(AND,NULL,$1, + makeA_Expr(NOT,NULL,NULL,$3)); + } + | select_w_o_sort UNION opt_union select_w_o_sort + { + if (IsA($4, SelectStmt)) + { + SelectStmt *n = (SelectStmt *)$4; + n->unionall = $3; + } + $$ = (Node *)makeA_Expr(OR,NULL,$1,$4); + } + | select_w_o_sort INTERSECT select_w_o_sort + { + $$ = (Node *)makeA_Expr(AND,NULL,$1,$3); + } + ; -SelectStmt: SELECT opt_unique res_target_list2 +/***S*I***/ +SubSelect: SELECT opt_unique res_target_list2 result from_clause where_clause group_clause having_clause - union_clause sort_clause for_update_clause { SelectStmt *n = makeNode(SelectStmt); n->unique = $2; + n->unionall = FALSE; n->targetList = $3; + /***S*I***/ + /* This is new: Subselects support the INTO clause + * which allows queries that are not part of the + * SQL92 standard and should not be formulated! + * We need it for INTERSECT and EXCEPT and I did not + * want to create a new rule 'SubSelect1' including the + * feature. If it makes troubles we will have to add + * a new rule and change this to prevent INTOs in + * Subselects again */ n->into = $4; + n->fromClause = $5; n->whereClause = $6; n->groupClause = $7; n->havingClause = $8; - n->unionClause = $9; - n->sortClause = $10; - n->forUpdate = $11; - if (n->forUpdate != NULL) - { - if (n->unionClause != NULL) - elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION clause"); - if (n->unique != NULL) - elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause"); - if (n->groupClause != NULL) - elog(ERROR, "SELECT FOR UPDATE is not allowed with GROUP BY clause"); - if (n->havingClause != NULL) - elog(ERROR, "SELECT FOR UPDATE is not allowed with HAVING clause"); - } - else - $$ = (Node *)n; - } - ; - -SubSelect: SELECT opt_unique res_target_list2 - from_clause where_clause - group_clause having_clause - union_clause - { - SelectStmt *n = makeNode(SelectStmt); - n->unique = $2; - n->targetList = $3; - n->fromClause = $4; - n->whereClause = $5; - n->groupClause = $6; - n->havingClause = $7; - n->unionClause = $8; - $$ = (Node *)n; - } - ; - -union_clause: UNION opt_union select_list - { - SelectStmt *n = (SelectStmt *)lfirst($3); - n->unionall = $2; - $$ = $3; - } - | /*EMPTY*/ - { $$ = NIL; } - ; - -select_list: select_list UNION opt_union SubUnion - { - SelectStmt *n = (SelectStmt *)$4; - n->unionall = $3; - $$ = lappend($1, $4); - } - | SubUnion - { $$ = lcons($1, NIL); } - ; - -SubUnion: SELECT opt_unique res_target_list2 - from_clause where_clause - group_clause having_clause - { - SelectStmt *n = makeNode(SelectStmt); - n->unique = $2; - n->unionall = FALSE; - n->targetList = $3; - n->fromClause = $4; - n->whereClause = $5; - n->groupClause = $6; - n->havingClause = $7; $$ = (Node *)n; } ; diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index e6da4f20a04..b7a9d003dfb 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.50 1998/12/18 09:10:34 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.51 1999/01/18 00:09:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -93,6 +93,9 @@ static ScanKeyword ScanKeywords[] = { {"else", ELSE}, {"encoding", ENCODING}, {"end", END_TRANS}, + /***S*I***/ + {"except", EXCEPT}, + {"execute", EXECUTE}, {"exists", EXISTS}, {"explain", EXPLAIN}, @@ -120,6 +123,9 @@ static ScanKeyword ScanKeywords[] = { {"insensitive", INSENSITIVE}, {"insert", INSERT}, {"instead", INSTEAD}, + /***S*I***/ + {"intersect", INTERSECT}, + {"interval", INTERVAL}, {"into", INTO}, {"is", IS}, |