mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
Subselects in FROM clause, per ISO syntax: FROM (SELECT ...) [AS] alias.
(Don't forget that an alias is required.) Views reimplemented as expanding to subselect-in-FROM. Grouping, aggregates, DISTINCT in views actually work now (he says optimistically). No UNION support in subselects/views yet, but I have some ideas about that. Rule-related permissions checking moved out of rewriter and into executor. INITDB REQUIRED!
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
# Makefile for executor
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.13 2000/08/31 16:09:56 petere Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.14 2000/09/29 18:21:28 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -18,7 +18,7 @@ OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
|
||||
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
|
||||
nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSort.o \
|
||||
nodeUnique.o nodeGroup.o spi.o nodeSubplan.o \
|
||||
nodeTidscan.o
|
||||
nodeSubqueryscan.o nodeTidscan.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execAmi.c,v 1.51 2000/08/03 19:19:30 tgl Exp $
|
||||
* $Id: execAmi.c,v 1.52 2000/09/29 18:21:28 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -44,6 +44,7 @@
|
||||
#include "executor/nodeSeqscan.h"
|
||||
#include "executor/nodeSort.h"
|
||||
#include "executor/nodeSubplan.h"
|
||||
#include "executor/nodeSubqueryscan.h"
|
||||
#include "executor/nodeUnique.h"
|
||||
|
||||
static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
|
||||
@ -304,6 +305,14 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
|
||||
ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
ExecTidReScan((TidScan *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_Material:
|
||||
ExecMaterialReScan((Material *) node, exprCtxt, parent);
|
||||
break;
|
||||
@ -348,10 +357,6 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
|
||||
ExecReScanAppend((Append *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
ExecTidReScan((TidScan *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecReScan: node type %d not supported",
|
||||
nodeTag(node));
|
||||
|
@ -27,7 +27,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.127 2000/09/12 21:06:48 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.128 2000/09/29 18:21:28 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -69,13 +69,11 @@ static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
|
||||
static TupleTableSlot *EvalPlanQualNext(EState *estate);
|
||||
static void EndEvalPlanQual(EState *estate);
|
||||
static void ExecCheckQueryPerms(CmdType operation, Query *parseTree,
|
||||
Plan *plan);
|
||||
static void ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
int resultRelation, bool resultIsScanned);
|
||||
static void ExecCheckRTPerms(List *rangeTable, CmdType operation,
|
||||
int resultRelation, bool resultIsScanned);
|
||||
static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
|
||||
bool isResultRelation, bool resultIsScanned);
|
||||
Plan *plan);
|
||||
static void ExecCheckPlanPerms(Plan *plan, List *rangeTable,
|
||||
CmdType operation);
|
||||
static void ExecCheckRTPerms(List *rangeTable, CmdType operation);
|
||||
static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
|
||||
|
||||
/* end of local decls */
|
||||
|
||||
@ -390,51 +388,15 @@ ExecutorEnd(QueryDesc *queryDesc, EState *estate)
|
||||
static void
|
||||
ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
|
||||
{
|
||||
List *rangeTable = parseTree->rtable;
|
||||
int resultRelation = parseTree->resultRelation;
|
||||
bool resultIsScanned = false;
|
||||
List *lp;
|
||||
|
||||
/*
|
||||
* If we have a result relation, determine whether the result rel is
|
||||
* scanned or merely written. If scanned, we will insist on read
|
||||
* permission as well as modify permission.
|
||||
*
|
||||
* Note: it might look faster to apply rangeTableEntry_used(), but
|
||||
* that's not correct since it will trigger on jointree references
|
||||
* to the RTE. We only want to know about actual Var nodes.
|
||||
*/
|
||||
if (resultRelation > 0)
|
||||
{
|
||||
List *qvars = pull_varnos((Node *) parseTree);
|
||||
|
||||
resultIsScanned = intMember(resultRelation, qvars);
|
||||
freeList(qvars);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check RTEs in the query's primary rangetable.
|
||||
*/
|
||||
ExecCheckRTPerms(rangeTable, operation, resultRelation, resultIsScanned);
|
||||
|
||||
/*
|
||||
* Check SELECT FOR UPDATE access rights.
|
||||
*/
|
||||
foreach(lp, parseTree->rowMark)
|
||||
{
|
||||
RowMark *rm = lfirst(lp);
|
||||
|
||||
if (!(rm->info & ROW_ACL_FOR_UPDATE))
|
||||
continue;
|
||||
|
||||
ExecCheckRTEPerms(rt_fetch(rm->rti, rangeTable),
|
||||
CMD_UPDATE, true, false);
|
||||
}
|
||||
ExecCheckRTPerms(parseTree->rtable, operation);
|
||||
|
||||
/*
|
||||
* Search for subplans and APPEND nodes to check their rangetables.
|
||||
*/
|
||||
ExecCheckPlanPerms(plan, operation, resultRelation, resultIsScanned);
|
||||
ExecCheckPlanPerms(plan, parseTree->rtable, operation);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -447,8 +409,7 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
|
||||
* in the query's main rangetable. But at the moment, they're not.
|
||||
*/
|
||||
static void
|
||||
ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
int resultRelation, bool resultIsScanned)
|
||||
ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
|
||||
{
|
||||
List *subp;
|
||||
|
||||
@ -461,28 +422,37 @@ ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
{
|
||||
SubPlan *subplan = (SubPlan *) lfirst(subp);
|
||||
|
||||
ExecCheckRTPerms(subplan->rtable, CMD_SELECT, 0, false);
|
||||
ExecCheckPlanPerms(subplan->plan, CMD_SELECT, 0, false);
|
||||
ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
|
||||
ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
|
||||
}
|
||||
foreach(subp, plan->subPlan)
|
||||
{
|
||||
SubPlan *subplan = (SubPlan *) lfirst(subp);
|
||||
|
||||
ExecCheckRTPerms(subplan->rtable, CMD_SELECT, 0, false);
|
||||
ExecCheckPlanPerms(subplan->plan, CMD_SELECT, 0, false);
|
||||
ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
|
||||
ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
|
||||
}
|
||||
|
||||
/* Check lower plan nodes */
|
||||
|
||||
ExecCheckPlanPerms(plan->lefttree, operation,
|
||||
resultRelation, resultIsScanned);
|
||||
ExecCheckPlanPerms(plan->righttree, operation,
|
||||
resultRelation, resultIsScanned);
|
||||
ExecCheckPlanPerms(plan->lefttree, rangeTable, operation);
|
||||
ExecCheckPlanPerms(plan->righttree, rangeTable, operation);
|
||||
|
||||
/* Do node-type-specific checks */
|
||||
|
||||
switch (nodeTag(plan))
|
||||
{
|
||||
case T_SubqueryScan:
|
||||
{
|
||||
SubqueryScan *scan = (SubqueryScan *) plan;
|
||||
RangeTblEntry *rte;
|
||||
|
||||
/* Recursively check the subquery */
|
||||
rte = rt_fetch(scan->scan.scanrelid, rangeTable);
|
||||
Assert(rte->subquery != NULL);
|
||||
ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
|
||||
break;
|
||||
}
|
||||
case T_Append:
|
||||
{
|
||||
Append *app = (Append *) plan;
|
||||
@ -490,43 +460,31 @@ ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
|
||||
if (app->inheritrelid > 0)
|
||||
{
|
||||
/* Append implements expansion of inheritance */
|
||||
ExecCheckRTPerms(app->inheritrtable, operation);
|
||||
|
||||
/*
|
||||
* Append implements expansion of inheritance; all
|
||||
* members of inheritrtable list will be plugged into
|
||||
* same RTE slot. Therefore, they are either all
|
||||
* result relations or none.
|
||||
*/
|
||||
List *rtable;
|
||||
|
||||
foreach(rtable, app->inheritrtable)
|
||||
/* Check appended plans w/outer rangetable */
|
||||
foreach(appendplans, app->appendplans)
|
||||
{
|
||||
ExecCheckRTEPerms((RangeTblEntry *) lfirst(rtable),
|
||||
operation,
|
||||
(app->inheritrelid == resultRelation),
|
||||
resultIsScanned);
|
||||
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
|
||||
rangeTable,
|
||||
operation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Append implements UNION, which must be a SELECT */
|
||||
List *rtables;
|
||||
List *rtables = app->unionrtables;
|
||||
|
||||
foreach(rtables, app->unionrtables)
|
||||
/* Check appended plans with their rangetables */
|
||||
foreach(appendplans, app->appendplans)
|
||||
{
|
||||
ExecCheckRTPerms((List *) lfirst(rtables),
|
||||
CMD_SELECT, 0, false);
|
||||
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
|
||||
(List *) lfirst(rtables),
|
||||
CMD_SELECT);
|
||||
rtables = lnext(rtables);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check appended plans */
|
||||
foreach(appendplans, app->appendplans)
|
||||
{
|
||||
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
|
||||
operation,
|
||||
resultRelation,
|
||||
resultIsScanned);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -538,28 +496,17 @@ ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
/*
|
||||
* ExecCheckRTPerms
|
||||
* Check access permissions for all relations listed in a range table.
|
||||
*
|
||||
* If resultRelation is not 0, it is the RT index of the relation to be
|
||||
* treated as the result relation. All other relations are assumed to be
|
||||
* read-only for the query.
|
||||
*/
|
||||
static void
|
||||
ExecCheckRTPerms(List *rangeTable, CmdType operation,
|
||||
int resultRelation, bool resultIsScanned)
|
||||
ExecCheckRTPerms(List *rangeTable, CmdType operation)
|
||||
{
|
||||
int rtindex = 0;
|
||||
List *lp;
|
||||
|
||||
foreach(lp, rangeTable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(lp);
|
||||
|
||||
++rtindex;
|
||||
|
||||
ExecCheckRTEPerms(rte,
|
||||
operation,
|
||||
(rtindex == resultRelation),
|
||||
resultIsScanned);
|
||||
ExecCheckRTEPerms(rte, operation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -568,45 +515,48 @@ ExecCheckRTPerms(List *rangeTable, CmdType operation,
|
||||
* Check access permissions for a single RTE.
|
||||
*/
|
||||
static void
|
||||
ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
|
||||
bool isResultRelation, bool resultIsScanned)
|
||||
ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
|
||||
{
|
||||
char *relName;
|
||||
Oid userid;
|
||||
int32 aclcheck_result;
|
||||
|
||||
if (rte->skipAcl)
|
||||
{
|
||||
|
||||
/*
|
||||
* This happens if the access to this table is due to a view query
|
||||
* rewriting - the rewrite handler already checked the permissions
|
||||
* against the view owner, so we just skip this entry.
|
||||
*/
|
||||
/*
|
||||
* If it's a subquery RTE, ignore it --- it will be checked when
|
||||
* ExecCheckPlanPerms finds the SubqueryScan node for it.
|
||||
*/
|
||||
if (rte->subquery)
|
||||
return;
|
||||
}
|
||||
|
||||
relName = rte->relname;
|
||||
|
||||
/*
|
||||
* userid to check as: current user unless we have a setuid indication.
|
||||
*
|
||||
* Note: GetUserId() is presently fast enough that there's no harm
|
||||
* in calling it separately for each RTE. If that stops being true,
|
||||
* we could call it once in ExecCheckQueryPerms and pass the userid
|
||||
* down from there. But for now, no need for the extra clutter.
|
||||
*/
|
||||
userid = GetUserId();
|
||||
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
|
||||
|
||||
#define CHECK(MODE) pg_aclcheck(relName, userid, MODE)
|
||||
|
||||
if (isResultRelation)
|
||||
if (rte->checkForRead)
|
||||
{
|
||||
if (resultIsScanned)
|
||||
{
|
||||
aclcheck_result = CHECK(ACL_RD);
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
elog(ERROR, "%s: %s",
|
||||
relName, aclcheck_error_strings[aclcheck_result]);
|
||||
}
|
||||
aclcheck_result = CHECK(ACL_RD);
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
elog(ERROR, "%s: %s",
|
||||
relName, aclcheck_error_strings[aclcheck_result]);
|
||||
}
|
||||
|
||||
if (rte->checkForWrite)
|
||||
{
|
||||
/*
|
||||
* Note: write access in a SELECT context means SELECT FOR UPDATE.
|
||||
* Right now we don't distinguish that from true update as far as
|
||||
* permissions checks are concerned.
|
||||
*/
|
||||
switch (operation)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
@ -615,6 +565,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
aclcheck_result = CHECK(ACL_WR);
|
||||
break;
|
||||
case CMD_SELECT:
|
||||
case CMD_DELETE:
|
||||
case CMD_UPDATE:
|
||||
aclcheck_result = CHECK(ACL_WR);
|
||||
@ -625,13 +576,10 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
|
||||
aclcheck_result = ACLCHECK_OK; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
elog(ERROR, "%s: %s",
|
||||
relName, aclcheck_error_strings[aclcheck_result]);
|
||||
}
|
||||
else
|
||||
aclcheck_result = CHECK(ACL_RD);
|
||||
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
elog(ERROR, "%s: %s",
|
||||
relName, aclcheck_error_strings[aclcheck_result]);
|
||||
}
|
||||
|
||||
|
||||
@ -755,26 +703,23 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
|
||||
/*
|
||||
* Have to lock relations selected for update
|
||||
*/
|
||||
estate->es_rowMark = NULL;
|
||||
if (parseTree->rowMark != NULL)
|
||||
estate->es_rowMark = NIL;
|
||||
if (parseTree->rowMarks != NIL)
|
||||
{
|
||||
List *l;
|
||||
|
||||
foreach(l, parseTree->rowMark)
|
||||
foreach(l, parseTree->rowMarks)
|
||||
{
|
||||
RowMark *rm = lfirst(l);
|
||||
Oid relid;
|
||||
Index rti = lfirsti(l);
|
||||
Oid relid = getrelid(rti, rangeTable);
|
||||
Relation relation;
|
||||
execRowMark *erm;
|
||||
|
||||
if (!(rm->info & ROW_MARK_FOR_UPDATE))
|
||||
continue;
|
||||
relid = getrelid(rm->rti, rangeTable);
|
||||
relation = heap_open(relid, RowShareLock);
|
||||
erm = (execRowMark *) palloc(sizeof(execRowMark));
|
||||
erm->relation = relation;
|
||||
erm->rti = rm->rti;
|
||||
sprintf(erm->resname, "ctid%u", rm->rti);
|
||||
erm->rti = rti;
|
||||
sprintf(erm->resname, "ctid%u", rti);
|
||||
estate->es_rowMark = lappend(estate->es_rowMark, erm);
|
||||
}
|
||||
}
|
||||
@ -1097,7 +1042,7 @@ lnext: ;
|
||||
* ctid!! */
|
||||
tupleid = &tuple_ctid;
|
||||
}
|
||||
else if (estate->es_rowMark != NULL)
|
||||
else if (estate->es_rowMark != NIL)
|
||||
{
|
||||
List *l;
|
||||
|
||||
@ -1115,10 +1060,12 @@ lnext: ;
|
||||
erm->resname,
|
||||
&datum,
|
||||
&isNull))
|
||||
elog(ERROR, "ExecutePlan: NO (junk) `%s' was found!", erm->resname);
|
||||
elog(ERROR, "ExecutePlan: NO (junk) `%s' was found!",
|
||||
erm->resname);
|
||||
|
||||
if (isNull)
|
||||
elog(ERROR, "ExecutePlan: (junk) `%s' is NULL!", erm->resname);
|
||||
elog(ERROR, "ExecutePlan: (junk) `%s' is NULL!",
|
||||
erm->resname);
|
||||
|
||||
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
|
||||
test = heap_mark4update(erm->relation, &tuple, &buffer);
|
||||
@ -1625,10 +1572,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
rte->eref = makeNode(Attr);
|
||||
rte->eref->relname = rte->relname;
|
||||
/* inh, inFromCl, skipAcl won't be used, leave them zero */
|
||||
/* other fields won't be used, leave them zero */
|
||||
|
||||
/* Set up single-entry range table */
|
||||
econtext->ecxt_range_table = lcons(rte, NIL);
|
||||
econtext->ecxt_range_table = makeList1(rte);
|
||||
|
||||
estate->es_result_relation_constraints =
|
||||
(List **) palloc(ncheck * sizeof(List *));
|
||||
|
@ -12,7 +12,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.19 2000/08/13 02:50:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.20 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -90,6 +90,7 @@
|
||||
#include "executor/nodeSeqscan.h"
|
||||
#include "executor/nodeSort.h"
|
||||
#include "executor/nodeSubplan.h"
|
||||
#include "executor/nodeSubqueryscan.h"
|
||||
#include "executor/nodeUnique.h"
|
||||
#include "miscadmin.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
@ -153,6 +154,15 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
|
||||
result = ExecInitIndexScan((IndexScan *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
result = ExecInitTidScan((TidScan *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
result = ExecInitSubqueryScan((SubqueryScan *) node, estate,
|
||||
parent);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
@ -165,6 +175,14 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
|
||||
result = ExecInitMergeJoin((MergeJoin *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecInitHash((Hash *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecInitHashJoin((HashJoin *) node, estate, parent);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
@ -189,18 +207,6 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
|
||||
result = ExecInitAgg((Agg *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecInitHash((Hash *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecInitHashJoin((HashJoin *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
result = ExecInitTidScan((TidScan *) node, estate, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecInitNode: node %d unsupported", nodeTag(node));
|
||||
result = FALSE;
|
||||
@ -272,6 +278,14 @@ ExecProcNode(Plan *node, Plan *parent)
|
||||
result = ExecIndexScan((IndexScan *) node);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
result = ExecTidScan((TidScan *) node);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
result = ExecSubqueryScan((SubqueryScan *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
@ -284,6 +298,14 @@ ExecProcNode(Plan *node, Plan *parent)
|
||||
result = ExecMergeJoin((MergeJoin *) node);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecHash((Hash *) node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecHashJoin((HashJoin *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
@ -308,18 +330,6 @@ ExecProcNode(Plan *node, Plan *parent)
|
||||
result = ExecAgg((Agg *) node);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecHash((Hash *) node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecHashJoin((HashJoin *) node);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
result = ExecTidScan((TidScan *) node);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecProcNode: node %d unsupported", nodeTag(node));
|
||||
result = NULL;
|
||||
@ -356,6 +366,12 @@ ExecCountSlotsNode(Plan *node)
|
||||
case T_IndexScan:
|
||||
return ExecCountSlotsIndexScan((IndexScan *) node);
|
||||
|
||||
case T_TidScan:
|
||||
return ExecCountSlotsTidScan((TidScan *) node);
|
||||
|
||||
case T_SubqueryScan:
|
||||
return ExecCountSlotsSubqueryScan((SubqueryScan *) node);
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
@ -366,6 +382,12 @@ ExecCountSlotsNode(Plan *node)
|
||||
case T_MergeJoin:
|
||||
return ExecCountSlotsMergeJoin((MergeJoin *) node);
|
||||
|
||||
case T_Hash:
|
||||
return ExecCountSlotsHash((Hash *) node);
|
||||
|
||||
case T_HashJoin:
|
||||
return ExecCountSlotsHashJoin((HashJoin *) node);
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
@ -385,15 +407,6 @@ ExecCountSlotsNode(Plan *node)
|
||||
case T_Agg:
|
||||
return ExecCountSlotsAgg((Agg *) node);
|
||||
|
||||
case T_Hash:
|
||||
return ExecCountSlotsHash((Hash *) node);
|
||||
|
||||
case T_HashJoin:
|
||||
return ExecCountSlotsHashJoin((HashJoin *) node);
|
||||
|
||||
case T_TidScan:
|
||||
return ExecCountSlotsTidScan((TidScan *) node);
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecCountSlotsNode: node not yet supported: %d",
|
||||
nodeTag(node));
|
||||
@ -462,6 +475,14 @@ ExecEndNode(Plan *node, Plan *parent)
|
||||
ExecEndIndexScan((IndexScan *) node);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
ExecEndTidScan((TidScan *) node);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
ExecEndSubqueryScan((SubqueryScan *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
@ -474,6 +495,14 @@ ExecEndNode(Plan *node, Plan *parent)
|
||||
ExecEndMergeJoin((MergeJoin *) node);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
ExecEndHash((Hash *) node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
ExecEndHashJoin((HashJoin *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
@ -498,22 +527,6 @@ ExecEndNode(Plan *node, Plan *parent)
|
||||
ExecEndAgg((Agg *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* XXX add hooks to these
|
||||
* ----------------
|
||||
*/
|
||||
case T_Hash:
|
||||
ExecEndHash((Hash *) node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
ExecEndHashJoin((HashJoin *) node);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
ExecEndTidScan((TidScan *) node);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecEndNode: node %d unsupported", nodeTag(node));
|
||||
break;
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.39 2000/09/12 21:06:48 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.40 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -698,6 +698,22 @@ NodeGetResultTupleSlot(Plan *node)
|
||||
}
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
{
|
||||
CommonScanState *scanstate = ((TidScan *) node)->scan.scanstate;
|
||||
|
||||
slot = scanstate->cstate.cs_ResultTupleSlot;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
{
|
||||
CommonScanState *scanstate = ((SubqueryScan *) node)->scan.scanstate;
|
||||
|
||||
slot = scanstate->cstate.cs_ResultTupleSlot;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_Material:
|
||||
{
|
||||
MaterialState *matstate = ((Material *) node)->matstate;
|
||||
@ -762,14 +778,6 @@ NodeGetResultTupleSlot(Plan *node)
|
||||
}
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
{
|
||||
CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;
|
||||
|
||||
slot = scanstate->cstate.cs_ResultTupleSlot;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* ----------------
|
||||
* should never get here
|
||||
|
289
src/backend/executor/nodeSubqueryscan.c
Normal file
289
src/backend/executor/nodeSubqueryscan.c
Normal file
@ -0,0 +1,289 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeSubqueryscan.c
|
||||
* Support routines for scanning subqueries (subselects in rangetable).
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.1 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecSubqueryScan scans a subquery.
|
||||
* ExecSubqueryNext retrieve next tuple in sequential order.
|
||||
* ExecInitSubqueryScan creates and initializes a subqueryscan node.
|
||||
* ExecEndSubqueryScan releases any storage allocated.
|
||||
* ExecSubqueryReScan rescans the relation
|
||||
*
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "catalog/pg_type.h"
|
||||
#include "executor/execdebug.h"
|
||||
#include "executor/execdefs.h"
|
||||
#include "executor/execdesc.h"
|
||||
#include "executor/nodeSubqueryscan.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "tcop/pquery.h"
|
||||
|
||||
static TupleTableSlot *SubqueryNext(SubqueryScan *node);
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Scan Support
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/* ----------------------------------------------------------------
|
||||
* SubqueryNext
|
||||
*
|
||||
* This is a workhorse for ExecSubqueryScan
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static TupleTableSlot *
|
||||
SubqueryNext(SubqueryScan *node)
|
||||
{
|
||||
SubqueryScanState *subquerystate;
|
||||
EState *estate;
|
||||
ScanDirection direction;
|
||||
int execdir;
|
||||
TupleTableSlot *slot;
|
||||
Const countOne;
|
||||
|
||||
/* ----------------
|
||||
* get information from the estate and scan state
|
||||
* ----------------
|
||||
*/
|
||||
estate = node->scan.plan.state;
|
||||
subquerystate = (SubqueryScanState *) node->scan.scanstate;
|
||||
direction = estate->es_direction;
|
||||
execdir = ScanDirectionIsBackward(direction) ? EXEC_BACK : EXEC_FOR;
|
||||
slot = subquerystate->csstate.css_ScanTupleSlot;
|
||||
|
||||
/*
|
||||
* Check if we are evaluating PlanQual for tuple of this relation.
|
||||
* Additional checking is not good, but no other way for now. We could
|
||||
* introduce new nodes for this case and handle SubqueryScan --> NewNode
|
||||
* switching in Init/ReScan plan...
|
||||
*/
|
||||
if (estate->es_evTuple != NULL &&
|
||||
estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
|
||||
{
|
||||
ExecClearTuple(slot);
|
||||
if (estate->es_evTupleNull[node->scan.scanrelid - 1])
|
||||
return slot; /* return empty slot */
|
||||
|
||||
/* probably ought to use ExecStoreTuple here... */
|
||||
slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
|
||||
slot->ttc_shouldFree = false;
|
||||
|
||||
/* Flag for the next call that no more tuples */
|
||||
estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
|
||||
return (slot);
|
||||
}
|
||||
|
||||
memset(&countOne, 0, sizeof(countOne));
|
||||
countOne.type = T_Const;
|
||||
countOne.consttype = INT4OID;
|
||||
countOne.constlen = sizeof(int4);
|
||||
countOne.constvalue = Int32GetDatum(1);
|
||||
countOne.constisnull = false;
|
||||
countOne.constbyval = true;
|
||||
countOne.constisset = false;
|
||||
countOne.constiscast = false;
|
||||
|
||||
/* ----------------
|
||||
* get the next tuple from the sub-query
|
||||
* ----------------
|
||||
*/
|
||||
slot = ExecutorRun(subquerystate->sss_SubQueryDesc,
|
||||
subquerystate->sss_SubEState,
|
||||
execdir,
|
||||
NULL, /* offset */
|
||||
(Node *) &countOne);
|
||||
|
||||
subquerystate->csstate.css_ScanTupleSlot = slot;
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSubqueryScan(node)
|
||||
*
|
||||
* Scans the subquery sequentially and returns the next qualifying
|
||||
* tuple.
|
||||
* It calls the ExecScan() routine and passes it the access method
|
||||
* which retrieve tuples sequentially.
|
||||
*
|
||||
*/
|
||||
|
||||
TupleTableSlot *
|
||||
ExecSubqueryScan(SubqueryScan *node)
|
||||
{
|
||||
/* ----------------
|
||||
* use SubqueryNext as access method
|
||||
* ----------------
|
||||
*/
|
||||
return ExecScan(&node->scan, (ExecScanAccessMtd) SubqueryNext);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitSubqueryScan
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
|
||||
{
|
||||
SubqueryScanState *subquerystate;
|
||||
RangeTblEntry *rte;
|
||||
|
||||
/* ----------------
|
||||
* SubqueryScan should not have any "normal" children.
|
||||
* ----------------
|
||||
*/
|
||||
Assert(outerPlan((Plan *) node) == NULL);
|
||||
Assert(innerPlan((Plan *) node) == NULL);
|
||||
|
||||
/* ----------------
|
||||
* assign the node's execution state
|
||||
* ----------------
|
||||
*/
|
||||
node->scan.plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create new SubqueryScanState for node
|
||||
* ----------------
|
||||
*/
|
||||
subquerystate = makeNode(SubqueryScanState);
|
||||
node->scan.scanstate = (CommonScanState *) subquerystate;
|
||||
|
||||
/* ----------------
|
||||
* Miscellaneous initialization
|
||||
*
|
||||
* + create expression context for node
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignExprContext(estate, &subquerystate->csstate.cstate);
|
||||
|
||||
#define SUBQUERYSCAN_NSLOTS 2
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &subquerystate->csstate.cstate);
|
||||
|
||||
/* ----------------
|
||||
* initialize subquery
|
||||
* ----------------
|
||||
*/
|
||||
rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
|
||||
Assert(rte->subquery != NULL);
|
||||
|
||||
subquerystate->sss_SubQueryDesc = CreateQueryDesc(rte->subquery,
|
||||
node->subplan,
|
||||
None);
|
||||
subquerystate->sss_SubEState = CreateExecutorState();
|
||||
|
||||
ExecutorStart(subquerystate->sss_SubQueryDesc,
|
||||
subquerystate->sss_SubEState);
|
||||
|
||||
subquerystate->csstate.css_ScanTupleSlot = NULL;
|
||||
subquerystate->csstate.cstate.cs_TupFromTlist = false;
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignResultTypeFromTL((Plan *) node, &subquerystate->csstate.cstate);
|
||||
ExecAssignProjectionInfo((Plan *) node, &subquerystate->csstate.cstate);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsSubqueryScan(SubqueryScan *node)
|
||||
{
|
||||
/*
|
||||
* The subplan has its own tuple table and must not be counted here!
|
||||
*/
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
SUBQUERYSCAN_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndSubqueryScan
|
||||
*
|
||||
* frees any storage allocated through C routines.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndSubqueryScan(SubqueryScan *node)
|
||||
{
|
||||
SubqueryScanState *subquerystate;
|
||||
|
||||
/* ----------------
|
||||
* get information from node
|
||||
* ----------------
|
||||
*/
|
||||
subquerystate = (SubqueryScanState *) node->scan.scanstate;
|
||||
|
||||
/* ----------------
|
||||
* Free the projection info and the scan attribute info
|
||||
*
|
||||
* Note: we don't ExecFreeResultType(subquerystate)
|
||||
* because the rule manager depends on the tupType
|
||||
* returned by ExecMain(). So for now, this
|
||||
* is freed at end-transaction time. -cim 6/2/91
|
||||
* ----------------
|
||||
*/
|
||||
ExecFreeProjectionInfo(&subquerystate->csstate.cstate);
|
||||
ExecFreeExprContext(&subquerystate->csstate.cstate);
|
||||
|
||||
/* ----------------
|
||||
* close down subquery
|
||||
* ----------------
|
||||
*/
|
||||
ExecutorEnd(subquerystate->sss_SubQueryDesc,
|
||||
subquerystate->sss_SubEState);
|
||||
|
||||
/* XXX we seem to be leaking the querydesc and sub-EState... */
|
||||
|
||||
subquerystate->csstate.css_ScanTupleSlot = NULL;
|
||||
|
||||
/* ----------------
|
||||
* clean out the tuple table
|
||||
* ----------------
|
||||
*/
|
||||
ExecClearTuple(subquerystate->csstate.cstate.cs_ResultTupleSlot);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSubqueryReScan
|
||||
*
|
||||
* Rescans the relation.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecSubqueryReScan(SubqueryScan *node, ExprContext *exprCtxt, Plan *parent)
|
||||
{
|
||||
SubqueryScanState *subquerystate;
|
||||
EState *estate;
|
||||
|
||||
subquerystate = (SubqueryScanState *) node->scan.scanstate;
|
||||
estate = node->scan.plan.state;
|
||||
|
||||
/* If this is re-scanning of PlanQual ... */
|
||||
if (estate->es_evTuple != NULL &&
|
||||
estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
|
||||
{
|
||||
estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ExecReScan(node->subplan, NULL, NULL);
|
||||
subquerystate->csstate.css_ScanTupleSlot = NULL;
|
||||
}
|
Reference in New Issue
Block a user