OSDN Git Service

Complain about INSERT ... SELECT ... ORDER BY, which we do not
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 20 Jul 1999 00:18:01 +0000 (00:18 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 20 Jul 1999 00:18:01 +0000 (00:18 +0000)
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.

src/backend/parser/gram.y

index 5749e48..ec41d7e 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * 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
@@ -2468,14 +2468,15 @@ OptimizableStmt:  SelectStmt
  *****************************************************************************/
 
 /* 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;
                                }
                ;
 
@@ -2503,15 +2504,16 @@ insert_rest:  VALUES '(' target_list ')'
                                        $$->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;
@@ -2520,6 +2522,7 @@ insert_rest:  VALUES '(' target_list ')'
                                        $$->havingClause = n->havingClause;
                                        $$->unionClause = n->unionClause;
                                        $$->intersectClause = n->intersectClause;
+                                       $$->unionall = n->unionall;
                                        $$->forUpdate = n->forUpdate;
                                }
                | '(' columnList ')' VALUES '(' target_list ')'
@@ -2537,9 +2540,9 @@ insert_rest:  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;
@@ -2550,6 +2553,8 @@ insert_rest:  VALUES '(' target_list ')'
                                        $$->havingClause = n->havingClause;
                                        $$->unionClause = n->unionClause;
                                        $$->intersectClause = n->intersectClause;
+                                       $$->unionall = n->unionall;
+                                       $$->forUpdate = n->forUpdate;
                                }
                ;
 
@@ -2682,8 +2687,9 @@ opt_cursor:  BINARY                                               { $$ = TRUE; }
  *                             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)
@@ -2691,94 +2697,105 @@ opt_cursor:  BINARY                                            { $$ = TRUE; }
 
 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; 
@@ -2798,6 +2815,12 @@ select_clause: '(' select_clause ')'
                                  {
                                     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);
                        }
@@ -2822,7 +2845,8 @@ SubSelect:        SELECT opt_unique target_list
                                         * 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);