summaryrefslogtreecommitdiff
path: root/src/backend/parser/gram.y
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/gram.y')
-rw-r--r--src/backend/parser/gram.y349
1 files changed, 110 insertions, 239 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 31aea152fa6..f13b942abd8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.193 2000/09/29 18:21:36 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.194 2000/10/05 19:11:33 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -78,6 +78,7 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
static Node *makeTypeCast(Node *arg, TypeName *typename);
static Node *makeRowExpr(char *opr, List *largs, List *rargs);
static void mapTargetColumns(List *source, List *target);
+static SelectStmt *findLeftmostSelect(Node *node);
static bool exprIsNullConstant(Node *arg);
static Node *doNegate(Node *n);
static void doNegateFloat(Value *v);
@@ -112,25 +113,26 @@ static void doNegateFloat(Value *v);
VersionStmt *vstmt;
DefineStmt *dstmt;
RuleStmt *rstmt;
- InsertStmt *astmt;
+ InsertStmt *istmt;
}
%type <node> stmt,
- AlterSchemaStmt, AlterTableStmt, ClosePortalStmt,
- CopyStmt, CreateStmt, CreateAsStmt, CreateSchemaStmt, CreateSeqStmt, DefineStmt, DropStmt,
- TruncateStmt, CommentStmt,
- ExtendStmt, FetchStmt, GrantStmt, CreateTrigStmt, DropSchemaStmt, DropTrigStmt,
- CreatePLangStmt, DropPLangStmt,
- IndexStmt, ListenStmt, UnlistenStmt, LockStmt, OptimizableStmt,
- ProcedureStmt, ReindexStmt, RemoveAggrStmt, RemoveOperStmt,
- RemoveFuncStmt, RemoveStmt,
- RenameStmt, RevokeStmt, RuleStmt, SetSessionStmt, TransactionStmt, ViewStmt, LoadStmt,
- CreatedbStmt, DropdbStmt, VacuumStmt, CursorStmt, SubSelect,
- UpdateStmt, InsertStmt, select_clause, SelectStmt, NotifyStmt, DeleteStmt,
- ClusterStmt, ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt,
- CreateUserStmt, AlterUserStmt, DropUserStmt, RuleActionStmt,
- RuleActionStmtOrEmpty, ConstraintsSetStmt,
- CreateGroupStmt, AlterGroupStmt, DropGroupStmt
+ AlterGroupStmt, AlterSchemaStmt, AlterTableStmt, AlterUserStmt,
+ ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt,
+ CopyStmt, CreateAsStmt, CreateGroupStmt, CreatePLangStmt,
+ CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
+ CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt,
+ DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt,
+ DropUserStmt, DropdbStmt, ExplainStmt, ExtendStmt, FetchStmt,
+ GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt,
+ NotifyStmt, OptimizableStmt, ProcedureStmt, ReindexStmt,
+ RemoveAggrStmt, RemoveFuncStmt, RemoveOperStmt, RemoveStmt,
+ RenameStmt, RevokeStmt, RuleActionStmt, RuleActionStmtOrEmpty,
+ RuleStmt, SelectStmt, SetSessionStmt, TransactionStmt, TruncateStmt,
+ UnlistenStmt, UpdateStmt, VacuumStmt, VariableResetStmt,
+ VariableSetStmt, VariableShowStmt, ViewStmt
+
+%type <node> select_clause, select_subclause
%type <list> SessionList
%type <node> SessionClause
@@ -212,7 +214,7 @@ static void doNegateFloat(Value *v);
%type <list> OptSeqList
%type <defelt> OptSeqElem
-%type <astmt> insert_rest
+%type <istmt> insert_rest
%type <node> OptTableElement, ConstraintElem
%type <node> columnDef
@@ -1533,16 +1535,16 @@ OptInherit: INHERITS '(' relation_name_list ')' { $$ = $3; }
CreateAsStmt: CREATE OptTemp TABLE relation_name OptUnder OptCreateAs AS SelectStmt
{
- SelectStmt *n = (SelectStmt *)$8;
- if ($5 != NIL)
- yyerror("CREATE TABLE/AS SELECT does not support UNDER");
- if ($6 != NIL)
- mapTargetColumns($6, n->targetList);
+ SelectStmt *n = findLeftmostSelect($8);
if (n->into != NULL)
elog(ERROR,"CREATE TABLE/AS SELECT may not specify INTO");
n->istemp = $2;
n->into = $4;
- $$ = (Node *)n;
+ if ($5 != NIL)
+ yyerror("CREATE TABLE/AS SELECT does not support UNDER");
+ if ($6 != NIL)
+ mapTargetColumns($6, n->targetList);
+ $$ = $8;
}
;
@@ -2909,11 +2911,7 @@ ViewStmt: CREATE VIEW name opt_column_list AS SelectStmt
ViewStmt *n = makeNode(ViewStmt);
n->viewname = $3;
n->aliases = $4;
- n->query = (Query *)$6;
- if (((SelectStmt *)n->query)->unionClause != NULL)
- elog(ERROR,"UNION in views is not implemented");
- if (((SelectStmt *)n->query)->forUpdate != NULL)
- elog(ERROR, "SELECT FOR UPDATE is not allowed in CREATE VIEW");
+ n->query = (Query *) $6;
$$ = (Node *)n;
}
;
@@ -3156,78 +3154,37 @@ InsertStmt: INSERT INTO relation_name insert_rest
insert_rest: VALUES '(' target_list ')'
{
$$ = makeNode(InsertStmt);
- $$->cols = NULL;
- $$->distinctClause = NIL;
+ $$->cols = NIL;
$$->targetList = $3;
- $$->fromClause = NIL;
- $$->whereClause = NULL;
- $$->groupClause = NIL;
- $$->havingClause = NULL;
- $$->unionClause = NIL;
+ $$->selectStmt = NULL;
}
| DEFAULT VALUES
{
$$ = makeNode(InsertStmt);
- $$->distinctClause = NIL;
+ $$->cols = NIL;
$$->targetList = NIL;
- $$->fromClause = NIL;
- $$->whereClause = NULL;
- $$->groupClause = NIL;
- $$->havingClause = NULL;
- $$->unionClause = NIL;
- $$->intersectClause = NIL;
- }
-/* We want the full power of SelectStatements including INTERSECT and EXCEPT
- * for insertion. However, we can't support sort or limit clauses.
- */
+ $$->selectStmt = NULL;
+ }
| SelectStmt
{
- SelectStmt *n = (SelectStmt *) $1;
- if (n->sortClause)
- elog(ERROR, "ORDER BY is not allowed in INSERT/SELECT");
$$ = makeNode(InsertStmt);
$$->cols = NIL;
- $$->distinctClause = n->distinctClause;
- $$->targetList = n->targetList;
- $$->fromClause = n->fromClause;
- $$->whereClause = n->whereClause;
- $$->groupClause = n->groupClause;
- $$->havingClause = n->havingClause;
- $$->unionClause = n->unionClause;
- $$->intersectClause = n->intersectClause;
- $$->unionall = n->unionall;
- $$->forUpdate = n->forUpdate;
+ $$->targetList = NIL;
+ $$->selectStmt = $1;
}
| '(' columnList ')' VALUES '(' target_list ')'
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
- $$->distinctClause = NIL;
$$->targetList = $6;
- $$->fromClause = NIL;
- $$->whereClause = NULL;
- $$->groupClause = NIL;
- $$->havingClause = NULL;
- $$->unionClause = NIL;
- $$->intersectClause = NIL;
+ $$->selectStmt = NULL;
}
| '(' columnList ')' SelectStmt
{
- SelectStmt *n = (SelectStmt *) $4;
- if (n->sortClause)
- elog(ERROR, "ORDER BY is not allowed in INSERT/SELECT");
$$ = makeNode(InsertStmt);
$$->cols = $2;
- $$->distinctClause = n->distinctClause;
- $$->targetList = n->targetList;
- $$->fromClause = n->fromClause;
- $$->whereClause = n->whereClause;
- $$->groupClause = n->groupClause;
- $$->havingClause = n->havingClause;
- $$->unionClause = n->unionClause;
- $$->intersectClause = n->intersectClause;
- $$->unionall = n->unionall;
- $$->forUpdate = n->forUpdate;
+ $$->targetList = NIL;
+ $$->selectStmt = $4;
}
;
@@ -3324,26 +3281,10 @@ UpdateStmt: UPDATE opt_only relation_name
*****************************************************************************/
CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt
{
- SelectStmt *n;
-
- n= (SelectStmt *)$6;
- /* from PORTAL name */
- /*
- * 15 august 1991 -- since 3.0 postgres does locking
- * right, we discovered that portals were violating
- * locking protocol. portal locks cannot span xacts.
- * as a short-term fix, we installed the check here.
- * -- mao
- */
- if (!IsTransactionBlock())
- elog(ERROR,"Named portals may only be used in begin/end transaction blocks");
-
+ SelectStmt *n = findLeftmostSelect($6);
n->portalname = $2;
n->binary = $3;
- if (n->forUpdate != NULL)
- elog(ERROR,"DECLARE/UPDATE is not supported"
- "\n\tCursors must be READ ONLY");
- $$ = (Node *)n;
+ $$ = $6;
}
;
@@ -3364,92 +3305,22 @@ opt_cursor: BINARY { $$ = TRUE; }
/* A complete SELECT statement looks like this. Note sort, for_update,
* and limit clauses can only appear once, not in each set operation.
*
- * The rule returns a SelectStmt Node having the set operations attached to
- * unionClause and intersectClause (NIL if no set operations were present)
+ * The rule returns either a SelectStmt node or a SetOperationStmt tree.
+ * One-time clauses are attached to the leftmost SelectStmt leaf.
+ *
+ * NOTE: only the leftmost SelectStmt leaf should have INTO, either.
+ * However, this is not checked by the grammar; parse analysis must check it.
*/
SelectStmt: select_clause sort_clause for_update_clause opt_select_limit
{
- if IsA($1, SelectStmt)
- {
- /* 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;
- }
- else
- {
- /* 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);
+ SelectStmt *n = findLeftmostSelect($1);
- /* 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 whole operator tree to intersectClause */
- first_select->intersectClause = (List *) op;
-
- /* 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 CREATE RULE");
+ n->sortClause = $2;
+ n->forUpdate = $3;
+ n->limitOffset = nth(0, $4);
+ n->limitCount = nth(1, $4);
+ $$ = $1;
}
;
@@ -3458,82 +3329,69 @@ SelectStmt: select_clause sort_clause for_update_clause opt_select_limit
* the ordering of the set operations. Without '(' and ')' we want the
* operations to be ordered per the precedence specs at the head of this file.
*
+ * Since parentheses around SELECTs also appear in the expression grammar,
+ * there is a parse ambiguity if parentheses are allowed at the top level of a
+ * select_clause: are the parens part of the expression or part of the select?
+ * We separate select_clause into two levels to resolve this: select_clause
+ * can have top-level parentheses, select_subclause cannot.
+ *
* 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.
- * The SelectStatements to be connected are the left and right arguments to
- * the A_Expr Nodes.
- * If no set operations appear in the query, the tree consists only of one
- * SelectStmt Node.
*/
-select_clause: '(' select_clause ')'
+select_clause: '(' select_subclause ')'
{
$$ = $2;
}
- | SubSelect
+ | select_subclause
{
$$ = $1;
}
- | select_clause EXCEPT opt_all select_clause
- {
- $$ = (Node *)makeA_Expr(AND,NULL,$1,
- makeA_Expr(NOT,NULL,NULL,$4));
- if ($3)
- elog(ERROR, "EXCEPT ALL is not implemented yet");
- }
- | select_clause UNION opt_all select_clause
- {
- if (IsA($4, SelectStmt))
- {
- 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);
- }
- | select_clause INTERSECT opt_all select_clause
- {
- $$ = (Node *)makeA_Expr(AND,NULL,$1,$4);
- if ($3)
- elog(ERROR, "INTERSECT ALL is not implemented yet");
- }
- ;
+ ;
-SubSelect: SELECT opt_distinct target_list
+select_subclause: SELECT opt_distinct target_list
result from_clause where_clause
group_clause having_clause
{
SelectStmt *n = makeNode(SelectStmt);
n->distinctClause = $2;
- n->unionall = FALSE;
n->targetList = $3;
- /* This is new: Subselects support the INTO clause
- * which allows queries that are not part of the
- * SQL92 standard and should not be formulated!
- * We need it for INTERSECT and EXCEPT and I did not
- * 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.
- */
n->istemp = (bool) ((Value *) lfirst($4))->val.ival;
n->into = (char *) lnext($4);
-
n->fromClause = $5;
n->whereClause = $6;
n->groupClause = $7;
n->havingClause = $8;
$$ = (Node *)n;
}
- ;
+ | select_clause UNION opt_all select_clause
+ {
+ SetOperationStmt *n = makeNode(SetOperationStmt);
+ n->op = SETOP_UNION;
+ n->all = $3;
+ n->larg = $1;
+ n->rarg = $4;
+ $$ = (Node *) n;
+ }
+ | select_clause INTERSECT opt_all select_clause
+ {
+ SetOperationStmt *n = makeNode(SetOperationStmt);
+ n->op = SETOP_INTERSECT;
+ n->all = $3;
+ n->larg = $1;
+ n->rarg = $4;
+ $$ = (Node *) n;
+ }
+ | select_clause EXCEPT opt_all select_clause
+ {
+ SetOperationStmt *n = makeNode(SetOperationStmt);
+ n->op = SETOP_EXCEPT;
+ n->all = $3;
+ n->larg = $1;
+ n->rarg = $4;
+ $$ = (Node *) n;
+ }
+ ;
/* easy way to return two values. Can someone improve this? bjm */
result: INTO OptTempTableName { $$ = $2; }
@@ -3763,7 +3621,7 @@ table_ref: relation_expr
$1->name = $2;
$$ = (Node *) $1;
}
- | '(' select_clause ')' alias_clause
+ | '(' select_subclause ')' alias_clause
{
RangeSubselect *n = makeNode(RangeSubselect);
n->subquery = $2;
@@ -4316,7 +4174,7 @@ 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 '(' SubSelect ')'
+row_expr: '(' row_descriptor ')' IN '(' select_subclause ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = $2;
@@ -4326,7 +4184,7 @@ row_expr: '(' row_descriptor ')' IN '(' SubSelect ')'
n->subselect = $6;
$$ = (Node *)n;
}
- | '(' row_descriptor ')' NOT IN '(' SubSelect ')'
+ | '(' row_descriptor ')' NOT IN '(' select_subclause ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = $2;
@@ -4336,7 +4194,7 @@ row_expr: '(' row_descriptor ')' IN '(' SubSelect ')'
n->subselect = $7;
$$ = (Node *)n;
}
- | '(' row_descriptor ')' all_Op sub_type '(' SubSelect ')'
+ | '(' row_descriptor ')' all_Op sub_type '(' select_subclause ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = $2;
@@ -4349,7 +4207,7 @@ row_expr: '(' row_descriptor ')' IN '(' SubSelect ')'
n->subselect = $7;
$$ = (Node *)n;
}
- | '(' row_descriptor ')' all_Op '(' SubSelect ')'
+ | '(' row_descriptor ')' all_Op '(' select_subclause ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = $2;
@@ -4680,7 +4538,7 @@ a_expr: c_expr
$$ = n;
}
}
- | a_expr all_Op sub_type '(' SubSelect ')'
+ | a_expr all_Op sub_type '(' select_subclause ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = makeList1($1);
@@ -5076,7 +4934,7 @@ c_expr: attr
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
- | '(' SubSelect ')'
+ | '(' select_subclause ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = NIL;
@@ -5086,7 +4944,7 @@ c_expr: attr
n->subselect = $2;
$$ = (Node *)n;
}
- | EXISTS '(' SubSelect ')'
+ | EXISTS '(' select_subclause ')'
{
SubLink *n = makeNode(SubLink);
n->lefthand = NIL;
@@ -5185,7 +5043,7 @@ trim_list: a_expr FROM expr_list
{ $$ = $1; }
;
-in_expr: SubSelect
+in_expr: select_subclause
{
SubLink *n = makeNode(SubLink);
n->subselect = $1;
@@ -5912,6 +5770,19 @@ mapTargetColumns(List *src, List *dst)
} /* mapTargetColumns() */
+/* findLeftmostSelect()
+ * Find the leftmost SelectStmt in a SetOperationStmt parsetree.
+ */
+static SelectStmt *
+findLeftmostSelect(Node *node)
+{
+ while (node && IsA(node, SetOperationStmt))
+ node = ((SetOperationStmt *) node)->larg;
+ Assert(node && IsA(node, SelectStmt));
+ return (SelectStmt *) node;
+}
+
+
/* xlateSqlFunc()
* Convert alternate function names to internal Postgres functions.
*