aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r--src/backend/parser/analyze.c173
1 files changed, 101 insertions, 72 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index b86dab4bea0..45e81aec608 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.162 2000/11/04 18:29:09 momjian Exp $
+ * $Id: analyze.c,v 1.163 2000/11/05 00:15:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -46,8 +46,8 @@ static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
-static Query *transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt);
-static Node *transformSetOperationTree(ParseState *pstate, Node *node);
+static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
+static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
@@ -257,11 +257,12 @@ transformStmt(ParseState *pstate, Node *parseTree)
break;
case T_SelectStmt:
- result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
- break;
-
- case T_SetOperationStmt:
- result = transformSetOperationStmt(pstate, (SetOperationStmt *) parseTree);
+ if (((SelectStmt *) parseTree)->op == SETOP_NONE)
+ result = transformSelectStmt(pstate,
+ (SelectStmt *) parseTree);
+ else
+ result = transformSetOperationStmt(pstate,
+ (SelectStmt *) parseTree);
break;
default:
@@ -1173,7 +1174,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
found=1;
}
if (!found)
- elog(ERROR, "columns in foreign key table of constraint not found.");
+ elog(ERROR, "columns referenced in foreign key constraint not found.");
}
/*
@@ -1772,29 +1773,30 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
/*
* transformSetOperationsStmt -
- * transforms a SetOperations Statement
+ * transforms a set-operations tree
*
- * SetOperations is actually just a SELECT, but with UNION/INTERSECT/EXCEPT
+ * A set-operation tree is just a SELECT, but with UNION/INTERSECT/EXCEPT
* structure to it. We must transform each leaf SELECT and build up a top-
* level Query that contains the leaf SELECTs as subqueries in its rangetable.
- * The SetOperations tree (with leaf SelectStmts replaced by RangeTblRef nodes)
- * becomes the setOperations field of the top-level Query.
+ * The tree of set operations is converted into the setOperations field of
+ * the top-level Query.
*/
static Query *
-transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt)
+transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
{
Query *qry = makeNode(Query);
- Node *node;
SelectStmt *leftmostSelect;
Query *leftmostQuery;
+ SetOperationStmt *sostmt;
char *into;
+ bool istemp;
char *portalname;
bool binary;
- bool istemp;
List *sortClause;
Node *limitOffset;
Node *limitCount;
List *forUpdate;
+ Node *node;
List *lefttl,
*dtlist;
int tllen;
@@ -1802,50 +1804,55 @@ transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt)
qry->commandType = CMD_SELECT;
/*
- * Find leftmost leaf SelectStmt and extract the one-time-only items
- * from it.
+ * Find leftmost leaf SelectStmt; extract the one-time-only items
+ * from it and from the top-level node.
*/
- node = stmt->larg;
- while (node && IsA(node, SetOperationStmt))
- node = ((SetOperationStmt *) node)->larg;
- Assert(node && IsA(node, SelectStmt));
- leftmostSelect = (SelectStmt *) node;
-
+ leftmostSelect = stmt->larg;
+ while (leftmostSelect && leftmostSelect->op != SETOP_NONE)
+ leftmostSelect = leftmostSelect->larg;
+ Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
+ leftmostSelect->larg == NULL);
into = leftmostSelect->into;
- portalname = leftmostSelect->portalname;
- binary = leftmostSelect->binary;
istemp = leftmostSelect->istemp;
- sortClause = leftmostSelect->sortClause;
- limitOffset = leftmostSelect->limitOffset;
- limitCount = leftmostSelect->limitCount;
- forUpdate = leftmostSelect->forUpdate;
+ portalname = stmt->portalname;
+ binary = stmt->binary;
/* clear them to prevent complaints in transformSetOperationTree() */
leftmostSelect->into = NULL;
- leftmostSelect->portalname = NULL;
- leftmostSelect->binary = false;
leftmostSelect->istemp = false;
- leftmostSelect->sortClause = NIL;
- leftmostSelect->limitOffset = NULL;
- leftmostSelect->limitCount = NULL;
- leftmostSelect->forUpdate = NIL;
+ stmt->portalname = NULL;
+ stmt->binary = false;
+
+ /*
+ * These are not one-time, exactly, but we want to process them here
+ * and not let transformSetOperationTree() see them --- else it'll just
+ * recurse right back here!
+ */
+ sortClause = stmt->sortClause;
+ limitOffset = stmt->limitOffset;
+ limitCount = stmt->limitCount;
+ forUpdate = stmt->forUpdate;
+
+ stmt->sortClause = NIL;
+ stmt->limitOffset = NULL;
+ stmt->limitCount = NULL;
+ stmt->forUpdate = NIL;
- /* We don't actually support forUpdate with set ops at the moment. */
+ /* We don't support forUpdate with set ops at the moment. */
if (forUpdate)
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
/*
* Recursively transform the components of the tree.
*/
- stmt = (SetOperationStmt *)
- transformSetOperationTree(pstate, (Node *) stmt);
- Assert(stmt && IsA(stmt, SetOperationStmt));
- qry->setOperations = (Node *) stmt;
+ sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt);
+ Assert(sostmt && IsA(sostmt, SetOperationStmt));
+ qry->setOperations = (Node *) sostmt;
/*
* Re-find leftmost SELECT (now it's a sub-query in rangetable)
*/
- node = stmt->larg;
+ node = sostmt->larg;
while (node && IsA(node, SetOperationStmt))
node = ((SetOperationStmt *) node)->larg;
Assert(node && IsA(node, RangeTblRef));
@@ -1858,7 +1865,7 @@ transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt)
*/
qry->targetList = NIL;
lefttl = leftmostQuery->targetList;
- foreach(dtlist, stmt->colTypes)
+ foreach(dtlist, sostmt->colTypes)
{
Oid colType = (Oid) lfirsti(dtlist);
char *colName = ((TargetEntry *) lfirst(lefttl))->resdom->resname;
@@ -1953,11 +1960,47 @@ transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt)
* Recursively transform leaves and internal nodes of a set-op tree
*/
static Node *
-transformSetOperationTree(ParseState *pstate, Node *node)
+transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
{
- if (IsA(node, SelectStmt))
+ bool isLeaf;
+
+ Assert(stmt && IsA(stmt, SelectStmt));
+
+ /*
+ * Validity-check both leaf and internal SELECTs for disallowed ops.
+ */
+ if (stmt->into)
+ elog(ERROR, "INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT");
+ if (stmt->portalname) /* should not happen */
+ elog(ERROR, "Portal may not appear in UNION/INTERSECT/EXCEPT");
+ /* We don't support forUpdate with set ops at the moment. */
+ if (stmt->forUpdate)
+ elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
+
+ /*
+ * If an internal node of a set-op tree has ORDER BY, UPDATE, or LIMIT
+ * clauses attached, we need to treat it like a leaf node to generate
+ * an independent sub-Query tree. Otherwise, it can be represented by
+ * a SetOperationStmt node underneath the parent Query.
+ */
+ if (stmt->op == SETOP_NONE)
{
- SelectStmt *stmt = (SelectStmt *) node;
+ Assert(stmt->larg == NULL && stmt->rarg == NULL);
+ isLeaf = true;
+ }
+ else
+ {
+ Assert(stmt->larg != NULL && stmt->rarg != NULL);
+ if (stmt->sortClause || stmt->limitOffset || stmt->limitCount ||
+ stmt->forUpdate)
+ isLeaf = true;
+ else
+ isLeaf = false;
+ }
+
+ if (isLeaf)
+ {
+ /* Process leaf SELECT */
List *save_rtable;
List *selectList;
Query *selectQuery;
@@ -1966,20 +2009,6 @@ transformSetOperationTree(ParseState *pstate, Node *node)
RangeTblRef *rtr;
/*
- * Validity-check leaf SELECTs for disallowed ops. INTO check is
- * necessary, the others should have been disallowed by grammar.
- */
- if (stmt->into)
- elog(ERROR, "INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT");
- if (stmt->portalname)
- elog(ERROR, "Portal is only allowed on first SELECT of UNION/INTERSECT/EXCEPT");
- if (stmt->sortClause)
- elog(ERROR, "ORDER BY is only allowed at end of UNION/INTERSECT/EXCEPT");
- if (stmt->limitOffset || stmt->limitCount)
- elog(ERROR, "LIMIT is only allowed at end of UNION/INTERSECT/EXCEPT");
- if (stmt->forUpdate)
- elog(ERROR, "FOR UPDATE is only allowed at end of UNION/INTERSECT/EXCEPT");
- /*
* Transform SelectStmt into a Query. We do not want any previously
* transformed leaf queries to be visible in the outer context of
* this sub-query, so temporarily make the top-level pstate have an
@@ -2011,21 +2040,26 @@ transformSetOperationTree(ParseState *pstate, Node *node)
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
return (Node *) rtr;
}
- else if (IsA(node, SetOperationStmt))
+ else
{
- SetOperationStmt *op = (SetOperationStmt *) node;
+ /* Process an internal node (set operation node) */
+ SetOperationStmt *op = makeNode(SetOperationStmt);
List *lcoltypes;
List *rcoltypes;
const char *context;
- context = (op->op == SETOP_UNION ? "UNION" :
- (op->op == SETOP_INTERSECT ? "INTERSECT" :
+ context = (stmt->op == SETOP_UNION ? "UNION" :
+ (stmt->op == SETOP_INTERSECT ? "INTERSECT" :
"EXCEPT"));
+
+ op->op = stmt->op;
+ op->all = stmt->all;
+
/*
* Recursively transform the child nodes.
*/
- op->larg = transformSetOperationTree(pstate, op->larg);
- op->rarg = transformSetOperationTree(pstate, op->rarg);
+ op->larg = transformSetOperationTree(pstate, stmt->larg);
+ op->rarg = transformSetOperationTree(pstate, stmt->rarg);
/*
* Verify that the two children have the same number of non-junk
* columns, and determine the types of the merged output columns.
@@ -2048,14 +2082,9 @@ transformSetOperationTree(ParseState *pstate, Node *node)
lcoltypes = lnext(lcoltypes);
rcoltypes = lnext(rcoltypes);
}
+
return (Node *) op;
}
- else
- {
- elog(ERROR, "transformSetOperationTree: unexpected node %d",
- (int) nodeTag(node));
- return NULL; /* keep compiler quiet */
- }
}
/*