]> granicus.if.org Git - postgresql/blobdiff - src/backend/parser/gram.y
Fix problems with parentheses around sub-SELECT --- for the last time,
[postgresql] / src / backend / parser / gram.y
index 82cc5a9d270e8cabf84a42c541026635db1d7da3..8da450499ee7f3c9ae4529f7b8ea8483e50f9a07 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.214 2001/01/06 10:50:02 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.215 2001/01/15 20:36:36 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -146,7 +146,8 @@ static void doNegateFloat(Value *v);
                UnlistenStmt, UpdateStmt, VacuumStmt, VariableResetStmt,
                VariableSetStmt, VariableShowStmt, ViewStmt, CheckPointStmt
 
-%type <node>   select_no_parens, select_clause, simple_select
+%type <node>   select_no_parens, select_with_parens, select_clause,
+                               simple_select
 
 %type <node>    alter_column_action
 %type <ival>    drop_behavior
@@ -2666,16 +2667,7 @@ RuleActionMulti:  RuleActionMulti ';' RuleActionStmtOrEmpty
                                }
                ;
 
-/*
- * Allowing RuleActionStmt to be a SelectStmt creates an ambiguity:
- * is the RuleActionList "((SELECT foo))" a standalone RuleActionStmt,
- * or a one-entry RuleActionMulti list?  We don't really care, but yacc
- * wants to know.  We use operator precedence to resolve the ambiguity:
- * giving this rule a higher precedence than ')' will force a reduce
- * rather than shift decision, causing the one-entry-list interpretation
- * to be chosen.
- */
-RuleActionStmt:        SelectStmt                              %prec TYPECAST
+RuleActionStmt:        SelectStmt
                | InsertStmt
                | UpdateStmt
                | DeleteStmt
@@ -3262,32 +3254,48 @@ opt_cursor:  BINARY                                             { $$ = TRUE; }
  * The rule returns either a single SelectStmt node or a tree of them,
  * representing a set-operation tree.
  *
- * To avoid ambiguity problems with nested parentheses, we have to define
- * a "select_no_parens" nonterminal in which there are no parentheses
- * at the outermost level.  This is used in the production
- *             c_expr: '(' select_no_parens ')'
- * This gives a unique parsing of constructs where a subselect is nested
- * in an expression with extra parentheses: the parentheses are not part
- * of the subselect but of the outer expression.  yacc is not quite bright
- * enough to handle the situation completely, however.  To prevent a shift/
- * reduce conflict, we also have to attach a precedence to the
- *             SelectStmt: select_no_parens
- * rule that is higher than the precedence of ')'.  This means that when
- * "((SELECT foo" has been parsed in an expression context, and the
- * next token is ')', the parser will follow the '(' SelectStmt ')' reduction
- * path rather than '(' select_no_parens ')'.  The upshot is that excess
- * parens don't work in this context: SELECT ((SELECT foo)) will give a
- * parse error, whereas SELECT ((SELECT foo) UNION (SELECT bar)) is OK.
- * This is ugly, but it beats not allowing excess parens anywhere...
- *
- * In all other contexts, we can use SelectStmt which allows outer parens.
+ * There is an ambiguity when a sub-SELECT is within an a_expr and there
+ * are excess parentheses: do the parentheses belong to the sub-SELECT or
+ * to the surrounding a_expr?  We don't really care, but yacc wants to know.
+ * To resolve the ambiguity, we are careful to define the grammar so that
+ * the decision is staved off as long as possible: as long as we can keep
+ * absorbing parentheses into the sub-SELECT, we will do so, and only when
+ * it's no longer possible to do that will we decide that parens belong to
+ * the expression.  For example, in "SELECT (((SELECT 2)) + 3)" the extra
+ * parentheses are treated as part of the sub-select.  The necessity of doing
+ * it that way is shown by "SELECT (((SELECT 2)) UNION SELECT 2)".  Had we
+ * parsed "((SELECT 2))" as an a_expr, it'd be too late to go back to the
+ * SELECT viewpoint when we see the UNION.
+ *
+ * This approach is implemented by defining a nonterminal select_with_parens,
+ * which represents a SELECT with at least one outer layer of parentheses,
+ * and being careful to use select_with_parens, never '(' SelectStmt ')',
+ * in the expression grammar.  We will then have shift-reduce conflicts
+ * which we can resolve in favor of always treating '(' <select> ')' as
+ * a select_with_parens.  To resolve the conflicts, the productions that
+ * conflict with the select_with_parens productions are manually given
+ * precedences lower than the precedence of ')', thereby ensuring that we
+ * shift ')' (and then reduce to select_with_parens) rather than trying to
+ * reduce the inner <select> nonterminal to something else.  We use UMINUS
+ * precedence for this, which is a fairly arbitrary choice.
+ *
+ * To be able to define select_with_parens itself without ambiguity, we need
+ * a nonterminal select_no_parens that represents a SELECT structure with no
+ * outermost parentheses.  This is a little bit tedious, but it works.
+ *
+ * In non-expression contexts, we use SelectStmt which can represent a SELECT
+ * with or without outer parentheses.
  */
 
