Move the handling of SELECT FOR UPDATE locking and rechecking out of

execMain.c and into a new plan node type LockRows.  Like the recent change
to put table updating into a ModifyTable plan node, this increases planning
flexibility by allowing the operations to occur below the top level of the
plan tree.  It's necessary in any case to restore the previous behavior of
having FOR UPDATE locking occur before ModifyTable does.

This partially refactors EvalPlanQual to allow multiple rows-under-test
to be inserted into the EPQ machinery before starting an EPQ test query.
That isn't sufficient to fix EPQ's general bogosity in the face of plans
that return multiple rows per test row, though.  Since this patch is
mostly about getting some plan node infrastructure in place and not about
fixing ten-year-old bugs, I will leave EPQ improvements for another day.

Another behavioral change that we could now think about is doing FOR UPDATE
before LIMIT, but that too seems like it should be treated as a followon
patch.
This commit is contained in:
Tom Lane 2009-10-12 18:10:51 +00:00
parent 05d249717d
commit 0adaf4cb31
32 changed files with 882 additions and 320 deletions

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.191 2009/10/10 01:43:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.192 2009/10/12 18:10:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -718,6 +718,9 @@ ExplainNode(Plan *plan, PlanState *planstate,
break;
}
break;
case T_LockRows:
pname = sname = "LockRows";
break;
case T_Limit:
pname = sname = "Limit";
break;

View File

@ -4,7 +4,7 @@
# Makefile for executor
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.30 2009/10/10 01:43:45 tgl Exp $
# $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.31 2009/10/12 18:10:41 tgl Exp $
#
#-------------------------------------------------------------------------
@ -17,12 +17,12 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
nodeBitmapAnd.o nodeBitmapOr.o \
nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
nodeModifyTable.o \
nodeHashjoin.o nodeIndexscan.o nodeLimit.o nodeLockRows.o \
nodeMaterial.o nodeMergejoin.o nodeModifyTable.o \
nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
nodeLimit.o nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeWindowAgg.o tstoreReceiver.o spi.o
include $(top_srcdir)/src/backend/common.mk

View File

@ -1,4 +1,4 @@
$PostgreSQL: pgsql/src/backend/executor/README,v 1.9 2009/10/10 01:43:45 tgl Exp $
$PostgreSQL: pgsql/src/backend/executor/README,v 1.10 2009/10/12 18:10:41 tgl Exp $
The Postgres Executor
=====================
@ -157,7 +157,8 @@ if need be) and re-evaluate the query qualifications to see if it would
still meet the quals. If so, we regenerate the updated tuple (if we are
doing an UPDATE) from the modified tuple, and finally update/delete the
modified tuple. SELECT FOR UPDATE/SHARE behaves similarly, except that its
action is just to lock the modified tuple.
action is just to lock the modified tuple and return results based on that
version of the tuple.
To implement this checking, we actually re-run the entire query from scratch
for each modified tuple, but with the scan node that sourced the original
@ -195,5 +196,5 @@ It should be noted also that UPDATE/DELETE expect at most one tuple to
result from the modified query, whereas in the FOR UPDATE case it's possible
for multiple tuples to result (since we could be dealing with a join in
which multiple tuples join to the modified tuple). We want FOR UPDATE to
lock all relevant tuples, so we pass all tuples output by all the stacked
recheck queries back to the executor toplevel for locking.
lock all relevant tuples, so we process all tuples output by all the stacked
recheck queries.

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.105 2009/10/10 01:43:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.106 2009/10/12 18:10:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -28,6 +28,7 @@
#include "executor/nodeHashjoin.h"
#include "executor/nodeIndexscan.h"
#include "executor/nodeLimit.h"
#include "executor/nodeLockRows.h"
#include "executor/nodeMaterial.h"
#include "executor/nodeMergejoin.h"
#include "executor/nodeModifyTable.h"
@ -232,6 +233,10 @@ ExecReScan(PlanState *node, ExprContext *exprCtxt)
ExecReScanSetOp((SetOpState *) node, exprCtxt);
break;
case T_LockRowsState:
ExecReScanLockRows((LockRowsState *) node, exprCtxt);
break;
case T_LimitState:
ExecReScanLimit((LimitState *) node, exprCtxt);
break;
@ -444,8 +449,9 @@ ExecSupportsBackwardScan(Plan *node)
/* these don't evaluate tlist */
return true;
case T_LockRows:
case T_Limit:
/* doesn't evaluate tlist */
/* these don't evaluate tlist */
return ExecSupportsBackwardScan(outerPlan(node));
default:

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.10 2009/06/11 14:48:56 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.11 2009/10/12 18:10:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -155,8 +155,7 @@ execCurrentOf(CurrentOfExpr *cexpr,
* scan node. Fail if it's not there or buried underneath
* aggregation.
*/
scanstate = search_plan_tree(ExecGetActivePlanTree(queryDesc),
table_oid);
scanstate = search_plan_tree(queryDesc->planstate, table_oid);
if (!scanstate)
ereport(ERROR,
(errcode(ERRCODE_INVALID_CURSOR_STATE),

View File

@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.332 2009/10/10 01:43:47 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.333 2009/10/12 18:10:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -66,6 +66,8 @@ typedef struct evalPlanQual
Index rti;
EState *estate;
PlanState *planstate;
PlanState *origplanstate;
TupleTableSlot *resultslot;
struct evalPlanQual *next; /* stack of active PlanQual plans */
struct evalPlanQual *free; /* list of free PlanQual plans */
} evalPlanQual;
@ -79,7 +81,6 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
long numberTuples,
ScanDirection direction,
DestReceiver *dest);
static TupleTableSlot *EvalPlanQualNext(EState *estate);
static void EndEvalPlanQual(EState *estate);
static void ExecCheckRTPerms(List *rangeTable);
static void ExecCheckRTEPerms(RangeTblEntry *rte);
@ -695,21 +696,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
}
/*
* Detect whether we're doing SELECT INTO. If so, set the es_into_oids
* flag appropriately so that the plan tree will be initialized with the
* correct tuple descriptors. (Other SELECT INTO stuff comes later.)
*/
estate->es_select_into = false;
if (operation == CMD_SELECT && plannedstmt->intoClause != NULL)
{
estate->es_select_into = true;
estate->es_into_oids = interpretOidsOption(plannedstmt->intoClause->options);
}
/*
* Have to lock relations selected FOR UPDATE/FOR SHARE before we
* initialize the plan tree, else we'd be doing a lock upgrade. While we
* are at it, build the ExecRowMark list.
* Similarly, we have to lock relations selected FOR UPDATE/FOR SHARE
* before we initialize the plan tree, else we'd be risking lock
* upgrades. While we are at it, build the ExecRowMark list.
*/
estate->es_rowMarks = NIL;
foreach(l, plannedstmt->rowMarks)
@ -729,15 +718,28 @@ InitPlan(QueryDesc *queryDesc, int eflags)
erm->relation = relation;
erm->rti = rc->rti;
erm->prti = rc->prti;
erm->rowmarkId = rc->rowmarkId;
erm->forUpdate = rc->forUpdate;
erm->noWait = rc->noWait;
/* We'll locate the junk attrs below */
/* remaining fields are filled during LockRows plan node init */
erm->ctidAttNo = InvalidAttrNumber;
erm->toidAttNo = InvalidAttrNumber;
ItemPointerSetInvalid(&(erm->curCtid));
estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
}
/*
* Detect whether we're doing SELECT INTO. If so, set the es_into_oids
* flag appropriately so that the plan tree will be initialized with the
* correct tuple descriptors. (Other SELECT INTO stuff comes later.)
*/
estate->es_select_into = false;
if (operation == CMD_SELECT && plannedstmt->intoClause != NULL)
{
estate->es_select_into = true;
estate->es_into_oids = interpretOidsOption(plannedstmt->intoClause->options);
}
/*
* Initialize the executor's tuple table to empty.
*/
@ -749,7 +751,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_evalPlanQual = NULL;
estate->es_evTupleNull = NULL;
estate->es_evTuple = NULL;
estate->es_useEvalPlan = false;
/*
* Initialize private state information for each SubPlan. We must do this
@ -826,37 +827,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
/* Want to return the cleaned tuple type */
tupType = j->jf_cleanTupType;
/* For SELECT FOR UPDATE/SHARE, find the junk attrs now */
foreach(l, estate->es_rowMarks)
{
ExecRowMark *erm = (ExecRowMark *) lfirst(l);
char resname[32];
/* always need the ctid */
snprintf(resname, sizeof(resname), "ctid%u",
erm->prti);
erm->ctidAttNo = ExecFindJunkAttribute(j, resname);
if (!AttributeNumberIsValid(erm->ctidAttNo))
elog(ERROR, "could not find junk \"%s\" column",
resname);
/* if child relation, need tableoid too */
if (erm->rti != erm->prti)
{
snprintf(resname, sizeof(resname), "tableoid%u",
erm->prti);
erm->toidAttNo = ExecFindJunkAttribute(j, resname);
if (!AttributeNumberIsValid(erm->toidAttNo))
elog(ERROR, "could not find junk \"%s\" column",
resname);
}
}
}
else
{
estate->es_junkFilter = NULL;
if (estate->es_rowMarks)
elog(ERROR, "SELECT FOR UPDATE/SHARE, but no junk columns");
}
}
@ -1190,8 +1160,6 @@ ExecutePlan(EState *estate,
ScanDirection direction,
DestReceiver *dest)
{
JunkFilter *junkfilter;
TupleTableSlot *planSlot;
TupleTableSlot *slot;
long current_tuple_count;
@ -1216,23 +1184,14 @@ ExecutePlan(EState *estate,
/*
* Execute the plan and obtain a tuple
*/
lnext: ;
if (estate->es_useEvalPlan)
{
planSlot = EvalPlanQualNext(estate);
if (TupIsNull(planSlot))
planSlot = ExecProcNode(planstate);
}
else
planSlot = ExecProcNode(planstate);
slot = ExecProcNode(planstate);
/*
* if the tuple is null, then we assume there is nothing more to
* process so we just end the loop...
*/
if (TupIsNull(planSlot))
if (TupIsNull(slot))
break;
slot = planSlot;
/*
* If we have a junk filter, then project a new tuple with the junk
@ -1241,128 +1200,13 @@ lnext: ;
* Store this new "clean" tuple in the junkfilter's resultSlot.
* (Formerly, we stored it back over the "dirty" tuple, which is WRONG
* because that tuple slot has the wrong descriptor.)
*
* But first, extract all the junk information we need.
*/
if ((junkfilter = estate->es_junkFilter) != NULL)
{
/*
* Process any FOR UPDATE or FOR SHARE locking requested.
*/
if (estate->es_rowMarks != NIL)
{
ListCell *l;
lmark: ;
foreach(l, estate->es_rowMarks)
{
ExecRowMark *erm = lfirst(l);
Datum datum;
bool isNull;
HeapTupleData tuple;
Buffer buffer;
ItemPointerData update_ctid;
TransactionId update_xmax;
TupleTableSlot *newSlot;
LockTupleMode lockmode;
HTSU_Result test;
/* if child rel, must check whether it produced this row */
if (erm->rti != erm->prti)
{
Oid tableoid;
datum = ExecGetJunkAttribute(slot,
erm->toidAttNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
elog(ERROR, "tableoid is NULL");
tableoid = DatumGetObjectId(datum);
if (tableoid != RelationGetRelid(erm->relation))
{
/* this child is inactive right now */
ItemPointerSetInvalid(&(erm->curCtid));
continue;
}
}
/* okay, fetch the tuple by ctid */
datum = ExecGetJunkAttribute(slot,
erm->ctidAttNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
elog(ERROR, "ctid is NULL");
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
if (erm->forUpdate)
lockmode = LockTupleExclusive;
else
lockmode = LockTupleShared;
test = heap_lock_tuple(erm->relation, &tuple, &buffer,
&update_ctid, &update_xmax,
estate->es_output_cid,
lockmode, erm->noWait);
ReleaseBuffer(buffer);
switch (test)
{
case HeapTupleSelfUpdated:
/* treat it as deleted; do not process */
goto lnext;
case HeapTupleMayBeUpdated:
break;
case HeapTupleUpdated:
if (IsXactIsoLevelSerializable)
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
if (!ItemPointerEquals(&update_ctid,
&tuple.t_self))
{
/* updated, so look at updated version */
newSlot = EvalPlanQual(estate,
erm->rti,
planstate,
&update_ctid,
update_xmax);
if (!TupIsNull(newSlot))
{
slot = planSlot = newSlot;
estate->es_useEvalPlan = true;
goto lmark;
}
}
/*
* if tuple was deleted or PlanQual failed for
* updated tuple - we must not return this tuple!
*/
goto lnext;
default:
elog(ERROR, "unrecognized heap_lock_tuple status: %u",
test);
}
/* Remember tuple TID for WHERE CURRENT OF */
erm->curCtid = tuple.t_self;
}
}
/*
* Create a new "clean" tuple with all junk attributes removed.
*/
slot = ExecFilterJunk(junkfilter, slot);
}
if (estate->es_junkFilter != NULL)
slot = ExecFilterJunk(estate->es_junkFilter, slot);
/*
* If we are supposed to send the tuple somewhere, do so.
* (In practice this is probably always the case at this point.)
* (In practice, this is probably always the case at this point.)
*/
if (sendTuples)
(*dest->receiveSlot) (slot, dest);
@ -1509,18 +1353,104 @@ EvalPlanQual(EState *estate, Index rti,
PlanState *subplanstate,
ItemPointer tid, TransactionId priorXmax)
{
evalPlanQual *epq;
EState *epqstate;
Relation relation;
HeapTupleData tuple;
HeapTuple copyTuple = NULL;
SnapshotData SnapshotDirty;
bool endNode;
TupleTableSlot *slot;
HeapTuple copyTuple;
Assert(rti != 0);
/*
* find relation containing target tuple
* Get the updated version of the row; if fail, return NULL.
*/
copyTuple = EvalPlanQualFetch(estate, rti, tid, priorXmax);
if (copyTuple == NULL)
return NULL;
/*
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
*/
*tid = copyTuple->t_self;
/*
* Need to run a recheck subquery. Find or create a PQ stack entry.
*/
EvalPlanQualPush(estate, rti, subplanstate);
/*
* free old RTE' tuple, if any, and store target tuple where relation's
* scan node will see it
*/
EvalPlanQualSetTuple(estate, rti, copyTuple);
/*
* Run the EPQ query, but just for one tuple.
*/
slot = EvalPlanQualNext(estate);
/*
* If we got a result, we must copy it out of the EPQ query's local
* context before we shut down the EPQ query.
*/
if (TupIsNull(slot))
slot = NULL; /* in case we got back an empty slot */
else
{
TupleDesc tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
evalPlanQual *epq = estate->es_evalPlanQual;
if (epq->resultslot == NULL)
{
epq->resultslot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(epq->resultslot, tupdesc);
}
else
{
TupleDesc oldtupdesc = epq->resultslot->tts_tupleDescriptor;
ExecSetSlotDescriptor(epq->resultslot, tupdesc);
FreeTupleDesc(oldtupdesc);
}
slot = ExecCopySlot(epq->resultslot, slot);
}
/*
* Shut it down ...
*/
EvalPlanQualPop(estate, subplanstate);
return slot;
}
/*
* Fetch a copy of the newest version of an outdated tuple
*
* estate - executor state data
* rti - rangetable index of table containing tuple
* *tid - t_ctid from the outdated tuple (ie, next updated version)
* priorXmax - t_xmax from the outdated tuple
*
* Returns a palloc'd copy of the newest tuple version, or NULL if we find
* that there is no newest version (ie, the row was deleted not updated).
*
* XXX this does not lock the new row version ... wouldn't it be better if
* it did? As-is, caller might have to repeat all its work.
*/
HeapTuple
EvalPlanQualFetch(EState *estate, Index rti,
ItemPointer tid, TransactionId priorXmax)
{
HeapTuple copyTuple = NULL;
Relation relation;
HeapTupleData tuple;
SnapshotData SnapshotDirty;
Assert(rti != 0);
/*
* Find relation containing target tuple --- must be either a result
* relation of the query, or a SELECT FOR UPDATE target
*/
if (estate->es_result_relation_info != NULL &&
estate->es_result_relation_info->ri_RangeTableIndex == rti)
@ -1661,21 +1591,29 @@ EvalPlanQual(EState *estate, Index rti,
}
/*
* For UPDATE/DELETE we have to return tid of actual row we're executing
* PQ for.
* Return the copied tuple
*/
*tid = tuple.t_self;
return copyTuple;
}
/*
* Push a new level of EPQ state, and prepare to execute the given subplan
*/
void
EvalPlanQualPush(EState *estate, Index rti, PlanState *subplanstate)
{
evalPlanQual *epq;
bool endNode;
Assert(rti != 0);
/*
* Need to run a recheck subquery. Find or create a PQ stack entry.
*/
epq = estate->es_evalPlanQual;
endNode = true;
if (epq != NULL && epq->rti == 0)
{
/* Top PQ stack entry is idle, so re-use it */
Assert(!(estate->es_useEvalPlan) && epq->next == NULL);
Assert(epq->next == NULL);
epq->rti = rti;
endNode = false;
}
@ -1720,6 +1658,8 @@ EvalPlanQual(EState *estate, Index rti,
newepq->free = NULL;
newepq->estate = NULL;
newepq->planstate = NULL;
newepq->origplanstate = NULL;
newepq->resultslot = NULL;
}
else
{
@ -1736,6 +1676,7 @@ EvalPlanQual(EState *estate, Index rti,
}
Assert(epq->rti == rti);
Assert(estate->es_evalPlanQual == epq);
/*
* Ok - we're requested for the same RTE. Unfortunately we still have to
@ -1764,7 +1705,20 @@ EvalPlanQual(EState *estate, Index rti,
* es_result_relation_info) and reset locally changeable
* state in the epq (including es_param_exec_vals, es_evTupleNull).
*/
epq->origplanstate = subplanstate;
EvalPlanQualStart(epq, estate, subplanstate->plan, epq->next);
}
/*
* Install one test tuple into current EPQ level
*/
void
EvalPlanQualSetTuple(EState *estate, Index rti, HeapTuple tuple)
{
evalPlanQual *epq = estate->es_evalPlanQual;
EState *epqstate;
Assert(rti != 0);
/*
* free old RTE' tuple, if any, and store target tuple where relation's
@ -1773,12 +1727,13 @@ EvalPlanQual(EState *estate, Index rti,
epqstate = epq->estate;
if (epqstate->es_evTuple[rti - 1] != NULL)
heap_freetuple(epqstate->es_evTuple[rti - 1]);
epqstate->es_evTuple[rti - 1] = copyTuple;
return EvalPlanQualNext(estate);
epqstate->es_evTuple[rti - 1] = tuple;
}
static TupleTableSlot *
/*
* Fetch the next row (if any) from EvalPlanQual testing
*/
TupleTableSlot *
EvalPlanQualNext(EState *estate)
{
evalPlanQual *epq = estate->es_evalPlanQual;
@ -1787,39 +1742,48 @@ EvalPlanQualNext(EState *estate)
Assert(epq->rti != 0);
lpqnext:;
oldcontext = MemoryContextSwitchTo(epq->estate->es_query_cxt);
slot = ExecProcNode(epq->planstate);
MemoryContextSwitchTo(oldcontext);
/*
* No more tuples for this PQ. Continue previous one.
*/
if (TupIsNull(slot))
return slot;
}
/*
* Shut down and pop the specified level of EvalPlanQual machinery,
* plus any levels nested within it
*/
void
EvalPlanQualPop(EState *estate, PlanState *subplanstate)
{
evalPlanQual *epq = estate->es_evalPlanQual;
for (;;)
{
PlanState *epqplanstate = epq->origplanstate;
evalPlanQual *oldepq;
Assert(epq->rti != 0);
/* stop execution */
EvalPlanQualStop(epq);
epq->origplanstate = NULL;
/* pop old PQ from the stack */
oldepq = epq->next;
if (oldepq == NULL)
{
/* this is the first (oldest) PQ - mark as free */
epq->rti = 0;
estate->es_useEvalPlan = false;
/* and continue Query execution */
return NULL;
break;
}
Assert(oldepq->rti != 0);
/* push current PQ to freePQ stack */
oldepq->free = epq;
epq = oldepq;
estate->es_evalPlanQual = epq;
goto lpqnext;
if (epqplanstate == subplanstate)
break;
}
return slot;
}
static void
@ -1839,13 +1803,13 @@ EndEvalPlanQual(EState *estate)
/* stop execution */
EvalPlanQualStop(epq);
epq->origplanstate = NULL;
/* pop old PQ from the stack */
oldepq = epq->next;
if (oldepq == NULL)
{
/* this is the first (oldest) PQ - mark as free */
epq->rti = 0;
estate->es_useEvalPlan = false;
break;
}
Assert(oldepq->rti != 0);
@ -1887,11 +1851,11 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, Plan *planTree,
epqstate->es_snapshot = estate->es_snapshot;
epqstate->es_crosscheck_snapshot = estate->es_crosscheck_snapshot;
epqstate->es_range_table = estate->es_range_table;
epqstate->es_junkFilter = estate->es_junkFilter;
epqstate->es_output_cid = estate->es_output_cid;
epqstate->es_result_relations = estate->es_result_relations;
epqstate->es_num_result_relations = estate->es_num_result_relations;
epqstate->es_result_relation_info = estate->es_result_relation_info;
epqstate->es_junkFilter = estate->es_junkFilter;
/* es_trig_target_relations must NOT be copied */
epqstate->es_param_list_info = estate->es_param_list_info;
if (estate->es_plannedstmt->nParamExec > 0)
@ -2005,24 +1969,6 @@ EvalPlanQualStop(evalPlanQual *epq)
epq->planstate = NULL;
}
/*
* ExecGetActivePlanTree --- get the active PlanState tree from a QueryDesc
*
* Ordinarily this is just the one mentioned in the QueryDesc, but if we
* are looking at a row returned by the EvalPlanQual machinery, we need
* to look at the subsidiary state instead.
*/
PlanState *
ExecGetActivePlanTree(QueryDesc *queryDesc)
{
EState *estate = queryDesc->estate;
if (estate && estate->es_useEvalPlan && estate->es_evalPlanQual != NULL)
return estate->es_evalPlanQual->planstate;
else
return queryDesc->planstate;
}
/*
* Support for SELECT INTO (a/k/a CREATE TABLE AS)

View File

@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.67 2009/10/10 01:43:47 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.68 2009/10/12 18:10:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -91,6 +91,7 @@
#include "executor/nodeHashjoin.h"
#include "executor/nodeIndexscan.h"
#include "executor/nodeLimit.h"
#include "executor/nodeLockRows.h"
#include "executor/nodeMaterial.h"
#include "executor/nodeMergejoin.h"
#include "executor/nodeModifyTable.h"
@ -286,6 +287,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
estate, eflags);
break;
case T_LockRows:
result = (PlanState *) ExecInitLockRows((LockRows *) node,
estate, eflags);
break;
case T_Limit:
result = (PlanState *) ExecInitLimit((Limit *) node,
estate, eflags);
@ -456,6 +462,10 @@ ExecProcNode(PlanState *node)
result = ExecSetOp((SetOpState *) node);
break;
case T_LockRowsState:
result = ExecLockRows((LockRowsState *) node);
break;
case T_LimitState:
result = ExecLimit((LimitState *) node);
break;
@ -676,6 +686,10 @@ ExecEndNode(PlanState *node)
ExecEndSetOp((SetOpState *) node);
break;
case T_LockRowsState:
ExecEndLockRows((LockRowsState *) node);
break;
case T_LimitState:
ExecEndLimit((LimitState *) node);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.163 2009/10/08 22:34:57 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.164 2009/10/12 18:10:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -106,14 +106,14 @@ CreateExecutorState(void)
estate->es_crosscheck_snapshot = InvalidSnapshot; /* no crosscheck */
estate->es_range_table = NIL;
estate->es_junkFilter = NULL;
estate->es_output_cid = (CommandId) 0;
estate->es_result_relations = NULL;
estate->es_num_result_relations = 0;
estate->es_result_relation_info = NULL;
estate->es_junkFilter = NULL;
estate->es_trig_target_relations = NIL;
estate->es_trig_tuple_slot = NULL;
@ -124,9 +124,10 @@ CreateExecutorState(void)
estate->es_tupleTable = NIL;
estate->es_rowMarks = NIL;
estate->es_processed = 0;
estate->es_lastoid = InvalidOid;
estate->es_rowMarks = NIL;
estate->es_instrument = false;
estate->es_select_into = false;
@ -142,7 +143,6 @@ CreateExecutorState(void)
estate->es_evalPlanQual = NULL;
estate->es_evTupleNull = NULL;
estate->es_evTuple = NULL;
estate->es_useEvalPlan = false;
/*
* Return the executor state structure

View File

@ -0,0 +1,342 @@
/*-------------------------------------------------------------------------
*
* nodeLockRows.c
* Routines to handle FOR UPDATE/FOR SHARE row locking
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeLockRows.c,v 1.1 2009/10/12 18:10:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* ExecLockRows - fetch locked rows
* ExecInitLockRows - initialize node and subnodes..
* ExecEndLockRows - shutdown node and subnodes
*/
#include "postgres.h"
#include "access/xact.h"
#include "executor/executor.h"
#include "executor/nodeLockRows.h"
#include "storage/bufmgr.h"
/* ----------------------------------------------------------------
* ExecLockRows
* ----------------------------------------------------------------
*/
TupleTableSlot * /* return: a tuple or NULL */
ExecLockRows(LockRowsState *node)
{
TupleTableSlot *slot;
EState *estate;
PlanState *outerPlan;
bool epq_pushed;
ListCell *lc;
/*
* get information from the node
*/
estate = node->ps.state;
outerPlan = outerPlanState(node);
/*
* Get next tuple from subplan, if any; but if we are evaluating
* an EvalPlanQual substitution, first finish that.
*/
lnext:
if (node->lr_useEvalPlan)
{
slot = EvalPlanQualNext(estate);
if (TupIsNull(slot))
{
EvalPlanQualPop(estate, outerPlan);
node->lr_useEvalPlan = false;
slot = ExecProcNode(outerPlan);
}
}
else
slot = ExecProcNode(outerPlan);
if (TupIsNull(slot))
return NULL;
/*
* Attempt to lock the source tuple(s).
*/
epq_pushed = false;
foreach(lc, node->lr_rowMarks)
{
ExecRowMark *erm = (ExecRowMark *) lfirst(lc);
Datum datum;
bool isNull;
HeapTupleData tuple;
Buffer buffer;
ItemPointerData update_ctid;
TransactionId update_xmax;
LockTupleMode lockmode;
HTSU_Result test;
HeapTuple copyTuple;
/* if child rel, must check whether it produced this row */
if (erm->rti != erm->prti)
{
Oid tableoid;
datum = ExecGetJunkAttribute(slot,
erm->toidAttNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
elog(ERROR, "tableoid is NULL");
tableoid = DatumGetObjectId(datum);
if (tableoid != RelationGetRelid(erm->relation))
{
/* this child is inactive right now */
ItemPointerSetInvalid(&(erm->curCtid));
continue;
}
}
/* fetch the tuple's ctid */
datum = ExecGetJunkAttribute(slot,
erm->ctidAttNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
elog(ERROR, "ctid is NULL");
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
/* okay, try to lock the tuple */
if (erm->forUpdate)
lockmode = LockTupleExclusive;
else
lockmode = LockTupleShared;
test = heap_lock_tuple(erm->relation, &tuple, &buffer,
&update_ctid, &update_xmax,
estate->es_output_cid,
lockmode, erm->noWait);
ReleaseBuffer(buffer);
switch (test)
{
case HeapTupleSelfUpdated:
/* treat it as deleted; do not process */
if (epq_pushed)
EvalPlanQualPop(estate, outerPlan);
goto lnext;
case HeapTupleMayBeUpdated:
/* got the lock successfully */
break;
case HeapTupleUpdated:
if (IsXactIsoLevelSerializable)
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
if (ItemPointerEquals(&update_ctid,
&tuple.t_self))
{
/* Tuple was deleted, so don't return it */
if (epq_pushed)
EvalPlanQualPop(estate, outerPlan);
goto lnext;
}
/* updated, so look at updated version */
copyTuple = EvalPlanQualFetch(estate, erm->rti,
&update_ctid, update_xmax);
if (copyTuple == NULL)
{
/* Tuple was deleted, so don't return it */
if (epq_pushed)
EvalPlanQualPop(estate, outerPlan);
goto lnext;
}
/*
* Need to run a recheck subquery.
* Find or create a PQ stack entry.
*/
if (!epq_pushed)
{
EvalPlanQualPush(estate, erm->rti, outerPlan);
epq_pushed = true;
}
/* Store target tuple for relation's scan node */
EvalPlanQualSetTuple(estate, erm->rti, copyTuple);
/* Continue loop until we have all target tuples */
break;
default:
elog(ERROR, "unrecognized heap_lock_tuple status: %u",
test);
}
/* Remember locked tuple's TID for WHERE CURRENT OF */
erm->curCtid = tuple.t_self;
}
/* If we need to do EvalPlanQual testing, loop back to do that */
if (epq_pushed)
{
node->lr_useEvalPlan = true;
goto lnext;
}
/* Got all locks, so return the current tuple */
return slot;
}
/* ----------------------------------------------------------------
* ExecInitLockRows
*
* This initializes the LockRows node state structures and
* the node's subplan.
* ----------------------------------------------------------------
*/
LockRowsState *
ExecInitLockRows(LockRows *node, EState *estate, int eflags)
{
LockRowsState *lrstate;
Plan *outerPlan;
JunkFilter *j;
ListCell *lc;
/* check for unsupported flags */
Assert(!(eflags & EXEC_FLAG_MARK));
/*
* create state structure
*/
lrstate = makeNode(LockRowsState);
lrstate->ps.plan = (Plan *) node;
lrstate->ps.state = estate;
lrstate->lr_useEvalPlan = false;
/*
* Miscellaneous initialization
*
* LockRows nodes never call ExecQual or ExecProject.
*/
/*
* Tuple table initialization (XXX not actually used...)
*/
ExecInitResultTupleSlot(estate, &lrstate->ps);
/*
* then initialize outer plan
*/
outerPlan = outerPlan(node);
outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags);
/*
* LockRows nodes do no projections, so initialize projection info for this
* node appropriately
*/
ExecAssignResultTypeFromTL(&lrstate->ps);
lrstate->ps.ps_ProjInfo = NULL;
/*
* Initialize a junkfilter that we'll use to extract the ctid junk
* attributes. (We won't actually apply the filter to remove the
* junk, we just pass the rows on as-is. This is because the
* junkfilter isn't smart enough to not remove junk attrs that
* might be needed further up.)
*/
j = ExecInitJunkFilter(outerPlan->targetlist, false,
ExecInitExtraTupleSlot(estate));
lrstate->lr_junkFilter = j;
/*
* Locate the ExecRowMark(s) that this node is responsible for.
* (InitPlan should already have built the global list of ExecRowMarks.)
*/
lrstate->lr_rowMarks = NIL;
foreach(lc, node->rowMarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(lc);
ExecRowMark *erm = NULL;
char resname[32];
ListCell *lce;
/* ignore "parent" rowmarks; they are irrelevant at runtime */
if (rc->isParent)
continue;
foreach(lce, estate->es_rowMarks)
{
erm = (ExecRowMark *) lfirst(lce);
if (erm->rti == rc->rti &&
erm->prti == rc->prti &&
erm->rowmarkId == rc->rowmarkId)
break;
erm = NULL;
}
if (erm == NULL)
elog(ERROR, "failed to find ExecRowMark for RowMarkClause");
if (AttributeNumberIsValid(erm->ctidAttNo))
elog(ERROR, "ExecRowMark is already claimed");
/* Locate the junk attribute columns in the subplan output */
/* always need the ctid */
snprintf(resname, sizeof(resname), "ctid%u", erm->rowmarkId);
erm->ctidAttNo = ExecFindJunkAttribute(j, resname);
if (!AttributeNumberIsValid(erm->ctidAttNo))
elog(ERROR, "could not find junk \"%s\" column",
resname);
/* if child relation, need tableoid too */
if (erm->rti != erm->prti)
{
snprintf(resname, sizeof(resname), "tableoid%u", erm->rowmarkId);
erm->toidAttNo = ExecFindJunkAttribute(j, resname);
if (!AttributeNumberIsValid(erm->toidAttNo))
elog(ERROR, "could not find junk \"%s\" column",
resname);
}
lrstate->lr_rowMarks = lappend(lrstate->lr_rowMarks, erm);
}
return lrstate;
}
/* ----------------------------------------------------------------
* ExecEndLockRows
*
* This shuts down the subplan and frees resources allocated
* to this node.
* ----------------------------------------------------------------
*/
void
ExecEndLockRows(LockRowsState *node)
{
ExecEndNode(outerPlanState(node));
}
void
ExecReScanLockRows(LockRowsState *node, ExprContext *exprCtxt)
{
node->lr_useEvalPlan = false;
/*
* if chgParam of subnode is not null then plan will be re-scanned by
* first ExecProcNode.
*/
if (((PlanState *) node)->lefttree->chgParam == NULL)
ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
}

View File

@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.41 2009/09/27 21:10:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.42 2009/10/12 18:10:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -99,11 +99,12 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
/*
* SubqueryScan should not have any "normal" children. Also, if planner
* left anything in subrtable, it's fishy.
* left anything in subrtable/subrowmark, it's fishy.
*/
Assert(outerPlan(node) == NULL);
Assert(innerPlan(node) == NULL);
Assert(node->subrtable == NIL);
Assert(node->subrowmark == NIL);
/*
* create state structure

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.443 2009/10/10 01:43:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.444 2009/10/12 18:10:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -423,6 +423,7 @@ _copySubqueryScan(SubqueryScan *from)
*/
COPY_NODE_FIELD(subplan);
COPY_NODE_FIELD(subrtable);
COPY_NODE_FIELD(subrowmark);
return newnode;
}
@ -794,6 +795,27 @@ _copySetOp(SetOp *from)
return newnode;
}
/*
* _copyLockRows
*/
static LockRows *
_copyLockRows(LockRows *from)
{
LockRows *newnode = makeNode(LockRows);
/*
* copy node superclass fields
*/
CopyPlanFields((Plan *) from, (Plan *) newnode);
/*
* copy remainder of node
*/
COPY_NODE_FIELD(rowMarks);
return newnode;
}
/*
* _copyLimit
*/
@ -1813,6 +1835,7 @@ _copyRowMarkClause(RowMarkClause *from)
COPY_SCALAR_FIELD(rti);
COPY_SCALAR_FIELD(prti);
COPY_SCALAR_FIELD(rowmarkId);
COPY_SCALAR_FIELD(forUpdate);
COPY_SCALAR_FIELD(noWait);
COPY_SCALAR_FIELD(isParent);
@ -3589,6 +3612,9 @@ copyObject(void *from)
case T_SetOp:
retval = _copySetOp(from);
break;
case T_LockRows:
retval = _copyLockRows(from);
break;
case T_Limit:
retval = _copyLimit(from);
break;

View File

@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.365 2009/10/08 02:39:20 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.366 2009/10/12 18:10:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -2194,6 +2194,7 @@ _equalRowMarkClause(RowMarkClause *a, RowMarkClause *b)
{
COMPARE_SCALAR_FIELD(rti);
COMPARE_SCALAR_FIELD(prti);
COMPARE_SCALAR_FIELD(rowmarkId);
COMPARE_SCALAR_FIELD(forUpdate);
COMPARE_SCALAR_FIELD(noWait);
COMPARE_SCALAR_FIELD(isParent);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.367 2009/10/10 01:43:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.368 2009/10/12 18:10:45 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@ -454,6 +454,7 @@ _outSubqueryScan(StringInfo str, SubqueryScan *node)
WRITE_NODE_FIELD(subplan);
WRITE_NODE_FIELD(subrtable);
WRITE_NODE_FIELD(subrowmark);
}
static void
@ -720,6 +721,16 @@ _outSetOp(StringInfo str, SetOp *node)
WRITE_LONG_FIELD(numGroups);
}
static void
_outLockRows(StringInfo str, LockRows *node)
{
WRITE_NODE_TYPE("LOCKROWS");
_outPlanInfo(str, (Plan *) node);
WRITE_NODE_FIELD(rowMarks);
}
static void
_outLimit(StringInfo str, Limit *node)
{
@ -1494,11 +1505,14 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node)
WRITE_NODE_FIELD(paramlist);
WRITE_NODE_FIELD(subplans);
WRITE_NODE_FIELD(subrtables);
WRITE_NODE_FIELD(subrowmarks);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(finalrtable);
WRITE_NODE_FIELD(finalrowmarks);
WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems);
WRITE_UINT_FIELD(lastPHId);
WRITE_UINT_FIELD(lastRowmarkId);
WRITE_BOOL_FIELD(transientPlan);
}
@ -1561,6 +1575,7 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
WRITE_FLOAT_FIELD(tuples, "%.0f");
WRITE_NODE_FIELD(subplan);
WRITE_NODE_FIELD(subrtable);
WRITE_NODE_FIELD(subrowmark);
WRITE_NODE_FIELD(baserestrictinfo);
WRITE_NODE_FIELD(joininfo);
WRITE_BOOL_FIELD(has_eclass_joins);
@ -2001,6 +2016,7 @@ _outRowMarkClause(StringInfo str, RowMarkClause *node)
WRITE_UINT_FIELD(rti);
WRITE_UINT_FIELD(prti);
WRITE_UINT_FIELD(rowmarkId);
WRITE_BOOL_FIELD(forUpdate);
WRITE_BOOL_FIELD(noWait);
WRITE_BOOL_FIELD(isParent);
@ -2503,6 +2519,9 @@ _outNode(StringInfo str, void *obj)
case T_SetOp:
_outSetOp(str, obj);
break;
case T_LockRows:
_outLockRows(str, obj);
break;
case T_Limit:
_outLimit(str, obj);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.224 2009/10/08 02:39:21 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.225 2009/10/12 18:10:45 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@ -294,6 +294,7 @@ _readRowMarkClause(void)
READ_UINT_FIELD(rti);
READ_UINT_FIELD(prti);
READ_UINT_FIELD(rowmarkId);
READ_BOOL_FIELD(forUpdate);
READ_BOOL_FIELD(noWait);
READ_BOOL_FIELD(isParent);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.186 2009/09/17 20:49:28 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.187 2009/10/12 18:10:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -632,6 +632,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
false, tuple_fraction,
&subroot);
rel->subrtable = subroot->parse->rtable;
rel->subrowmark = subroot->parse->rowMarks;
/* Copy number of output rows from subplan */
rel->tuples = rel->subplan->plan_rows;
@ -971,10 +972,10 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
* since that could change the set of rows returned.
*
* 2. If the subquery contains any window functions, we can't push quals
* into it, because that would change the results.
* into it, because that could change the results.
*
* 3. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
* quals into it, because that would change the results.
* quals into it, because that could change the results.
*
* 4. For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
* push quals into each component query, but the quals can only reference

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.264 2009/10/10 01:43:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.265 2009/10/12 18:10:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1336,7 +1336,8 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
scan_clauses,
scan_relid,
best_path->parent->subplan,
best_path->parent->subrtable);
best_path->parent->subrtable,
best_path->parent->subrowmark);
copy_path_costsize(&scan_plan->scan.plan, best_path);
@ -2508,7 +2509,8 @@ make_subqueryscan(List *qptlist,
List *qpqual,
Index scanrelid,
Plan *subplan,
List *subrtable)
List *subrtable,
List *subrowmark)
{
SubqueryScan *node = makeNode(SubqueryScan);
Plan *plan = &node->scan.plan;
@ -2528,6 +2530,7 @@ make_subqueryscan(List *qptlist,
node->scan.scanrelid = scanrelid;
node->subplan = subplan;
node->subrtable = subrtable;
node->subrowmark = subrowmark;
return node;
}
@ -3590,6 +3593,31 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
return node;
}
/*
* make_lockrows
* Build a LockRows plan node
*/
LockRows *
make_lockrows(Plan *lefttree, List *rowMarks)
{
LockRows *node = makeNode(LockRows);
Plan *plan = &node->plan;
copy_plan_costsize(plan, lefttree);
/* charge cpu_tuple_cost to reflect locking costs (underestimate?) */
plan->total_cost += cpu_tuple_cost * plan->plan_rows;
plan->targetlist = lefttree->targetlist;
plan->qual = NIL;
plan->lefttree = lefttree;
plan->righttree = NULL;
node->rowMarks = rowMarks;
return node;
}
/*
* Note: offset_est and count_est are passed in to save having to repeat
* work already done to estimate the values of the limitOffset and limitCount
@ -3792,6 +3820,7 @@ is_projection_capable_plan(Plan *plan)
case T_Sort:
case T_Unique:
case T_SetOp:
case T_LockRows:
case T_Limit:
case T_ModifyTable:
case T_Append:

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.258 2009/10/10 01:43:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.259 2009/10/12 18:10:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -132,7 +132,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
PlannerInfo *root;
Plan *top_plan;
ListCell *lp,
*lr;
*lrt,
*lrm;
/* Cursor options may come from caller or from DECLARE CURSOR stmt */
if (parse->utilityStmt &&
@ -151,11 +152,14 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->paramlist = NIL;
glob->subplans = NIL;
glob->subrtables = NIL;
glob->subrowmarks = NIL;
glob->rewindPlanIDs = NULL;
glob->finalrtable = NIL;
glob->finalrowmarks = NIL;
glob->relationOids = NIL;
glob->invalItems = NIL;
glob->lastPHId = 0;
glob->lastRowmarkId = 0;
glob->transientPlan = false;
/* Determine what fraction of the plan is likely to be scanned */
@ -202,15 +206,25 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
/* final cleanup of the plan */
Assert(glob->finalrtable == NIL);
top_plan = set_plan_references(glob, top_plan, root->parse->rtable);
Assert(glob->finalrowmarks == NIL);
top_plan = set_plan_references(glob, top_plan,
root->parse->rtable,
root->parse->rowMarks);
/* ... and the subplans (both regular subplans and initplans) */
Assert(list_length(glob->subplans) == list_length(glob->subrtables));
forboth(lp, glob->subplans, lr, glob->subrtables)
Assert(list_length(glob->subplans) == list_length(glob->subrowmarks));
lrt = list_head(glob->subrtables);
lrm = list_head(glob->subrowmarks);
foreach(lp, glob->subplans)
{
Plan *subplan = (Plan *) lfirst(lp);
List *subrtable = (List *) lfirst(lr);
List *subrtable = (List *) lfirst(lrt);
List *subrowmark = (List *) lfirst(lrm);
lfirst(lp) = set_plan_references(glob, subplan, subrtable);
lfirst(lp) = set_plan_references(glob, subplan,
subrtable, subrowmark);
lrt = lnext(lrt);
lrm = lnext(lrm);
}
/* build the PlannedStmt result */
@ -227,7 +241,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->intoClause = parse->intoClause;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = parse->rowMarks;
result->rowMarks = glob->finalrowmarks;
result->relationOids = glob->relationOids;
result->invalItems = glob->invalItems;
result->nParamExec = list_length(glob->paramlist);
@ -349,6 +363,21 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
}
}
/*
* Assign unique IDs (unique within this planner run) to RowMarkClauses.
* We can't identify them just by RT index because that will change
* during final rtable flattening, and we don't want to have to go back
* and change the resnames assigned to junk CTID tlist entries at that
* point. Do it now before expanding inheritance sets, because child
* relations should inherit their parents' rowmarkId.
*/
foreach(l, parse->rowMarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
rc->rowmarkId = ++(root->glob->lastRowmarkId);
}
/*
* Expand any rangetable entries that are inheritance sets into "append
* relations". This can add entries to the rangetable, but they must be
@ -1588,7 +1617,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
}
/*
* Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node.
* If there is a LIMIT/OFFSET clause, add the LIMIT node.
*/
if (parse->limitCount || parse->limitOffset)
{
@ -1599,6 +1628,15 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
count_est);
}
/*
* Finally, if there is a FOR UPDATE/SHARE clause, add the LockRows node.
*/
if (parse->rowMarks)
{
result_plan = (Plan *) make_lockrows(result_plan,
parse->rowMarks);
}
/* Compute result-relations list if needed */
if (parse->resultRelation)
root->resultRelations = list_make1_int(parse->resultRelation);

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.151 2009/10/10 01:43:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.152 2009/10/12 18:10:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -166,12 +166,14 @@ static bool extract_query_dependencies_walker(Node *node,
* glob: global data for planner run
* plan: the topmost node of the plan
* rtable: the rangetable for the current subquery
* rowmarks: the RowMarkClause list for the current subquery
*
* The return value is normally the same Plan node passed in, but can be
* different when the passed-in Plan is a SubqueryScan we decide isn't needed.
*
* The flattened rangetable entries are appended to glob->finalrtable, and
* plan dependencies are appended to glob->relationOids (for relations)
* The flattened rangetable entries are appended to glob->finalrtable,
* and we also append rowmarks entries to glob->finalrowmarks.
* Plan dependencies are appended to glob->relationOids (for relations)
* and glob->invalItems (for everything else).
*
* Notice that we modify Plan nodes in-place, but use expression_tree_mutator
@ -180,7 +182,8 @@ static bool extract_query_dependencies_walker(Node *node,
* it's not so safe to assume that for expression tree nodes.
*/
Plan *
set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable)
set_plan_references(PlannerGlobal *glob, Plan *plan,
List *rtable, List *rowmarks)
{
int rtoffset = list_length(glob->finalrtable);
ListCell *lc;
@ -230,6 +233,26 @@ set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable)
newrte->relid);
}
/*
* Adjust RT indexes of RowMarkClauses and add to final rowmarks list
*/
foreach(lc, rowmarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(lc);
RowMarkClause *newrc;
/* flat copy to duplicate all the scalar fields */
newrc = (RowMarkClause *) palloc(sizeof(RowMarkClause));
memcpy(newrc, rc, sizeof(RowMarkClause));
/* adjust indexes */
newrc->rti += rtoffset;
newrc->prti += rtoffset;
/* rowmarkId must NOT be adjusted */
glob->finalrowmarks = lappend(glob->finalrowmarks, newrc);
}
/* Now fix the Plan tree */
return set_plan_refs(glob, plan, rtoffset);
}
@ -396,6 +419,27 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
*/
Assert(plan->qual == NIL);
break;
case T_LockRows:
{
LockRows *splan = (LockRows *) plan;
/*
* Like the plan types above, LockRows doesn't evaluate its
* tlist or quals. But we have to fix up the RT indexes
* in its rowmarks.
*/
set_dummy_tlist_references(plan, rtoffset);
Assert(splan->plan.qual == NIL);
foreach(l, splan->rowMarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
rc->rti += rtoffset;
rc->prti += rtoffset;
}
}
break;
case T_Limit:
{
Limit *splan = (Limit *) plan;
@ -553,10 +597,12 @@ set_subqueryscan_references(PlannerGlobal *glob,
Plan *result;
/* First, recursively process the subplan */
plan->subplan = set_plan_references(glob, plan->subplan, plan->subrtable);
plan->subplan = set_plan_references(glob, plan->subplan,
plan->subrtable, plan->subrowmark);
/* subrtable is no longer needed in the plan tree */
/* subrtable/subrowmark are no longer needed in the plan tree */
plan->subrtable = NIL;
plan->subrowmark = NIL;
if (trivial_subqueryscan(plan))
{

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.154 2009/10/10 01:43:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.155 2009/10/12 18:10:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,7 +53,8 @@ typedef struct finalize_primnode_context
} finalize_primnode_context;
static Node *build_subplan(PlannerInfo *root, Plan *plan, List *rtable,
static Node *build_subplan(PlannerInfo *root, Plan *plan,
List *rtable, List *rowmarks,
SubLinkType subLinkType, Node *testexpr,
bool adjust_testexpr, bool unknownEqFalse);
static List *generate_subquery_params(PlannerInfo *root, List *tlist,
@ -333,7 +334,8 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
&subroot);
/* And convert to SubPlan or InitPlan format. */
result = build_subplan(root, plan, subroot->parse->rtable,
result = build_subplan(root, plan,
subroot->parse->rtable, subroot->parse->rowMarks,
subLinkType, testexpr, true, isTopQual);
/*
@ -375,6 +377,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
/* OK, convert to SubPlan format. */
hashplan = (SubPlan *) build_subplan(root, plan,
subroot->parse->rtable,
subroot->parse->rowMarks,
ANY_SUBLINK, newtestexpr,
false, true);
/* Check we got what we expected */
@ -402,7 +405,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
* as explained in the comments for make_subplan.
*/
static Node *
build_subplan(PlannerInfo *root, Plan *plan, List *rtable,
build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
SubLinkType subLinkType, Node *testexpr,
bool adjust_testexpr, bool unknownEqFalse)
{
@ -585,6 +588,7 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable,
*/
root->glob->subplans = lappend(root->glob->subplans, plan);
root->glob->subrtables = lappend(root->glob->subrtables, rtable);
root->glob->subrowmarks = lappend(root->glob->subrowmarks, rowmarks);
splan->plan_id = list_length(root->glob->subplans);
if (isInitPlan)
@ -944,6 +948,8 @@ SS_process_ctes(PlannerInfo *root)
root->glob->subplans = lappend(root->glob->subplans, plan);
root->glob->subrtables = lappend(root->glob->subrtables,
subroot->parse->rtable);
root->glob->subrowmarks = lappend(root->glob->subrowmarks,
subroot->parse->rowMarks);
splan->plan_id = list_length(root->glob->subplans);
root->init_plans = lappend(root->init_plans, splan);
@ -2035,6 +2041,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
case T_Unique:
case T_SetOp:
case T_Group:
case T_LockRows:
break;
default:
@ -2191,6 +2198,8 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
plan);
root->glob->subrtables = lappend(root->glob->subrtables,
root->parse->rtable);
root->glob->subrowmarks = lappend(root->glob->subrowmarks,
root->parse->rowMarks);
/*
* Create a SubPlan node and add it to the outer list of InitPlans. Note

View File

@ -16,7 +16,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.96 2009/04/19 19:46:33 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.97 2009/10/12 18:10:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -123,25 +123,20 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
*/
CheckSelectLocking(parse);
/*
* Currently the executor only supports FOR UPDATE/SHARE at top level
*/
if (root->query_level > 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SELECT FOR UPDATE/SHARE is not allowed in subqueries")));
foreach(l, parse->rowMarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
Var *var;
char *resname;
char resname[32];
TargetEntry *tle;
/* ignore child rels */
if (rc->rti != rc->prti)
continue;
/* we should have an ID for the RowMarkClause */
Assert(rc->rowmarkId != 0);
/* always need the ctid */
var = makeVar(rc->rti,
SelfItemPointerAttributeNumber,
@ -149,12 +144,12 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
-1,
0);
resname = (char *) palloc(32);
snprintf(resname, 32, "ctid%u", rc->rti);
snprintf(resname, sizeof(resname),
"ctid%u", rc->rowmarkId);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
resname,
pstrdup(resname),
true);
tlist = lappend(tlist, tle);
@ -168,12 +163,12 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
-1,
0);
resname = (char *) palloc(32);
snprintf(resname, 32, "tableoid%u", rc->rti);
snprintf(resname, sizeof(resname),
"tableoid%u", rc->rowmarkId);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
resname,
pstrdup(resname),
true);
tlist = lappend(tlist, tle);

View File

@ -22,7 +22,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.175 2009/10/10 01:43:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.176 2009/10/12 18:10:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -247,7 +247,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
NIL,
rtr->rtindex,
subplan,
subroot->parse->rtable);
subroot->parse->rtable,
subroot->parse->rowMarks);
/*
* We don't bother to determine the subquery's output ordering since
@ -1281,6 +1282,8 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
newrc->rti = childRTindex;
newrc->prti = rti;
/* children use the same rowmarkId as their parent */
newrc->rowmarkId = oldrc->rowmarkId;
newrc->forUpdate = oldrc->forUpdate;
newrc->noWait = oldrc->noWait;
newrc->isParent = false;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.94 2009/06/11 14:48:59 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.95 2009/10/12 18:10:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -84,6 +84,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->tuples = 0;
rel->subplan = NULL;
rel->subrtable = NIL;
rel->subrowmark = NIL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost.startup = 0;
rel->baserestrictcost.per_tuple = 0;
@ -337,6 +338,7 @@ build_join_rel(PlannerInfo *root,
joinrel->tuples = 0;
joinrel->subplan = NULL;
joinrel->subrtable = NIL;
joinrel->subrowmark = NIL;
joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost.startup = 0;
joinrel->baserestrictcost.per_tuple = 0;

View File

@ -17,7 +17,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.391 2009/09/09 03:32:52 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.392 2009/10/12 18:10:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1388,7 +1388,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
/*
* If an internal node of a set-op tree has ORDER BY, UPDATE, or LIMIT
* If an internal node of a set-op tree has ORDER BY, LIMIT, or FOR UPDATE
* clauses attached, we need to treat it like a leaf node to generate an
* independent sub-Query tree. Otherwise, it can be represented by a
* SetOperationStmt node underneath the parent Query.
@ -2251,6 +2251,7 @@ applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
rc = makeNode(RowMarkClause);
rc->rti = rtindex;
rc->prti = rtindex;
rc->rowmarkId = 0; /* not used until plan time */
rc->forUpdate = forUpdate;
rc->noWait = noWait;
rc->isParent = false;

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.544 2009/10/09 21:02:56 petere Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.545 2009/10/12 18:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200910101
#define CATALOG_VERSION_NO 200910121
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.161 2009/10/10 01:43:50 tgl Exp $
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.162 2009/10/12 18:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -169,7 +169,13 @@ extern void ExecConstraints(ResultRelInfo *resultRelInfo,
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
PlanState *subplanstate,
ItemPointer tid, TransactionId priorXmax);
extern PlanState *ExecGetActivePlanTree(QueryDesc *queryDesc);
extern HeapTuple EvalPlanQualFetch(EState *estate, Index rti,
ItemPointer tid, TransactionId priorXmax);
extern void EvalPlanQualPush(EState *estate, Index rti,
PlanState *subplanstate);
extern void EvalPlanQualSetTuple(EState *estate, Index rti, HeapTuple tuple);
extern TupleTableSlot *EvalPlanQualNext(EState *estate);
extern void EvalPlanQualPop(EState *estate, PlanState *subplanstate);
extern DestReceiver *CreateIntoRelDestReceiver(void);
/*

View File

@ -0,0 +1,24 @@
/*-------------------------------------------------------------------------
*
* nodeLockRows.h
*
*
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/nodeLockRows.h,v 1.1 2009/10/12 18:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef NODELOCKROWS_H
#define NODELOCKROWS_H
#include "nodes/execnodes.h"
extern LockRowsState *ExecInitLockRows(LockRows *node, EState *estate, int eflags);
extern TupleTableSlot *ExecLockRows(LockRowsState *node);
extern void ExecEndLockRows(LockRowsState *node);
extern void ExecReScanLockRows(LockRowsState *node, ExprContext *exprCtxt);
#endif /* NODELOCKROWS_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.209 2009/10/10 01:43:50 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.210 2009/10/12 18:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -332,6 +332,8 @@ typedef struct EState
Snapshot es_crosscheck_snapshot; /* crosscheck time qual for RI */
List *es_range_table; /* List of RangeTblEntry */
JunkFilter *es_junkFilter; /* top-level junk filter, if any */
/* If query can insert/delete tuples, the command ID to mark them with */
CommandId es_output_cid;
@ -339,7 +341,6 @@ typedef struct EState
ResultRelInfo *es_result_relations; /* array of ResultRelInfos */
int es_num_result_relations; /* length of array */
ResultRelInfo *es_result_relation_info; /* currently active array elt */
JunkFilter *es_junkFilter; /* top-level junk filter, if any */
/* Stuff used for firing triggers: */
List *es_trig_target_relations; /* trigger-only ResultRelInfos */
@ -354,9 +355,10 @@ typedef struct EState
List *es_tupleTable; /* List of TupleTableSlots */
List *es_rowMarks; /* List of ExecRowMarks */
uint32 es_processed; /* # of tuples processed */
Oid es_lastoid; /* last oid processed (by INSERT) */
List *es_rowMarks; /* not good place, but there is no other */
bool es_instrument; /* true requests runtime instrumentation */
bool es_select_into; /* true if doing SELECT INTO */
@ -378,19 +380,22 @@ typedef struct EState
struct evalPlanQual *es_evalPlanQual; /* chain of PlanQual states */
bool *es_evTupleNull; /* local array of EPQ status */
HeapTuple *es_evTuple; /* shared array of EPQ substitute tuples */
bool es_useEvalPlan; /* evaluating EPQ tuples? */
} EState;
/*
* es_rowMarks is a list of these structs. See RowMarkClause for details
* about rti and prti. toidAttno is not used in a "plain" rowmark.
* es_rowMarks is a list of these structs. Each LockRows node has its own
* list, which is the subset of locks that it is supposed to enforce; note
* that the per-node lists point to the same structs that are in the global
* list. See RowMarkClause for details about rti, prti, and rowmarkId.
* toidAttno is not used in a "plain" (non-inherited) rowmark.
*/
typedef struct ExecRowMark
{
Relation relation; /* opened and RowShareLock'd relation */
Index rti; /* its range table index */
Index prti; /* parent range table index, if child */
Index rowmarkId; /* unique identifier assigned by planner */
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
bool noWait; /* NOWAIT option */
AttrNumber ctidAttNo; /* resno of its ctid junk attribute */
@ -1636,6 +1641,20 @@ typedef struct SetOpState
TupleHashIterator hashiter; /* for iterating through hash table */
} SetOpState;
/* ----------------
* LockRowsState information
*
* LockRows nodes are used to enforce FOR UPDATE/FOR SHARE locking.
* ----------------
*/
typedef struct LockRowsState
{
PlanState ps; /* its first field is NodeTag */
List *lr_rowMarks; /* List of ExecRowMarks */
JunkFilter *lr_junkFilter; /* needed for getting ctid columns */
bool lr_useEvalPlan; /* evaluating EPQ tuples? */
} LockRowsState;
/* ----------------
* LimitState information
*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.229 2009/10/10 01:43:50 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.230 2009/10/12 18:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -71,6 +71,7 @@ typedef enum NodeTag
T_Unique,
T_Hash,
T_SetOp,
T_LockRows,
T_Limit,
/* this one isn't a subclass of Plan: */
T_PlanInvalItem,
@ -110,6 +111,7 @@ typedef enum NodeTag
T_UniqueState,
T_HashState,
T_SetOpState,
T_LockRowsState,
T_LimitState,
/*

View File

@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.405 2009/10/07 22:14:26 alvherre Exp $
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.406 2009/10/12 18:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -809,12 +809,15 @@ typedef struct WindowClause
* the target rel itself in its role as a child). The child entries have
* rti == child rel's RT index, prti == parent's RT index, and can therefore
* be recognized as children by the fact that prti != rti.
* rowmarkId is a unique ID for the RowMarkClause across an entire query,
* and is assigned during planning; it's always zero upstream of the planner.
*/
typedef struct RowMarkClause
{
NodeTag type;
Index rti; /* range table index of target relation */
Index prti; /* range table index of parent relation */
Index rowmarkId; /* unique identifier assigned by planner */
bool forUpdate; /* true = FOR UPDATE, false = FOR SHARE */
bool noWait; /* NOWAIT option */
bool isParent; /* set by planner when expanding inheritance */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.111 2009/10/10 01:43:50 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.112 2009/10/12 18:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -343,7 +343,7 @@ typedef struct TidScan
*
* Note: subrtable is used just to carry the subquery rangetable from
* createplan.c to setrefs.c; it should always be NIL by the time the
* executor sees the plan.
* executor sees the plan. Similarly for subrowmark.
* ----------------
*/
typedef struct SubqueryScan
@ -351,6 +351,7 @@ typedef struct SubqueryScan
Scan scan;
Plan *subplan;
List *subrtable; /* temporary workspace for planner */
List *subrowmark; /* temporary workspace for planner */
} SubqueryScan;
/* ----------------
@ -614,6 +615,19 @@ typedef struct SetOp
long numGroups; /* estimated number of groups in input */
} SetOp;
/* ----------------
* lock-rows node
*
* rowMarks identifies the rels to be locked by this node; it should be
* a subset of the rowMarks listed in the top-level PlannedStmt.
* ----------------
*/
typedef struct LockRows
{
Plan plan;
List *rowMarks; /* a list of RowMarkClause's */
} LockRows;
/* ----------------
* limit node
*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.176 2009/10/10 01:43:50 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.177 2009/10/12 18:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -68,16 +68,22 @@ typedef struct PlannerGlobal
List *subrtables; /* Rangetables for SubPlan nodes */
List *subrowmarks; /* RowMarkClauses for SubPlan nodes */
Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */
List *finalrtable; /* "flat" rangetable for executor */
List *finalrowmarks; /* "flat" list of RowMarkClauses */
List *relationOids; /* OIDs of relations the plan depends on */
List *invalItems; /* other dependencies, as PlanInvalItems */
Index lastPHId; /* highest PlaceHolderVar ID assigned */
Index lastRowmarkId; /* highest RowMarkClause ID assigned */
bool transientPlan; /* redo plan when TransactionXmin changes? */
} PlannerGlobal;
@ -281,6 +287,7 @@ typedef struct PlannerInfo
* tuples - number of tuples in relation (not considering restrictions)
* subplan - plan for subquery (NULL if it's not a subquery)
* subrtable - rangetable for subquery (NIL if it's not a subquery)
* subrowmark - rowmarks for subquery (NIL if it's not a subquery)
*
* Note: for a subquery, tuples and subplan are not set immediately
* upon creation of the RelOptInfo object; they are filled in when
@ -364,6 +371,7 @@ typedef struct RelOptInfo
double tuples;
struct Plan *subplan; /* if subquery */
List *subrtable; /* if subquery */
List *subrowmark; /* if subquery */
/* used by various scans and joins: */
List *baserestrictinfo; /* RestrictInfo structures (if base

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.119 2009/10/10 01:43:50 tgl Exp $
* $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.120 2009/10/12 18:10:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -40,7 +40,8 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
*/
extern Plan *create_plan(PlannerInfo *root, Path *best_path);
extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
Index scanrelid, Plan *subplan, List *subrtable);
Index scanrelid, Plan *subplan,
List *subrtable, List *subrowmark);
extern Append *make_append(List *appendplans, List *tlist);
extern RecursiveUnion *make_recursive_union(List *tlist,
Plan *lefttree, Plan *righttree, int wtParam,
@ -67,6 +68,7 @@ extern Group *make_group(PlannerInfo *root, List *tlist, List *qual,
Plan *lefttree);
extern Plan *materialize_finished_plan(Plan *subplan);
extern Unique *make_unique(Plan *lefttree, List *distinctList);
extern LockRows *make_lockrows(Plan *lefttree, List *rowMarks);
extern Limit *make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
int64 offset_est, int64 count_est);
extern SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
@ -108,7 +110,8 @@ extern RestrictInfo *build_implied_join_equality(Oid opno,
*/
extern Plan *set_plan_references(PlannerGlobal *glob,
Plan *plan,
List *rtable);
List *rtable,
List *rowmarks);
extern List *set_returning_clause_references(PlannerGlobal *glob,
List *rlist,
Plan *topplan,