*
*
* 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
* AUTHOR DATE MAJOR EVENT
*****************************************************************************/
/* 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
+ * 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
{
$4->relname = $3;
- $$ = (Node *)$4;
+ $$ = (Node *) $4;
}
;
$$->unionClause = NIL;
$$->intersectClause = NIL;
}
- /* We want the full power of SelectStatements including INTERSECT and EXCEPT
- * for insertion */
+/* We want the full power of SelectStatements including INTERSECT and EXCEPT
+ * for insertion. However, we can't support sort or limit clauses.
+ */
| SelectStmt
{
- SelectStmt *n;
-
- n = (SelectStmt *)$1;
+ SelectStmt *n = (SelectStmt *) $1;
+ if (n->sortClause)
+ elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
$$ = makeNode(InsertStmt);
- $$->cols = NULL;
+ $$->cols = NIL;
$$->unique = n->unique;
$$->targetList = n->targetList;
$$->fromClause = n->fromClause;
$$->havingClause = n->havingClause;
$$->unionClause = n->unionClause;
$$->intersectClause = n->intersectClause;
+ $$->unionall = n->unionall;
$$->forUpdate = n->forUpdate;
}
| '(' columnList ')' VALUES '(' target_list ')'
}
| '(' columnList ')' SelectStmt
{
- SelectStmt *n;
-
- n = (SelectStmt *)$4;
+ SelectStmt *n = (SelectStmt *) $4;
+ if (n->sortClause)
+ elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->unique = n->unique;
$$->havingClause = n->havingClause;
$$->unionClause = n->unionClause;
$$->intersectClause = n->intersectClause;
+ $$->unionall = n->unionall;
+ $$->forUpdate = n->forUpdate;
}
;
* 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
* unionClause and intersectClause (NIL if no set operations were present)
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)
{
- SelectStmt *n = (SelectStmt *)$1;
- n->sortClause = $2;
- n->forUpdate = $3;
- n->limitOffset = nth(0, $4);
- n->limitCount = nth(1, $4);
- $$ = (Node *)n;
+ /* There were no set operations, so just attach the
+ * one-time clauses.
+ */
+ SelectStmt *n = (SelectStmt *) $1;
+ n->sortClause = $2;
+ 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
{
- 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!");
- }
+ /* There were set operations. The root of the operator
+ * tree is delivered by $1, but we must hand back a
+ * SelectStmt node not an A_Expr Node.
+ * So we find the leftmost 'SelectStmt' in the operator
+ * tree $1 (which is the first Select Statement in the
+ * query), which will be the returned node.
+ * Then we attach the whole operator tree to that node's
+ * 'intersectClause', and a list of all 'SelectStmt' Nodes
+ * in the tree to its 'unionClause'. (NOTE that this means
+ * the top node has indirect recursive pointers to itself!
+ * This would cause trouble if we tried copyObject!!)
+ * The intersectClause and unionClause subtrees will be
+ * left untouched by the main parser, and will only be
+ * processed when control gets to the function
+ * Except_Intersect_Rewrite() (in rewriteHandler.c).
+ */
+ Node *op = (Node *) $1;
+ List *select_list = NIL;
+ SelectStmt *first_select;
+ 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(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
- * represents the first Select Statement of the query!) */
- first_select = (SelectStmt *)lfirst(select_list);
+ /* 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 list of all SeletStmt Nodes to unionClause */
+ first_select->unionClause = select_list;
- /* Attach the whole operator tree to intersectClause */
- first_select->intersectClause = (List *) op;
+ /* 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;
+ /* finally attach the sort clause &etc */
+ first_select->sortClause = $2;
+ first_select->forUpdate = $3;
+ first_select->limitOffset = nth(0, $4);
+ first_select->limitCount = nth(1, $4);
+ $$ = (Node *) first_select;
}
if (((SelectStmt *)$$)->forUpdate != NULL && QueryIsRule)
elog(ERROR, "SELECT FOR UPDATE is not allowed in RULES");
}
;
-/* 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
+/* This rule parses Select statements that can appear within set operations,
+ * including UNION, INTERSECT and EXCEPT. '(' and ')' can be used to specify
+ * the ordering of the set operations. Without '(' and ')' we want the
* 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
- * 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 A_Expr Nodes.
- * If no set operations show up in the query the tree consists only of one
- * SelectStmt Node */
+ * If no set operations appear in the query, the tree consists only of one
+ * SelectStmt Node.
+ */
select_clause: '(' select_clause ')'
{
$$ = $2;
{
SelectStmt *n = (SelectStmt *)$4;
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);
}
* 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 */
+ * Subselects again.
+ */
n->istemp = (bool) ((Value *) lfirst($4))->val.ival;
n->into = (char *) lnext($4);