Commit 3db4056e authored by Tom Lane's avatar Tom Lane

Fix problems with parentheses around sub-SELECT --- for the last time,

I hope.  I finally realized that we were going at it backwards: when
there are excess parentheses, they need to be treated as part of the
sub-SELECT, not as part of the surrounding expression.  Although either
choice yields an unambiguous grammar, only this way produces a grammar
that is LALR(1).  With the old approach we were guaranteed to fail on
either 'SELECT (((SELECT 2)) + 3)' or
'SELECT (((SELECT 2)) UNION SELECT 2)' depending on which way we
resolve the initial shift/reduce conflict.  With the new way, the same
reduction track can be followed in both cases until we have advanced
far enough to know whether we are done with the sub-SELECT or not.
parent efd6cade
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -146,7 +146,8 @@ static void doNegateFloat(Value *v); ...@@ -146,7 +146,8 @@ static void doNegateFloat(Value *v);
UnlistenStmt, UpdateStmt, VacuumStmt, VariableResetStmt, UnlistenStmt, UpdateStmt, VacuumStmt, VariableResetStmt,
VariableSetStmt, VariableShowStmt, ViewStmt, CheckPointStmt 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 <node> alter_column_action
%type <ival> drop_behavior %type <ival> drop_behavior
...@@ -2666,16 +2667,7 @@ RuleActionMulti: RuleActionMulti ';' RuleActionStmtOrEmpty ...@@ -2666,16 +2667,7 @@ RuleActionMulti: RuleActionMulti ';' RuleActionStmtOrEmpty
} }
; ;
/* RuleActionStmt: SelectStmt
* 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
| InsertStmt | InsertStmt
| UpdateStmt | UpdateStmt
| DeleteStmt | DeleteStmt
...@@ -3262,32 +3254,48 @@ opt_cursor: BINARY { $$ = TRUE; } ...@@ -3262,32 +3254,48 @@ opt_cursor: BINARY { $$ = TRUE; }
* The rule returns either a single SelectStmt node or a tree of them, * The rule returns either a single SelectStmt node or a tree of them,
* representing a set-operation tree. * representing a set-operation tree.
* *
* To avoid ambiguity problems with nested parentheses, we have to define * There is an ambiguity when a sub-SELECT is within an a_expr and there
* a "select_no_parens" nonterminal in which there are no parentheses * are excess parentheses: do the parentheses belong to the sub-SELECT or
* at the outermost level. This is used in the production * to the surrounding a_expr? We don't really care, but yacc wants to know.
* c_expr: '(' select_no_parens ')' * To resolve the ambiguity, we are careful to define the grammar so that
* This gives a unique parsing of constructs where a subselect is nested * the decision is staved off as long as possible: as long as we can keep
* in an expression with extra parentheses: the parentheses are not part * absorbing parentheses into the sub-SELECT, we will do so, and only when
* of the subselect but of the outer expression. yacc is not quite bright * it's no longer possible to do that will we decide that parens belong to
* enough to handle the situation completely, however. To prevent a shift/ * the expression. For example, in "SELECT (((SELECT 2)) + 3)" the extra
* reduce conflict, we also have to attach a precedence to the * parentheses are treated as part of the sub-select. The necessity of doing
* SelectStmt: select_no_parens * it that way is shown by "SELECT (((SELECT 2)) UNION SELECT 2)". Had we
* rule that is higher than the precedence of ')'. This means that when * parsed "((SELECT 2))" as an a_expr, it'd be too late to go back to the
* "((SELECT foo" has been parsed in an expression context, and the * SELECT viewpoint when we see the UNION.
* next token is ')', the parser will follow the '(' SelectStmt ')' reduction *
* path rather than '(' select_no_parens ')'. The upshot is that excess * This approach is implemented by defining a nonterminal select_with_parens,
* parens don't work in this context: SELECT ((SELECT foo)) will give a * which represents a SELECT with at least one outer layer of parentheses,
* parse error, whereas SELECT ((SELECT foo) UNION (SELECT bar)) is OK. * and being careful to use select_with_parens, never '(' SelectStmt ')',
* This is ugly, but it beats not allowing excess parens anywhere... * in the expression grammar. We will then have shift-reduce conflicts
* * which we can resolve in favor of always treating '(' <select> ')' as
* In all other contexts, we can use SelectStmt which allows outer parens. * 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; $$ = $2;
} }
...@@ -3318,13 +3326,7 @@ select_no_parens: simple_select ...@@ -3318,13 +3326,7 @@ select_no_parens: simple_select
; ;
select_clause: simple_select select_clause: simple_select
{ | select_with_parens
$$ = $1;
}
| '(' SelectStmt ')'
{
$$ = $2;
}
; ;
/* /*
...@@ -3342,8 +3344,10 @@ select_clause: simple_select ...@@ -3342,8 +3344,10 @@ select_clause: simple_select
* (SELECT foo UNION SELECT bar) ORDER BY baz * (SELECT foo UNION SELECT bar) ORDER BY baz
* not * not
* SELECT foo UNION (SELECT bar ORDER BY baz) * SELECT foo UNION (SELECT bar ORDER BY baz)
* Likewise FOR UPDATE and LIMIT. This does not limit functionality, * Likewise FOR UPDATE and LIMIT. Therefore, those clauses are described
* because you can reintroduce sort and limit clauses inside parentheses. * 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. * NOTE: only the leftmost component SelectStmt should have INTO.
* However, this is not checked by the grammar; parse analysis must check it. * However, this is not checked by the grammar; parse analysis must check it.
...@@ -3614,11 +3618,11 @@ table_ref: relation_expr ...@@ -3614,11 +3618,11 @@ table_ref: relation_expr
$1->name = $2; $1->name = $2;
$$ = (Node *) $1; $$ = (Node *) $1;
} }
| '(' SelectStmt ')' alias_clause | select_with_parens alias_clause
{ {
RangeSubselect *n = makeNode(RangeSubselect); RangeSubselect *n = makeNode(RangeSubselect);
n->subquery = $2; n->subquery = $1;
n->name = $4; n->name = $2;
$$ = (Node *) n; $$ = (Node *) n;
} }
| joined_table | joined_table
...@@ -3788,7 +3792,7 @@ relation_expr: relation_name ...@@ -3788,7 +3792,7 @@ relation_expr: relation_name
$$->inhOpt = INH_DEFAULT; $$->inhOpt = INH_DEFAULT;
$$->name = NULL; $$->name = NULL;
} }
| relation_name '*' %prec '=' | relation_name '*'
{ {
/* inheritance query */ /* inheritance query */
$$ = makeNode(RangeVar); $$ = makeNode(RangeVar);
...@@ -3796,7 +3800,7 @@ relation_expr: relation_name ...@@ -3796,7 +3800,7 @@ relation_expr: relation_name
$$->inhOpt = INH_YES; $$->inhOpt = INH_YES;
$$->name = NULL; $$->name = NULL;
} }
| ONLY relation_name %prec '=' | ONLY relation_name
{ {
/* no inheritance */ /* no inheritance */
$$ = makeNode(RangeVar); $$ = makeNode(RangeVar);
...@@ -4146,27 +4150,27 @@ opt_interval: datetime { $$ = makeList1($1); } ...@@ -4146,27 +4150,27 @@ opt_interval: datetime { $$ = makeList1($1); }
* Define row_descriptor to allow yacc to break the reduce/reduce conflict * Define row_descriptor to allow yacc to break the reduce/reduce conflict
* with singleton expressions. * with singleton expressions.
*/ */
row_expr: '(' row_descriptor ')' IN '(' SelectStmt ')' row_expr: '(' row_descriptor ')' IN select_with_parens
{ {
SubLink *n = makeNode(SubLink); SubLink *n = makeNode(SubLink);
n->lefthand = $2; n->lefthand = $2;
n->oper = (List *) makeA_Expr(OP, "=", NULL, NULL); n->oper = (List *) makeA_Expr(OP, "=", NULL, NULL);
n->useor = FALSE; n->useor = FALSE;
n->subLinkType = ANY_SUBLINK; n->subLinkType = ANY_SUBLINK;
n->subselect = $6; n->subselect = $5;
$$ = (Node *)n; $$ = (Node *)n;
} }
| '(' row_descriptor ')' NOT IN '(' SelectStmt ')' | '(' row_descriptor ')' NOT IN select_with_parens
{ {
SubLink *n = makeNode(SubLink); SubLink *n = makeNode(SubLink);
n->lefthand = $2; n->lefthand = $2;
n->oper = (List *) makeA_Expr(OP, "<>", NULL, NULL); n->oper = (List *) makeA_Expr(OP, "<>", NULL, NULL);
n->useor = TRUE; n->useor = TRUE;
n->subLinkType = ALL_SUBLINK; n->subLinkType = ALL_SUBLINK;
n->subselect = $7; n->subselect = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
| '(' row_descriptor ')' all_Op sub_type '(' SelectStmt ')' | '(' row_descriptor ')' all_Op sub_type select_with_parens
{ {
SubLink *n = makeNode(SubLink); SubLink *n = makeNode(SubLink);
n->lefthand = $2; n->lefthand = $2;
...@@ -4176,10 +4180,10 @@ row_expr: '(' row_descriptor ')' IN '(' SelectStmt ')' ...@@ -4176,10 +4180,10 @@ row_expr: '(' row_descriptor ')' IN '(' SelectStmt ')'
else else
n->useor = FALSE; n->useor = FALSE;
n->subLinkType = $5; n->subLinkType = $5;
n->subselect = $7; n->subselect = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
| '(' row_descriptor ')' all_Op '(' SelectStmt ')' | '(' row_descriptor ')' all_Op select_with_parens
{ {
SubLink *n = makeNode(SubLink); SubLink *n = makeNode(SubLink);
n->lefthand = $2; n->lefthand = $2;
...@@ -4189,7 +4193,7 @@ row_expr: '(' row_descriptor ')' IN '(' SelectStmt ')' ...@@ -4189,7 +4193,7 @@ row_expr: '(' row_descriptor ')' IN '(' SelectStmt ')'
else else
n->useor = FALSE; n->useor = FALSE;
n->subLinkType = MULTIEXPR_SUBLINK; n->subLinkType = MULTIEXPR_SUBLINK;
n->subselect = $6; n->subselect = $5;
$$ = (Node *)n; $$ = (Node *)n;
} }
| '(' row_descriptor ')' all_Op '(' row_descriptor ')' | '(' row_descriptor ')' all_Op '(' row_descriptor ')'
...@@ -4291,9 +4295,9 @@ a_expr: c_expr ...@@ -4291,9 +4295,9 @@ a_expr: c_expr
* If you add more explicitly-known operators, be sure to add them * If you add more explicitly-known operators, be sure to add them
* also to b_expr and to the MathOp list above. * also to b_expr and to the MathOp list above.
*/ */
| '+' a_expr %prec UMINUS | '+' a_expr %prec UMINUS
{ $$ = makeA_Expr(OP, "+", NULL, $2); } { $$ = makeA_Expr(OP, "+", NULL, $2); }
| '-' a_expr %prec UMINUS | '-' a_expr %prec UMINUS
{ $$ = doNegate($2); } { $$ = doNegate($2); }
| '%' a_expr | '%' a_expr
{ $$ = makeA_Expr(OP, "%", NULL, $2); } { $$ = makeA_Expr(OP, "%", NULL, $2); }
...@@ -4458,12 +4462,12 @@ a_expr: c_expr ...@@ -4458,12 +4462,12 @@ a_expr: c_expr
makeA_Expr(OP, "<", $1, $4), makeA_Expr(OP, "<", $1, $4),
makeA_Expr(OP, ">", $1, $6)); 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 */ /* 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->lefthand = makeList1($1);
n->oper = (List *) makeA_Expr(OP, "=", NULL, NULL); n->oper = (List *) makeA_Expr(OP, "=", NULL, NULL);
n->useor = FALSE; n->useor = FALSE;
...@@ -4474,7 +4478,7 @@ a_expr: c_expr ...@@ -4474,7 +4478,7 @@ a_expr: c_expr
{ {
Node *n = NULL; Node *n = NULL;
List *l; List *l;
foreach(l, (List *) $4) foreach(l, (List *) $3)
{ {
Node *cmp = makeA_Expr(OP, "=", $1, lfirst(l)); Node *cmp = makeA_Expr(OP, "=", $1, lfirst(l));
if (n == NULL) if (n == NULL)
...@@ -4485,12 +4489,12 @@ a_expr: c_expr ...@@ -4485,12 +4489,12 @@ a_expr: c_expr
$$ = n; $$ = n;
} }
} }
| a_expr NOT IN '(' in_expr ')' | a_expr NOT IN in_expr
{ {
/* in_expr returns a SubLink or a list of a_exprs */ /* 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->lefthand = makeList1($1);
n->oper = (List *) makeA_Expr(OP, "<>", NULL, NULL); n->oper = (List *) makeA_Expr(OP, "<>", NULL, NULL);
n->useor = FALSE; n->useor = FALSE;
...@@ -4501,7 +4505,7 @@ a_expr: c_expr ...@@ -4501,7 +4505,7 @@ a_expr: c_expr
{ {
Node *n = NULL; Node *n = NULL;
List *l; List *l;
foreach(l, (List *) $5) foreach(l, (List *) $4)
{ {
Node *cmp = makeA_Expr(OP, "<>", $1, lfirst(l)); Node *cmp = makeA_Expr(OP, "<>", $1, lfirst(l));
if (n == NULL) if (n == NULL)
...@@ -4512,14 +4516,14 @@ a_expr: c_expr ...@@ -4512,14 +4516,14 @@ a_expr: c_expr
$$ = n; $$ = n;
} }
} }
| a_expr all_Op sub_type '(' SelectStmt ')' | a_expr all_Op sub_type select_with_parens
{ {
SubLink *n = makeNode(SubLink); SubLink *n = makeNode(SubLink);
n->lefthand = makeList1($1); n->lefthand = makeList1($1);
n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL); n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL);
n->useor = FALSE; /* doesn't matter since only one col */ n->useor = FALSE; /* doesn't matter since only one col */
n->subLinkType = $3; n->subLinkType = $3;
n->subselect = $5; n->subselect = $4;
$$ = (Node *)n; $$ = (Node *)n;
} }
| row_expr | row_expr
...@@ -4539,9 +4543,9 @@ b_expr: c_expr ...@@ -4539,9 +4543,9 @@ b_expr: c_expr
{ $$ = $1; } { $$ = $1; }
| b_expr TYPECAST Typename | b_expr TYPECAST Typename
{ $$ = makeTypeCast($1, $3); } { $$ = makeTypeCast($1, $3); }
| '+' b_expr %prec UMINUS | '+' b_expr %prec UMINUS
{ $$ = makeA_Expr(OP, "+", NULL, $2); } { $$ = makeA_Expr(OP, "+", NULL, $2); }
| '-' b_expr %prec UMINUS | '-' b_expr %prec UMINUS
{ $$ = doNegate($2); } { $$ = doNegate($2); }
| '%' b_expr | '%' b_expr
{ $$ = makeA_Expr(OP, "%", NULL, $2); } { $$ = makeA_Expr(OP, "%", NULL, $2); }
...@@ -4908,24 +4912,24 @@ c_expr: attr ...@@ -4908,24 +4912,24 @@ c_expr: attr
n->agg_distinct = FALSE; n->agg_distinct = FALSE;
$$ = (Node *)n; $$ = (Node *)n;
} }
| '(' select_no_parens ')' | select_with_parens %prec UMINUS
{ {
SubLink *n = makeNode(SubLink); SubLink *n = makeNode(SubLink);
n->lefthand = NIL; n->lefthand = NIL;
n->oper = NIL; n->oper = NIL;
n->useor = FALSE; n->useor = FALSE;
n->subLinkType = EXPR_SUBLINK; n->subLinkType = EXPR_SUBLINK;
n->subselect = $2; n->subselect = $1;
$$ = (Node *)n; $$ = (Node *)n;
} }
| EXISTS '(' SelectStmt ')' | EXISTS select_with_parens
{ {
SubLink *n = makeNode(SubLink); SubLink *n = makeNode(SubLink);
n->lefthand = NIL; n->lefthand = NIL;
n->oper = NIL; n->oper = NIL;
n->useor = FALSE; n->useor = FALSE;
n->subLinkType = EXISTS_SUBLINK; n->subLinkType = EXISTS_SUBLINK;
n->subselect = $3; n->subselect = $2;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
...@@ -5037,14 +5041,14 @@ trim_list: a_expr FROM expr_list ...@@ -5037,14 +5041,14 @@ trim_list: a_expr FROM expr_list
{ $$ = $1; } { $$ = $1; }
; ;
in_expr: SelectStmt in_expr: select_with_parens
{ {
SubLink *n = makeNode(SubLink); SubLink *n = makeNode(SubLink);
n->subselect = $1; n->subselect = $1;
$$ = (Node *)n; $$ = (Node *)n;
} }
| in_expr_nodes | '(' in_expr_nodes ')'
{ $$ = (Node *)$1; } { $$ = (Node *)$2; }
; ;
in_expr_nodes: a_expr in_expr_nodes: a_expr
......
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