Commit 2908bd53 authored by Tom Lane's avatar Tom Lane

Complain about INSERT ... SELECT ... ORDER BY, which we do not

support, but which the grammar was accepting.  Also, fix several bugs
having to do with failure to copy fields up from a subselect to a select
or insert node.
parent d6ce220f
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.93 1999/07/17 20:17:21 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.94 1999/07/20 00:18:01 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -2468,14 +2468,15 @@ OptimizableStmt: SelectStmt ...@@ -2468,14 +2468,15 @@ OptimizableStmt: SelectStmt
*****************************************************************************/ *****************************************************************************/
/* This rule used 'opt_column_list' between 'relation_name' and 'insert_rest' /* This rule used 'opt_column_list' between 'relation_name' and 'insert_rest'
* originally. When the second rule of 'insert_rest' was changed to use * originally. When the second rule of 'insert_rest' was changed to use the
* the new 'SelectStmt' rule (for INTERSECT and EXCEPT) it produced a shift/reduce * 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 * conflict. So I just changed the rules 'InsertStmt' and 'insert_rest' to
* the same statements without any shift/reduce conflicts */ * accept the same statements without any shift/reduce conflicts
InsertStmt: INSERT INTO relation_name insert_rest */
InsertStmt: INSERT INTO relation_name insert_rest
{ {
$4->relname = $3; $4->relname = $3;
$$ = (Node *)$4; $$ = (Node *) $4;
} }
; ;
...@@ -2503,15 +2504,16 @@ insert_rest: VALUES '(' target_list ')' ...@@ -2503,15 +2504,16 @@ insert_rest: VALUES '(' target_list ')'
$$->unionClause = NIL; $$->unionClause = NIL;
$$->intersectClause = NIL; $$->intersectClause = NIL;
} }
/* We want the full power of SelectStatements including INTERSECT and EXCEPT /* We want the full power of SelectStatements including INTERSECT and EXCEPT
* for insertion */ * for insertion. However, we can't support sort or limit clauses.
*/
| SelectStmt | SelectStmt
{ {
SelectStmt *n; SelectStmt *n = (SelectStmt *) $1;
if (n->sortClause)
n = (SelectStmt *)$1; elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
$$ = makeNode(InsertStmt); $$ = makeNode(InsertStmt);
$$->cols = NULL; $$->cols = NIL;
$$->unique = n->unique; $$->unique = n->unique;
$$->targetList = n->targetList; $$->targetList = n->targetList;
$$->fromClause = n->fromClause; $$->fromClause = n->fromClause;
...@@ -2520,6 +2522,7 @@ insert_rest: VALUES '(' target_list ')' ...@@ -2520,6 +2522,7 @@ insert_rest: VALUES '(' target_list ')'
$$->havingClause = n->havingClause; $$->havingClause = n->havingClause;
$$->unionClause = n->unionClause; $$->unionClause = n->unionClause;
$$->intersectClause = n->intersectClause; $$->intersectClause = n->intersectClause;
$$->unionall = n->unionall;
$$->forUpdate = n->forUpdate; $$->forUpdate = n->forUpdate;
} }
| '(' columnList ')' VALUES '(' target_list ')' | '(' columnList ')' VALUES '(' target_list ')'
...@@ -2537,9 +2540,9 @@ insert_rest: VALUES '(' target_list ')' ...@@ -2537,9 +2540,9 @@ insert_rest: VALUES '(' target_list ')'
} }
| '(' columnList ')' SelectStmt | '(' columnList ')' SelectStmt
{ {
SelectStmt *n; SelectStmt *n = (SelectStmt *) $4;
if (n->sortClause)
n = (SelectStmt *)$4; elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
$$ = makeNode(InsertStmt); $$ = makeNode(InsertStmt);
$$->cols = $2; $$->cols = $2;
$$->unique = n->unique; $$->unique = n->unique;
...@@ -2550,6 +2553,8 @@ insert_rest: VALUES '(' target_list ')' ...@@ -2550,6 +2553,8 @@ insert_rest: VALUES '(' target_list ')'
$$->havingClause = n->havingClause; $$->havingClause = n->havingClause;
$$->unionClause = n->unionClause; $$->unionClause = n->unionClause;
$$->intersectClause = n->intersectClause; $$->intersectClause = n->intersectClause;
$$->unionall = n->unionall;
$$->forUpdate = n->forUpdate;
} }
; ;
...@@ -2682,8 +2687,9 @@ opt_cursor: BINARY { $$ = TRUE; } ...@@ -2682,8 +2687,9 @@ opt_cursor: BINARY { $$ = TRUE; }
* SELECT STATEMENTS * SELECT STATEMENTS
* *
*****************************************************************************/ *****************************************************************************/
/* 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. /* A complete SELECT statement looks like this. Note sort, for_update,
* and limit clauses can only appear once, not in each subselect.
* *
* The rule returns a SelectStmt Node having the set operations attached to * The rule returns a SelectStmt Node having the set operations attached to
* unionClause and intersectClause (NIL if no set operations were present) * unionClause and intersectClause (NIL if no set operations were present)
...@@ -2691,94 +2697,105 @@ opt_cursor: BINARY { $$ = TRUE; } ...@@ -2691,94 +2697,105 @@ opt_cursor: BINARY { $$ = TRUE; }
SelectStmt: select_clause sort_clause for_update_clause opt_select_limit SelectStmt: select_clause sort_clause for_update_clause opt_select_limit
{ {
/* There were no set operations, so just attach the sortClause */
if IsA($1, SelectStmt) if IsA($1, SelectStmt)
{ {
SelectStmt *n = (SelectStmt *)$1; /* There were no set operations, so just attach the
n->sortClause = $2; * one-time clauses.
n->forUpdate = $3; */
n->limitOffset = nth(0, $4); SelectStmt *n = (SelectStmt *) $1;
n->limitCount = nth(1, $4); n->sortClause = $2;
$$ = (Node *)n; n->forUpdate = $3;
n->limitOffset = nth(0, $4);
n->limitCount = nth(1, $4);
$$ = (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 else
{ {
List *select_list = NIL; /* There were set operations. The root of the operator
SelectStmt *first_select; * tree is delivered by $1, but we must hand back a
Node *op = (Node *) $1; * SelectStmt node not an A_Expr Node.
bool intersect_present = FALSE, unionall_present = FALSE; * So we find the leftmost 'SelectStmt' in the operator
* tree $1 (which is the first Select Statement in the
/* Take the operator tree as an argument and * query), which will be the returned node.
* create a list of all SelectStmt Nodes found in the tree. * Then we attach the whole operator tree to that node's
* * 'intersectClause', and a list of all 'SelectStmt' Nodes
* If one of the SelectStmt Nodes has the 'unionall' flag * in the tree to its 'unionClause'. (NOTE that this means
* set to true the 'unionall_present' flag is also set to * the top node has indirect recursive pointers to itself!
* true */ * This would cause trouble if we tried copyObject!!)
create_select_list((Node *)op, &select_list, &unionall_present); * The intersectClause and unionClause subtrees will be
* left untouched by the main parser, and will only be
/* Replace all the A_Expr Nodes in the operator tree by * processed when control gets to the function
* Expr Nodes. * Except_Intersect_Rewrite() (in rewriteHandler.c).
* */
* If an INTERSECT or an EXCEPT is present, the Node *op = (Node *) $1;
* 'intersect_present' flag is set to true */ List *select_list = NIL;
op = A_Expr_to_Expr(op, &intersect_present); SelectStmt *first_select;
bool intersect_present = false,
/* If both flags are set to true we have a UNION ALL unionall_present = false;
* statement mixed up with INTERSECT or EXCEPT
* which can not be handled at the moment */ /* Take the operator tree as an argument and create a
if (intersect_present && unionall_present) * list of all SelectStmt Nodes found in the tree.
{ *
elog(ERROR,"UNION ALL not allowed in mixed set operations!"); * 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(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);
/* Get the leftmost SeletStmt Node (which automatically /* If both flags are set to true we have a UNION ALL
* represents the first Select Statement of the query!) */ * statement mixed up with INTERSECT or EXCEPT
first_select = (SelectStmt *)lfirst(select_list); * 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 */ /* Attach the list of all SeletStmt Nodes to unionClause */
first_select->unionClause = select_list; first_select->unionClause = select_list;
/* Attach the whole operator tree to intersectClause */ /* Attach the whole operator tree to intersectClause */
first_select->intersectClause = (List *) op; first_select->intersectClause = (List *) op;
/* finally attach the sort clause */ /* finally attach the sort clause &etc */
first_select->sortClause = $2; first_select->sortClause = $2;
first_select->forUpdate = $3; first_select->forUpdate = $3;
$$ = (Node *)first_select; first_select->limitOffset = nth(0, $4);
first_select->limitCount = nth(1, $4);
$$ = (Node *) first_select;
} }
if (((SelectStmt *)$$)->forUpdate != NULL && QueryIsRule) if (((SelectStmt *)$$)->forUpdate != NULL && QueryIsRule)
elog(ERROR, "SELECT FOR UPDATE is not allowed in RULES"); elog(ERROR, "SELECT FOR UPDATE is not allowed in RULES");
} }
; ;
/* This rule parses Select statements including UNION INTERSECT and EXCEPT. /* This rule parses Select statements that can appear within set operations,
* '(' and ')' can be used to specify the order of the operations * including UNION, INTERSECT and EXCEPT. '(' and ')' can be used to specify
* (UNION EXCEPT INTERSECT). Without the use of '(' and ')' we want the * the ordering of the set operations. Without '(' and ')' we want the
* operations to be left associative. * operations to be left associative.
* *
* The sort_clause is not handled here! * Note that sort clauses cannot be included at this level --- a sort clause
* can only appear at the end of the complete Select, and it will be handled
* by the topmost SelectStmt rule. Likewise FOR UPDATE and LIMIT.
* *
* The rule builds up an operator tree using A_Expr Nodes. AND Nodes represent * 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. * INTERSECTs, OR Nodes represent UNIONs, and AND NOT nodes represent EXCEPTs.
* The SelectStatements to be connected are the left and right arguments to * The SelectStatements to be connected are the left and right arguments to
* the A_Expr Nodes. * the A_Expr Nodes.
* If no set operations show up in the query the tree consists only of one * If no set operations appear in the query, the tree consists only of one
* SelectStmt Node */ * SelectStmt Node.
*/
select_clause: '(' select_clause ')' select_clause: '(' select_clause ')'
{ {
$$ = $2; $$ = $2;
...@@ -2798,6 +2815,12 @@ select_clause: '(' select_clause ')' ...@@ -2798,6 +2815,12 @@ select_clause: '(' select_clause ')'
{ {
SelectStmt *n = (SelectStmt *)$4; SelectStmt *n = (SelectStmt *)$4;
n->unionall = $3; n->unionall = $3;
/* NOTE: if UNION ALL appears with a parenthesized set
* operation to its right, the ALL is silently discarded.
* Should we generate an error instead? I think it may
* be OK since ALL with UNION to its right is ignored
* anyway...
*/
} }
$$ = (Node *)makeA_Expr(OR,NULL,$1,$4); $$ = (Node *)makeA_Expr(OR,NULL,$1,$4);
} }
...@@ -2822,7 +2845,8 @@ SubSelect: SELECT opt_unique target_list ...@@ -2822,7 +2845,8 @@ SubSelect: SELECT opt_unique target_list
* want to create a new rule 'SubSelect1' including the * want to create a new rule 'SubSelect1' including the
* feature. If it makes troubles we will have to add * feature. If it makes troubles we will have to add
* a new rule and change this to prevent INTOs in * a new rule and change this to prevent INTOs in
* Subselects again */ * Subselects again.
*/
n->istemp = (bool) ((Value *) lfirst($4))->val.ival; n->istemp = (bool) ((Value *) lfirst($4))->val.ival;
n->into = (char *) lnext($4); n->into = (char *) lnext($4);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment