*
*
* 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
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
}
;
-/*
- * 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
* 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;
}
;
select_clause: simple_select
- {
- $$ = $1;
- }
- | '(' SelectStmt ')'
- {
- $$ = $2;
- }
+ | select_with_parens
;
/*
* (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.
$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
$$->inhOpt = INH_DEFAULT;
$$->name = NULL;
}
- | relation_name '*' %prec '='
+ | relation_name '*'
{
/* inheritance query */
$$ = makeNode(RangeVar);
$$->inhOpt = INH_YES;
$$->name = NULL;
}
- | ONLY relation_name %prec '='
+ | ONLY relation_name
{
/* no inheritance */
$$ = makeNode(RangeVar);
* 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;
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;
else
n->useor = FALSE;
n->subLinkType = MULTIEXPR_SUBLINK;
- n->subselect = $6;
+ n->subselect = $5;
$$ = (Node *)n;
}
| '(' row_descriptor ')' all_Op '(' row_descriptor ')'
* 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); }
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;
{
Node *n = NULL;
List *l;
- foreach(l, (List *) $4)
+ foreach(l, (List *) $3)
{
Node *cmp = makeA_Expr(OP, "=", $1, lfirst(l));
if (n == NULL)
$$ = 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;
{
Node *n = NULL;
List *l;
- foreach(l, (List *) $5)
+ foreach(l, (List *) $4)
{
Node *cmp = makeA_Expr(OP, "<>", $1, lfirst(l));
if (n == NULL)
$$ = 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
{ $$ = $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); }
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;
}
;
{ $$ = $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