aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/analyze.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2000-10-05 19:11:39 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2000-10-05 19:11:39 +0000
commit05e3d0ee8666b74f11ffad16f46e372459d6e53e (patch)
treeb273892bfda60f6bad315e84aaa2e9826e226931 /src/backend/parser/analyze.c
parent5292637f52c6db8a22f99177f228273cb69fc510 (diff)
downloadpostgresql-05e3d0ee8666b74f11ffad16f46e372459d6e53e.tar.gz
postgresql-05e3d0ee8666b74f11ffad16f46e372459d6e53e.zip
Reimplementation of UNION/INTERSECT/EXCEPT. INTERSECT/EXCEPT now meet the
SQL92 semantics, including support for ALL option. All three can be used in subqueries and views. DISTINCT and ORDER BY work now in views, too. This rewrite fixes many problems with cross-datatype UNIONs and INSERT/SELECT where the SELECT yields different datatypes than the INSERT needs. I did that by making UNION subqueries and SELECT in INSERT be treated like subselects-in-FROM, thereby allowing an extra level of targetlist where the datatype conversions can be inserted safely. INITDB NEEDED!
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r--src/backend/parser/analyze.c697
1 files changed, 478 insertions, 219 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 169075c47e2..ffedca05ed9 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.159 2000/09/29 18:21:36 tgl Exp $
+ * $Id: analyze.c,v 1.160 2000/10/05 19:11:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,8 +20,10 @@
#include "nodes/makefuncs.h"
#include "parser/analyze.h"
#include "parser/parse.h"
+#include "parser/parsetree.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
+#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
@@ -44,11 +46,13 @@ 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 *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
-static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
+static List *getSetColTypes(ParseState *pstate, Node *node);
static void transformForUpdate(Query *qry, List *forUpdate);
static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint);
static void transformConstraintAttrs(List *constraintList);
@@ -156,7 +160,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
{
ViewStmt *n = (ViewStmt *) parseTree;
- n->query = (Query *) transformStmt(pstate, (Node *) n->query);
+ n->query = transformStmt(pstate, (Node *) n->query);
/*
* If a list of column names was given, run through and
@@ -258,20 +262,17 @@ transformStmt(ParseState *pstate, Node *parseTree)
break;
case T_SelectStmt:
- if (!((SelectStmt *) parseTree)->portalname)
- {
- result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
- result->limitOffset = ((SelectStmt *) parseTree)->limitOffset;
- result->limitCount = ((SelectStmt *) parseTree)->limitCount;
- }
- else
- result = transformCursorStmt(pstate, (SelectStmt *) parseTree);
+ result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
+ break;
+
+ case T_SetOperationStmt:
+ result = transformSetOperationStmt(pstate, (SetOperationStmt *) parseTree);
break;
default:
/*
- * other statments don't require any transformation-- just
+ * other statements don't require any transformation-- just
* return the original parsetree, yea!
*/
result = makeNode(Query);
@@ -313,7 +314,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
if (pstate->p_hasAggs)
parseCheckAggregates(pstate, qry, qual);
- return (Query *) qry;
+ return qry;
}
/*
@@ -324,7 +325,6 @@ static Query *
transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
{
Query *qry = makeNode(Query);
- Node *qual;
List *icolumns;
List *attrnos;
List *attnos;
@@ -335,59 +335,89 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->commandType = CMD_INSERT;
pstate->p_is_insert = true;
- /*----------
- * Initial processing steps are just like SELECT, which should not
- * be surprising, since we may be handling an INSERT ... SELECT.
- * It is important that we finish processing all the SELECT subclauses
- * before we start doing any INSERT-specific processing; otherwise
- * the behavior of SELECT within INSERT might be different from a
- * stand-alone SELECT. (Indeed, Postgres up through 6.5 had bugs of
- * just that nature...)
- *----------
- */
-
- /* set up a range table --- note INSERT target is not in it yet */
- makeRangeTable(pstate, stmt->fromClause);
-
- qry->targetList = transformTargetList(pstate, stmt->targetList);
-
- qual = transformWhereClause(pstate, stmt->whereClause);
-
- /*
- * Initial processing of HAVING clause is just like WHERE clause.
- * Additional work will be done in optimizer/plan/planner.c.
- */
- qry->havingQual = transformWhereClause(pstate, stmt->havingClause);
-
- qry->groupClause = transformGroupClause(pstate,
- stmt->groupClause,
- qry->targetList);
-
- /* An InsertStmt has no sortClause */
- qry->sortClause = NIL;
-
- qry->distinctClause = transformDistinctClause(pstate,
- stmt->distinctClause,
- qry->targetList,
- &qry->sortClause);
-
- qry->hasSubLinks = pstate->p_hasSubLinks;
- qry->hasAggs = pstate->p_hasAggs;
- if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
- parseCheckAggregates(pstate, qry, qual);
-
/*
- * The INSERT INTO ... SELECT ... could have a UNION in child, so
- * unionClause may be false
+ * Is it INSERT ... SELECT or INSERT ... VALUES?
*/
- qry->unionall = stmt->unionall;
+ if (stmt->selectStmt)
+ {
+ List *selectList;
+ Query *selectQuery;
+ RangeTblEntry *rte;
+ RangeTblRef *rtr;
- /*
- * Just hand through the unionClause and intersectClause. We will
- * handle it in the function Except_Intersect_Rewrite()
- */
- qry->unionClause = stmt->unionClause;
- qry->intersectClause = stmt->intersectClause;
+ /*
+ * Process the source SELECT.
+ *
+ * It is important that this be handled just like a standalone SELECT;
+ * otherwise the behavior of SELECT within INSERT might be different
+ * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
+ * bugs of just that nature...)
+ */
+ selectList = parse_analyze(makeList1(stmt->selectStmt), pstate);
+ Assert(length(selectList) == 1);
+
+ selectQuery = (Query *) lfirst(selectList);
+ Assert(IsA(selectQuery, Query));
+ Assert(selectQuery->commandType == CMD_SELECT);
+ if (selectQuery->into || selectQuery->isPortal)
+ elog(ERROR, "INSERT ... SELECT may not specify INTO");
+ /*
+ * Make the source be a subquery in the INSERT's rangetable,
+ * and add it to the joinlist.
+ */
+ rte = addRangeTableEntryForSubquery(pstate,
+ selectQuery,
+ makeAttr("*SELECT*", NULL),
+ true);
+ rtr = makeNode(RangeTblRef);
+ /* assume new rte is at end */
+ rtr->rtindex = length(pstate->p_rtable);
+ Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
+ pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
+ /*
+ * Generate a targetlist for the INSERT that selects all
+ * the non-resjunk columns from the subquery. (We need this to
+ * be separate from the subquery's tlist because we may add
+ * columns, insert datatype coercions, etc.)
+ *
+ * HACK: constants in the INSERT's targetlist are copied up as-is
+ * rather than being referenced as subquery outputs. This is mainly
+ * to ensure that when we try to coerce them to the target column's
+ * datatype, the right things happen for UNKNOWN constants.
+ * Otherwise this fails:
+ * INSERT INTO foo SELECT 'bar', ... FROM baz
+ */
+ qry->targetList = NIL;
+ foreach(tl, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ Resdom *resnode = tle->resdom;
+ Node *expr;
+
+ if (resnode->resjunk)
+ continue;
+ if (tle->expr && IsA(tle->expr, Const))
+ expr = tle->expr;
+ else
+ expr = (Node *) makeVar(rtr->rtindex,
+ resnode->resno,
+ resnode->restype,
+ resnode->restypmod,
+ 0);
+ resnode = copyObject(resnode);
+ resnode->resno = (AttrNumber) pstate->p_last_resno++;
+ qry->targetList = lappend(qry->targetList,
+ makeTargetEntry(resnode, expr));
+ }
+ }
+ else
+ {
+ /*
+ * For INSERT ... VALUES, transform the given list of values
+ * to form a targetlist for the INSERT.
+ */
+ qry->targetList = transformTargetList(pstate, stmt->targetList);
+ }
/*
* Now we are done with SELECT-like processing, and can get on with
@@ -400,11 +430,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
*/
setTargetTable(pstate, stmt->relname, false, false);
- /* now the range table and jointree will not change */
- qry->rtable = pstate->p_rtable;
- qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
- qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
-
/* Prepare to assign non-conflicting resnos to resjunk attributes */
if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
@@ -412,7 +437,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/* Validate stmt->cols list, or build default list if no list given */
icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
- /* Prepare non-junk columns for assignment to target table */
+ /*
+ * Prepare columns for assignment to target table.
+ */
numuseratts = 0;
attnos = attrnos;
foreach(tl, qry->targetList)
@@ -421,18 +448,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
Resdom *resnode = tle->resdom;
Ident *id;
- if (resnode->resjunk)
- {
-
- /*
- * Resjunk nodes need no additional processing, but be sure
- * they have names and resnos that do not match any target
- * columns; else rewriter or planner might get confused.
- */
- resnode->resname = "?resjunk?";
- resnode->resno = (AttrNumber) pstate->p_last_resno++;
- continue;
- }
+ Assert(!resnode->resjunk);
if (icolumns == NIL || attnos == NIL)
elog(ERROR, "INSERT has more expressions than target columns");
id = (Ident *) lfirst(icolumns);
@@ -458,7 +474,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
* have defaults and were not assigned to by the user.
*
* XXX wouldn't it make more sense to do this further downstream, after
- * the rule rewriter?
+ * the rule rewriter? As is, altering a column default will not change
+ * the behavior of INSERTs in already-defined rules.
*/
rd_att = pstate->p_target_relation->rd_att;
if (rd_att->constr && rd_att->constr->num_defval > 0)
@@ -498,13 +515,17 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
}
}
- if (stmt->forUpdate != NULL)
- transformForUpdate(qry, stmt->forUpdate);
+ /* done building the range table and jointree */
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+ qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
- /* in case of subselects in default clauses... */
qry->hasSubLinks = pstate->p_hasSubLinks;
+ qry->hasAggs = pstate->p_hasAggs;
+ if (pstate->p_hasAggs)
+ parseCheckAggregates(pstate, qry, NULL);
- return (Query *) qry;
+ return qry;
}
/*
@@ -1608,6 +1629,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
* transformSelectStmt -
* transforms a Select Statement
*
+ * Note: this is also used for DECLARE CURSOR statements.
*/
static Query *
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
@@ -1617,13 +1639,42 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->commandType = CMD_SELECT;
+ if (stmt->portalname)
+ {
+ /* DECLARE CURSOR */
+ if (stmt->into)
+ elog(ERROR, "DECLARE CURSOR must not specify INTO");
+ if (stmt->forUpdate)
+ elog(ERROR, "DECLARE/UPDATE is not supported"
+ "\n\tCursors must be READ ONLY");
+ /*
+ * 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, "DECLARE CURSOR may only be used in begin/end transaction blocks");
+
+ qry->into = stmt->portalname;
+ qry->isTemp = stmt->istemp;
+ qry->isPortal = TRUE;
+ qry->isBinary = stmt->binary; /* internal portal */
+ }
+ else
+ {
+ /* SELECT */
+ qry->into = stmt->into;
+ qry->isTemp = stmt->istemp;
+ qry->isPortal = FALSE;
+ qry->isBinary = FALSE;
+ }
+
/* set up a range table */
makeRangeTable(pstate, stmt->fromClause);
- qry->into = stmt->into;
- qry->isTemp = stmt->istemp;
- qry->isPortal = FALSE;
-
+ /* transform targetlist and WHERE */
qry->targetList = transformTargetList(pstate, stmt->targetList);
qual = transformWhereClause(pstate, stmt->whereClause);
@@ -1647,34 +1698,357 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->targetList,
&qry->sortClause);
+ qry->limitOffset = stmt->limitOffset;
+ qry->limitCount = stmt->limitCount;
+
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry, qual);
+ qry->rtable = pstate->p_rtable;
+ qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
+
+ if (stmt->forUpdate != NULL)
+ transformForUpdate(qry, stmt->forUpdate);
+
+ return qry;
+}
+
+/*
+ * transformSetOperationsStmt -
+ * transforms a SetOperations Statement
+ *
+ * SetOperations is actually 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.
+ */
+static Query *
+transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt)
+{
+ Query *qry = makeNode(Query);
+ Node *node;
+ SelectStmt *leftmostSelect;
+ Query *leftmostQuery;
+ char *into;
+ char *portalname;
+ bool binary;
+ bool istemp;
+ List *sortClause;
+ Node *limitOffset;
+ Node *limitCount;
+ List *forUpdate;
+ List *lefttl,
+ *dtlist;
+ int tllen;
+
+ qry->commandType = CMD_SELECT;
+
/*
- * The INSERT INTO ... SELECT ... could have a UNION in child, so
- * unionClause may be false
+ * Find leftmost leaf SelectStmt and extract the one-time-only items
+ * from it.
*/
- qry->unionall = stmt->unionall;
+ node = stmt->larg;
+ while (node && IsA(node, SetOperationStmt))
+ node = ((SetOperationStmt *) node)->larg;
+ Assert(node && IsA(node, SelectStmt));
+ leftmostSelect = (SelectStmt *) node;
+
+ into = leftmostSelect->into;
+ portalname = leftmostSelect->portalname;
+ binary = leftmostSelect->binary;
+ istemp = leftmostSelect->istemp;
+ sortClause = leftmostSelect->sortClause;
+ limitOffset = leftmostSelect->limitOffset;
+ limitCount = leftmostSelect->limitCount;
+ forUpdate = leftmostSelect->forUpdate;
+
+ /* 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;
+
+ /* We don't actually support forUpdate with set ops at the moment. */
+ if (forUpdate)
+ elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
/*
- * Just hand through the unionClause and intersectClause. We will
- * handle it in the function Except_Intersect_Rewrite()
+ * Recursively transform the components of the tree.
*/
- qry->unionClause = stmt->unionClause;
- qry->intersectClause = stmt->intersectClause;
+ stmt = (SetOperationStmt *)
+ transformSetOperationTree(pstate, (Node *) stmt);
+ Assert(stmt && IsA(stmt, SetOperationStmt));
+ qry->setOperations = (Node *) stmt;
+
+ /*
+ * Re-find leftmost SELECT (now it's a sub-query in rangetable)
+ */
+ node = stmt->larg;
+ while (node && IsA(node, SetOperationStmt))
+ node = ((SetOperationStmt *) node)->larg;
+ Assert(node && IsA(node, RangeTblRef));
+ leftmostQuery = rt_fetch(((RangeTblRef *) node)->rtindex,
+ pstate->p_rtable)->subquery;
+ Assert(leftmostQuery != NULL);
+ /*
+ * Generate dummy targetlist for outer query using column names of
+ * leftmost select and common datatypes of topmost set operation
+ */
+ qry->targetList = NIL;
+ lefttl = leftmostQuery->targetList;
+ foreach(dtlist, stmt->colTypes)
+ {
+ Oid colType = (Oid) lfirsti(dtlist);
+ char *colName = ((TargetEntry *) lfirst(lefttl))->resdom->resname;
+ Resdom *resdom;
+ Node *expr;
+
+ resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
+ colType,
+ -1,
+ pstrdup(colName),
+ false);
+ expr = (Node *) makeVar(1,
+ resdom->resno,
+ colType,
+ -1,
+ 0);
+ qry->targetList = lappend(qry->targetList,
+ makeTargetEntry(resdom, expr));
+ lefttl = lnext(lefttl);
+ }
+ /*
+ * Insert one-time items into top-level query
+ *
+ * This needs to agree with transformSelectStmt!
+ */
+ if (portalname)
+ {
+ /* DECLARE CURSOR */
+ if (into)
+ elog(ERROR, "DECLARE CURSOR must not specify INTO");
+ if (forUpdate)
+ elog(ERROR, "DECLARE/UPDATE is not supported"
+ "\n\tCursors must be READ ONLY");
+ /*
+ * 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, "DECLARE CURSOR may only be used in begin/end transaction blocks");
+
+ qry->into = portalname;
+ qry->isTemp = istemp;
+ qry->isPortal = TRUE;
+ qry->isBinary = binary; /* internal portal */
+ }
+ else
+ {
+ /* SELECT */
+ qry->into = into;
+ qry->isTemp = istemp;
+ qry->isPortal = FALSE;
+ qry->isBinary = FALSE;
+ }
+
+ /*
+ * For now, we don't support resjunk sort clauses on the output of a
+ * setOperation tree --- you can only use the SQL92-spec options of
+ * selecting an output column by name or number. Enforce by checking
+ * that transformSortClause doesn't add any items to tlist.
+ */
+ tllen = length(qry->targetList);
+
+ qry->sortClause = transformSortClause(pstate,
+ sortClause,
+ qry->targetList);
+
+ if (tllen != length(qry->targetList))
+ elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns");
+
+ qry->limitOffset = limitOffset;
+ qry->limitCount = limitCount;
+
+ qry->hasSubLinks = pstate->p_hasSubLinks;
+ qry->hasAggs = pstate->p_hasAggs;
+ if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
+ parseCheckAggregates(pstate, qry, NULL);
qry->rtable = pstate->p_rtable;
- qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
+ qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
- if (stmt->forUpdate != NULL)
- transformForUpdate(qry, stmt->forUpdate);
+ if (forUpdate != NULL)
+ transformForUpdate(qry, forUpdate);
- return (Query *) qry;
+ return qry;
+}
+
+/*
+ * transformSetOperationTree
+ * Recursively transform leaves and internal nodes of a set-op tree
+ */
+static Node *
+transformSetOperationTree(ParseState *pstate, Node *node)
+{
+ if (IsA(node, SelectStmt))
+ {
+ SelectStmt *stmt = (SelectStmt *) node;
+ List *save_rtable;
+ List *selectList;
+ Query *selectQuery;
+ char selectName[32];
+ RangeTblEntry *rte;
+ 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
+ * empty rtable. (We needn't do the same with the joinlist because
+ * we aren't entering anything in the top-level joinlist.)
+ */
+ save_rtable = pstate->p_rtable;
+ pstate->p_rtable = NIL;
+ selectList = parse_analyze(makeList1(stmt), pstate);
+ pstate->p_rtable = save_rtable;
+
+ Assert(length(selectList) == 1);
+ selectQuery = (Query *) lfirst(selectList);
+ /*
+ * Make the leaf query be a subquery in the top-level rangetable.
+ */
+ sprintf(selectName, "*SELECT* %d", length(pstate->p_rtable) + 1);
+ rte = addRangeTableEntryForSubquery(pstate,
+ selectQuery,
+ makeAttr(pstrdup(selectName),
+ NULL),
+ false);
+ /*
+ * Return a RangeTblRef to replace the SelectStmt in the set-op tree.
+ */
+ rtr = makeNode(RangeTblRef);
+ /* assume new rte is at end */
+ rtr->rtindex = length(pstate->p_rtable);
+ Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
+ return (Node *) rtr;
+ }
+ else if (IsA(node, SetOperationStmt))
+ {
+ SetOperationStmt *op = (SetOperationStmt *) node;
+ List *lcoltypes;
+ List *rcoltypes;
+ const char *context;
+
+ context = (op->op == SETOP_UNION ? "UNION" :
+ (op->op == SETOP_INTERSECT ? "INTERSECT" :
+ "EXCEPT"));
+ /*
+ * Recursively transform the child nodes.
+ */
+ op->larg = transformSetOperationTree(pstate, op->larg);
+ op->rarg = transformSetOperationTree(pstate, op->rarg);
+ /*
+ * Verify that the two children have the same number of non-junk
+ * columns, and determine the types of the merged output columns.
+ */
+ lcoltypes = getSetColTypes(pstate, op->larg);
+ rcoltypes = getSetColTypes(pstate, op->rarg);
+ if (length(lcoltypes) != length(rcoltypes))
+ elog(ERROR, "Each %s query must have the same number of columns",
+ context);
+ op->colTypes = NIL;
+ while (lcoltypes != NIL)
+ {
+ Oid lcoltype = (Oid) lfirsti(lcoltypes);
+ Oid rcoltype = (Oid) lfirsti(rcoltypes);
+ Oid rescoltype;
+
+ rescoltype = select_common_type(makeListi2(lcoltype, rcoltype),
+ context);
+ op->colTypes = lappendi(op->colTypes, rescoltype);
+ lcoltypes = lnext(lcoltypes);
+ rcoltypes = lnext(rcoltypes);
+ }
+ return (Node *) op;
+ }
+ else
+ {
+ elog(ERROR, "transformSetOperationTree: unexpected node %d",
+ (int) nodeTag(node));
+ return NULL; /* keep compiler quiet */
+ }
}
/*
+ * getSetColTypes
+ * Get output column types of an (already transformed) set-op node
+ */
+static List *
+getSetColTypes(ParseState *pstate, Node *node)
+{
+ if (IsA(node, RangeTblRef))
+ {
+ RangeTblRef *rtr = (RangeTblRef *) node;
+ RangeTblEntry *rte = rt_fetch(rtr->rtindex, pstate->p_rtable);
+ Query *selectQuery = rte->subquery;
+ List *result = NIL;
+ List *tl;
+
+ Assert(selectQuery != NULL);
+ /* Get types of non-junk columns */
+ foreach(tl, selectQuery->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(tl);
+ Resdom *resnode = tle->resdom;
+
+ if (resnode->resjunk)
+ continue;
+ result = lappendi(result, resnode->restype);
+ }
+ return result;
+ }
+ else if (IsA(node, SetOperationStmt))
+ {
+ SetOperationStmt *op = (SetOperationStmt *) node;
+
+ /* Result already computed during transformation of node */
+ Assert(op->colTypes != NIL);
+ return op->colTypes;
+ }
+ else
+ {
+ elog(ERROR, "getSetColTypes: unexpected node %d",
+ (int) nodeTag(node));
+ return NIL; /* keep compiler quiet */
+ }
+}
+
+
+/*
* transformUpdateStmt -
* transforms an update statement
*
@@ -1756,26 +2130,6 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
if (origTargetList != NIL)
elog(ERROR, "UPDATE target count mismatch --- internal error");
- return (Query *) qry;
-}
-
-/*
- * transformCursorStmt -
- * transform a Create Cursor Statement
- *
- */
-static Query *
-transformCursorStmt(ParseState *pstate, SelectStmt *stmt)
-{
- Query *qry;
-
- qry = transformSelectStmt(pstate, stmt);
-
- qry->into = stmt->portalname;
- qry->isTemp = stmt->istemp;
- qry->isPortal = TRUE;
- qry->isBinary = stmt->binary; /* internal portal */
-
return qry;
}
@@ -2039,106 +2393,11 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
return qry;
}
-
-/* This function steps through the tree
- * built up by the select_w_o_sort rule
- * and builds a list of all SelectStmt Nodes found
- * The built up list is handed back in **select_list.
- * If one of the SelectStmt Nodes has the 'unionall' flag
- * set to true *unionall_present hands back 'true' */
-void
-create_select_list(Node *ptr, List **select_list, bool *unionall_present)
-{
- if (IsA(ptr, SelectStmt))
- {
- *select_list = lappend(*select_list, ptr);
- if (((SelectStmt *) ptr)->unionall == TRUE)
- *unionall_present = TRUE;
- return;
- }
-
- /* Recursively call for all arguments. A NOT expr has no lexpr! */
- if (((A_Expr *) ptr)->lexpr != NULL)
- create_select_list(((A_Expr *) ptr)->lexpr, select_list, unionall_present);
- create_select_list(((A_Expr *) ptr)->rexpr, select_list, unionall_present);
-}
-
-/* Changes the A_Expr Nodes to Expr Nodes and exchanges ANDs and ORs.
- * The reason for the exchange is easy: We implement INTERSECTs and EXCEPTs
- * by rewriting these queries to semantically equivalent queries that use
- * IN and NOT IN subselects. To be able to use all three operations
- * (UNIONs INTERSECTs and EXCEPTs) in one complex query we have to
- * translate the queries into Disjunctive Normal Form (DNF). Unfortunately
- * there is no function 'dnfify' but there is a function 'cnfify'
- * which produces DNF when we exchange ANDs and ORs before calling
- * 'cnfify' and exchange them back in the result.
- *
- * If an EXCEPT or INTERSECT is present *intersect_present
- * hands back 'true' */
-Node *
-A_Expr_to_Expr(Node *ptr, bool *intersect_present)
-{
- Node *result = NULL;
-
- switch (nodeTag(ptr))
- {
- case T_A_Expr:
- {
- A_Expr *a = (A_Expr *) ptr;
-
- switch (a->oper)
- {
- case AND:
- {
- Expr *expr = makeNode(Expr);
- Node *lexpr = A_Expr_to_Expr(((A_Expr *) ptr)->lexpr, intersect_present);
- Node *rexpr = A_Expr_to_Expr(((A_Expr *) ptr)->rexpr, intersect_present);
-
- *intersect_present = TRUE;
-
- expr->typeOid = BOOLOID;
- expr->opType = OR_EXPR;
- expr->args = makeList2(lexpr, rexpr);
- result = (Node *) expr;
- break;
- }
- case OR:
- {
- Expr *expr = makeNode(Expr);
- Node *lexpr = A_Expr_to_Expr(((A_Expr *) ptr)->lexpr, intersect_present);
- Node *rexpr = A_Expr_to_Expr(((A_Expr *) ptr)->rexpr, intersect_present);
-
- expr->typeOid = BOOLOID;
- expr->opType = AND_EXPR;
- expr->args = makeList2(lexpr, rexpr);
- result = (Node *) expr;
- break;
- }
- case NOT:
- {
- Expr *expr = makeNode(Expr);
- Node *rexpr = A_Expr_to_Expr(((A_Expr *) ptr)->rexpr, intersect_present);
-
- expr->typeOid = BOOLOID;
- expr->opType = NOT_EXPR;
- expr->args = makeList1(rexpr);
- result = (Node *) expr;
- break;
- }
- }
- break;
- }
- default:
- result = ptr;
- }
- return result;
-}
-
void
CheckSelectForUpdate(Query *qry)
{
- if (qry->unionClause || qry->intersectClause)
- elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT clause");
+ if (qry->setOperations)
+ elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
if (qry->distinctClause != NIL)
elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
if (qry->groupClause != NIL)