Allow ORDER BY, LIMIT in sub-selects. Fix most (not all) cases where
the grammar did not allow redundant parentheses around sub-selects. Distinguish LIMIT ALL from LIMIT 0; make the latter behave as one would expect.
This commit is contained in:
parent
66436e66e1
commit
11f7b29054
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.1 2000/10/26 21:35:15 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.2 2000/11/05 00:15:52 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -188,17 +188,11 @@ recompute_limits(Limit *node)
|
|||||||
econtext,
|
econtext,
|
||||||
&isNull,
|
&isNull,
|
||||||
NULL));
|
NULL));
|
||||||
/* Interpret NULL count as no count */
|
/* Interpret NULL count as no count (LIMIT ALL) */
|
||||||
if (isNull)
|
if (isNull)
|
||||||
limitstate->noCount = true;
|
limitstate->noCount = true;
|
||||||
else
|
else if (limitstate->count < 0)
|
||||||
{
|
limitstate->count = 0;
|
||||||
/* Currently, LIMIT 0 is specified as meaning no limit.
|
|
||||||
* I think this is pretty bogus, but ...
|
|
||||||
*/
|
|
||||||
if (limitstate->count <= 0)
|
|
||||||
limitstate->noCount = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.128 2000/10/31 10:22:10 petere Exp $
|
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.129 2000/11/05 00:15:52 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1826,6 +1826,7 @@ _copySelectStmt(SelectStmt *from)
|
|||||||
Node_Copy(from, newnode, distinctClause);
|
Node_Copy(from, newnode, distinctClause);
|
||||||
if (from->into)
|
if (from->into)
|
||||||
newnode->into = pstrdup(from->into);
|
newnode->into = pstrdup(from->into);
|
||||||
|
newnode->istemp = from->istemp;
|
||||||
Node_Copy(from, newnode, targetList);
|
Node_Copy(from, newnode, targetList);
|
||||||
Node_Copy(from, newnode, fromClause);
|
Node_Copy(from, newnode, fromClause);
|
||||||
Node_Copy(from, newnode, whereClause);
|
Node_Copy(from, newnode, whereClause);
|
||||||
@ -1835,10 +1836,13 @@ _copySelectStmt(SelectStmt *from)
|
|||||||
if (from->portalname)
|
if (from->portalname)
|
||||||
newnode->portalname = pstrdup(from->portalname);
|
newnode->portalname = pstrdup(from->portalname);
|
||||||
newnode->binary = from->binary;
|
newnode->binary = from->binary;
|
||||||
newnode->istemp = from->istemp;
|
|
||||||
Node_Copy(from, newnode, limitOffset);
|
Node_Copy(from, newnode, limitOffset);
|
||||||
Node_Copy(from, newnode, limitCount);
|
Node_Copy(from, newnode, limitCount);
|
||||||
Node_Copy(from, newnode, forUpdate);
|
Node_Copy(from, newnode, forUpdate);
|
||||||
|
newnode->op = from->op;
|
||||||
|
newnode->all = from->all;
|
||||||
|
Node_Copy(from, newnode, larg);
|
||||||
|
Node_Copy(from, newnode, rarg);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.78 2000/10/31 10:22:10 petere Exp $
|
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.79 2000/11/05 00:15:52 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -686,6 +686,8 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b)
|
|||||||
return false;
|
return false;
|
||||||
if (!equalstr(a->into, b->into))
|
if (!equalstr(a->into, b->into))
|
||||||
return false;
|
return false;
|
||||||
|
if (a->istemp != b->istemp)
|
||||||
|
return false;
|
||||||
if (!equal(a->targetList, b->targetList))
|
if (!equal(a->targetList, b->targetList))
|
||||||
return false;
|
return false;
|
||||||
if (!equal(a->fromClause, b->fromClause))
|
if (!equal(a->fromClause, b->fromClause))
|
||||||
@ -702,14 +704,20 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b)
|
|||||||
return false;
|
return false;
|
||||||
if (a->binary != b->binary)
|
if (a->binary != b->binary)
|
||||||
return false;
|
return false;
|
||||||
if (a->istemp != b->istemp)
|
|
||||||
return false;
|
|
||||||
if (!equal(a->limitOffset, b->limitOffset))
|
if (!equal(a->limitOffset, b->limitOffset))
|
||||||
return false;
|
return false;
|
||||||
if (!equal(a->limitCount, b->limitCount))
|
if (!equal(a->limitCount, b->limitCount))
|
||||||
return false;
|
return false;
|
||||||
if (!equal(a->forUpdate, b->forUpdate))
|
if (!equal(a->forUpdate, b->forUpdate))
|
||||||
return false;
|
return false;
|
||||||
|
if (a->op != b->op)
|
||||||
|
return false;
|
||||||
|
if (a->all != b->all)
|
||||||
|
return false;
|
||||||
|
if (!equal(a->larg, b->larg))
|
||||||
|
return false;
|
||||||
|
if (!equal(a->rarg, b->rarg))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.93 2000/10/26 21:36:09 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.94 2000/11/05 00:15:53 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -809,25 +809,26 @@ union_planner(Query *parse,
|
|||||||
if (IsA(parse->limitCount, Const))
|
if (IsA(parse->limitCount, Const))
|
||||||
{
|
{
|
||||||
Const *limitc = (Const *) parse->limitCount;
|
Const *limitc = (Const *) parse->limitCount;
|
||||||
int count = (int) (limitc->constvalue);
|
int32 count = DatumGetInt32(limitc->constvalue);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The constant can legally be either 0 ("ALL") or a
|
* A NULL-constant LIMIT represents "LIMIT ALL",
|
||||||
* positive integer. If it is not ALL, we also need
|
* which we treat the same as no limit (ie,
|
||||||
* to consider the OFFSET part of LIMIT.
|
* expect to retrieve all the tuples).
|
||||||
*/
|
*/
|
||||||
if (count > 0)
|
if (!limitc->constisnull && count > 0)
|
||||||
{
|
{
|
||||||
tuple_fraction = (double) count;
|
tuple_fraction = (double) count;
|
||||||
|
/* We must also consider the OFFSET, if present */
|
||||||
if (parse->limitOffset != NULL)
|
if (parse->limitOffset != NULL)
|
||||||
{
|
{
|
||||||
if (IsA(parse->limitOffset, Const))
|
if (IsA(parse->limitOffset, Const))
|
||||||
{
|
{
|
||||||
int offset;
|
int32 offset;
|
||||||
|
|
||||||
limitc = (Const *) parse->limitOffset;
|
limitc = (Const *) parse->limitOffset;
|
||||||
offset = (int) (limitc->constvalue);
|
offset = DatumGetInt32(limitc->constvalue);
|
||||||
if (offset > 0)
|
if (!limitc->constisnull && offset > 0)
|
||||||
tuple_fraction += (double) offset;
|
tuple_fraction += (double) offset;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -850,14 +851,14 @@ union_planner(Query *parse,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for a retrieve-into-portal, ie DECLARE CURSOR.
|
* If no LIMIT, check for retrieve-into-portal, ie DECLARE CURSOR.
|
||||||
*
|
*
|
||||||
* We have no real idea how many tuples the user will ultimately
|
* We have no real idea how many tuples the user will ultimately
|
||||||
* FETCH from a cursor, but it seems a good bet that he
|
* FETCH from a cursor, but it seems a good bet that he
|
||||||
* doesn't want 'em all. Optimize for 10% retrieval (you
|
* doesn't want 'em all. Optimize for 10% retrieval (you
|
||||||
* gotta better number?)
|
* gotta better number?)
|
||||||
*/
|
*/
|
||||||
if (parse->isPortal)
|
else if (parse->isPortal)
|
||||||
tuple_fraction = 0.10;
|
tuple_fraction = 0.10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
|
||||||
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
|
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
|
||||||
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Query *transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt);
|
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Node *transformSetOperationTree(ParseState *pstate, Node *node);
|
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
||||||
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
|
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
|
||||||
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
|
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
|
||||||
@ -257,11 +257,12 @@ transformStmt(ParseState *pstate, Node *parseTree)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_SelectStmt:
|
case T_SelectStmt:
|
||||||
result = transformSelectStmt(pstate, (SelectStmt *) parseTree);
|
if (((SelectStmt *) parseTree)->op == SETOP_NONE)
|
||||||
break;
|
result = transformSelectStmt(pstate,
|
||||||
|
(SelectStmt *) parseTree);
|
||||||
case T_SetOperationStmt:
|
else
|
||||||
result = transformSetOperationStmt(pstate, (SetOperationStmt *) parseTree);
|
result = transformSetOperationStmt(pstate,
|
||||||
|
(SelectStmt *) parseTree);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1173,7 +1174,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
|
|||||||
found=1;
|
found=1;
|
||||||
}
|
}
|
||||||
if (!found)
|
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 -
|
* 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-
|
* 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.
|
* level Query that contains the leaf SELECTs as subqueries in its rangetable.
|
||||||
* The SetOperations tree (with leaf SelectStmts replaced by RangeTblRef nodes)
|
* The tree of set operations is converted into the setOperations field of
|
||||||
* becomes the setOperations field of the top-level Query.
|
* the top-level Query.
|
||||||
*/
|
*/
|
||||||
static Query *
|
static Query *
|
||||||
transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt)
|
transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
|
||||||
{
|
{
|
||||||
Query *qry = makeNode(Query);
|
Query *qry = makeNode(Query);
|
||||||
Node *node;
|
|
||||||
SelectStmt *leftmostSelect;
|
SelectStmt *leftmostSelect;
|
||||||
Query *leftmostQuery;
|
Query *leftmostQuery;
|
||||||
|
SetOperationStmt *sostmt;
|
||||||
char *into;
|
char *into;
|
||||||
|
bool istemp;
|
||||||
char *portalname;
|
char *portalname;
|
||||||
bool binary;
|
bool binary;
|
||||||
bool istemp;
|
|
||||||
List *sortClause;
|
List *sortClause;
|
||||||
Node *limitOffset;
|
Node *limitOffset;
|
||||||
Node *limitCount;
|
Node *limitCount;
|
||||||
List *forUpdate;
|
List *forUpdate;
|
||||||
|
Node *node;
|
||||||
List *lefttl,
|
List *lefttl,
|
||||||
*dtlist;
|
*dtlist;
|
||||||
int tllen;
|
int tllen;
|
||||||
@ -1802,50 +1804,55 @@ transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt)
|
|||||||
qry->commandType = CMD_SELECT;
|
qry->commandType = CMD_SELECT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find leftmost leaf SelectStmt and extract the one-time-only items
|
* Find leftmost leaf SelectStmt; extract the one-time-only items
|
||||||
* from it.
|
* from it and from the top-level node.
|
||||||
*/
|
*/
|
||||||
node = stmt->larg;
|
leftmostSelect = stmt->larg;
|
||||||
while (node && IsA(node, SetOperationStmt))
|
while (leftmostSelect && leftmostSelect->op != SETOP_NONE)
|
||||||
node = ((SetOperationStmt *) node)->larg;
|
leftmostSelect = leftmostSelect->larg;
|
||||||
Assert(node && IsA(node, SelectStmt));
|
Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
|
||||||
leftmostSelect = (SelectStmt *) node;
|
leftmostSelect->larg == NULL);
|
||||||
|
|
||||||
into = leftmostSelect->into;
|
into = leftmostSelect->into;
|
||||||
portalname = leftmostSelect->portalname;
|
|
||||||
binary = leftmostSelect->binary;
|
|
||||||
istemp = leftmostSelect->istemp;
|
istemp = leftmostSelect->istemp;
|
||||||
sortClause = leftmostSelect->sortClause;
|
portalname = stmt->portalname;
|
||||||
limitOffset = leftmostSelect->limitOffset;
|
binary = stmt->binary;
|
||||||
limitCount = leftmostSelect->limitCount;
|
|
||||||
forUpdate = leftmostSelect->forUpdate;
|
|
||||||
|
|
||||||
/* clear them to prevent complaints in transformSetOperationTree() */
|
/* clear them to prevent complaints in transformSetOperationTree() */
|
||||||
leftmostSelect->into = NULL;
|
leftmostSelect->into = NULL;
|
||||||
leftmostSelect->portalname = NULL;
|
|
||||||
leftmostSelect->binary = false;
|
|
||||||
leftmostSelect->istemp = false;
|
leftmostSelect->istemp = false;
|
||||||
leftmostSelect->sortClause = NIL;
|
stmt->portalname = NULL;
|
||||||
leftmostSelect->limitOffset = NULL;
|
stmt->binary = false;
|
||||||
leftmostSelect->limitCount = NULL;
|
|
||||||
leftmostSelect->forUpdate = NIL;
|
|
||||||
|
|
||||||
/* We don't actually support forUpdate with set ops at the moment. */
|
/*
|
||||||
|
* 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 support forUpdate with set ops at the moment. */
|
||||||
if (forUpdate)
|
if (forUpdate)
|
||||||
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
|
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recursively transform the components of the tree.
|
* Recursively transform the components of the tree.
|
||||||
*/
|
*/
|
||||||
stmt = (SetOperationStmt *)
|
sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt);
|
||||||
transformSetOperationTree(pstate, (Node *) stmt);
|
Assert(sostmt && IsA(sostmt, SetOperationStmt));
|
||||||
Assert(stmt && IsA(stmt, SetOperationStmt));
|
qry->setOperations = (Node *) sostmt;
|
||||||
qry->setOperations = (Node *) stmt;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Re-find leftmost SELECT (now it's a sub-query in rangetable)
|
* Re-find leftmost SELECT (now it's a sub-query in rangetable)
|
||||||
*/
|
*/
|
||||||
node = stmt->larg;
|
node = sostmt->larg;
|
||||||
while (node && IsA(node, SetOperationStmt))
|
while (node && IsA(node, SetOperationStmt))
|
||||||
node = ((SetOperationStmt *) node)->larg;
|
node = ((SetOperationStmt *) node)->larg;
|
||||||
Assert(node && IsA(node, RangeTblRef));
|
Assert(node && IsA(node, RangeTblRef));
|
||||||
@ -1858,7 +1865,7 @@ transformSetOperationStmt(ParseState *pstate, SetOperationStmt *stmt)
|
|||||||
*/
|
*/
|
||||||
qry->targetList = NIL;
|
qry->targetList = NIL;
|
||||||
lefttl = leftmostQuery->targetList;
|
lefttl = leftmostQuery->targetList;
|
||||||
foreach(dtlist, stmt->colTypes)
|
foreach(dtlist, sostmt->colTypes)
|
||||||
{
|
{
|
||||||
Oid colType = (Oid) lfirsti(dtlist);
|
Oid colType = (Oid) lfirsti(dtlist);
|
||||||
char *colName = ((TargetEntry *) lfirst(lefttl))->resdom->resname;
|
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
|
* Recursively transform leaves and internal nodes of a set-op tree
|
||||||
*/
|
*/
|
||||||
static Node *
|
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 *save_rtable;
|
||||||
List *selectList;
|
List *selectList;
|
||||||
Query *selectQuery;
|
Query *selectQuery;
|
||||||
@ -1965,20 +2008,6 @@ transformSetOperationTree(ParseState *pstate, Node *node)
|
|||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
RangeTblRef *rtr;
|
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
|
* Transform SelectStmt into a Query. We do not want any previously
|
||||||
* transformed leaf queries to be visible in the outer context of
|
* transformed leaf queries to be visible in the outer context of
|
||||||
@ -2011,21 +2040,26 @@ transformSetOperationTree(ParseState *pstate, Node *node)
|
|||||||
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
|
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
|
||||||
return (Node *) rtr;
|
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 *lcoltypes;
|
||||||
List *rcoltypes;
|
List *rcoltypes;
|
||||||
const char *context;
|
const char *context;
|
||||||
|
|
||||||
context = (op->op == SETOP_UNION ? "UNION" :
|
context = (stmt->op == SETOP_UNION ? "UNION" :
|
||||||
(op->op == SETOP_INTERSECT ? "INTERSECT" :
|
(stmt->op == SETOP_INTERSECT ? "INTERSECT" :
|
||||||
"EXCEPT"));
|
"EXCEPT"));
|
||||||
|
|
||||||
|
op->op = stmt->op;
|
||||||
|
op->all = stmt->all;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recursively transform the child nodes.
|
* Recursively transform the child nodes.
|
||||||
*/
|
*/
|
||||||
op->larg = transformSetOperationTree(pstate, op->larg);
|
op->larg = transformSetOperationTree(pstate, stmt->larg);
|
||||||
op->rarg = transformSetOperationTree(pstate, op->rarg);
|
op->rarg = transformSetOperationTree(pstate, stmt->rarg);
|
||||||
/*
|
/*
|
||||||
* Verify that the two children have the same number of non-junk
|
* Verify that the two children have the same number of non-junk
|
||||||
* columns, and determine the types of the merged output columns.
|
* columns, and determine the types of the merged output columns.
|
||||||
@ -2048,14 +2082,9 @@ transformSetOperationTree(ParseState *pstate, Node *node)
|
|||||||
lcoltypes = lnext(lcoltypes);
|
lcoltypes = lnext(lcoltypes);
|
||||||
rcoltypes = lnext(rcoltypes);
|
rcoltypes = lnext(rcoltypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Node *) op;
|
return (Node *) op;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
elog(ERROR, "transformSetOperationTree: unexpected node %d",
|
|
||||||
(int) nodeTag(node));
|
|
||||||
return NULL; /* keep compiler quiet */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.203 2000/11/04 21:04:55 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.204 2000/11/05 00:15:54 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -80,7 +80,11 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
|
|||||||
static Node *makeTypeCast(Node *arg, TypeName *typename);
|
static Node *makeTypeCast(Node *arg, TypeName *typename);
|
||||||
static Node *makeRowExpr(char *opr, List *largs, List *rargs);
|
static Node *makeRowExpr(char *opr, List *largs, List *rargs);
|
||||||
static void mapTargetColumns(List *source, List *target);
|
static void mapTargetColumns(List *source, List *target);
|
||||||
static SelectStmt *findLeftmostSelect(Node *node);
|
static SelectStmt *findLeftmostSelect(SelectStmt *node);
|
||||||
|
static void insertSelectOptions(SelectStmt *stmt,
|
||||||
|
List *sortClause, List *forUpdate,
|
||||||
|
Node *limitOffset, Node *limitCount);
|
||||||
|
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
|
||||||
static bool exprIsNullConstant(Node *arg);
|
static bool exprIsNullConstant(Node *arg);
|
||||||
static Node *doNegate(Node *n);
|
static Node *doNegate(Node *n);
|
||||||
static void doNegateFloat(Value *v);
|
static void doNegateFloat(Value *v);
|
||||||
@ -134,7 +138,7 @@ static void doNegateFloat(Value *v);
|
|||||||
UnlistenStmt, UpdateStmt, VacuumStmt, VariableResetStmt,
|
UnlistenStmt, UpdateStmt, VacuumStmt, VariableResetStmt,
|
||||||
VariableSetStmt, VariableShowStmt, ViewStmt
|
VariableSetStmt, VariableShowStmt, ViewStmt
|
||||||
|
|
||||||
%type <node> select_clause, select_subclause
|
%type <node> select_no_parens, select_clause, simple_select
|
||||||
|
|
||||||
%type <list> SessionList
|
%type <list> SessionList
|
||||||
%type <node> SessionClause
|
%type <node> SessionClause
|
||||||
@ -174,8 +178,8 @@ static void doNegateFloat(Value *v);
|
|||||||
%type <chr> operation, TriggerOneEvent
|
%type <chr> operation, TriggerOneEvent
|
||||||
|
|
||||||
%type <list> stmtblock, stmtmulti,
|
%type <list> stmtblock, stmtmulti,
|
||||||
result, OptTempTableName, relation_name_list, OptTableElementList,
|
into_clause, OptTempTableName, relation_name_list,
|
||||||
OptUnder, OptInherit, definition, opt_distinct,
|
OptTableElementList, OptUnder, OptInherit, definition, opt_distinct,
|
||||||
opt_with, func_args, func_args_list, func_as,
|
opt_with, func_args, func_args_list, func_as,
|
||||||
oper_argtypes, RuleActionList, RuleActionMulti,
|
oper_argtypes, RuleActionList, RuleActionMulti,
|
||||||
opt_column_list, columnList, opt_va_list, va_list,
|
opt_column_list, columnList, opt_va_list, va_list,
|
||||||
@ -183,13 +187,13 @@ static void doNegateFloat(Value *v);
|
|||||||
from_clause, from_list, opt_array_bounds,
|
from_clause, from_list, opt_array_bounds,
|
||||||
expr_list, attrs, target_list, update_target_list,
|
expr_list, attrs, target_list, update_target_list,
|
||||||
def_list, opt_indirection, group_clause, TriggerFuncArgs,
|
def_list, opt_indirection, group_clause, TriggerFuncArgs,
|
||||||
opt_select_limit
|
select_limit, opt_select_limit
|
||||||
|
|
||||||
%type <typnam> func_arg, func_return, aggr_argtype
|
%type <typnam> func_arg, func_return, aggr_argtype
|
||||||
|
|
||||||
%type <boolean> opt_arg, TriggerForOpt, TriggerForType, OptTemp
|
%type <boolean> opt_arg, TriggerForOpt, TriggerForType, OptTemp
|
||||||
|
|
||||||
%type <list> for_update_clause, update_list
|
%type <list> for_update_clause, opt_for_update_clause, update_list
|
||||||
%type <boolean> opt_all
|
%type <boolean> opt_all
|
||||||
%type <boolean> opt_table
|
%type <boolean> opt_table
|
||||||
%type <boolean> opt_chain, opt_trans
|
%type <boolean> opt_chain, opt_trans
|
||||||
@ -385,6 +389,7 @@ static void doNegateFloat(Value *v);
|
|||||||
%right UMINUS
|
%right UMINUS
|
||||||
%left '.'
|
%left '.'
|
||||||
%left '[' ']'
|
%left '[' ']'
|
||||||
|
%left '(' ')'
|
||||||
%left TYPECAST
|
%left TYPECAST
|
||||||
%%
|
%%
|
||||||
|
|
||||||
@ -444,6 +449,7 @@ stmt : AlterSchemaStmt
|
|||||||
| ListenStmt
|
| ListenStmt
|
||||||
| UnlistenStmt
|
| UnlistenStmt
|
||||||
| LockStmt
|
| LockStmt
|
||||||
|
| NotifyStmt
|
||||||
| ProcedureStmt
|
| ProcedureStmt
|
||||||
| ReindexStmt
|
| ReindexStmt
|
||||||
| RemoveAggrStmt
|
| RemoveAggrStmt
|
||||||
@ -1527,7 +1533,14 @@ OptInherit: INHERITS '(' relation_name_list ')' { $$ = $3; }
|
|||||||
|
|
||||||
CreateAsStmt: CREATE OptTemp TABLE relation_name OptUnder OptCreateAs AS SelectStmt
|
CreateAsStmt: CREATE OptTemp TABLE relation_name OptUnder OptCreateAs AS SelectStmt
|
||||||
{
|
{
|
||||||
SelectStmt *n = findLeftmostSelect($8);
|
/*
|
||||||
|
* When the SelectStmt is a set-operation tree, we must
|
||||||
|
* stuff the INTO information into the leftmost component
|
||||||
|
* Select, because that's where analyze.c will expect
|
||||||
|
* to find it. Similarly, the output column names must
|
||||||
|
* be attached to that Select's target list.
|
||||||
|
*/
|
||||||
|
SelectStmt *n = findLeftmostSelect((SelectStmt *) $8);
|
||||||
if (n->into != NULL)
|
if (n->into != NULL)
|
||||||
elog(ERROR,"CREATE TABLE/AS SELECT may not specify INTO");
|
elog(ERROR,"CREATE TABLE/AS SELECT may not specify INTO");
|
||||||
n->istemp = $2;
|
n->istemp = $2;
|
||||||
@ -1541,7 +1554,7 @@ CreateAsStmt: CREATE OptTemp TABLE relation_name OptUnder OptCreateAs AS Select
|
|||||||
;
|
;
|
||||||
|
|
||||||
OptCreateAs: '(' CreateAsList ')' { $$ = $2; }
|
OptCreateAs: '(' CreateAsList ')' { $$ = $2; }
|
||||||
| /*EMPTY*/ { $$ = NULL; }
|
| /*EMPTY*/ { $$ = NIL; }
|
||||||
;
|
;
|
||||||
|
|
||||||
CreateAsList: CreateAsList ',' CreateAsElement { $$ = lappend($1, $3); }
|
CreateAsList: CreateAsList ',' CreateAsElement { $$ = lappend($1, $3); }
|
||||||
@ -2682,7 +2695,6 @@ RuleStmt: CREATE RULE name AS
|
|||||||
;
|
;
|
||||||
|
|
||||||
RuleActionList: NOTHING { $$ = NIL; }
|
RuleActionList: NOTHING { $$ = NIL; }
|
||||||
| SelectStmt { $$ = makeList1($1); }
|
|
||||||
| RuleActionStmt { $$ = makeList1($1); }
|
| RuleActionStmt { $$ = makeList1($1); }
|
||||||
| '[' RuleActionMulti ']' { $$ = $2; }
|
| '[' RuleActionMulti ']' { $$ = $2; }
|
||||||
| '(' RuleActionMulti ')' { $$ = $2; }
|
| '(' RuleActionMulti ')' { $$ = $2; }
|
||||||
@ -2703,7 +2715,17 @@ RuleActionMulti: RuleActionMulti ';' RuleActionStmtOrEmpty
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
RuleActionStmt: InsertStmt
|
/*
|
||||||
|
* 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
|
||||||
| UpdateStmt
|
| UpdateStmt
|
||||||
| DeleteStmt
|
| DeleteStmt
|
||||||
| NotifyStmt
|
| NotifyStmt
|
||||||
@ -3070,7 +3092,6 @@ OptimizableStmt: SelectStmt
|
|||||||
| CursorStmt
|
| CursorStmt
|
||||||
| UpdateStmt
|
| UpdateStmt
|
||||||
| InsertStmt
|
| InsertStmt
|
||||||
| NotifyStmt
|
|
||||||
| DeleteStmt /* by default all are $$=$1 */
|
| DeleteStmt /* by default all are $$=$1 */
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -3225,7 +3246,7 @@ UpdateStmt: UPDATE opt_only relation_name
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt
|
CursorStmt: DECLARE name opt_cursor CURSOR FOR SelectStmt
|
||||||
{
|
{
|
||||||
SelectStmt *n = findLeftmostSelect($6);
|
SelectStmt *n = (SelectStmt *)$6;
|
||||||
n->portalname = $2;
|
n->portalname = $2;
|
||||||
n->binary = $3;
|
n->binary = $3;
|
||||||
$$ = $6;
|
$$ = $6;
|
||||||
@ -3246,55 +3267,99 @@ opt_cursor: BINARY { $$ = TRUE; }
|
|||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/* A complete SELECT statement looks like this. Note sort, for_update,
|
/* A complete SELECT statement looks like this.
|
||||||
* and limit clauses can only appear once, not in each set operation.
|
|
||||||
*
|
*
|
||||||
* The rule returns either a SelectStmt node or a SetOperationStmt tree.
|
* The rule returns either a single SelectStmt node or a tree of them,
|
||||||
* One-time clauses are attached to the leftmost SelectStmt leaf.
|
* representing a set-operation tree.
|
||||||
*
|
*
|
||||||
* NOTE: only the leftmost SelectStmt leaf should have INTO, either.
|
* To avoid ambiguity problems with nested parentheses, we have to define
|
||||||
* However, this is not checked by the grammar; parse analysis must check it.
|
* 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SelectStmt: select_clause sort_clause for_update_clause opt_select_limit
|
SelectStmt: select_no_parens %prec TYPECAST
|
||||||
{
|
{
|
||||||
SelectStmt *n = findLeftmostSelect($1);
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| '(' SelectStmt ')'
|
||||||
|
{
|
||||||
|
$$ = $2;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
n->sortClause = $2;
|
select_no_parens: simple_select
|
||||||
n->forUpdate = $3;
|
{
|
||||||
n->limitOffset = nth(0, $4);
|
$$ = $1;
|
||||||
n->limitCount = nth(1, $4);
|
}
|
||||||
|
| select_clause sort_clause opt_for_update_clause opt_select_limit
|
||||||
|
{
|
||||||
|
insertSelectOptions((SelectStmt *) $1, $2, $3,
|
||||||
|
nth(0, $4), nth(1, $4));
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| select_clause for_update_clause opt_select_limit
|
||||||
|
{
|
||||||
|
insertSelectOptions((SelectStmt *) $1, NIL, $2,
|
||||||
|
nth(0, $3), nth(1, $3));
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| select_clause select_limit
|
||||||
|
{
|
||||||
|
insertSelectOptions((SelectStmt *) $1, NIL, NIL,
|
||||||
|
nth(0, $2), nth(1, $2));
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
/* This rule parses Select statements that can appear within set operations,
|
select_clause: simple_select
|
||||||
|
{
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
| '(' SelectStmt ')'
|
||||||
|
{
|
||||||
|
$$ = $2;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This rule parses SELECT statements that can appear within set operations,
|
||||||
* including UNION, INTERSECT and EXCEPT. '(' and ')' can be used to specify
|
* including UNION, INTERSECT and EXCEPT. '(' and ')' can be used to specify
|
||||||
* the ordering of the set operations. Without '(' and ')' we want the
|
* 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.
|
* operations to be ordered per the precedence specs at the head of this file.
|
||||||
*
|
*
|
||||||
* Since parentheses around SELECTs also appear in the expression grammar,
|
* As with select_no_parens, simple_select cannot have outer parentheses,
|
||||||
* there is a parse ambiguity if parentheses are allowed at the top level of a
|
* but can have parenthesized subclauses.
|
||||||
* 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
|
* Note that sort clauses cannot be included at this level --- SQL92 requires
|
||||||
* can only appear at the end of the complete Select, and it will be handled
|
* SELECT foo UNION SELECT bar ORDER BY baz
|
||||||
* by the topmost SelectStmt rule. Likewise FOR UPDATE and LIMIT.
|
* to be parsed as
|
||||||
|
* (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.
|
||||||
|
*
|
||||||
|
* NOTE: only the leftmost component SelectStmt should have INTO.
|
||||||
|
* However, this is not checked by the grammar; parse analysis must check it.
|
||||||
*/
|
*/
|
||||||
select_clause: '(' select_subclause ')'
|
simple_select: SELECT opt_distinct target_list
|
||||||
{
|
into_clause from_clause where_clause
|
||||||
$$ = $2;
|
|
||||||
}
|
|
||||||
| select_subclause
|
|
||||||
{
|
|
||||||
$$ = $1;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
select_subclause: SELECT opt_distinct target_list
|
|
||||||
result from_clause where_clause
|
|
||||||
group_clause having_clause
|
group_clause having_clause
|
||||||
{
|
{
|
||||||
SelectStmt *n = makeNode(SelectStmt);
|
SelectStmt *n = makeNode(SelectStmt);
|
||||||
@ -3310,35 +3375,20 @@ select_subclause: SELECT opt_distinct target_list
|
|||||||
}
|
}
|
||||||
| select_clause UNION opt_all select_clause
|
| select_clause UNION opt_all select_clause
|
||||||
{
|
{
|
||||||
SetOperationStmt *n = makeNode(SetOperationStmt);
|
$$ = makeSetOp(SETOP_UNION, $3, $1, $4);
|
||||||
n->op = SETOP_UNION;
|
|
||||||
n->all = $3;
|
|
||||||
n->larg = $1;
|
|
||||||
n->rarg = $4;
|
|
||||||
$$ = (Node *) n;
|
|
||||||
}
|
}
|
||||||
| select_clause INTERSECT opt_all select_clause
|
| select_clause INTERSECT opt_all select_clause
|
||||||
{
|
{
|
||||||
SetOperationStmt *n = makeNode(SetOperationStmt);
|
$$ = makeSetOp(SETOP_INTERSECT, $3, $1, $4);
|
||||||
n->op = SETOP_INTERSECT;
|
|
||||||
n->all = $3;
|
|
||||||
n->larg = $1;
|
|
||||||
n->rarg = $4;
|
|
||||||
$$ = (Node *) n;
|
|
||||||
}
|
}
|
||||||
| select_clause EXCEPT opt_all select_clause
|
| select_clause EXCEPT opt_all select_clause
|
||||||
{
|
{
|
||||||
SetOperationStmt *n = makeNode(SetOperationStmt);
|
$$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4);
|
||||||
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 */
|
/* easy way to return two values. Can someone improve this? bjm */
|
||||||
result: INTO OptTempTableName { $$ = $2; }
|
into_clause: INTO OptTempTableName { $$ = $2; }
|
||||||
| /*EMPTY*/ { $$ = lcons(makeInteger(FALSE), NIL); }
|
| /*EMPTY*/ { $$ = lcons(makeInteger(FALSE), NIL); }
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -3391,7 +3441,6 @@ opt_distinct: DISTINCT { $$ = makeList1(NIL); }
|
|||||||
;
|
;
|
||||||
|
|
||||||
sort_clause: ORDER BY sortby_list { $$ = $3; }
|
sort_clause: ORDER BY sortby_list { $$ = $3; }
|
||||||
| /*EMPTY*/ { $$ = NIL; }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
sortby_list: sortby { $$ = makeList1($1); }
|
sortby_list: sortby { $$ = makeList1($1); }
|
||||||
@ -3413,7 +3462,7 @@ OptUseOp: USING all_Op { $$ = $2; }
|
|||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
opt_select_limit: LIMIT select_limit_value ',' select_offset_value
|
select_limit: LIMIT select_limit_value ',' select_offset_value
|
||||||
{ $$ = makeList2($4, $2); }
|
{ $$ = makeList2($4, $2); }
|
||||||
| LIMIT select_limit_value OFFSET select_offset_value
|
| LIMIT select_limit_value OFFSET select_offset_value
|
||||||
{ $$ = makeList2($4, $2); }
|
{ $$ = makeList2($4, $2); }
|
||||||
@ -3423,20 +3472,22 @@ opt_select_limit: LIMIT select_limit_value ',' select_offset_value
|
|||||||
{ $$ = makeList2($2, $4); }
|
{ $$ = makeList2($2, $4); }
|
||||||
| OFFSET select_offset_value
|
| OFFSET select_offset_value
|
||||||
{ $$ = makeList2($2, NULL); }
|
{ $$ = makeList2($2, NULL); }
|
||||||
| /* EMPTY */
|
;
|
||||||
{ $$ = makeList2(NULL, NULL); }
|
|
||||||
|
opt_select_limit: select_limit { $$ = $1; }
|
||||||
|
| /* EMPTY */ { $$ = makeList2(NULL,NULL); }
|
||||||
;
|
;
|
||||||
|
|
||||||
select_limit_value: Iconst
|
select_limit_value: Iconst
|
||||||
{
|
{
|
||||||
Const *n = makeNode(Const);
|
Const *n = makeNode(Const);
|
||||||
|
|
||||||
if ($1 < 1)
|
if ($1 < 0)
|
||||||
elog(ERROR, "Selection limit must be ALL or a positive integer value");
|
elog(ERROR, "LIMIT must not be negative");
|
||||||
|
|
||||||
n->consttype = INT4OID;
|
n->consttype = INT4OID;
|
||||||
n->constlen = sizeof(int4);
|
n->constlen = sizeof(int4);
|
||||||
n->constvalue = (Datum)$1;
|
n->constvalue = Int32GetDatum($1);
|
||||||
n->constisnull = FALSE;
|
n->constisnull = FALSE;
|
||||||
n->constbyval = TRUE;
|
n->constbyval = TRUE;
|
||||||
n->constisset = FALSE;
|
n->constisset = FALSE;
|
||||||
@ -3445,12 +3496,13 @@ select_limit_value: Iconst
|
|||||||
}
|
}
|
||||||
| ALL
|
| ALL
|
||||||
{
|
{
|
||||||
|
/* LIMIT ALL is represented as a NULL constant */
|
||||||
Const *n = makeNode(Const);
|
Const *n = makeNode(Const);
|
||||||
|
|
||||||
n->consttype = INT4OID;
|
n->consttype = INT4OID;
|
||||||
n->constlen = sizeof(int4);
|
n->constlen = sizeof(int4);
|
||||||
n->constvalue = (Datum) 0;
|
n->constvalue = (Datum) 0;
|
||||||
n->constisnull = FALSE;
|
n->constisnull = TRUE;
|
||||||
n->constbyval = TRUE;
|
n->constbyval = TRUE;
|
||||||
n->constisset = FALSE;
|
n->constisset = FALSE;
|
||||||
n->constiscast = FALSE;
|
n->constiscast = FALSE;
|
||||||
@ -3471,9 +3523,12 @@ select_offset_value: Iconst
|
|||||||
{
|
{
|
||||||
Const *n = makeNode(Const);
|
Const *n = makeNode(Const);
|
||||||
|
|
||||||
|
if ($1 < 0)
|
||||||
|
elog(ERROR, "OFFSET must not be negative");
|
||||||
|
|
||||||
n->consttype = INT4OID;
|
n->consttype = INT4OID;
|
||||||
n->constlen = sizeof(int4);
|
n->constlen = sizeof(int4);
|
||||||
n->constvalue = (Datum)$1;
|
n->constvalue = Int32GetDatum($1);
|
||||||
n->constisnull = FALSE;
|
n->constisnull = FALSE;
|
||||||
n->constbyval = TRUE;
|
n->constbyval = TRUE;
|
||||||
n->constisset = FALSE;
|
n->constisset = FALSE;
|
||||||
@ -3490,6 +3545,7 @@ select_offset_value: Iconst
|
|||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* jimmy bell-style recursive queries aren't supported in the
|
* jimmy bell-style recursive queries aren't supported in the
|
||||||
* current system.
|
* current system.
|
||||||
@ -3522,6 +3578,9 @@ having_clause: HAVING a_expr
|
|||||||
|
|
||||||
for_update_clause: FOR UPDATE update_list { $$ = $3; }
|
for_update_clause: FOR UPDATE update_list { $$ = $3; }
|
||||||
| FOR READ ONLY { $$ = NULL; }
|
| FOR READ ONLY { $$ = NULL; }
|
||||||
|
;
|
||||||
|
|
||||||
|
opt_for_update_clause: for_update_clause { $$ = $1; }
|
||||||
| /* EMPTY */ { $$ = NULL; }
|
| /* EMPTY */ { $$ = NULL; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -3565,7 +3624,7 @@ table_ref: relation_expr
|
|||||||
$1->name = $2;
|
$1->name = $2;
|
||||||
$$ = (Node *) $1;
|
$$ = (Node *) $1;
|
||||||
}
|
}
|
||||||
| '(' select_subclause ')' alias_clause
|
| '(' SelectStmt ')' alias_clause
|
||||||
{
|
{
|
||||||
RangeSubselect *n = makeNode(RangeSubselect);
|
RangeSubselect *n = makeNode(RangeSubselect);
|
||||||
n->subquery = $2;
|
n->subquery = $2;
|
||||||
@ -4101,7 +4160,7 @@ 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 '(' select_subclause ')'
|
row_expr: '(' row_descriptor ')' IN '(' SelectStmt ')'
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->lefthand = $2;
|
n->lefthand = $2;
|
||||||
@ -4111,7 +4170,7 @@ row_expr: '(' row_descriptor ')' IN '(' select_subclause ')'
|
|||||||
n->subselect = $6;
|
n->subselect = $6;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| '(' row_descriptor ')' NOT IN '(' select_subclause ')'
|
| '(' row_descriptor ')' NOT IN '(' SelectStmt ')'
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->lefthand = $2;
|
n->lefthand = $2;
|
||||||
@ -4121,7 +4180,7 @@ row_expr: '(' row_descriptor ')' IN '(' select_subclause ')'
|
|||||||
n->subselect = $7;
|
n->subselect = $7;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| '(' row_descriptor ')' all_Op sub_type '(' select_subclause ')'
|
| '(' row_descriptor ')' all_Op sub_type '(' SelectStmt ')'
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->lefthand = $2;
|
n->lefthand = $2;
|
||||||
@ -4134,7 +4193,7 @@ row_expr: '(' row_descriptor ')' IN '(' select_subclause ')'
|
|||||||
n->subselect = $7;
|
n->subselect = $7;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| '(' row_descriptor ')' all_Op '(' select_subclause ')'
|
| '(' row_descriptor ')' all_Op '(' SelectStmt ')'
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->lefthand = $2;
|
n->lefthand = $2;
|
||||||
@ -4458,7 +4517,7 @@ a_expr: c_expr
|
|||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| a_expr all_Op sub_type '(' select_subclause ')'
|
| a_expr all_Op sub_type '(' SelectStmt ')'
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->lefthand = makeList1($1);
|
n->lefthand = makeList1($1);
|
||||||
@ -4851,7 +4910,7 @@ c_expr: attr
|
|||||||
n->agg_distinct = FALSE;
|
n->agg_distinct = FALSE;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| '(' select_subclause ')'
|
| '(' select_no_parens ')'
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->lefthand = NIL;
|
n->lefthand = NIL;
|
||||||
@ -4861,7 +4920,7 @@ c_expr: attr
|
|||||||
n->subselect = $2;
|
n->subselect = $2;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| EXISTS '(' select_subclause ')'
|
| EXISTS '(' SelectStmt ')'
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->lefthand = NIL;
|
n->lefthand = NIL;
|
||||||
@ -4960,7 +5019,7 @@ trim_list: a_expr FROM expr_list
|
|||||||
{ $$ = $1; }
|
{ $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
in_expr: select_subclause
|
in_expr: SelectStmt
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->subselect = $1;
|
n->subselect = $1;
|
||||||
@ -5688,20 +5747,71 @@ mapTargetColumns(List *src, List *dst)
|
|||||||
src = lnext(src);
|
src = lnext(src);
|
||||||
dst = lnext(dst);
|
dst = lnext(dst);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
} /* mapTargetColumns() */
|
} /* mapTargetColumns() */
|
||||||
|
|
||||||
|
|
||||||
/* findLeftmostSelect()
|
/* findLeftmostSelect()
|
||||||
* Find the leftmost SelectStmt in a SetOperationStmt parsetree.
|
* Find the leftmost component SelectStmt in a set-operation parsetree.
|
||||||
*/
|
*/
|
||||||
static SelectStmt *
|
static SelectStmt *
|
||||||
findLeftmostSelect(Node *node)
|
findLeftmostSelect(SelectStmt *node)
|
||||||
{
|
{
|
||||||
while (node && IsA(node, SetOperationStmt))
|
while (node && node->op != SETOP_NONE)
|
||||||
node = ((SetOperationStmt *) node)->larg;
|
node = node->larg;
|
||||||
Assert(node && IsA(node, SelectStmt));
|
Assert(node && IsA(node, SelectStmt) && node->larg == NULL);
|
||||||
return (SelectStmt *) node;
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* insertSelectOptions()
|
||||||
|
* Insert ORDER BY, etc into an already-constructed SelectStmt.
|
||||||
|
*
|
||||||
|
* This routine is just to avoid duplicating code in SelectStmt productions.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
insertSelectOptions(SelectStmt *stmt,
|
||||||
|
List *sortClause, List *forUpdate,
|
||||||
|
Node *limitOffset, Node *limitCount)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Tests here are to reject constructs like
|
||||||
|
* (SELECT foo ORDER BY bar) ORDER BY baz
|
||||||
|
*/
|
||||||
|
if (sortClause)
|
||||||
|
{
|
||||||
|
if (stmt->sortClause)
|
||||||
|
elog(ERROR, "Multiple ORDER BY clauses not allowed");
|
||||||
|
stmt->sortClause = sortClause;
|
||||||
|
}
|
||||||
|
if (forUpdate)
|
||||||
|
{
|
||||||
|
if (stmt->forUpdate)
|
||||||
|
elog(ERROR, "Multiple FOR UPDATE clauses not allowed");
|
||||||
|
stmt->forUpdate = forUpdate;
|
||||||
|
}
|
||||||
|
if (limitOffset)
|
||||||
|
{
|
||||||
|
if (stmt->limitOffset)
|
||||||
|
elog(ERROR, "Multiple OFFSET clauses not allowed");
|
||||||
|
stmt->limitOffset = limitOffset;
|
||||||
|
}
|
||||||
|
if (limitCount)
|
||||||
|
{
|
||||||
|
if (stmt->limitCount)
|
||||||
|
elog(ERROR, "Multiple LIMIT clauses not allowed");
|
||||||
|
stmt->limitCount = limitCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Node *
|
||||||
|
makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg)
|
||||||
|
{
|
||||||
|
SelectStmt *n = makeNode(SelectStmt);
|
||||||
|
|
||||||
|
n->op = op;
|
||||||
|
n->all = all;
|
||||||
|
n->larg = (SelectStmt *) larg;
|
||||||
|
n->rarg = (SelectStmt *) rarg;
|
||||||
|
return (Node *) n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* back to source text
|
* back to source text
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.67 2000/10/26 21:37:45 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.68 2000/11/05 00:15:53 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -941,6 +941,10 @@ get_select_query_def(Query *query, deparse_context *context)
|
|||||||
if (query->limitCount != NULL)
|
if (query->limitCount != NULL)
|
||||||
{
|
{
|
||||||
appendStringInfo(buf, " LIMIT ");
|
appendStringInfo(buf, " LIMIT ");
|
||||||
|
if (IsA(query->limitCount, Const) &&
|
||||||
|
((Const *) query->limitCount)->constisnull)
|
||||||
|
appendStringInfo(buf, "ALL");
|
||||||
|
else
|
||||||
get_rule_expr(query->limitCount, context);
|
get_rule_expr(query->limitCount, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: parsenodes.h,v 1.117 2000/10/18 16:16:10 momjian Exp $
|
* $Id: parsenodes.h,v 1.118 2000/11/05 00:15:53 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -782,7 +782,7 @@ typedef struct InsertStmt
|
|||||||
/*
|
/*
|
||||||
* An INSERT statement has *either* VALUES or SELECT, never both.
|
* An INSERT statement has *either* VALUES or SELECT, never both.
|
||||||
* If VALUES, a targetList is supplied (empty for DEFAULT VALUES).
|
* If VALUES, a targetList is supplied (empty for DEFAULT VALUES).
|
||||||
* If SELECT, a complete SelectStmt (or SetOperation tree) is supplied.
|
* If SELECT, a complete SelectStmt (or set-operation tree) is supplied.
|
||||||
*/
|
*/
|
||||||
List *targetList; /* the target list (of ResTarget) */
|
List *targetList; /* the target list (of ResTarget) */
|
||||||
Node *selectStmt; /* the source SELECT */
|
Node *selectStmt; /* the source SELECT */
|
||||||
@ -816,51 +816,71 @@ typedef struct UpdateStmt
|
|||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
* Select Statement
|
* Select Statement
|
||||||
* ----------------------
|
|
||||||
*/
|
|
||||||
typedef struct SelectStmt
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
List *distinctClause; /* NULL, list of DISTINCT ON exprs, or
|
|
||||||
* lcons(NIL,NIL) for all (SELECT
|
|
||||||
* DISTINCT) */
|
|
||||||
char *into; /* name of table (for select into table) */
|
|
||||||
List *targetList; /* the target list (of ResTarget) */
|
|
||||||
List *fromClause; /* the from clause */
|
|
||||||
Node *whereClause; /* qualifications */
|
|
||||||
List *groupClause; /* GROUP BY clauses */
|
|
||||||
Node *havingClause; /* having conditional-expression */
|
|
||||||
List *sortClause; /* sort clause (a list of SortGroupBy's) */
|
|
||||||
char *portalname; /* the portal (cursor) to create */
|
|
||||||
bool binary; /* a binary (internal) portal? */
|
|
||||||
bool istemp; /* into is a temp table */
|
|
||||||
Node *limitOffset; /* # of result tuples to skip */
|
|
||||||
Node *limitCount; /* # of result tuples to return */
|
|
||||||
List *forUpdate; /* FOR UPDATE clause */
|
|
||||||
} SelectStmt;
|
|
||||||
|
|
||||||
/* ----------------------
|
|
||||||
* Select Statement with Set Operations
|
|
||||||
*
|
*
|
||||||
* UNION/INTERSECT/EXCEPT operations are represented in the output of gram.y
|
* A "simple" SELECT is represented in the output of gram.y by a single
|
||||||
* as a tree whose leaves are SelectStmts and internal nodes are
|
* SelectStmt node. A SELECT construct containing set operators (UNION,
|
||||||
* SetOperationStmts. The statement-wide info (ORDER BY, etc clauses)
|
* INTERSECT, EXCEPT) is represented by a tree of SelectStmt nodes, in
|
||||||
* is placed in the leftmost SelectStmt leaf.
|
* which the leaf nodes are component SELECTs and the internal nodes
|
||||||
*
|
* represent UNION, INTERSECT, or EXCEPT operators. Using the same node
|
||||||
* After parse analysis, there is a top-level Query node containing the leaf
|
* type for both leaf and internal nodes allows gram.y to stick ORDER BY,
|
||||||
* SELECTs as subqueries in its range table. Its setOperations field is the
|
* LIMIT, etc, clause values into a SELECT statement without worrying
|
||||||
* SetOperationStmt tree with leaf SelectStmt nodes replaced by RangeTblRef
|
* whether it is a simple or compound SELECT.
|
||||||
* nodes. The statement-wide options such as ORDER BY are attached to this
|
|
||||||
* top-level Query.
|
|
||||||
* ----------------------
|
* ----------------------
|
||||||
*/
|
*/
|
||||||
typedef enum SetOperation
|
typedef enum SetOperation
|
||||||
{
|
{
|
||||||
|
SETOP_NONE = 0,
|
||||||
SETOP_UNION,
|
SETOP_UNION,
|
||||||
SETOP_INTERSECT,
|
SETOP_INTERSECT,
|
||||||
SETOP_EXCEPT
|
SETOP_EXCEPT
|
||||||
} SetOperation;
|
} SetOperation;
|
||||||
|
|
||||||
|
typedef struct SelectStmt
|
||||||
|
{
|
||||||
|
NodeTag type;
|
||||||
|
/*
|
||||||
|
* These fields are used only in "leaf" SelectStmts.
|
||||||
|
*/
|
||||||
|
List *distinctClause; /* NULL, list of DISTINCT ON exprs, or
|
||||||
|
* lcons(NIL,NIL) for all (SELECT
|
||||||
|
* DISTINCT) */
|
||||||
|
char *into; /* name of table (for select into table) */
|
||||||
|
bool istemp; /* into is a temp table? */
|
||||||
|
List *targetList; /* the target list (of ResTarget) */
|
||||||
|
List *fromClause; /* the FROM clause */
|
||||||
|
Node *whereClause; /* WHERE qualification */
|
||||||
|
List *groupClause; /* GROUP BY clauses */
|
||||||
|
Node *havingClause; /* HAVING conditional-expression */
|
||||||
|
/*
|
||||||
|
* These fields are used in both "leaf" SelectStmts and upper-level
|
||||||
|
* SelectStmts. portalname/binary may only be set at the top level.
|
||||||
|
*/
|
||||||
|
List *sortClause; /* sort clause (a list of SortGroupBy's) */
|
||||||
|
char *portalname; /* the portal (cursor) to create */
|
||||||
|
bool binary; /* a binary (internal) portal? */
|
||||||
|
Node *limitOffset; /* # of result tuples to skip */
|
||||||
|
Node *limitCount; /* # of result tuples to return */
|
||||||
|
List *forUpdate; /* FOR UPDATE clause */
|
||||||
|
/*
|
||||||
|
* These fields are used only in upper-level SelectStmts.
|
||||||
|
*/
|
||||||
|
SetOperation op; /* type of set op */
|
||||||
|
bool all; /* ALL specified? */
|
||||||
|
struct SelectStmt *larg; /* left child */
|
||||||
|
struct SelectStmt *rarg; /* right child */
|
||||||
|
/* Eventually add fields for CORRESPONDING spec here */
|
||||||
|
} SelectStmt;
|
||||||
|
|
||||||
|
/* ----------------------
|
||||||
|
* Set Operation node for post-analysis query trees
|
||||||
|
*
|
||||||
|
* After parse analysis, a SELECT with set operations is represented by a
|
||||||
|
* top-level Query node containing the leaf SELECTs as subqueries in its
|
||||||
|
* range table. Its setOperations field shows the tree of set operations,
|
||||||
|
* with leaf SelectStmt nodes replaced by RangeTblRef nodes, and internal
|
||||||
|
* nodes replaced by SetOperationStmt nodes.
|
||||||
|
* ----------------------
|
||||||
|
*/
|
||||||
typedef struct SetOperationStmt
|
typedef struct SetOperationStmt
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
@ -870,7 +890,7 @@ typedef struct SetOperationStmt
|
|||||||
Node *rarg; /* right child */
|
Node *rarg; /* right child */
|
||||||
/* Eventually add fields for CORRESPONDING spec here */
|
/* Eventually add fields for CORRESPONDING spec here */
|
||||||
|
|
||||||
/* This field is filled in during parse analysis: */
|
/* Fields derived during parse analysis: */
|
||||||
List *colTypes; /* integer list of OIDs of output column types */
|
List *colTypes; /* integer list of OIDs of output column types */
|
||||||
} SetOperationStmt;
|
} SetOperationStmt;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user