selecting from cursor
I hope I'm not taking too much time from people here...
Here's where I'm now (and don't beat me too hard :)
I'm done with change of RangeTblEntry into three different node types:
RangeTblEntryRelation,RangeTblEntrySubSelect,RangeTblEntryPortal which
have different fields. All the existing places instead of using
rte->subquery to determine type now use IsA(rte, RangeTblEntrySubSelect),
and later access fields after casting ((RangeTblEntrySubSelect *)rte)->xxx
Some functions that always work on Relation RTEs are declared to accept
RangeTblEntryRelation. Asserts are added everywhere before casting of RTE
into specific type. (Unless there was an IsA before, then I didn't put an
Assert).
Let me know if that is an acceptable way of doing things, or casting makes
things too ugly. (I believe its the best way, unions are more dangerous
in this context).
Let me know if it would make sense to rename these types into
RTERelation,RTESubSelect,RTEPortal to cut down on their length ...
Now, I'm going to more interesting stuff, planner:
SELECT blah FROM CURSOR FOO, list_of_tables
This really should place CURSOR FOO at the very top of join list, since
(generally), you don't know what cursor is opened for, and you cannot
ReScan a portal.
I'm still trying to find out how to explain that to optimizer, but if
someone gives me a hint, I'd appreciate it.
Alternatively, we can store everything we retrieved from portal for future
ReScan purposes, but I really don't like this idea.
By the same token, I think, optimizer must reject queries of type:
"SELECT ... FROM CURSOR FOO, CURSOR BAR" because such thing will lead to a
nested loop scan, which is impossible because, again, you can't ReScan a
cursor.
Let me know if I'm very off track with these assumptions, and if you have
to throw rocks at me, pick little ones ;)
-alex
Alex Pilosov <alex@pilosoft.com> writes:
I'm done with change of RangeTblEntry into three different node types:
RangeTblEntryRelation,RangeTblEntrySubSelect,RangeTblEntryPortal which
have different fields. All the existing places instead of using
rte->subquery to determine type now use IsA(rte, RangeTblEntrySubSelect),
and later access fields after casting ((RangeTblEntrySubSelect *)rte)->xxx
Some functions that always work on Relation RTEs are declared to accept
RangeTblEntryRelation. Asserts are added everywhere before casting of RTE
into specific type. (Unless there was an IsA before, then I didn't put an
Assert).
Let me know if that is an acceptable way of doing things, or casting makes
things too ugly. (I believe its the best way, unions are more dangerous
in this context).
And what are you doing with the places that don't care which kind of RTE
they are dealing with (which is most of them IIRC)? While you haven't
shown us the proposed changes, I really suspect that a union would be
cleaner, because it'd avoid ugliness in those places. Bear in mind that
the three RTE types that you have are going to become five or six real
soon now, because I have other things to fix that need to be done that
way --- so the notational advantage of a union is going to increase.
... you cannot ReScan a portal.
That's gonna have to be fixed. If you're not up for it, don't implement
this. Given that cursors (are supposed to) support FETCH BACKWARDS,
I really don't see why they shouldn't be expected to handle ReScan...
regards, tom lane
On Mon, 2 Jul 2001, Tom Lane wrote:
Alex Pilosov <alex@pilosoft.com> writes:
I'm done with change of RangeTblEntry into three different node types:
RangeTblEntryRelation,RangeTblEntrySubSelect,RangeTblEntryPortal which
have different fields. All the existing places instead of using
rte->subquery to determine type now use IsA(rte, RangeTblEntrySubSelect),
and later access fields after casting ((RangeTblEntrySubSelect *)rte)->xxxSome functions that always work on Relation RTEs are declared to accept
RangeTblEntryRelation. Asserts are added everywhere before casting of RTE
into specific type. (Unless there was an IsA before, then I didn't put an
Assert).Let me know if that is an acceptable way of doing things, or casting makes
things too ugly. (I believe its the best way, unions are more dangerous
in this context).And what are you doing with the places that don't care which kind of RTE
they are dealing with (which is most of them IIRC)? While you haven't
They just have things declared as RangeTblEntry *, and as long as they
don't access type-specific fields, they are fine.
shown us the proposed changes, I really suspect that a union would be
cleaner, because it'd avoid ugliness in those places. Bear in mind that
I have attached the diff of what I have now. Please take a look through
it. Its not exactly done (only the parser and pieces of executor parts of
"FROM CURSOR" are there, and I didn't attach gram.y diff because its just
too ugly now), so its definitely not ready for application, but with these
changes, it compiles and 'make check' goes through fine ;)
the three RTE types that you have are going to become five or six real
soon now, because I have other things to fix that need to be done that
way --- so the notational advantage of a union is going to increase.... you cannot ReScan a portal.
That's gonna have to be fixed. If you're not up for it, don't implement
this. Given that cursors (are supposed to) support FETCH BACKWARDS,
I really don't see why they shouldn't be expected to handle ReScan...
I thought only scrollable cursors can do that. What if cursor isn't
scrollable? Should it error during the execution?
For scrollable cursors, Rescan should be implemented as 'scroll backwards
until you can't scroll no more', correct?
-alex
Erm, forgot to attach the patch. Here it is.
On Mon, 2 Jul 2001, Alex Pilosov wrote:
Show quoted text
They just have things declared as RangeTblEntry *, and as long as they
don't access type-specific fields, they are fine.shown us the proposed changes, I really suspect that a union would be
cleaner, because it'd avoid ugliness in those places. Bear in mind thatI have attached the diff of what I have now. Please take a look through
it. Its not exactly done (only the parser and pieces of executor parts of
"FROM CURSOR" are there, and I didn't attach gram.y diff because its just
too ugly now), so its definitely not ready for application, but with these
changes, it compiles and 'make check' goes through fine ;)
Attachments:
back.difftext/plain; charset=US-ASCII; name=back.diffDownload
Index: backend/commands/explain.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.65
diff --unified -r1.65 explain.c
--- backend/commands/explain.c 2001/03/22 03:59:22 1.65
+++ backend/commands/explain.c 2001/06/30 23:21:14
@@ -261,11 +261,12 @@
case T_TidScan:
if (((Scan *) plan)->scanrelid > 0)
{
- RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
+ RangeTblEntryRelation *rte = (RangeTblEntryRelation *)
+ rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
/* Assume it's on a real relation */
- Assert(rte->relname);
+ Assert( IsA(rte, RangeTblEntryRelation) );
appendStringInfo(str, " on %s",
stringStringInfo(rte->relname));
@@ -355,11 +356,14 @@
{
SubqueryScan *subqueryscan = (SubqueryScan *) plan;
Plan *subnode = subqueryscan->subplan;
- RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
- es->rtable);
+
+ RangeTblEntrySubSelect *rte = (RangeTblEntrySubSelect *)
+ rt_fetch(subqueryscan->scan.scanrelid,
+ es->rtable);
+
List *saved_rtable = es->rtable;
- Assert(rte->subquery != NULL);
+ Assert(IsA(rte, RangeTblEntrySubSelect));
es->rtable = rte->subquery->rtable;
for (i = 0; i < indent; i++)
Index: backend/executor/execMain.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.143
diff --unified -r1.143 execMain.c
--- backend/executor/execMain.c 2001/06/01 02:41:35 1.143
+++ backend/executor/execMain.c 2001/06/30 23:25:35
@@ -341,11 +341,12 @@
case T_SubqueryScan:
{
SubqueryScan *scan = (SubqueryScan *) plan;
- RangeTblEntry *rte;
+ RangeTblEntrySubSelect *rte;
/* Recursively check the subquery */
- rte = rt_fetch(scan->scan.scanrelid, rangeTable);
- Assert(rte->subquery != NULL);
+ rte = (RangeTblEntrySubSelect *)
+ rt_fetch(scan->scan.scanrelid, rangeTable);
+ Assert(IsA(rte, RangeTblEntrySubSelect));
ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
break;
}
@@ -400,10 +401,10 @@
* If it's a subquery RTE, ignore it --- it will be checked when
* ExecCheckPlanPerms finds the SubqueryScan node for it.
*/
- if (rte->subquery)
- return;
+ if (!IsA(rte, RangeTblEntryRelation)) return;
- relName = rte->relname;
+
+ relName = ((RangeTblEntryRelation *)rte)->relname;
/*
* userid to check as: current user unless we have a setuid
Index: backend/executor/nodeIndexscan.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/executor/nodeIndexscan.c,v
retrieving revision 1.60
diff --unified -r1.60 nodeIndexscan.c
--- backend/executor/nodeIndexscan.c 2001/05/27 20:42:18 1.60
+++ backend/executor/nodeIndexscan.c 2001/06/30 23:27:09
@@ -588,7 +588,7 @@
int **runtimeKeyInfo;
bool have_runtime_keys;
List *rangeTable;
- RangeTblEntry *rtentry;
+ RangeTblEntryRelation *rtentry;
Index relid;
Oid reloid;
Relation currentRelation;
@@ -987,7 +987,9 @@
* open the base relation
*/
relid = node->scan.scanrelid;
- rtentry = rt_fetch(relid, rangeTable);
+ rtentry = (RangeTblEntryRelation *)rt_fetch(relid, rangeTable);
+ Assert(IsA(rtentry, RangeTblEntryRelation));
+
reloid = rtentry->relid;
ExecOpenScanR(reloid, /* relation */
Index: backend/executor/nodeSeqscan.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/executor/nodeSeqscan.c,v
retrieving revision 1.30
diff --unified -r1.30 nodeSeqscan.c
--- backend/executor/nodeSeqscan.c 2001/05/27 20:42:19 1.30
+++ backend/executor/nodeSeqscan.c 2001/06/30 23:28:09
@@ -147,7 +147,7 @@
{
Index relid;
List *rangeTable;
- RangeTblEntry *rtentry;
+ RangeTblEntryRelation *rtentry;
Oid reloid;
ScanDirection direction;
Relation currentRelation;
@@ -159,7 +159,10 @@
*/
relid = node->scanrelid;
rangeTable = estate->es_range_table;
- rtentry = rt_fetch(relid, rangeTable);
+
+ rtentry = (RangeTblEntryRelation *)rt_fetch(relid, rangeTable);
+ Assert(IsA(rtentry, RangeTblEntryRelation));
+
reloid = rtentry->relid;
direction = estate->es_direction;
Index: backend/executor/nodeSubqueryscan.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/executor/nodeSubqueryscan.c,v
retrieving revision 1.9
diff --unified -r1.9 nodeSubqueryscan.c
--- backend/executor/nodeSubqueryscan.c 2001/05/27 20:42:20 1.9
+++ backend/executor/nodeSubqueryscan.c 2001/06/30 23:32:40
@@ -107,7 +107,7 @@
ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
{
SubqueryScanState *subquerystate;
- RangeTblEntry *rte;
+ RangeTblEntrySubSelect *rte;
EState *sp_estate;
/*
@@ -146,8 +146,10 @@
*
* This should agree with ExecInitSubPlan
*/
- rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
- Assert(rte->subquery != NULL);
+ rte = (RangeTblEntrySubSelect *)rt_fetch(
+ node->scan.scanrelid,
+ estate->es_range_table);
+ Assert(IsA(rte, RangeTblEntrySubSelect));
sp_estate = CreateExecutorState();
subquerystate->sss_SubEState = sp_estate;
Index: backend/executor/nodeTidscan.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/executor/nodeTidscan.c,v
retrieving revision 1.17
diff --unified -r1.17 nodeTidscan.c
--- backend/executor/nodeTidscan.c 2001/05/27 20:42:20 1.17
+++ backend/executor/nodeTidscan.c 2001/06/30 23:31:30
@@ -385,7 +385,7 @@
int numTids;
int tidPtr;
List *rangeTable;
- RangeTblEntry *rtentry;
+ RangeTblEntryRelation *rtentry;
Oid relid;
Oid reloid;
Relation currentRelation;
@@ -458,9 +458,11 @@
* open the base relation
*/
relid = node->scan.scanrelid;
- rtentry = rt_fetch(relid, rangeTable);
- reloid = rtentry->relid;
+ rtentry = (RangeTblEntryRelation *)rt_fetch(relid, rangeTable);
+ Assert(IsA(rtentry, RangeTblEntryRelation));
+
+ reloid = rtentry->relid;
currentRelation = heap_open(reloid, AccessShareLock);
scanstate->css_currentRelation = currentRelation;
scanstate->css_currentScanDesc = 0;
Index: backend/nodes/copyfuncs.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.144
diff --unified -r1.144 copyfuncs.c
--- backend/nodes/copyfuncs.c 2001/06/09 23:21:54 1.144
+++ backend/nodes/copyfuncs.c 2001/06/30 23:56:14
@@ -1448,15 +1448,8 @@
return newnode;
}
-static RangeTblEntry *
-_copyRangeTblEntry(RangeTblEntry *from)
-{
- RangeTblEntry *newnode = makeNode(RangeTblEntry);
-
- if (from->relname)
- newnode->relname = pstrdup(from->relname);
- newnode->relid = from->relid;
- Node_Copy(from, newnode, subquery);
+static void copy_rte_generic(RangeTblEntry *from, RangeTblEntry *newnode) {
+/* copy generic fields */
Node_Copy(from, newnode, alias);
Node_Copy(from, newnode, eref);
newnode->inh = from->inh;
@@ -1464,10 +1457,41 @@
newnode->checkForRead = from->checkForRead;
newnode->checkForWrite = from->checkForWrite;
newnode->checkAsUser = from->checkAsUser;
+}
+static RangeTblEntryRelation *
+_copyRangeTblEntryRelation(RangeTblEntryRelation *from)
+{
+ RangeTblEntryRelation *newnode = makeNode(RangeTblEntryRelation);
+ copy_rte_generic( (RangeTblEntry *) from, (RangeTblEntry *) newnode);
+ newnode->relname = pstrdup(from->relname);
+ newnode->relid = from->relid;
return newnode;
}
+static RangeTblEntrySubSelect *
+_copyRangeTblEntrySubSelect(RangeTblEntrySubSelect *from)
+{
+ RangeTblEntrySubSelect *newnode = makeNode(RangeTblEntrySubSelect);
+ copy_rte_generic( (RangeTblEntry *) from, (RangeTblEntry *) newnode);
+ Node_Copy(from, newnode, subquery);
+ return newnode;
+}
+
+static RangeTblEntryPortal *
+_copyRangeTblEntryPortal(RangeTblEntryPortal *from)
+{
+ RangeTblEntryPortal *newnode = makeNode(RangeTblEntryPortal);
+ copy_rte_generic( (RangeTblEntry *) from, (RangeTblEntry *) newnode);
+
+/* XXX: can't deep copy TupleDesc, this may be a major problem! */
+ newnode->tupleDesc = palloc (sizeof(TupleDesc));
+ memcpy( newnode->tupleDesc, from->tupleDesc, sizeof(TupleDesc));
+
+ newnode->portal = pstrdup(from->portal);
+ return newnode;
+}
+
static FkConstraint *
_copyFkConstraint(FkConstraint *from)
{
@@ -2938,9 +2962,15 @@
break;
case T_TargetEntry:
retval = _copyTargetEntry(from);
+ break;
+ case T_RangeTblEntryRelation:
+ retval = _copyRangeTblEntryRelation(from);
+ break;
+ case T_RangeTblEntryPortal:
+ retval = _copyRangeTblEntryPortal(from);
break;
- case T_RangeTblEntry:
- retval = _copyRangeTblEntry(from);
+ case T_RangeTblEntrySubSelect:
+ retval = _copyRangeTblEntrySubSelect(from);
break;
case T_SortClause:
retval = _copySortClause(from);
Index: backend/nodes/equalfuncs.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.92
diff --unified -r1.92 equalfuncs.c
--- backend/nodes/equalfuncs.c 2001/06/09 23:21:54 1.92
+++ backend/nodes/equalfuncs.c 2001/07/01 00:03:14
@@ -1626,14 +1626,8 @@
}
static bool
-_equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
+_equalRangeTblEntryGeneric(RangeTblEntry *a, RangeTblEntry *b)
{
- if (!equalstr(a->relname, b->relname))
- return false;
- if (a->relid != b->relid)
- return false;
- if (!equal(a->subquery, b->subquery))
- return false;
if (!equal(a->alias, b->alias))
return false;
if (!equal(a->eref, b->eref))
@@ -1648,11 +1642,45 @@
return false;
if (a->checkAsUser != b->checkAsUser)
return false;
+ return true;
+}
+static bool
+_equalRangeTblEntryRelation(RangeTblEntryRelation *a,
+ RangeTblEntryRelation *b)
+{
+ if (!_equalRangeTblEntryGeneric( (RangeTblEntry *)a, (RangeTblEntry *) b))
+ return false;
+ if (!equalstr(a->relname, b->relname))
+ return false;
+ if (a->relid != b->relid)
+ return false;
return true;
}
static bool
+_equalRangeTblEntrySubSelect(RangeTblEntrySubSelect *a,
+ RangeTblEntrySubSelect *b)
+{
+ if (!_equalRangeTblEntryGeneric( (RangeTblEntry *)a, (RangeTblEntry *) b))
+ return false;
+ if (!equal(a->subquery, b->subquery))
+ return false;
+ return true;
+}
+
+static bool
+_equalRangeTblEntryPortal( RangeTblEntryPortal *a,
+ RangeTblEntryPortal *b)
+{
+ if (!_equalRangeTblEntryGeneric( (RangeTblEntry *)a, (RangeTblEntry *) b))
+ return false;
+ if (!equalstr(a->portal, b->portal))
+ return false;
+ return true;
+}
+
+static bool
_equalSortClause(SortClause *a, SortClause *b)
{
if (a->tleSortGroupRef != b->tleSortGroupRef)
@@ -2103,9 +2131,15 @@
break;
case T_TargetEntry:
retval = _equalTargetEntry(a, b);
+ break;
+ case T_RangeTblEntryRelation:
+ retval = _equalRangeTblEntryRelation(a, b);
+ break;
+ case T_RangeTblEntryPortal:
+ retval = _equalRangeTblEntryPortal(a, b);
break;
- case T_RangeTblEntry:
- retval = _equalRangeTblEntry(a, b);
+ case T_RangeTblEntrySubSelect:
+ retval = _equalRangeTblEntrySubSelect(a, b);
break;
case T_SortClause:
retval = _equalSortClause(a, b);
Index: backend/nodes/outfuncs.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.141
diff --unified -r1.141 outfuncs.c
--- backend/nodes/outfuncs.c 2001/05/20 20:28:18 1.141
+++ backend/nodes/outfuncs.c 2001/07/01 00:18:51
@@ -966,14 +966,8 @@
}
static void
-_outRangeTblEntry(StringInfo str, RangeTblEntry *node)
+_outRangeTblEntryGeneric(StringInfo str, RangeTblEntry *node)
{
- appendStringInfo(str, " RTE :relname ");
- _outToken(str, node->relname);
- appendStringInfo(str, " :relid %u ",
- node->relid);
- appendStringInfo(str, " :subquery ");
- _outNode(str, node->subquery);
appendStringInfo(str, " :alias ");
_outNode(str, node->alias);
appendStringInfo(str, " :eref ");
@@ -987,6 +981,35 @@
node->checkAsUser);
}
+/* we put generic attributes last */
+
+static void
+_outRangeTblEntryRelation(StringInfo str, RangeTblEntryRelation *node)
+{
+ appendStringInfo(str, " RTEREL :relname ");
+ _outToken(str, node->relname);
+ appendStringInfo(str, " :relid %u ",
+ node->relid);
+ _outRangeTblEntryGeneric(str, (RangeTblEntry *) node);
+}
+
+static void
+_outRangeTblEntrySubSelect(StringInfo str, RangeTblEntrySubSelect *node)
+{
+ appendStringInfo(str, " RTESUBSELECT :subquery ");
+ _outNode(str, node->subquery);
+ _outRangeTblEntryGeneric(str, (RangeTblEntry *) node);
+}
+
+static void
+_outRangeTblEntryPortal(StringInfo str, RangeTblEntryPortal *node)
+{
+ appendStringInfo(str, " RTEPORTAL :portal ");
+ _outToken(str, node->portal);
+ _outRangeTblEntryGeneric(str, (RangeTblEntry *) node);
+}
+
+
/*
* Path is a subclass of Node.
*/
@@ -1581,9 +1604,15 @@
break;
case T_TargetEntry:
_outTargetEntry(str, obj);
+ break;
+ case T_RangeTblEntryRelation:
+ _outRangeTblEntryRelation(str, obj);
+ break;
+ case T_RangeTblEntrySubSelect:
+ _outRangeTblEntrySubSelect(str, obj);
break;
- case T_RangeTblEntry:
- _outRangeTblEntry(str, obj);
+ case T_RangeTblEntryPortal:
+ _outRangeTblEntryPortal(str, obj);
break;
case T_Path:
_outPath(str, obj);
Index: backend/nodes/print.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/nodes/print.c,v
retrieving revision 1.47
diff --unified -r1.47 print.c
--- backend/nodes/print.c 2001/03/22 03:59:32 1.47
+++ backend/nodes/print.c 2001/07/01 00:32:50
@@ -125,18 +125,27 @@
List *l;
int i = 1;
+ RangeTblEntryRelation * rte_rel;
+
printf("resno\trelname(refname)\trelid\tinFromCl\n");
printf("-----\t----------------\t-----\t--------\n");
foreach(l, rtable)
{
RangeTblEntry *rte = lfirst(l);
- if (rte->relname)
+ if (IsA(rte, RangeTblEntryRelation)) {
+ rte_rel = (RangeTblEntryRelation *) rte;
printf("%d\t%s (%s)\t%u",
- i, rte->relname, rte->eref->relname, rte->relid);
- else
+ i, rte_rel->relname, rte->eref->relname, rte_rel->relid);
+ }
+ else if (IsA(rte, RangeTblEntrySubSelect)) {
printf("%d\t[subquery] (%s)\t",
i, rte->eref->relname);
+ }
+ else if (IsA(rte, RangeTblEntryPortal)) {
+ printf("%d\t[portal] (%s)\t",
+ i, rte->eref->relname);
+ }
printf("\t%s\t%s\n",
(rte->inh ? "inh" : ""),
(rte->inFromCl ? "inFromCl" : ""));
@@ -176,11 +185,13 @@
break;
default:
{
- RangeTblEntry *rte;
+ RangeTblEntryRelation *rte;
Assert(var->varno > 0 &&
(int) var->varno <= length(rtable));
- rte = rt_fetch(var->varno, rtable);
+/* apilosov: this can be wrong XXX */
+ rte = (RangeTblEntryRelation *)
+ rt_fetch(var->varno, rtable);
relname = rte->eref->relname;
attname = get_rte_attribute_name(rte, var->varattno);
}
@@ -357,16 +368,19 @@
p->plan_rows, p->plan_width);
if (IsA(p, Scan) ||IsA(p, SeqScan))
{
- RangeTblEntry *rte;
+ RangeTblEntryRelation *rte;
- rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
+ rte = (RangeTblEntryRelation *)
+ rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
+ Assert( IsA(rte, RangeTblEntryRelation));
StrNCpy(extraInfo, rte->relname, NAMEDATALEN);
}
else if (IsA(p, IndexScan))
{
- RangeTblEntry *rte;
+ RangeTblEntryRelation *rte;
- rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
+ rte = (RangeTblEntryRelation *)
+ rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
StrNCpy(extraInfo, rte->relname, NAMEDATALEN);
}
else
Index: backend/nodes/readfuncs.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/nodes/readfuncs.c,v
retrieving revision 1.110
diff --unified -r1.110 readfuncs.c
--- backend/nodes/readfuncs.c 2001/06/05 05:26:04 1.110
+++ backend/nodes/readfuncs.c 2001/07/01 00:20:36
@@ -1364,25 +1364,11 @@
* ----------------
*/
static RangeTblEntry *
-_readRangeTblEntry(void)
+_readRangeTblEntryGeneric(RangeTblEntry *local_node)
{
- RangeTblEntry *local_node;
char *token;
int length;
- local_node = makeNode(RangeTblEntry);
-
- token = pg_strtok(&length); /* eat :relname */
- token = pg_strtok(&length); /* get :relname */
- local_node->relname = nullable_string(token, length);
-
- token = pg_strtok(&length); /* eat :relid */
- token = pg_strtok(&length); /* get :relid */
- local_node->relid = atooid(token);
-
- token = pg_strtok(&length); /* eat :subquery */
- local_node->subquery = nodeRead(true); /* now read it */
-
token = pg_strtok(&length); /* eat :alias */
local_node->alias = nodeRead(true); /* now read it */
@@ -1412,6 +1398,50 @@
return local_node;
}
+static RangeTblEntryRelation *
+_readRangeTblEntryRelation(void)
+{
+ RangeTblEntryRelation *local_node;
+ char *token;
+ int length;
+
+ local_node = makeNode(RangeTblEntryRelation);
+
+ token = pg_strtok(&length); /* eat :relname */
+ token = pg_strtok(&length); /* get :relname */
+ local_node->relname = nullable_string(token, length);
+
+ token = pg_strtok(&length); /* eat :relid */
+ token = pg_strtok(&length); /* get :relid */
+ local_node->relid = atooid(token);
+ _readRangeTblEntryGeneric( (RangeTblEntry *)local_node);
+ return local_node;
+}
+
+static RangeTblEntrySubSelect *
+_readRangeTblEntrySubSelect(void)
+{
+ RangeTblEntrySubSelect *local_node;
+ char *token;
+ int length;
+
+ local_node = makeNode(RangeTblEntrySubSelect);
+
+ token = pg_strtok(&length); /* eat :subquery */
+ local_node->subquery = nodeRead(true); /* now read it */
+
+ _readRangeTblEntryGeneric( (RangeTblEntry *)local_node);
+
+ return local_node;
+}
+
+static RangeTblEntryPortal *
+_readRangeTblEntryPortal(void)
+{
+ elog(ERROR, "Cannot read RangeTblEntryPortal yet");
+ return NULL;
+}
+
/* ----------------
* _readPath
*
@@ -1930,8 +1960,12 @@
return_value = _readParam();
else if (length == 11 && strncmp(token, "TARGETENTRY", length) == 0)
return_value = _readTargetEntry();
- else if (length == 3 && strncmp(token, "RTE", length) == 0)
- return_value = _readRangeTblEntry();
+ else if (length == 6 && strncmp(token, "RTEREL", length) == 0)
+ return_value = _readRangeTblEntryRelation();
+ else if (length == 12 && strncmp(token, "RTESUBSELECT", length) == 0)
+ return_value = _readRangeTblEntrySubSelect();
+ else if (length == 9 && strncmp(token, "RTEPORTAL", length) == 0)
+ return_value = _readRangeTblEntryPortal();
else if (length == 4 && strncmp(token, "PATH", length) == 0)
return_value = _readPath();
else if (length == 9 && strncmp(token, "INDEXPATH", length) == 0)
Index: backend/optimizer/path/allpaths.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/optimizer/path/allpaths.c,v
retrieving revision 1.76
diff --unified -r1.76 allpaths.c
--- backend/optimizer/path/allpaths.c 2001/06/05 17:13:51 1.76
+++ backend/optimizer/path/allpaths.c 2001/07/01 01:53:30
@@ -33,9 +33,9 @@
static void set_base_rel_pathlists(Query *root);
static void set_plain_rel_pathlist(Query *root, RelOptInfo *rel,
- RangeTblEntry *rte);
+ RangeTblEntryRelation *rte);
static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
- Index rti, RangeTblEntry *rte,
+ Index rti, RangeTblEntryRelation *rte,
List *inheritlist);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels);
@@ -100,6 +100,8 @@
if (rel->issubquery)
{
+ RangeTblEntrySubSelect *rte_sel = (RangeTblEntrySubSelect *)rte;
+ Assert(IsA(rte, RangeTblEntrySubSelect));
/* Subquery --- generate a separate plan for it */
/*
@@ -129,7 +131,7 @@
* decision not to push down, because it'd result in a worse
* plan?
*/
- if (rte->subquery->setOperations == NULL)
+ if (rte_sel->subquery->setOperations == NULL)
{
/* OK to consider pushing down individual quals */
List *upperrestrictlist = NIL;
@@ -147,7 +149,6 @@
}
else
{
-
/*
* We need to replace Vars in the clause (which
* must refer to outputs of the subquery) with
@@ -157,10 +158,10 @@
* replaced with Params, so they need no work.
*/
clause = ResolveNew(clause, rti, 0,
- rte->subquery->targetList,
+ rte_sel->subquery->targetList,
CMD_SELECT, 0);
- rte->subquery->havingQual =
- make_and_qual(rte->subquery->havingQual,
+ rte_sel->subquery->havingQual =
+ make_and_qual(rte_sel->subquery->havingQual,
clause);
/*
@@ -175,7 +176,7 @@
}
/* Generate the plan for the subquery */
- rel->subplan = subquery_planner(rte->subquery,
+ rel->subplan = subquery_planner(rte_sel->subquery,
-1.0 /* default case */ );
/* Copy number of output rows from subplan */
@@ -194,12 +195,15 @@
!= NIL)
{
/* Relation is root of an inheritance tree, process specially */
- set_inherited_rel_pathlist(root, rel, rti, rte, inheritlist);
+ set_inherited_rel_pathlist(root, rel, rti,
+ (RangeTblEntryRelation *)rte,
+ inheritlist);
}
else
{
/* Plain relation */
- set_plain_rel_pathlist(root, rel, rte);
+ Assert( IsA( rte, RangeTblEntryRelation ) );
+ set_plain_rel_pathlist(root, rel, (RangeTblEntryRelation *) rte);
}
}
}
@@ -209,7 +213,7 @@
* Build access paths for a plain relation (no subquery, no inheritance)
*/
static void
-set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
+set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntryRelation *rte)
{
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
@@ -259,7 +263,7 @@
*/
static void
set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
- Index rti, RangeTblEntry *rte,
+ Index rti, RangeTblEntryRelation *rte,
List *inheritlist)
{
int parentRTindex = rti;
@@ -295,11 +299,15 @@
foreach(il, inheritlist)
{
int childRTindex = lfirsti(il);
- RangeTblEntry *childrte;
+ RangeTblEntryRelation *childrte;
Oid childOID;
RelOptInfo *childrel;
+
+ childrte = (RangeTblEntryRelation *)
+ rt_fetch(childRTindex, root->rtable);
+
+ Assert(IsA(childrte, RangeTblEntryRelation));
- childrte = rt_fetch(childRTindex, root->rtable);
childOID = childrte->relid;
/*
Index: backend/optimizer/path/clausesel.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/optimizer/path/clausesel.c,v
retrieving revision 1.45
diff --unified -r1.45 clausesel.c
--- backend/optimizer/path/clausesel.c 2001/06/05 05:26:04 1.45
+++ backend/optimizer/path/clausesel.c 2001/07/01 00:44:28
@@ -383,7 +383,7 @@
{
RangeTblEntry *rte = rt_fetch(var->varno, root->rtable);
- if (rte->subquery)
+ if (IsA(rte, RangeTblEntrySubSelect))
{
/*
Index: backend/optimizer/plan/planner.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/optimizer/plan/planner.c,v
retrieving revision 1.108
diff --unified -r1.108 planner.c
--- backend/optimizer/plan/planner.c 2001/06/05 05:26:04 1.108
+++ backend/optimizer/plan/planner.c 2001/07/01 01:49:49
@@ -267,8 +267,10 @@
if (IsA(jtnode, RangeTblRef))
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- RangeTblEntry *rte = rt_fetch(varno, parse->rtable);
+ RangeTblEntrySubSelect *rte = (RangeTblEntrySubSelect *)
+ rt_fetch(varno, parse->rtable);
Query *subquery = rte->subquery;
+
/*
* Is this a subquery RTE, and if so, is the subquery simple
@@ -277,7 +279,8 @@
* Note: even if the subquery itself is simple enough, we can't
* pull it up if there is a reference to its whole tuple result.
*/
- if (subquery && is_simple_subquery(subquery) &&
+ if (IsA( rte, RangeTblEntrySubSelect ) &&
+ is_simple_subquery(subquery) &&
!contain_whole_tuple_var((Node *) parse, varno, 0))
{
int rtoffset;
Index: backend/optimizer/prep/preptlist.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/optimizer/prep/preptlist.c,v
retrieving revision 1.42
diff --unified -r1.42 preptlist.c
--- backend/optimizer/prep/preptlist.c 2001/03/22 03:59:38 1.42
+++ backend/optimizer/prep/preptlist.c 2001/07/01 00:48:06
@@ -58,7 +58,7 @@
{
RangeTblEntry *rte = rt_fetch(result_relation, range_table);
- if (rte->subquery != NULL || rte->relid == InvalidOid)
+ if (IsA(rte, RangeTblEntrySubSelect))
elog(ERROR, "preprocess_targetlist: subquery cannot be result relation");
}
Index: backend/optimizer/prep/prepunion.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/optimizer/prep/prepunion.c,v
retrieving revision 1.65
diff --unified -r1.65 prepunion.c
--- backend/optimizer/prep/prepunion.c 2001/06/05 05:26:04 1.65
+++ backend/optimizer/prep/prepunion.c 2001/07/01 00:55:59
@@ -82,6 +82,7 @@
{
SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations;
Node *node;
+ RangeTblEntrySubSelect * rte;
Query *leftmostQuery;
Assert(topop && IsA(topop, SetOperationStmt));
@@ -94,8 +95,12 @@
while (node && IsA(node, SetOperationStmt))
node = ((SetOperationStmt *) node)->larg;
Assert(node && IsA(node, RangeTblRef));
- leftmostQuery = rt_fetch(((RangeTblRef *) node)->rtindex,
- parse->rtable)->subquery;
+ rte = (RangeTblEntrySubSelect *)
+ rt_fetch(((RangeTblRef *) node)->rtindex, parse->rtable);
+ Assert(rte && IsA(rte, RangeTblEntrySubSelect));
+
+ leftmostQuery = rte->subquery;
+
Assert(leftmostQuery != NULL);
/*
@@ -126,11 +131,13 @@
if (IsA(setOp, RangeTblRef))
{
RangeTblRef *rtr = (RangeTblRef *) setOp;
- RangeTblEntry *rte = rt_fetch(rtr->rtindex, parse->rtable);
+ RangeTblEntrySubSelect *rte = (RangeTblEntrySubSelect *)
+ rt_fetch(rtr->rtindex, parse->rtable);
Query *subquery = rte->subquery;
Plan *subplan,
*plan;
+ Assert(IsA(rte, RangeTblEntrySubSelect));
Assert(subquery != NULL);
/*
@@ -532,16 +539,17 @@
List *
expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
{
- RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
+ RangeTblEntryRelation *rte = (RangeTblEntryRelation *) rt_fetch(rti, parse->rtable);
Oid parentOID = rte->relid;
List *inhOIDs;
List *inhRTIs;
List *l;
+ Assert(IsA(rte, RangeTblEntryRelation));
/* Does RT entry allow inheritance? */
if (!rte->inh)
return NIL;
- Assert(parentOID != InvalidOid && rte->subquery == NULL);
+
/* Always clear the parent's inh flag, see above comments */
rte->inh = false;
/* Fast path for common case of childless table */
@@ -565,7 +573,7 @@
foreach(l, inhOIDs)
{
Oid childOID = (Oid) lfirsti(l);
- RangeTblEntry *childrte;
+ RangeTblEntryRelation *childrte;
Index childRTindex;
/* parent will be in the list too; skip it if not dup requested */
Index: backend/optimizer/util/clauses.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.85
diff --unified -r1.85 clauses.c
--- backend/optimizer/util/clauses.c 2001/05/20 20:28:19 1.85
+++ backend/optimizer/util/clauses.c 2001/07/01 13:36:49
@@ -1707,8 +1707,8 @@
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
- if (rte->subquery)
- if (walker(rte->subquery, context))
+ if (IsA(rte, RangeTblEntrySubSelect))
+ if (walker( ((RangeTblEntrySubSelect *)rte)->subquery, context))
return true;
}
}
@@ -2068,13 +2068,13 @@
foreach(rt, query->rtable)
{
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+ RangeTblEntrySubSelect *rte = (RangeTblEntrySubSelect *) lfirst(rt);
- if (rte->subquery)
+ if (IsA(rte, RangeTblEntrySubSelect))
{
- RangeTblEntry *newrte;
+ RangeTblEntrySubSelect *newrte;
- FLATCOPY(newrte, rte, RangeTblEntry);
+ FLATCOPY(newrte, rte, RangeTblEntrySubSelect);
CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
MUTATE(newrte->subquery, newrte->subquery, Query *);
rte = newrte;
Index: backend/parser/analyze.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.189
diff --unified -r1.189 analyze.c
--- backend/parser/analyze.c 2001/06/04 23:27:23 1.189
+++ backend/parser/analyze.c 2001/06/30 22:18:39
@@ -1993,6 +1993,7 @@
Query *qry = makeNode(Query);
SelectStmt *leftmostSelect;
int leftmostRTI;
+ RangeTblEntrySubSelect * leftmostRTE;
Query *leftmostQuery;
SetOperationStmt *sostmt;
char *into;
@@ -2068,7 +2069,10 @@
node = ((SetOperationStmt *) node)->larg;
Assert(node && IsA(node, RangeTblRef));
leftmostRTI = ((RangeTblRef *) node)->rtindex;
- leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;
+ leftmostRTE = (RangeTblEntrySubSelect *) rt_fetch(leftmostRTI, pstate->p_rtable);
+ Assert(node && IsA(leftmostRTE, RangeTblEntrySubSelect));
+ leftmostQuery = leftmostRTE->subquery;
+
Assert(leftmostQuery != NULL);
/*
@@ -2337,7 +2341,7 @@
if (IsA(node, RangeTblRef))
{
RangeTblRef *rtr = (RangeTblRef *) node;
- RangeTblEntry *rte = rt_fetch(rtr->rtindex, pstate->p_rtable);
+ RangeTblEntrySubSelect *rte = (RangeTblEntrySubSelect *) rt_fetch(rtr->rtindex, pstate->p_rtable);
Query *selectQuery = rte->subquery;
List *result = NIL;
List *tl;
@@ -2850,17 +2854,25 @@
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
++i;
- if (rte->subquery)
+ if (IsA(rte, RangeTblEntrySubSelect))
{
/* FOR UPDATE of subquery is propagated to subquery's rels */
- transformForUpdate(rte->subquery, makeList1(NULL));
+ transformForUpdate( ((RangeTblEntrySubSelect *)rte)->subquery, makeList1(NULL));
}
- else
+ else if (IsA(rte, RangeTblEntryRelation))
{
if (!intMember(i, rowMarks)) /* avoid duplicates */
rowMarks = lappendi(rowMarks, i);
rte->checkForWrite = true;
}
+ else if (IsA(rte, RangeTblEntryPortal))
+ {
+ /* do nothing */
+ }
+ else
+ {
+ elog(ERROR, "FOR UPDATE: unknown RTE type");
+ }
}
}
else
@@ -2878,16 +2890,24 @@
++i;
if (strcmp(rte->eref->relname, relname) == 0)
{
- if (rte->subquery)
+ if (IsA(rte, RangeTblEntrySubSelect))
{
/* propagate to subquery */
- transformForUpdate(rte->subquery, makeList1(NULL));
+ transformForUpdate( ((RangeTblEntrySubSelect*) rte)->subquery, makeList1(NULL));
}
- else
+ else if (IsA(rte, RangeTblEntryRelation))
{
if (!intMember(i, rowMarks)) /* avoid duplicates */
rowMarks = lappendi(rowMarks, i);
rte->checkForWrite = true;
+ }
+ else if (IsA(rte, RangeTblEntryPortal))
+ {
+ /* do nothing */
+ }
+ else
+ {
+ elog(ERROR, "FOR UPDATE: unknown RTE type");
}
break;
}
Index: backend/parser/parse_clause.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/parser/parse_clause.c,v
retrieving revision 1.80
diff --unified -r1.80 parse_clause.c
--- backend/parser/parse_clause.c 2001/05/18 21:24:19 1.80
+++ backend/parser/parse_clause.c 2001/06/30 21:12:12
@@ -30,6 +30,7 @@
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "utils/guc.h"
+#include "utils/portal.h"
#define ORDER_CLAUSE 0
@@ -79,6 +80,7 @@
/*
* The grammar will have produced a list of RangeVars,
+ * FuncRangeVars, PortalRangeVars,
* RangeSubselects, and/or JoinExprs. Transform each one (possibly
* adding entries to the rtable), check for duplicate refnames, and
* then add it to the joinlist and namespace.
@@ -394,6 +396,52 @@
/*
+ * transformPortalRange --- transform FROM CURSOR FOO
+ */
+static RangeTblRef *
+transformPortalRange(ParseState *pstate, PortalRangeVar *r)
+{
+ RangeTblEntry *rte;
+ RangeTblRef *rtr;
+ Portal portal;
+ QueryDesc *queryDesc;
+ TupleDesc tupleDesc;
+ EState *estate;
+ char *portalname;
+
+ /* look up information for the portal */
+ portalname = r->portal;
+ portal = GetPortalByName(portalname);
+ if (!PortalIsValid(portal))
+ elog(NOTICE, "transformPortalRange: portal \"%s\" not found",
+ portalname);
+
+ queryDesc = PortalGetQueryDesc(portal);
+ tupleDesc = PortalGetTupleDesc(portal);
+ estate = PortalGetState(portal); /* XXX: check state ? */
+
+ if (queryDesc->operation != CMD_SELECT)
+ elog(ERROR, "Expected SELECT query from the portal %s", portalname);
+
+ /*
+ * OK, build an RTE for the subquery.
+ */
+ rte = addRangeTableEntryForPortal(pstate, tupleDesc, portalname, r->name);
+
+ /*
+ * We create a RangeTblRef, but we do not add it to the joinlist or
+ * namespace; our caller must do that if appropriate.
+ */
+ 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 rtr;
+}
+
+
+/*
* transformRangeSubselect --- transform a sub-SELECT appearing in FROM
*/
static RangeTblRef *
@@ -482,6 +530,15 @@
RangeTblRef *rtr;
rtr = transformTableEntry(pstate, (RangeVar *) n);
+ *containedRels = makeListi1(rtr->rtindex);
+ return (Node *) rtr;
+ }
+ if (IsA(n, PortalRangeVar))
+ {
+ /* reference to cursor */
+ RangeTblRef *rtr;
+
+ rtr = transformPortalRange(pstate, (PortalRangeVar *) n);
*containedRels = makeListi1(rtr->rtindex);
return (Node *) rtr;
}
Index: backend/parser/parse_func.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/parser/parse_func.c,v
retrieving revision 1.108
diff --unified -r1.108 parse_func.c
--- backend/parser/parse_func.c 2001/05/19 01:57:11 1.108
+++ backend/parser/parse_func.c 2001/06/30 22:26:58
@@ -334,7 +334,7 @@
if (rteorjoin == NULL)
rte = addImplicitRTE(pstate, refname);
- else if (IsA(rteorjoin, RangeTblEntry))
+ else if (IsA_RTE(rteorjoin))
rte = (RangeTblEntry *) rteorjoin;
else if (IsA(rteorjoin, JoinExpr))
{
@@ -385,7 +385,14 @@
* signal that the runtime representation will be a pointer
* not an Oid.
*/
- if (rte->relname == NULL)
+ if (IsA(rte, RangeTblEntryPortal))
+ {
+ /* RTE is a portal reference, possible? not supported yet */
+ elog(ERROR, "Cannot pass tuple from portal %s to function %s",
+ refname, funcname);
+ }
+
+ if (IsA(rte, RangeTblEntrySubSelect))
{
/*
* RTE is a subselect; must fail for lack of a specific type
@@ -406,7 +413,7 @@
}
}
- toid = typenameTypeId(rte->relname);
+ toid = typenameTypeId( ((RangeTblEntryRelation *)rte)->relname);
/* replace it in the arg list */
lfirst(i) = makeVar(vnum,
Index: backend/parser/parse_node.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/parser/parse_node.c,v
retrieving revision 1.54
diff --unified -r1.54 parse_node.c
--- backend/parser/parse_node.c 2001/05/22 16:37:16 1.54
+++ backend/parser/parse_node.c 2001/06/30 22:42:29
@@ -170,31 +170,46 @@
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
- if (rte->relid != InvalidOid)
+ if (IsA(rte, RangeTblEntryRelation))
{
/* Plain relation RTE --- get the attribute's type info */
+ RangeTblEntryRelation * rte_rel=(RangeTblEntryRelation *)rte;
HeapTuple tp;
Form_pg_attribute att_tup;
tp = SearchSysCache(ATTNUM,
- ObjectIdGetDatum(rte->relid),
+ ObjectIdGetDatum(rte_rel->relid),
Int16GetDatum(attrno),
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
elog(ERROR, "Relation %s does not have attribute %d",
- rte->relname, attrno);
+ rte_rel->relname, attrno);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
vartypeid = att_tup->atttypid;
type_mod = att_tup->atttypmod;
ReleaseSysCache(tp);
+ }
+ else if (IsA(rte, RangeTblEntryPortal))
+ { /* Portal RTE, get type info from tupleDesc */
+ RangeTblEntryPortal * rte_portal=(RangeTblEntryPortal *)rte;
+ Form_pg_attribute att_tup;
+
+ Assert( rte_portal->tupleDesc != NULL );
+ Assert( attrno <= (rte_portal->tupleDesc->natts) );
+
+ att_tup = rte_portal->tupleDesc->attrs[attrno];
+
+ vartypeid = att_tup->atttypid;
+ type_mod = att_tup->atttypmod;
}
- else
+ else if (IsA(rte, RangeTblEntrySubSelect))
{
/* Subselect RTE --- get type info from subselect's tlist */
List *tlistitem;
+ RangeTblEntrySubSelect * rte_sel=(RangeTblEntrySubSelect *)rte;
- foreach(tlistitem, rte->subquery->targetList)
+ foreach(tlistitem, rte_sel->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
@@ -207,7 +222,11 @@
/* falling off end of list shouldn't happen... */
if (tlistitem == NIL)
elog(ERROR, "Subquery %s does not have attribute %d",
- rte->eref->relname, attrno);
+ rte_sel->eref->relname, attrno);
+ }
+ else
+ {
+ elog(ERROR, "make_var: Unknown RTE type");
}
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
Index: backend/parser/parse_relation.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/parser/parse_relation.c,v
retrieving revision 1.55
diff --unified -r1.55 parse_relation.c
--- backend/parser/parse_relation.c 2001/05/07 00:43:23 1.55
+++ backend/parser/parse_relation.c 2001/07/01 01:02:00
@@ -316,7 +316,7 @@
* If the RTE represents a table (not a sub-select), consider system
* column names.
*/
- if (rte->relid != InvalidOid)
+ if (IsA(rte, RangeTblEntryRelation))
{
attnum = specialAttNum(colname);
if (attnum != InvalidAttrNumber)
@@ -459,7 +459,7 @@
sublevels_up = 0;
}
- if (IsA(rteorjoin, RangeTblEntry))
+ if (IsA_RTE(rteorjoin))
result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin,
colname);
else if (IsA(rteorjoin, JoinExpr))
@@ -491,7 +491,7 @@
bool inh,
bool inFromCl)
{
- RangeTblEntry *rte = makeNode(RangeTblEntry);
+ RangeTblEntryRelation *rte = makeNode(RangeTblEntryRelation);
char *refname = alias ? alias->relname : relname;
LOCKMODE lockmode;
Relation rel;
@@ -502,7 +502,6 @@
rte->relname = relname;
rte->alias = alias;
- rte->subquery = NULL;
/*
* Get the rel's OID. This access also ensures that we have an
@@ -567,10 +566,77 @@
if (pstate != NULL)
pstate->p_rtable = lappend(pstate->p_rtable, rte);
- return rte;
+ return (RangeTblEntry *) rte;
}
/*
+ * Add an entry for a portal reference or for a function-used-as-table
+ * to the pstate's range table (p_rtable).
+ *
+ * This is just like addRangeTableEntry() except that it makes a portal RTE.
+ */
+RangeTblEntry *
+addRangeTableEntryForPortal(ParseState *pstate,
+ TupleDesc tupleDesc,
+ char *portalname,
+ Attr *alias
+ )
+{
+ RangeTblEntryPortal *rte = makeNode(RangeTblEntryPortal);
+ char *refname = alias ? alias->relname : portalname;
+ Attr *eref;
+ int maxattrs;
+ int numaliases;
+ int varattno;
+
+ rte->portal = portalname;
+ rte->alias = alias;
+ rte->tupleDesc = tupleDesc;
+
+ eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
+ numaliases = length(eref->attrs);
+
+ /* fill in any unspecified alias columns */
+ varattno = 0;
+
+ maxattrs = tupleDesc->natts;
+ if (maxattrs < numaliases)
+ elog(ERROR, "Portal \"%s\" has %d columns available but %d aliases specified",
+ refname, maxattrs, numaliases);
+
+ /* fill in any unspecified alias columns */
+ for (varattno = numaliases; varattno < maxattrs; varattno++)
+ {
+ char *attrname;
+
+ attrname = pstrdup(NameStr(tupleDesc->attrs[varattno]->attname));
+ eref->attrs = lappend(eref->attrs, makeString(attrname));
+ }
+ rte->eref = eref;
+
+ /*----------
+ * Flags:
+ * - this RTE should be expanded to include descendant tables,
+ * - this RTE is in the FROM clause,
+ * - this RTE should be checked for read/write access rights.
+ */
+ rte->inh = false;
+ rte->inFromCl = true;
+ rte->checkForRead = false;
+ rte->checkForWrite = false;
+ rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
+
+ /*
+ * Add completed RTE to pstate's range table list, but not to join
+ * list nor namespace --- caller must do that if appropriate.
+ */
+ if (pstate != NULL)
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ return (RangeTblEntry *) rte;
+}
+
+/*
* Add an entry for a subquery to the pstate's range table (p_rtable).
*
* This is just like addRangeTableEntry() except that it makes a subquery RTE.
@@ -582,15 +648,13 @@
Attr *alias,
bool inFromCl)
{
- RangeTblEntry *rte = makeNode(RangeTblEntry);
+ RangeTblEntrySubSelect *rte = makeNode(RangeTblEntrySubSelect);
char *refname = alias->relname;
Attr *eref;
int numaliases;
int varattno;
List *tlistitem;
- rte->relname = NULL;
- rte->relid = InvalidOid;
rte->subquery = subquery;
rte->alias = alias;
@@ -644,7 +708,7 @@
if (pstate != NULL)
pstate->p_rtable = lappend(pstate->p_rtable, rte);
- return rte;
+ return (RangeTblEntry *) rte;
}
/*
@@ -746,13 +810,50 @@
/* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
- if (rte->relname)
+ if (IsA(rte, RangeTblEntryPortal))
{
+ /* Portal RTE */
+ TupleDesc td;
+ int maxattrs;
+ td = ((RangeTblEntryPortal *) rte)->tupleDesc;
+
+ maxattrs = td->natts;
+
+ for (varattno = 0; varattno < maxattrs; varattno++)
+ {
+ Form_pg_attribute attr = td->attrs[varattno];
+
+ if (colnames)
+ {
+ char *label;
+
+ if (varattno < length(rte->eref->attrs))
+ label = strVal(nth(varattno, rte->eref->attrs));
+ else
+ label = NameStr(attr->attname);
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
+
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex, attr->attnum,
+ attr->atttypid, attr->atttypmod,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
+ }
+ if (IsA(rte, RangeTblEntryRelation))
+ {
/* Ordinary relation RTE */
Relation rel;
int maxattrs;
- rel = heap_openr(rte->relname, AccessShareLock);
+ rel = heap_openr( ( (RangeTblEntryRelation *) rte)->relname,
+ AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
@@ -760,11 +861,6 @@
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
-#ifdef _DROP_COLUMN_HACK__
- if (COLUMN_IS_DROPPED(attr))
- continue;
-#endif /* _DROP_COLUMN_HACK__ */
-
if (colnames)
{
char *label;
@@ -787,17 +883,15 @@
*colvars = lappend(*colvars, varnode);
}
}
-
- heap_close(rel, AccessShareLock);
}
- else
+ else if (IsA(rte, RangeTblEntrySubSelect))
{
/* Subquery RTE */
List *aliasp = rte->eref->attrs;
List *tlistitem;
varattno = 0;
- foreach(tlistitem, rte->subquery->targetList)
+ foreach(tlistitem,((RangeTblEntrySubSelect *)rte)->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
@@ -827,7 +921,11 @@
*colvars = lappend(*colvars, varnode);
}
}
- }
+ }
+ else
+ {
+ elog(ERROR, "expandRTE: Unknown RTE type");
+ }
}
/*
@@ -903,11 +1001,11 @@
/* ----------
* get_rte_attribute_name
- * Get an attribute name from a RangeTblEntry
+ * Get an attribute name from a RangeTblEntryRelation
*
* This is unlike get_attname() because we use aliases if available.
* In particular, it will work on an RTE for a subselect, whereas
- * get_attname() only works on real relations.
+ * get_attname() only works on real relations. ??? (below)
*
* "*" is returned if the given attnum is InvalidAttrNumber --- this case
* occurs when a Var represents a whole tuple of a relation.
@@ -916,12 +1014,14 @@
* not guaranteed unique, and may not even have scope across the whole
* query. Cleanest fix would be to add refname/attname to Var nodes and
* just print those, rather than indulging in this hack.
+ *
* ----------
*/
char *
get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
{
char *attname;
+ RangeTblEntryRelation *rte_rel;
if (attnum == InvalidAttrNumber)
return "*";
@@ -937,17 +1037,18 @@
* if alias name list is too short (which probably can't happen
* anymore). Neither of these cases is valid for a subselect RTE.
*/
- if (rte->relid == InvalidOid)
+ if (!IsA(rte, RangeTblEntryRelation))
elog(ERROR, "Invalid attnum %d for rangetable entry %s",
attnum, rte->eref->relname);
+ rte_rel = (RangeTblEntryRelation *) rte;
/*
* Use the real name of the table's column
*/
- attname = get_attname(rte->relid, attnum);
+ attname = get_attname(rte_rel->relid, attnum);
if (attname == NULL)
elog(ERROR, "cache lookup of attribute %d in relation %u failed",
- attnum, rte->relid);
+ attnum, rte_rel->relid);
return attname;
}
@@ -1075,3 +1176,22 @@
pstate->parentParseState != NULL ? " in subquery" : "",
refname);
}
+
+/*
+ * getrelid
+ *
+ * Given the range index of a relation, return the corresponding
+ * relation OID. Note that InvalidOid will be returned if the
+ * RTE is for not for a relation. (SubSelect or Portal)
+ */
+
+/* this used to be a #define */
+Oid getrelid (int rangeindex, List * rangetable) {
+ RangeTblEntry *rte = rt_fetch(rangeindex, rangetable);
+ if (IsA(rte, RangeTblEntryRelation)) {
+ return ((RangeTblEntryRelation *)rte)->relid;
+ } else {
+ return InvalidOid;
+ }
+}
+
Index: backend/parser/parse_target.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/parser/parse_target.c,v
retrieving revision 1.67
diff --unified -r1.67 parse_target.c
--- backend/parser/parse_target.c 2001/05/21 18:42:08 1.67
+++ backend/parser/parse_target.c 2001/06/30 22:55:45
@@ -131,7 +131,7 @@
sublevels_up = 0;
}
- if (IsA(rteorjoin, RangeTblEntry))
+ if (IsA_RTE(rteorjoin))
p_target = nconc(p_target,
expandRelAttrs(pstate,
(RangeTblEntry *) rteorjoin));
Index: backend/rewrite/rewriteDefine.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/rewrite/rewriteDefine.c,v
retrieving revision 1.62
diff --unified -r1.62 rewriteDefine.c
--- backend/rewrite/rewriteDefine.c 2001/05/03 21:16:48 1.62
+++ backend/rewrite/rewriteDefine.c 2001/07/01 01:06:41
@@ -445,10 +445,11 @@
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
- if (rte->subquery)
+ if ( IsA(rte, RangeTblEntrySubSelect) )
{
/* Recurse into subquery in FROM */
- setRuleCheckAsUser(rte->subquery, userid);
+ setRuleCheckAsUser( ((RangeTblEntrySubSelect *) rte)->subquery,
+ userid);
}
else
rte->checkAsUser = userid;
Index: backend/rewrite/rewriteHandler.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/rewrite/rewriteHandler.c,v
retrieving revision 1.95
diff --unified -r1.95 rewriteHandler.c
--- backend/rewrite/rewriteHandler.c 2001/06/13 18:56:30 1.95
+++ backend/rewrite/rewriteHandler.c 2001/07/01 02:15:27
@@ -265,8 +265,9 @@
bool relIsUsed)
{
Query *rule_action;
- RangeTblEntry *rte,
- *subrte;
+ RangeTblEntry *rte;
+ RangeTblEntrySubSelect *rte_sel=NULL; /* pacify compiler */
+ RangeTblEntryRelation *subrte;
if (length(rule->actions) != 1)
elog(ERROR, "ApplyRetrieveRule: expected just one rule action");
@@ -287,26 +288,46 @@
* VIEWs are really easy --- just plug the view query in as a
* subselect, replacing the relation's original RTE.
*/
- rte = rt_fetch(rt_index, parsetree->rtable);
+ rte = (RangeTblEntry *) rt_fetch(rt_index, parsetree->rtable);
- rte->relname = NULL;
- rte->relid = InvalidOid;
- rte->subquery = rule_action;
- rte->inh = false; /* must not be set for a subquery */
+ /* We have to mutate whatever RTE kind it was into RTESubSelect */
+ if ( IsA(rte, RangeTblEntrySubSelect) ) { /* already was SubSelect */
+ rte_sel=(RangeTblEntrySubSelect *) rte;
+ } else if (IsA(rte, RangeTblEntryRelation)) {
+ /* clone the RTE, then set subquery */
+ rte_sel = makeNode(RangeTblEntrySubSelect);
+ rte_sel->alias = rte->alias;
+ rte_sel->eref = rte->eref;
+ rte_sel->inh = rte->inh;
+ rte_sel->inFromCl = rte->inFromCl;
+ /* these will be overwritten below, but copy them anyway */
+ rte_sel->checkForRead = rte->checkForRead;
+ rte_sel->checkForWrite = rte->checkForWrite;
+ rte_sel->checkAsUser = rte->checkAsUser;
+ /* put the new RTE into RT table, overwriting old one */
+ rt_store(rt_index, parsetree->rtable, rte_sel);
+ } else {
+ elog(ERROR, "ApplyRetrieveRule: unknown RTE type");
+ }
+ rte_sel->subquery = rule_action;
+ rte_sel->inh = false; /* must not be set for a subquery */
+
/*
* We move the view's permission check data down to its rangetable.
* The checks will actually be done against the *OLD* entry therein.
*/
- subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
+ subrte = (RangeTblEntryRelation *)
+ rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
+ Assert( IsA(subrte, RangeTblEntryRelation) );
Assert(subrte->relid == relation->rd_id);
- subrte->checkForRead = rte->checkForRead;
- subrte->checkForWrite = rte->checkForWrite;
- subrte->checkAsUser = rte->checkAsUser;
-
- rte->checkForRead = false; /* no permission check on subquery itself */
- rte->checkForWrite = false;
- rte->checkAsUser = InvalidOid;
+ subrte->checkForRead = rte_sel->checkForRead;
+ subrte->checkForWrite = rte_sel->checkForWrite;
+ subrte->checkAsUser = rte_sel->checkAsUser;
+
+ rte_sel->checkForRead = false; /* no permission check on subquery itself */
+ rte_sel->checkForWrite = false;
+ rte_sel->checkAsUser = InvalidOid;
/*
* FOR UPDATE of view?
@@ -355,10 +376,11 @@
(rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))
continue;
- if (rte->subquery)
+ if (IsA(rte, RangeTblEntrySubSelect) )
{
/* FOR UPDATE of subquery is propagated to subquery's rels */
- markQueryForUpdate(rte->subquery, false);
+ markQueryForUpdate( ((RangeTblEntrySubSelect *)rte)->subquery,
+ false);
}
else
{
@@ -423,6 +445,7 @@
while (rt_index < length(parsetree->rtable))
{
RangeTblEntry *rte;
+ RangeTblEntryRelation *rte_rel;
Relation rel;
List *locks;
RuleLock *rules;
@@ -437,13 +460,24 @@
rte = rt_fetch(rt_index, parsetree->rtable);
/*
+ * A portal RTE can't have associated rules, so there's nothing
+ * to do to this level of the query.
+ */
+
+ if (IsA(rte, RangeTblEntryPortal))
+ {
+ continue;
+ }
+
+ /*
* A subquery RTE can't have associated rules, so there's nothing
* to do to this level of the query, but we must recurse into the
* subquery to expand any rule references in it.
*/
- if (rte->subquery)
+ if (IsA(rte, RangeTblEntrySubSelect))
{
- rte->subquery = fireRIRrules(rte->subquery);
+ RangeTblEntrySubSelect * rte_sel = (RangeTblEntrySubSelect *) rte;
+ rte_sel->subquery = fireRIRrules(rte_sel->subquery);
continue;
}
@@ -459,6 +493,8 @@
if (!relIsUsed && rt_index != parsetree->resultRelation)
continue;
+ /* by here, rte must be RTERelation */
+ rte_rel = (RangeTblEntryRelation *) rte;
/*
* This may well be the first access to the relation during the
* current statement (it will be, if this Query was extracted from
@@ -479,7 +515,7 @@
else
lockmode = AccessShareLock;
- rel = heap_openr(rte->relname, lockmode);
+ rel = heap_openr(rte_rel->relname, lockmode);
/*
* Check to see if relation's OID matches the RTE. If not, the RTE
@@ -487,9 +523,9 @@
* Eventually we might want to reparse the referencing rule, but
* for now all we can do is punt.
*/
- if (RelationGetRelid(rel) != rte->relid)
+ if (RelationGetRelid(rel) != rte_rel->relid)
elog(ERROR, "Relation \"%s\" with OID %u no longer exists",
- rte->relname, rte->relid);
+ rte_rel->relname, rte_rel->relid);
/*
* Collect the RIR rules that we must apply
@@ -730,7 +766,7 @@
CmdType event;
List *product_queries = NIL;
int result_relation;
- RangeTblEntry *rt_entry;
+ RangeTblEntryRelation *rt_entry;
Relation rt_entry_relation;
RuleLock *rt_entry_locks;
@@ -756,7 +792,9 @@
*/
result_relation = parsetree->resultRelation;
Assert(result_relation != 0);
- rt_entry = rt_fetch(result_relation, parsetree->rtable);
+ rt_entry = (RangeTblEntryRelation *)
+ rt_fetch(result_relation, parsetree->rtable);
+ Assert( IsA( rt_entry, RangeTblEntryRelation) );
/*
* This may well be the first access to the result relation during the
@@ -924,7 +962,7 @@
RangeTblEntry *rte = rt_fetch(query->resultRelation,
query->rtable);
- if (rte->subquery)
+ if (IsA(rte, RangeTblEntrySubSelect))
{
switch (query->commandType)
{
Index: backend/rewrite/rewriteManip.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/rewrite/rewriteManip.c,v
retrieving revision 1.57
diff --unified -r1.57 rewriteManip.c
--- backend/rewrite/rewriteManip.c 2001/04/18 20:42:55 1.57
+++ backend/rewrite/rewriteManip.c 2001/07/01 01:23:47
@@ -531,7 +531,7 @@
getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
{
Query *selectquery;
- RangeTblEntry *selectrte;
+ RangeTblEntrySubSelect *selectrte;
RangeTblRef *rtr;
if (subquery_ptr)
@@ -559,7 +559,9 @@
elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
rtr = (RangeTblRef *) lfirst(parsetree->jointree->fromlist);
Assert(IsA(rtr, RangeTblRef));
- selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
+ selectrte = (RangeTblEntrySubSelect *)
+ rt_fetch(rtr->rtindex, parsetree->rtable);
+ Assert(IsA(selectrte, RangeTblEntrySubSelect));
selectquery = selectrte->subquery;
if (!(selectquery && IsA(selectquery, Query) &&
selectquery->commandType == CMD_SELECT))
Index: backend/utils/adt/ruleutils.c
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.77
diff --unified -r1.77 ruleutils.c
--- backend/utils/adt/ruleutils.c 2001/04/18 17:04:24 1.77
+++ backend/utils/adt/ruleutils.c 2001/07/01 01:36:48
@@ -636,13 +636,13 @@
deparse_context_for(char *relname, Oid relid)
{
deparse_namespace *dpns;
- RangeTblEntry *rte;
+ RangeTblEntryRelation *rte;
RangeTblRef *rtr;
dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
/* Build a minimal RTE for the rel */
- rte = makeNode(RangeTblEntry);
+ rte = makeNode(RangeTblEntryRelation);
rte->relname = relname;
rte->relid = relid;
rte->eref = makeNode(Attr);
@@ -1104,9 +1104,10 @@
if (IsA(setOp, RangeTblRef))
{
RangeTblRef *rtr = (RangeTblRef *) setOp;
- RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
+ RangeTblEntrySubSelect *rte = (RangeTblEntrySubSelect *)
+ rt_fetch(rtr->rtindex, query->rtable);
Query *subquery = rte->subquery;
-
+ Assert( IsA(rte, RangeTblEntrySubSelect) );
Assert(subquery != NULL);
get_query_def(subquery, buf, context->namespaces);
}
@@ -1212,8 +1213,9 @@
get_insert_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
- RangeTblEntry *select_rte = NULL;
+ RangeTblEntrySubSelect *select_rte = NULL; /* rte for underlying SELECT */
RangeTblEntry *rte;
+ RangeTblEntryRelation *rte_rel;
char *sep;
List *l;
@@ -1224,19 +1226,21 @@
foreach(l, query->rtable)
{
rte = (RangeTblEntry *) lfirst(l);
- if (rte->subquery == NULL)
+ if (!IsA(rte, RangeTblEntrySubSelect))
continue;
if (select_rte)
elog(ERROR, "get_insert_query_def: too many RTEs in INSERT!");
- select_rte = rte;
+ select_rte = (RangeTblEntrySubSelect *) rte;
}
/*
* Start the query with INSERT INTO relname
*/
- rte = rt_fetch(query->resultRelation, query->rtable);
+ rte_rel = (RangeTblEntryRelation *)
+ rt_fetch(query->resultRelation, query->rtable);
+ Assert(IsA( rte_rel, RangeTblEntryRelation ));
appendStringInfo(buf, "INSERT INTO %s",
- quote_identifier(rte->relname));
+ quote_identifier(rte_rel->relname));
/* Add the insert-column-names list */
sep = " (";
@@ -1285,13 +1289,16 @@
{
StringInfo buf = context->buf;
char *sep;
- RangeTblEntry *rte;
+ RangeTblEntryRelation *rte;
List *l;
/*
* Start the query with UPDATE relname SET
*/
- rte = rt_fetch(query->resultRelation, query->rtable);
+ rte = (RangeTblEntryRelation *)
+ rt_fetch(query->resultRelation, query->rtable);
+
+ Assert(IsA( rte, RangeTblEntryRelation ));
appendStringInfo(buf, "UPDATE %s%s SET ",
only_marker(rte),
quote_identifier(rte->relname));
@@ -1339,12 +1346,14 @@
get_delete_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
- RangeTblEntry *rte;
+ RangeTblEntryRelation *rte;
/*
* Start the query with DELETE FROM relname
*/
- rte = rt_fetch(query->resultRelation, query->rtable);
+ rte = (RangeTblEntryRelation *)
+ rt_fetch(query->resultRelation, query->rtable);
+ Assert(IsA( rte, RangeTblEntryRelation ));
appendStringInfo(buf, "DELETE FROM %s%s",
only_marker(rte),
quote_identifier(rte->relname));
@@ -2361,21 +2370,28 @@
int varno = ((RangeTblRef *) jtnode)->rtindex;
RangeTblEntry *rte = rt_fetch(varno, query->rtable);
- if (rte->relname)
+ if (IsA(rte, RangeTblEntryRelation))
{
/* Normal relation RTE */
appendStringInfo(buf, "%s%s",
- only_marker(rte),
- quote_identifier(rte->relname));
+ only_marker(rte),
+ quote_identifier( ((RangeTblEntryRelation *)rte)->relname));
}
- else
+ else if (IsA(rte, RangeTblEntrySubSelect))
{
/* Subquery RTE */
- Assert(rte->subquery != NULL);
appendStringInfoChar(buf, '(');
- get_query_def(rte->subquery, buf, context->namespaces);
+ get_query_def( ((RangeTblEntrySubSelect *)rte)->subquery,
+ buf, context->namespaces);
appendStringInfoChar(buf, ')');
- }
+ } else if (IsA(rte, RangeTblEntryPortal))
+ {
+ appendStringInfo(buf, " CURSOR %s",
+ ((RangeTblEntryPortal *)rte)->portal);
+ } else
+ elog(ERROR, "get_from_clause_item: unknown RTE type");
+
+
if (rte->alias != NULL)
{
appendStringInfo(buf, " %s",
Index: include/nodes/nodes.h
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/include/nodes/nodes.h,v
retrieving revision 1.90
diff --unified -r1.90 nodes.h
--- include/nodes/nodes.h 2001/06/09 23:21:55 1.90
+++ include/nodes/nodes.h 2001/06/30 22:21:45
@@ -215,7 +215,7 @@
T_Constraint,
T_DefElem,
T_TargetEntry,
- T_RangeTblEntry,
+ T_RangeTblEntryXXX, /* not used anymore */
T_SortClause,
T_GroupClause,
T_SubSelectXXX, /* not used anymore; tag# available */
@@ -225,6 +225,11 @@
T_RowMarkXXX, /* not used anymore; tag# available */
T_FkConstraint,
T_PrivGrantee,
+ T_RangeTblEntryRelation,
+ T_RangeTblEntrySubSelect,
+ T_RangeTblEntryPortal,
+/* T_FuncRangeVar, */
+ T_PortalRangeVar,
/*
* TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
@@ -262,6 +267,10 @@
#define IsA_Join(jp) \
(IsA(jp, Join) || IsA(jp, NestLoop) || \
IsA(jp, MergeJoin) || IsA(jp, HashJoin))
+
+#define IsA_RTE(rte) \
+ (IsA(rte, RangeTblEntrySubSelect) || IsA(rte, RangeTblEntryPortal) || \
+ IsA(rte, RangeTblEntryRelation))
/* ----------------------------------------------------------------
* extern declarations follow
Index: include/nodes/parsenodes.h
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.131
diff --unified -r1.131 parsenodes.h
--- include/nodes/parsenodes.h 2001/06/09 23:21:55 1.131
+++ include/nodes/parsenodes.h 2001/06/30 22:57:08
@@ -15,6 +15,7 @@
#define PARSENODES_H
#include "nodes/primnodes.h"
+#include "access/tupdesc.h"
/*****************************************************************************
* Query Tree
@@ -909,6 +910,7 @@
Node *limitOffset; /* # of result tuples to skip */
Node *limitCount; /* # of result tuples to return */
List *forUpdate; /* FOR UPDATE clause */
+ bool isFunc; /* select * from func(args) */
/*
* These fields are used only in upper-level SelectStmts.
@@ -1159,6 +1161,28 @@
} SortGroupBy;
/*
+ * FuncRangeVar - range variable, used in FROM clauses
+ * select x,y from foo(1) as bar(x,y)
+ */
+typedef struct FuncRangeVar
+{
+ NodeTag type;
+ Node *func;
+ Attr *name; /* optional table alias & column aliases */
+} FuncRangeVar;
+
+/*
+ * PortalRangeVar - range variable, used in FROM clauses
+ * select x,y from cursor foo
+ */
+typedef struct PortalRangeVar
+{
+ NodeTag type;
+ char *portal;
+ Attr *name; /* optional table alias & column aliases */
+} PortalRangeVar;
+
+/*
* RangeVar - range variable, used in FROM clauses
*/
typedef struct RangeVar
@@ -1265,32 +1289,46 @@
* (This allows rules to act as setuid gateways.)
*--------------------
*/
+#define RTE_COMMON_FIELDS \
+ NodeTag type; \
+ /* \
+ * Fields valid in all RTEs: \
+ */ \
+ Attr *alias; /* user-written alias clause, if any */ \
+ Attr *eref; /* expanded reference names */ \
+ bool inh; /* inheritance requested? */ \
+ bool inFromCl; /* present in FROM clause */ \
+ bool checkForRead; /* check rel for read access */ \
+ bool checkForWrite; /* check rel for write access */ \
+ Oid checkAsUser; /* if not zero, check access as this user */ \
+
typedef struct RangeTblEntry
{
- NodeTag type;
+ RTE_COMMON_FIELDS
+} RangeTblEntry;
- /*
- * Fields valid for a plain relation RTE (else NULL/zero):
- */
+typedef struct RangeTblEntryRelation
+{
+ RTE_COMMON_FIELDS
+ /* Fields valid for a plain relation RTE (else NULL/zero): */
char *relname; /* real name of the relation */
Oid relid; /* OID of the relation */
+} RangeTblEntryRelation;
- /*
- * Fields valid for a subquery RTE (else NULL):
- */
+typedef struct RangeTblEntrySubSelect
+{
+ RTE_COMMON_FIELDS
+ /* Fields valid for a subquery RTE (else NULL) */
Query *subquery; /* the sub-query */
-
- /*
- * Fields valid in all RTEs:
- */
- Attr *alias; /* user-written alias clause, if any */
- Attr *eref; /* expanded reference names */
- bool inh; /* inheritance requested? */
- bool inFromCl; /* present in FROM clause */
- bool checkForRead; /* check rel for read access */
- bool checkForWrite; /* check rel for write access */
- Oid checkAsUser; /* if not zero, check access as this user */
-} RangeTblEntry;
+} RangeTblEntrySubSelect;
+
+typedef struct RangeTblEntryPortal
+{
+ RTE_COMMON_FIELDS
+ /* fields valid for portal RTE */
+ TupleDesc tupleDesc; /* tupleDesc of underlying portal */
+ char *portal; /* portal's name */
+} RangeTblEntryPortal;
/*
* SortClause -
Index: include/parser/parse_relation.h
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/include/parser/parse_relation.h,v
retrieving revision 1.23
diff --unified -r1.23 parse_relation.h
--- include/parser/parse_relation.h 2001/03/22 04:00:58 1.23
+++ include/parser/parse_relation.h 2001/06/30 20:28:12
@@ -36,6 +36,11 @@
Query *subquery,
Attr *alias,
bool inFromCl);
+extern RangeTblEntry * addRangeTableEntryForPortal(ParseState *pstate,
+ TupleDesc tupleDesc,
+ char *portalname,
+ Attr *alias
+ );
extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
bool addToJoinList, bool addToNameSpace);
extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
Index: include/parser/parsetree.h
===================================================================
RCS file: /home/cvs/pgsql/pgsql/src/include/parser/parsetree.h,v
retrieving revision 1.13
diff --unified -r1.13 parsetree.h
--- include/parser/parsetree.h 2001/01/24 19:43:27 1.13
+++ include/parser/parsetree.h 2001/07/01 01:00:50
@@ -36,15 +36,8 @@
#define rt_store(rangetable_index, rangetable, rt) \
set_nth(rangetable, (rangetable_index)-1, rt)
-/*
- * getrelid
- *
- * Given the range index of a relation, return the corresponding
- * relation OID. Note that InvalidOid will be returned if the
- * RTE is for a sub-select rather than a relation.
- */
-#define getrelid(rangeindex,rangetable) \
- (rt_fetch(rangeindex, rangetable)->relid)
+/* getrelid is no longer a #define */
+extern Oid getrelid (int rangeindex, List * rangetable);
/*
* Given an RTE and an attribute number, return the appropriate
That's gonna have to be fixed. If you're not up for it, don't implement
this. Given that cursors (are supposed to) support FETCH BACKWARDS,
I really don't see why they shouldn't be expected to handle ReScan...I thought only scrollable cursors can do that. What if cursor isn't
scrollable? Should it error during the execution?
In PostgreSQL, all cursors are scrollable. The allowed grammar keyword is
simply ignored. I am actually not sure that this is optimal, since there
are a few very effective optimizations, that you can do if you know, that
ReScan is not needed (like e.g. not storing the result temporarily).
Andreas
Import Notes
Resolved by subject fallback
On Mon, 2 Jul 2001, Alex Pilosov wrote:
shown us the proposed changes, I really suspect that a union would be
cleaner, because it'd avoid ugliness in those places. Bear in mind thatI have attached the diff of what I have now. Please take a look through
it. Its not exactly done (only the parser and pieces of executor parts of
"FROM CURSOR" are there, and I didn't attach gram.y diff because its just
too ugly now), so its definitely not ready for application, but with these
changes, it compiles and 'make check' goes through fine ;)
Apparently the diff was too large for mailserver. I have posted it at
http://www.formenos.org/pg/rte.diff
95% of that diff is obvious changes. 5% are not-so-obvious :)
-alex
Alex Pilosov <alex@pilosoft.com> writes:
And what are you doing with the places that don't care which kind of RTE
they are dealing with (which is most of them IIRC)? While you haven't
They just have things declared as RangeTblEntry *, and as long as they
don't access type-specific fields, they are fine.
So you have four (soon to be six or seven) different structs that *must*
have the same fields? I don't think that's cleaner than a union ...
at the very least, declare it as structs containing RangeTblEntry,
similar to the way the various Plan node types work (see plannodes.h).
For scrollable cursors, Rescan should be implemented as 'scroll backwards
until you can't scroll no more', correct?
No, it should be implemented as Rescan. The portal mechanism needs to
expose the Rescan call for the contained querytree.
regards, tom lane
Zeugswetter Andreas SB <ZeugswetterA@wien.spardat.at> writes:
this. Given that cursors (are supposed to) support FETCH BACKWARDS,
I really don't see why they shouldn't be expected to handle ReScan...I thought only scrollable cursors can do that. What if cursor isn't
scrollable? Should it error during the execution?
In PostgreSQL, all cursors are scrollable. The allowed grammar keyword is
simply ignored. I am actually not sure that this is optimal, since there
are a few very effective optimizations, that you can do if you know, that
ReScan is not needed (like e.g. not storing the result temporarily).
It's worse than that: we don't distinguish plans for cursors from plans
for any other query, hence *all* query plans are supposed to be able to
run backwards. (In practice, a lot of them don't work :-(.) Someday
that needs to be improved. It would be good if the system understood
whether a particular plan node would ever be asked to rescan itself or
run backwards, and could optimize things on that basis.
regards, tom lane
On Tue, 3 Jul 2001, Tom Lane wrote:
Alex Pilosov <alex@pilosoft.com> writes:
And what are you doing with the places that don't care which kind of RTE
they are dealing with (which is most of them IIRC)? While you haven'tThey just have things declared as RangeTblEntry *, and as long as they
don't access type-specific fields, they are fine.So you have four (soon to be six or seven) different structs that *must*
have the same fields? I don't think that's cleaner than a union ...
at the very least, declare it as structs containing RangeTblEntry,
similar to the way the various Plan node types work (see plannodes.h).
Please see my diffs. Its implemented via #define to declare all common
fields.
I.E.:
#define RTE_COMMON_FIELDS \
NodeTag type; \
/* \
* Fields valid in all RTEs: \
*/ \
Attr *alias; /* user-written alias clause, if any */ \
Attr *eref; /* expanded reference names */ \
bool inh; /* inheritance requested? */ \
bool inFromCl; /* present in FROM clause */ \
bool checkForRead; /* check rel for read access */ \
bool checkForWrite; /* check rel for write access */ \
Oid checkAsUser; /* if not zero, check access as this user
*/ \
typedef struct RangeTblEntry
{
RTE_COMMON_FIELDS
} RangeTblEntry;
typedef struct RangeTblEntryRelation
{
RTE_COMMON_FIELDS
/* Fields valid for a plain relation RTE */
char *relname; /* real name of the relation */
Oid relid; /* OID of the relation */
} RangeTblEntryRelation;
If RTEs are done the way plan nodes done, the syntax would be pretty much
the same, only with one more indirection to access common fields.
This is how code looks with my changes:
RangeTblEntry *rte=rt_fetch(..)
For common fields
rte->eref
For type-specific fields
((RangeTblEntryRelation *) rte)->relid
This is how it would look if it was done like Plan nodes are done:
RangeTblEntry *rte=rt_fetch(..)
For common fields:
rte->common->eref
For type-specific fields:
((RangeTblEntryRelation *)rte)->relid
For scrollable cursors, Rescan should be implemented as 'scroll backwards
until you can't scroll no more', correct?No, it should be implemented as Rescan. The portal mechanism needs to
expose the Rescan call for the contained querytree.
Ok.
Alex Pilosov <alex@pilosoft.com> writes:
On Tue, 3 Jul 2001, Tom Lane wrote:
So you have four (soon to be six or seven) different structs that *must*
have the same fields? I don't think that's cleaner than a union ...
Please see my diffs. Its implemented via #define to declare all common
fields.
#define RTE_COMMON_FIELDS \
NodeTag type; \
[etc]
I don't think that technique is cleaner than a union, either ;-).
The macro definition is a pain in the neck: you have to play games with
semicolon placement, most tools won't autoindent it nicely, etc etc.
But the main point is that I think NodeType = RangeTblEntry with
a separate subtype field is a better way to go than making a bunch of
different NodeType values. When most of the fields are common, as in
this case, it's going to be true that many places only want to know
"is it a rangetable entry or not?"
regards, tom lane
On Tue, 3 Jul 2001, Tom Lane wrote:
Alex Pilosov <alex@pilosoft.com> writes:
On Tue, 3 Jul 2001, Tom Lane wrote:
So you have four (soon to be six or seven) different structs that *must*
have the same fields? I don't think that's cleaner than a union ...Please see my diffs. Its implemented via #define to declare all common
fields.
#define RTE_COMMON_FIELDS \
NodeTag type; \
[etc]I don't think that technique is cleaner than a union, either ;-).
The macro definition is a pain in the neck: you have to play games with
semicolon placement, most tools won't autoindent it nicely, etc etc.
True true. On other hand, unlike union, its automatically typechecked, you
cannot by mistake reference a field you shouldn't be referencing.
Strict typechecking allows one to explicitly declare which type your
function takes if you want, and force errors if you miscast something. I
think discipline is a good thing here...
But really its your call, no point in arguing :)
But the main point is that I think NodeType = RangeTblEntry with
a separate subtype field is a better way to go than making a bunch of
different NodeType values. When most of the fields are common, as in
this case, it's going to be true that many places only want to know
"is it a rangetable entry or not?"
That's why I have IsA_RTE(node) macro. (Same as we have IsA_Join and
IsA_JoinPath already). It
Alex Pilosov <alex@pilosoft.com> writes:
True true. On other hand, unlike union, its automatically typechecked, you
cannot by mistake reference a field you shouldn't be referencing.
Only true to the extent that you have cast a generic pointer to the
correct type to begin with. However, we've probably wasted more time
arguing the point than it's really worth.
I would suggest leaving off the final semicolon in the macro definition
so that you can write
typedef struct RangeTblEntryRelation
{
RTE_COMMON_FIELDS;
/* Fields valid for a plain relation RTE */
char *relname; /* real name of the relation */
Oid relid; /* OID of the relation */
Without this, tools like pgindent will almost certainly mess up these
struct declarations (I know emacs' C mode will get it wrong...)
regards, tom lane
On Mon, 2 Jul 2001, Alex Pilosov wrote:
Erm, forgot to attach the patch. Here it is.
(yow) don't even bother looking at this patch. mail server delayed this
message by almost a week, and by now, the code is totally changed.
I took Tom's suggestion and made RTE a union. So, the below is a new
definition of RTE:
I have most of portal-related code working, only executor needs some more
fixes. Code properly makes PortalScan Path entry, PortalScan Plan nodes,
etc. I have added PortalReScan to tell portal it needs to rescan itself.
I'll post a correct patch next week. Thank you to everyone and especially
Tom for bearing with my often stupid questions.
--cut here--rte definition--
typedef enum RTEType {
RTE_RELATION,
RTE_SUBSELECT,
RTE_PORTAL
} RTEType;
typedef struct RangeTblEntry
{
NodeTag type;
RTEType rtetype;
/*
* Fields valid in all RTEs:
*/
Attr *alias; /* user-written alias clause, if any */
Attr *eref; /* expanded reference names */
bool inh; /* inheritance requested? */
bool inFromCl; /* present in FROM clause */
bool checkForRead; /* check rel for read access */
bool checkForWrite; /* check rel for write access */
Oid checkAsUser; /* if not zero, check access as this user
*/
union {
struct {
/* Fields for a plain relation RTE (rtetype=RTE_RELATION) */
char *relname; /* real name of the relation */
Oid relid; /* OID of the relation */
} rel;
struct {
/* Fields for a subquery RTE (rtetype=RTE_SUBSELECT) */
Query *subquery; /* the sub-query */
} sub;
struct {
/* fields for portal RTE (rtetype=RTE_PORTAL) */
char *portalname; /* portal's name */
} portal;
} u;
} RangeTblEntry;