-SelectStmt: select_no_parens                   %prec TYPECAST
+SelectStmt: select_no_parens                   %prec UMINUS
+               | select_with_parens                    %prec UMINUS
+               ;
+
+select_with_parens: '(' select_no_parens ')'
                        {
-                               $$ = $1;
+                               $$ = $2;
                        }
-               | '(' SelectStmt ')'
+               | '(' select_with_parens ')'
                        {
                                $$ = $2;
                        }
@@ -3318,13 +3326,7 @@ select_no_parens: simple_select
                ;
 
 select_clause: simple_select
-                       {
-                               $$ = $1;
-                       }
-               | '(' SelectStmt ')'
-                       {
-                               $$ = $2;
-                       }
+               | select_with_parens
                ;
 
 /*
@@ -3342,8 +3344,10 @@ select_clause: simple_select
  *             (SELECT foo UNION SELECT bar) ORDER BY baz
  * not
  *             SELECT foo UNION (SELECT bar ORDER BY baz)
- * Likewise FOR UPDATE and LIMIT.  This does not limit functionality,
- * because you can reintroduce sort and limit clauses inside parentheses.
+ * Likewise FOR UPDATE and LIMIT.  Therefore, those clauses are described
+ * as part of the select_no_parens production, not simple_select.
+ * This does not limit functionality, because you can reintroduce sort and
+ * limit clauses inside parentheses.
  *
  * NOTE: only the leftmost component SelectStmt should have INTO.
  * However, this is not checked by the grammar; parse analysis must check it.
@@ -3614,11 +3618,11 @@ table_ref:  relation_expr
                                        $1->name = $2;
                                        $$ = (Node *) $1;
                                }
-               | '(' SelectStmt ')' alias_clause
+               | select_with_parens alias_clause
                                {
                                        RangeSubselect *n = makeNode(RangeSubselect);
-                                       n->subquery = $2;
-                                       n->name = $4;
+                                       n->subquery = $1;
+                                       n->name = $2;
                                        $$ = (Node *) n;
                                }
                | joined_table
@@ -3788,7 +3792,7 @@ relation_expr:    relation_name
                                        $$->inhOpt = INH_DEFAULT;
                                        $$->name = NULL;
                                }
-               | relation_name '*'                             %prec '='
+               | relation_name '*'
                                {
                                        /* inheritance query */
                                        $$ = makeNode(RangeVar);
@@ -3796,7 +3800,7 @@ relation_expr:    relation_name
                                        $$->inhOpt = INH_YES;
                                        $$->name = NULL;
                                }
-               | ONLY relation_name                    %prec '='
+               | ONLY relation_name
                                {
                                        /* no inheritance */
                                        $$ = makeNode(RangeVar);
@@ -4146,27 +4150,27 @@ opt_interval:  datetime                                                 { $$ = makeList1($1); }
  * Define row_descriptor to allow yacc to break the reduce/reduce conflict
  *  with singleton expressions.
  */
-row_expr: '(' row_descriptor ')' IN '(' SelectStmt ')'
+row_expr: '(' row_descriptor ')' IN select_with_parens
                                {
                                        SubLink *n = makeNode(SubLink);
                                        n->lefthand = $2;
                                        n->oper = (List *) makeA_Expr(OP, "=", NULL, NULL);
                                        n->useor = FALSE;
                                        n->subLinkType = ANY_SUBLINK;
-                                       n->subselect = $6;
+                                       n->subselect = $5;
                                        $$ = (Node *)n;
                                }
-               | '(' row_descriptor ')' NOT IN '(' SelectStmt ')'
+               | '(' row_descriptor ')' NOT IN select_with_parens
                                {
                                        SubLink *n = makeNode(SubLink);
                                        n->lefthand = $2;
                                        n->oper = (List *) makeA_Expr(OP, "<>", NULL, NULL);
                                        n->useor = TRUE;
                                        n->subLinkType = ALL_SUBLINK;
-                                       n->subselect = $7;
+                                       n->subselect = $6;
                                        $$ = (Node *)n;
                                }
-               | '(' row_descriptor ')' all_Op sub_type '(' SelectStmt ')'
+               | '(' row_descriptor ')' all_Op sub_type select_with_parens
                                {
                                        SubLink *n = makeNode(SubLink);
                                        n->lefthand = $2;
@@ -4176,10 +4180,10 @@ row_expr: '(' row_descriptor ')' IN '(' SelectStmt ')'
                                        else
                                                n->useor = FALSE;
                                        n->subLinkType = $5;
-                                       n->subselect = $7;
+                                       n->subselect = $6;
                                        $$ = (Node *)n;
                                }
-               | '(' row_descriptor ')' all_Op '(' SelectStmt ')'
+               | '(' row_descriptor ')' all_Op select_with_parens
                                {
                                        SubLink *n = makeNode(SubLink);
                                        n->lefthand = $2;
@@ -4189,7 +4193,7 @@ row_expr: '(' row_descriptor ')' IN '(' SelectStmt ')'
                                        else
                                                n->useor = FALSE;
                                        n->subLinkType = MULTIEXPR_SUBLINK;
-                                       n->subselect = $6;
+                                       n->subselect = $5;
                                        $$ = (Node *)n;
                                }
                | '(' row_descriptor ')' all_Op '(' row_descriptor ')'
@@ -4291,9 +4295,9 @@ a_expr:  c_expr
                 * If you add more explicitly-known operators, be sure to add them
                 * also to b_expr and to the MathOp list above.
                 */
-               | '+' a_expr %prec UMINUS
+               | '+' a_expr                                    %prec UMINUS
                                {       $$ = makeA_Expr(OP, "+", NULL, $2); }
-               | '-' a_expr %prec UMINUS
+               | '-' a_expr                                    %prec UMINUS
                                {       $$ = doNegate($2); }
                | '%' a_expr
                                {       $$ = makeA_Expr(OP, "%", NULL, $2); }
@@ -4458,12 +4462,12 @@ a_expr:  c_expr
                                                makeA_Expr(OP, "<", $1, $4),
                                                makeA_Expr(OP, ">", $1, $6));
                                }
-               | a_expr IN '(' in_expr ')'
+               | a_expr IN in_expr
                                {
                                        /* in_expr returns a SubLink or a list of a_exprs */
-                                       if (IsA($4, SubLink))
+                                       if (IsA($3, SubLink))
                                        {
-                                                       SubLink *n = (SubLink *)$4;
+                                                       SubLink *n = (SubLink *)$3;
                                                        n->lefthand = makeList1($1);
                                                        n->oper = (List *) makeA_Expr(OP, "=", NULL, NULL);
                                                        n->useor = FALSE;
@@ -4474,7 +4478,7 @@ a_expr:  c_expr
                                        {
                                                Node *n = NULL;
                                                List *l;
-                                               foreach(l, (List *) $4)
+                                               foreach(l, (List *) $3)
                                                {
                                                        Node *cmp = makeA_Expr(OP, "=", $1, lfirst(l));
                                                        if (n == NULL)
@@ -4485,12 +4489,12 @@ a_expr:  c_expr
                                                $$ = n;
                                        }
                                }
-               | a_expr NOT IN '(' in_expr ')'
+               | a_expr NOT IN in_expr
                                {
                                        /* in_expr returns a SubLink or a list of a_exprs */
-                                       if (IsA($5, SubLink))
+                                       if (IsA($4, SubLink))
                                        {
-                                               SubLink *n = (SubLink *)$5;
+                                               SubLink *n = (SubLink *)$4;
                                                n->lefthand = makeList1($1);
                                                n->oper = (List *) makeA_Expr(OP, "<>", NULL, NULL);
                                                n->useor = FALSE;
@@ -4501,7 +4505,7 @@ a_expr:  c_expr
                                        {
                                                Node *n = NULL;
                                                List *l;
-                                               foreach(l, (List *) $5)
+                                               foreach(l, (List *) $4)
                                                {
                                                        Node *cmp = makeA_Expr(OP, "<>", $1, lfirst(l));
                                                        if (n == NULL)
@@ -4512,14 +4516,14 @@ a_expr:  c_expr
                                                $$ = n;
                                        }
                                }
-               | a_expr all_Op sub_type '(' SelectStmt ')'
+               | a_expr all_Op sub_type select_with_parens
                                {
                                        SubLink *n = makeNode(SubLink);
                                        n->lefthand = makeList1($1);
                                        n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL);
                                        n->useor = FALSE; /* doesn't matter since only one col */
                                        n->subLinkType = $3;
-                                       n->subselect = $5;
+                                       n->subselect = $4;
                                        $$ = (Node *)n;
                                }
                | row_expr
@@ -4539,9 +4543,9 @@ b_expr:  c_expr
                                {       $$ = $1;  }
                | b_expr TYPECAST Typename
                                {       $$ = makeTypeCast($1, $3); }
-               | '+' b_expr %prec UMINUS
+               | '+' b_expr                                    %prec UMINUS
                                {       $$ = makeA_Expr(OP, "+", NULL, $2); }
-               | '-' b_expr %prec UMINUS
+               | '-' b_expr                                    %prec UMINUS
                                {       $$ = doNegate($2); }
                | '%' b_expr
                                {       $$ = makeA_Expr(OP, "%", NULL, $2); }
@@ -4908,24 +4912,24 @@ c_expr:  attr
                                        n->agg_distinct = FALSE;
                                        $$ = (Node *)n;
                                }
-               | '(' select_no_parens ')'
+               | select_with_parens                    %prec UMINUS
                                {
                                        SubLink *n = makeNode(SubLink);
                                        n->lefthand = NIL;
                                        n->oper = NIL;
                                        n->useor = FALSE;
                                        n->subLinkType = EXPR_SUBLINK;
-                                       n->subselect = $2;
+                                       n->subselect = $1;
                                        $$ = (Node *)n;
                                }
-               | EXISTS '(' SelectStmt ')'
+               | EXISTS select_with_parens
                                {
                                        SubLink *n = makeNode(SubLink);
                                        n->lefthand = NIL;
                                        n->oper = NIL;
                                        n->useor = FALSE;
                                        n->subLinkType = EXISTS_SUBLINK;
-                                       n->subselect = $3;
+                                       n->subselect = $2;
                                        $$ = (Node *)n;
                                }
                ;
@@ -5037,14 +5041,14 @@ trim_list:  a_expr FROM expr_list
                                { $$ = $1; }
                ;
 
-in_expr:  SelectStmt
+in_expr:  select_with_parens
                                {
                                        SubLink *n = makeNode(SubLink);
                                        n->subselect = $1;
                                        $$ = (Node *)n;
                                }
-               | in_expr_nodes
-                               {       $$ = (Node *)$1; }
+               | '(' in_expr_nodes ')'
+                               {       $$ = (Node *)$2; }
                ;
 
 in_expr_nodes:  a_expr