Split the processing of INSERT/UPDATE/DELETE operations out of execMain.c.

They are now handled by a new plan node type called ModifyTable, which is
placed at the top of the plan tree.  In itself this change doesn't do much,
except perhaps make the handling of RETURNING lists and inherited UPDATEs a
tad less klugy.  But it is necessary preparation for the intended extension of
allowing RETURNING queries inside WITH.

Marko Tiikkaja
This commit is contained in:
Tom Lane 2009-10-10 01:43:50 +00:00
parent b865d27582
commit 8a5849b7ff
30 changed files with 1546 additions and 1101 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/arch-dev.sgml,v 2.32 2009/04/27 16:27:35 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/arch-dev.sgml,v 2.33 2009/10/10 01:43:45 tgl Exp $ -->
<chapter id="overview">
<title>Overview of PostgreSQL Internals</title>
@ -58,7 +58,7 @@
The <firstterm>rewrite system</firstterm> takes
the query tree created by the parser stage and looks for
any <firstterm>rules</firstterm> (stored in the
<firstterm>system catalogs</firstterm>) to apply to
<firstterm>system catalogs</firstterm>) to apply to
the query tree. It performs the
transformations given in the <firstterm>rule bodies</firstterm>.
</para>
@ -77,7 +77,7 @@
<step>
<para>
The <firstterm>planner/optimizer</firstterm> takes
the (rewritten) query tree and creates a
the (rewritten) query tree and creates a
<firstterm>query plan</firstterm> that will be the input to the
<firstterm>executor</firstterm>.
</para>
@ -163,8 +163,8 @@
<para>
The <firstterm>parser</firstterm> defined in
<filename>gram.y</filename> and <filename>scan.l</filename> is
built using the Unix tools <application>yacc</application>
and <application>lex</application>.
built using the Unix tools <application>bison</application>
and <application>flex</application>.
</para>
</listitem>
<listitem>
@ -184,8 +184,8 @@
ASCII text) for valid syntax. If the syntax is correct a
<firstterm>parse tree</firstterm> is built up and handed back;
otherwise an error is returned. The parser and lexer are
implemented using the well-known Unix tools <application>yacc</>
and <application>lex</>.
implemented using the well-known Unix tools <application>bison</>
and <application>flex</>.
</para>
<para>
@ -208,13 +208,13 @@
<para>
The file <filename>scan.l</filename> is transformed to the C
source file <filename>scan.c</filename> using the program
<application>lex</application> and <filename>gram.y</filename> is
<application>flex</application> and <filename>gram.y</filename> is
transformed to <filename>gram.c</filename> using
<application>yacc</application>. After these transformations
<application>bison</application>. After these transformations
have taken place a normal C compiler can be used to create the
parser. Never make any changes to the generated C files as they
will be overwritten the next time <application>lex</application>
or <application>yacc</application> is called.
will be overwritten the next time <application>flex</application>
or <application>bison</application> is called.
<note>
<para>
@ -227,12 +227,12 @@
</para>
<para>
A detailed description of <application>yacc</application> or
A detailed description of <application>bison</application> or
the grammar rules given in <filename>gram.y</filename> would be
beyond the scope of this paper. There are many books and
documents dealing with <application>lex</application> and
<application>yacc</application>. You should be familiar with
<application>yacc</application> before you start to study the
documents dealing with <application>flex</application> and
<application>bison</application>. You should be familiar with
<application>bison</application> before you start to study the
grammar given in <filename>gram.y</filename> otherwise you won't
understand what happens there.
</para>
@ -299,7 +299,7 @@
called whenever an individual row had been accessed. This
implementation was removed in 1995 when the last official release
of the <productname>Berkeley Postgres</productname> project was
transformed into <productname>Postgres95</productname>.
transformed into <productname>Postgres95</productname>.
</para>
</listitem>
@ -479,7 +479,7 @@
<title>Executor</title>
<para>
The <firstterm>executor</firstterm> takes the plan handed back by the
The <firstterm>executor</firstterm> takes the plan created by the
planner/optimizer and recursively processes it to extract the required set
of rows. This is essentially a demand-pull pipeline mechanism.
Each time a plan node is called, it must deliver one more row, or
@ -488,7 +488,7 @@
<para>
To provide a concrete example, assume that the top
node is a <literal>MergeJoin</literal> node.
node is a <literal>MergeJoin</literal> node.
Before any merge can be done two rows have to be fetched (one from
each subplan). So the executor recursively calls itself to
process the subplans (it starts with the subplan attached to
@ -533,17 +533,21 @@
<command>DELETE</>. For <command>SELECT</>, the top-level executor
code only needs to send each row returned by the query plan tree off
to the client. For <command>INSERT</>, each returned row is inserted
into the target table specified for the <command>INSERT</>. (A simple
into the target table specified for the <command>INSERT</>. This is
done in a special top-level plan node called <literal>ModifyTable</>.
(A simple
<command>INSERT ... VALUES</> command creates a trivial plan tree
consisting of a single <literal>Result</> node, which computes just one
result row. But <command>INSERT ... SELECT</> can demand the full power
result row, and <literal>ModifyTable</> above it to perform the insertion.
But <command>INSERT ... SELECT</> can demand the full power
of the executor mechanism.) For <command>UPDATE</>, the planner arranges
that each computed row includes all the updated column values, plus
the <firstterm>TID</> (tuple ID, or row ID) of the original target row;
the executor top level uses this information to create a new updated row
and mark the old row deleted. For <command>DELETE</>, the only column
that is actually returned by the plan is the TID, and the executor top
level simply uses the TID to visit each target row and mark it deleted.
this data is fed into a <literal>ModifyTable</> node, which uses the
information to create a new updated row and mark the old row deleted.
For <command>DELETE</>, the only column that is actually returned by the
plan is the TID, and the <literal>ModifyTable</> node simply uses the TID
to visit each target row and mark it deleted.
</para>
</sect1>

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.190 2009/08/22 02:06:32 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.191 2009/10/10 01:43:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -581,6 +581,7 @@ ExplainNode(Plan *plan, PlanState *planstate,
const char *pname; /* node type name for text output */
const char *sname; /* node type name for non-text output */
const char *strategy = NULL;
const char *operation = NULL;
int save_indent = es->indent;
bool haschildren;
@ -591,6 +592,24 @@ ExplainNode(Plan *plan, PlanState *planstate,
case T_Result:
pname = sname = "Result";
break;
case T_ModifyTable:
sname = "ModifyTable";
switch (((ModifyTable *) plan)->operation)
{
case CMD_INSERT:
pname = operation = "Insert";
break;
case CMD_UPDATE:
pname = operation = "Update";
break;
case CMD_DELETE:
pname = operation = "Delete";
break;
default:
pname = "???";
break;
}
break;
case T_Append:
pname = sname = "Append";
break;
@ -736,6 +755,8 @@ ExplainNode(Plan *plan, PlanState *planstate,
ExplainPropertyText("Node Type", sname, es);
if (strategy)
ExplainPropertyText("Strategy", strategy, es);
if (operation)
ExplainPropertyText("Operation", operation, es);
if (relationship)
ExplainPropertyText("Parent Relationship", relationship, es);
if (plan_name)
@ -1023,6 +1044,7 @@ ExplainNode(Plan *plan, PlanState *planstate,
haschildren = plan->initPlan ||
outerPlan(plan) ||
innerPlan(plan) ||
IsA(plan, ModifyTable) ||
IsA(plan, Append) ||
IsA(plan, BitmapAnd) ||
IsA(plan, BitmapOr) ||
@ -1059,6 +1081,11 @@ ExplainNode(Plan *plan, PlanState *planstate,
/* special child plans */
switch (nodeTag(plan))
{
case T_ModifyTable:
ExplainMemberNodes(((ModifyTable *) plan)->plans,
((ModifyTableState *) planstate)->mt_plans,
outer_plan, es);
break;
case T_Append:
ExplainMemberNodes(((Append *) plan)->appendplans,
((AppendState *) planstate)->appendplans,
@ -1408,7 +1435,8 @@ ExplainScanTarget(Scan *plan, ExplainState *es)
}
/*
* Explain the constituent plans of an Append, BitmapAnd, or BitmapOr node.
* Explain the constituent plans of a ModifyTable, Append, BitmapAnd,
* or BitmapOr node.
*
* Ordinarily we don't pass down outer_plan to our child nodes, but in these
* cases we must, since the node could be an "inner indexscan" in which case

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.252 2009/08/04 16:08:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.253 2009/10/10 01:43:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -55,6 +55,7 @@ int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
static void InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx);
static HeapTuple GetTupleForTrigger(EState *estate,
PlanState *subplanstate,
ResultRelInfo *relinfo,
ItemPointer tid,
TupleTableSlot **newSlot);
@ -1793,7 +1794,8 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
}
bool
ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
ExecBRDeleteTriggers(EState *estate, PlanState *subplanstate,
ResultRelInfo *relinfo,
ItemPointer tupleid)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
@ -1806,7 +1808,8 @@ ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *newSlot;
int i;
trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
trigtuple = GetTupleForTrigger(estate, subplanstate, relinfo, tupleid,
&newSlot);
if (trigtuple == NULL)
return false;
@ -1862,7 +1865,7 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
{
HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo,
HeapTuple trigtuple = GetTupleForTrigger(estate, NULL, relinfo,
tupleid, NULL);
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
@ -1941,7 +1944,8 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
}
HeapTuple
ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
ExecBRUpdateTriggers(EState *estate, PlanState *subplanstate,
ResultRelInfo *relinfo,
ItemPointer tupleid, HeapTuple newtuple)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
@ -1954,16 +1958,18 @@ ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *newSlot;
int i;
trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
trigtuple = GetTupleForTrigger(estate, subplanstate, relinfo, tupleid,
&newSlot);
if (trigtuple == NULL)
return NULL;
/*
* In READ COMMITTED isolation level it's possible that newtuple was
* changed due to concurrent update.
* changed due to concurrent update. In that case we have a raw subplan
* output tuple and need to run it through the junk filter.
*/
if (newSlot != NULL)
intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot);
intuple = newtuple = ExecRemoveJunk(relinfo->ri_junkFilter, newSlot);
LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
@ -2014,7 +2020,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0)
{
HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo,
HeapTuple trigtuple = GetTupleForTrigger(estate, NULL, relinfo,
tupleid, NULL);
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
@ -2094,7 +2100,9 @@ ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
static HeapTuple
GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
GetTupleForTrigger(EState *estate,
PlanState *subplanstate,
ResultRelInfo *relinfo,
ItemPointer tid,
TupleTableSlot **newSlot)
{
@ -2111,6 +2119,9 @@ GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
*newSlot = NULL;
/* caller must pass a subplanstate if EvalPlanQual is possible */
Assert(subplanstate != NULL);
/*
* lock tuple for update
*/
@ -2143,6 +2154,7 @@ ltrmark:;
epqslot = EvalPlanQual(estate,
relinfo->ri_RangeTableIndex,
subplanstate,
&update_ctid,
update_xmax);
if (!TupIsNull(epqslot))

View File

@ -4,7 +4,7 @@
# Makefile for executor
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.29 2008/12/28 18:53:55 tgl Exp $
# $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.30 2009/10/10 01:43:45 tgl Exp $
#
#-------------------------------------------------------------------------
@ -18,6 +18,7 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
nodeBitmapAnd.o nodeBitmapOr.o \
nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.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 \

View File

@ -1,4 +1,4 @@
$PostgreSQL: pgsql/src/backend/executor/README,v 1.8 2009/01/09 15:46:10 tgl Exp $
$PostgreSQL: pgsql/src/backend/executor/README,v 1.9 2009/10/10 01:43:45 tgl Exp $
The Postgres Executor
=====================
@ -25,16 +25,17 @@ There is a moderately intelligent scheme to avoid rescanning nodes
unnecessarily (for example, Sort does not rescan its input if no parameters
of the input have changed, since it can just reread its stored sorted data).
The plan tree concept implements SELECT directly: it is only necessary to
deliver the top-level result tuples to the client, or insert them into
another table in the case of INSERT ... SELECT. (INSERT ... VALUES is
handled similarly, but the plan tree is just a Result node with no source
tables.) For UPDATE, the plan tree selects the tuples that need to be
updated (WHERE condition) and delivers a new calculated tuple value for each
such tuple, plus a "junk" (hidden) tuple CTID identifying the target tuple.
The executor's top level then uses this information to update the correct
tuple. DELETE is similar to UPDATE except that only a CTID need be
delivered by the plan tree.
For a SELECT, it is only necessary to deliver the top-level result tuples
to the client. For INSERT/UPDATE/DELETE, the actual table modification
operations happen in a top-level ModifyTable plan node. If the query
includes a RETURNING clause, the ModifyTable node delivers the computed
RETURNING rows as output, otherwise it returns nothing. Handling INSERT
is pretty straightforward: the tuples returned from the plan tree below
ModifyTable are inserted into the correct result relation. For UPDATE,
the plan tree returns the computed tuples to be updated, plus a "junk"
(hidden) CTID column identifying which table row is to be replaced by each
one. For DELETE, the plan tree need only deliver a CTID column, and the
ModifyTable node visits each of those rows and marks the row deleted.
XXX a great deal more documentation needs to be written here...

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.104 2009/09/12 22:12:03 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.105 2009/10/10 01:43:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -30,6 +30,7 @@
#include "executor/nodeLimit.h"
#include "executor/nodeMaterial.h"
#include "executor/nodeMergejoin.h"
#include "executor/nodeModifyTable.h"
#include "executor/nodeNestloop.h"
#include "executor/nodeRecursiveunion.h"
#include "executor/nodeResult.h"
@ -127,6 +128,10 @@ ExecReScan(PlanState *node, ExprContext *exprCtxt)
ExecReScanResult((ResultState *) node, exprCtxt);
break;
case T_ModifyTableState:
ExecReScanModifyTable((ModifyTableState *) node, exprCtxt);
break;
case T_AppendState:
ExecReScanAppend((AppendState *) node, exprCtxt);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.58 2009/01/01 17:23:41 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/execJunk.c,v 1.59 2009/10/10 01:43:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -34,9 +34,7 @@
* called 'resjunk'. If the value of this field is true then the
* corresponding attribute is a "junk" attribute.
*
* When we initialize a plan we call ExecInitJunkFilter to create
* and store the appropriate information in the es_junkFilter attribute of
* EState.
* When we initialize a plan we call ExecInitJunkFilter to create a filter.
*
* We then execute the plan, treating the resjunk attributes like any others.
*
@ -44,7 +42,7 @@
* ExecFindJunkAttribute/ExecGetJunkAttribute to retrieve the values of the
* junk attributes we are interested in, and ExecFilterJunk or ExecRemoveJunk
* to remove all the junk attributes from a tuple. This new "clean" tuple is
* then printed, replaced, deleted or inserted.
* then printed, inserted, or updated.
*
*-------------------------------------------------------------------------
*/

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.66 2009/09/27 21:10:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.67 2009/10/10 01:43:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -93,6 +93,7 @@
#include "executor/nodeLimit.h"
#include "executor/nodeMaterial.h"
#include "executor/nodeMergejoin.h"
#include "executor/nodeModifyTable.h"
#include "executor/nodeNestloop.h"
#include "executor/nodeRecursiveunion.h"
#include "executor/nodeResult.h"
@ -146,6 +147,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
estate, eflags);
break;
case T_ModifyTable:
result = (PlanState *) ExecInitModifyTable((ModifyTable *) node,
estate, eflags);
break;
case T_Append:
result = (PlanState *) ExecInitAppend((Append *) node,
estate, eflags);
@ -343,6 +349,10 @@ ExecProcNode(PlanState *node)
result = ExecResult((ResultState *) node);
break;
case T_ModifyTableState:
result = ExecModifyTable((ModifyTableState *) node);
break;
case T_AppendState:
result = ExecAppend((AppendState *) node);
break;
@ -524,7 +534,7 @@ MultiExecProcNode(PlanState *node)
* Recursively cleans up all the nodes in the plan rooted
* at 'node'.
*
* After this operation, the query plan will not be able to
* After this operation, the query plan will not be able to be
* processed any further. This should be called only after
* the query plan has been fully executed.
* ----------------------------------------------------------------
@ -553,6 +563,10 @@ ExecEndNode(PlanState *node)
ExecEndResult((ResultState *) node);
break;
case T_ModifyTableState:
ExecEndModifyTable((ModifyTableState *) node);
break;
case T_AppendState:
ExecEndAppend((AppendState *) node);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeAppend.c,v 1.75 2009/09/27 21:10:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeAppend.c,v 1.76 2009/10/10 01:43:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -74,50 +74,33 @@ static bool exec_append_initialize_next(AppendState *appendstate);
static bool
exec_append_initialize_next(AppendState *appendstate)
{
EState *estate;
int whichplan;
/*
* get information from the append node
*/
estate = appendstate->ps.state;
whichplan = appendstate->as_whichplan;
if (whichplan < appendstate->as_firstplan)
if (whichplan < 0)
{
/*
* if scanning in reverse, we start at the last scan in the list and
* then proceed back to the first.. in any case we inform ExecAppend
* that we are at the end of the line by returning FALSE
*/
appendstate->as_whichplan = appendstate->as_firstplan;
appendstate->as_whichplan = 0;
return FALSE;
}
else if (whichplan > appendstate->as_lastplan)
else if (whichplan >= appendstate->as_nplans)
{
/*
* as above, end the scan if we go beyond the last scan in our list..
*/
appendstate->as_whichplan = appendstate->as_lastplan;
appendstate->as_whichplan = appendstate->as_nplans - 1;
return FALSE;
}
else
{
/*
* initialize the scan
*
* If we are controlling the target relation, select the proper active
* ResultRelInfo and junk filter for this target.
*/
if (((Append *) appendstate->ps.plan)->isTarget)
{
Assert(whichplan < estate->es_num_result_relations);
estate->es_result_relation_info =
estate->es_result_relations + whichplan;
estate->es_junkFilter =
estate->es_result_relation_info->ri_junkFilter;
}
return TRUE;
}
}
@ -131,10 +114,6 @@ exec_append_initialize_next(AppendState *appendstate)
* append node may not be scanned, but this way all of the
* structures get allocated in the executor's top level memory
* block instead of that of the call to ExecAppend.)
*
* Special case: during an EvalPlanQual recheck query of an inherited
* target relation, we only want to initialize and scan the single
* subplan that corresponds to the target relation being checked.
* ----------------------------------------------------------------
*/
AppendState *
@ -144,7 +123,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
PlanState **appendplanstates;
int nplans;
int i;
Plan *initNode;
ListCell *lc;
/* check for unsupported flags */
Assert(!(eflags & EXEC_FLAG_MARK));
@ -164,27 +143,6 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
appendstate->appendplans = appendplanstates;
appendstate->as_nplans = nplans;
/*
* Do we want to scan just one subplan? (Special case for EvalPlanQual)
* XXX pretty dirty way of determining that this case applies ...
*/
if (node->isTarget && estate->es_evTuple != NULL)
{
int tplan;
tplan = estate->es_result_relation_info - estate->es_result_relations;
Assert(tplan >= 0 && tplan < nplans);
appendstate->as_firstplan = tplan;
appendstate->as_lastplan = tplan;
}
else
{
/* normal case, scan all subplans */
appendstate->as_firstplan = 0;
appendstate->as_lastplan = nplans - 1;
}
/*
* Miscellaneous initialization
*
@ -200,32 +158,27 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
/*
* call ExecInitNode on each of the plans to be executed and save the
* results into the array "appendplans". Note we *must* set
* estate->es_result_relation_info correctly while we initialize each
* sub-plan; ExecContextForcesOids depends on that!
* results into the array "appendplans".
*/
for (i = appendstate->as_firstplan; i <= appendstate->as_lastplan; i++)
i = 0;
foreach(lc, node->appendplans)
{
appendstate->as_whichplan = i;
exec_append_initialize_next(appendstate);
Plan *initNode = (Plan *) lfirst(lc);
initNode = (Plan *) list_nth(node->appendplans, i);
appendplanstates[i] = ExecInitNode(initNode, estate, eflags);
i++;
}
/*
* Initialize tuple type. (Note: in an inherited UPDATE situation, the
* tuple type computed here corresponds to the parent table, which is
* really a lie since tuples returned from child subplans will not all
* look the same.)
* initialize output tuple type
*/
ExecAssignResultTypeFromTL(&appendstate->ps);
appendstate->ps.ps_ProjInfo = NULL;
/*
* return the result from the first subplan's initialization
* initialize to scan first subplan
*/
appendstate->as_whichplan = appendstate->as_firstplan;
appendstate->as_whichplan = 0;
exec_append_initialize_next(appendstate);
return appendstate;
@ -260,9 +213,7 @@ ExecAppend(AppendState *node)
/*
* If the subplan gave us something then return it as-is. We do
* NOT make use of the result slot that was set up in
* ExecInitAppend, first because there's no reason to and second
* because it may have the wrong tuple descriptor in
* inherited-UPDATE cases.
* ExecInitAppend; there's no need for it.
*/
return result;
}
@ -305,13 +256,10 @@ ExecEndAppend(AppendState *node)
nplans = node->as_nplans;
/*
* shut down each of the subscans (that we've initialized)
* shut down each of the subscans
*/
for (i = 0; i < nplans; i++)
{
if (appendplans[i])
ExecEndNode(appendplans[i]);
}
ExecEndNode(appendplans[i]);
}
void
@ -319,7 +267,7 @@ ExecReScanAppend(AppendState *node, ExprContext *exprCtxt)
{
int i;
for (i = node->as_firstplan; i <= node->as_lastplan; i++)
for (i = 0; i < node->as_nplans; i++)
{
PlanState *subnode = node->appendplans[i];
@ -337,13 +285,8 @@ ExecReScanAppend(AppendState *node, ExprContext *exprCtxt)
* exprCtxt down to the subnodes (needed for appendrel indexscan).
*/
if (subnode->chgParam == NULL || exprCtxt != NULL)
{
/* make sure estate is correct for this subnode (needed??) */
node->as_whichplan = i;
exec_append_initialize_next(node);
ExecReScan(subnode, exprCtxt);
}
}
node->as_whichplan = node->as_firstplan;
node->as_whichplan = 0;
exec_append_initialize_next(node);
}

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.209 2009/10/02 17:57:30 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.210 2009/10/10 01:43:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1975,19 +1975,19 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount)
res = SPI_OK_SELECT;
break;
case CMD_INSERT:
if (queryDesc->plannedstmt->returningLists)
if (queryDesc->plannedstmt->hasReturning)
res = SPI_OK_INSERT_RETURNING;
else
res = SPI_OK_INSERT;
break;
case CMD_DELETE:
if (queryDesc->plannedstmt->returningLists)
if (queryDesc->plannedstmt->hasReturning)
res = SPI_OK_DELETE_RETURNING;
else
res = SPI_OK_DELETE;
break;
case CMD_UPDATE:
if (queryDesc->plannedstmt->returningLists)
if (queryDesc->plannedstmt->hasReturning)
res = SPI_OK_UPDATE_RETURNING;
else
res = SPI_OK_UPDATE;
@ -2011,7 +2011,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount)
_SPI_current->processed = queryDesc->estate->es_processed;
_SPI_current->lastoid = queryDesc->estate->es_lastoid;
if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->returningLists) &&
if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->hasReturning) &&
queryDesc->dest->mydest == DestSPI)
{
if (_SPI_checktuples())

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.442 2009/10/08 02:39:20 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.443 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -77,6 +77,7 @@ _copyPlannedStmt(PlannedStmt *from)
PlannedStmt *newnode = makeNode(PlannedStmt);
COPY_SCALAR_FIELD(commandType);
COPY_SCALAR_FIELD(hasReturning);
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(planTree);
COPY_NODE_FIELD(rtable);
@ -85,7 +86,6 @@ _copyPlannedStmt(PlannedStmt *from)
COPY_NODE_FIELD(intoClause);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(returningLists);
COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(relationOids);
COPY_NODE_FIELD(invalItems);
@ -154,6 +154,30 @@ _copyResult(Result *from)
return newnode;
}
/*
* _copyModifyTable
*/
static ModifyTable *
_copyModifyTable(ModifyTable *from)
{
ModifyTable *newnode = makeNode(ModifyTable);
/*
* copy node superclass fields
*/
CopyPlanFields((Plan *) from, (Plan *) newnode);
/*
* copy remainder of node
*/
COPY_SCALAR_FIELD(operation);
COPY_NODE_FIELD(resultRelations);
COPY_NODE_FIELD(plans);
COPY_NODE_FIELD(returningLists);
return newnode;
}
/*
* _copyAppend
*/
@ -171,7 +195,6 @@ _copyAppend(Append *from)
* copy remainder of node
*/
COPY_NODE_FIELD(appendplans);
COPY_SCALAR_FIELD(isTarget);
return newnode;
}
@ -3482,6 +3505,9 @@ copyObject(void *from)
case T_Result:
retval = _copyResult(from);
break;
case T_ModifyTable:
retval = _copyModifyTable(from);
break;
case T_Append:
retval = _copyAppend(from);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.366 2009/10/08 02:39:21 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.367 2009/10/10 01:43:49 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@ -242,6 +242,7 @@ _outPlannedStmt(StringInfo str, PlannedStmt *node)
WRITE_NODE_TYPE("PLANNEDSTMT");
WRITE_ENUM_FIELD(commandType, CmdType);
WRITE_BOOL_FIELD(hasReturning);
WRITE_BOOL_FIELD(canSetTag);
WRITE_NODE_FIELD(planTree);
WRITE_NODE_FIELD(rtable);
@ -250,7 +251,6 @@ _outPlannedStmt(StringInfo str, PlannedStmt *node)
WRITE_NODE_FIELD(intoClause);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(returningLists);
WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems);
@ -318,6 +318,19 @@ _outResult(StringInfo str, Result *node)
WRITE_NODE_FIELD(resconstantqual);
}
static void
_outModifyTable(StringInfo str, ModifyTable *node)
{
WRITE_NODE_TYPE("MODIFYTABLE");
_outPlanInfo(str, (Plan *) node);
WRITE_ENUM_FIELD(operation, CmdType);
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(plans);
WRITE_NODE_FIELD(returningLists);
}
static void
_outAppend(StringInfo str, Append *node)
{
@ -326,7 +339,6 @@ _outAppend(StringInfo str, Append *node)
_outPlanInfo(str, (Plan *) node);
WRITE_NODE_FIELD(appendplans);
WRITE_BOOL_FIELD(isTarget);
}
static void
@ -1501,7 +1513,6 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
WRITE_UINT_FIELD(query_level);
WRITE_NODE_FIELD(join_rel_list);
WRITE_NODE_FIELD(resultRelations);
WRITE_NODE_FIELD(returningLists);
WRITE_NODE_FIELD(init_plans);
WRITE_NODE_FIELD(cte_plan_ids);
WRITE_NODE_FIELD(eq_classes);
@ -2408,6 +2419,9 @@ _outNode(StringInfo str, void *obj)
case T_Result:
_outResult(str, obj);
break;
case T_ModifyTable:
_outModifyTable(str, obj);
break;
case T_Append:
_outAppend(str, obj);
break;

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.263 2009/09/17 20:49:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.264 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -579,7 +579,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
subplans = lappend(subplans, create_plan(root, subpath));
}
plan = make_append(subplans, false, tlist);
plan = make_append(subplans, tlist);
return (Plan *) plan;
}
@ -2621,7 +2621,7 @@ make_worktablescan(List *qptlist,
}
Append *
make_append(List *appendplans, bool isTarget, List *tlist)
make_append(List *appendplans, List *tlist)
{
Append *node = makeNode(Append);
Plan *plan = &node->plan;
@ -2657,7 +2657,6 @@ make_append(List *appendplans, bool isTarget, List *tlist)
plan->lefttree = NULL;
plan->righttree = NULL;
node->appendplans = appendplans;
node->isTarget = isTarget;
return node;
}
@ -3711,6 +3710,73 @@ make_result(PlannerInfo *root,
return node;
}
/*
* make_modifytable
* Build a ModifyTable plan node
*
* Currently, we don't charge anything extra for the actual table modification
* work, nor for the RETURNING expressions if any. It would only be window
* dressing, since these are always top-level nodes and there is no way for
* the costs to change any higher-level planning choices. But we might want
* to make it look better sometime.
*/
ModifyTable *
make_modifytable(CmdType operation, List *resultRelations,
List *subplans, List *returningLists)
{
ModifyTable *node = makeNode(ModifyTable);
Plan *plan = &node->plan;
double total_size;
ListCell *subnode;
Assert(list_length(resultRelations) == list_length(subplans));
Assert(returningLists == NIL ||
list_length(resultRelations) == list_length(returningLists));
/*
* Compute cost as sum of subplan costs.
*/
plan->startup_cost = 0;
plan->total_cost = 0;
plan->plan_rows = 0;
total_size = 0;
foreach(subnode, subplans)
{
Plan *subplan = (Plan *) lfirst(subnode);
if (subnode == list_head(subplans)) /* first node? */
plan->startup_cost = subplan->startup_cost;
plan->total_cost += subplan->total_cost;
plan->plan_rows += subplan->plan_rows;
total_size += subplan->plan_width * subplan->plan_rows;
}
if (plan->plan_rows > 0)
plan->plan_width = rint(total_size / plan->plan_rows);
else
plan->plan_width = 0;
node->plan.lefttree = NULL;
node->plan.righttree = NULL;
node->plan.qual = NIL;
/*
* Set up the visible plan targetlist as being the same as the first
* RETURNING list. This is for the use of EXPLAIN; the executor won't
* pay any attention to the targetlist.
*/
if (returningLists)
node->plan.targetlist = copyObject(linitial(returningLists));
else
node->plan.targetlist = NIL;
node->operation = operation;
node->resultRelations = resultRelations;
node->plans = subplans;
node->returningLists = returningLists;
return node;
}
/*
* is_projection_capable_plan
* Check whether a given Plan node is able to do projection.
@ -3727,6 +3793,7 @@ is_projection_capable_plan(Plan *plan)
case T_Unique:
case T_SetOp:
case T_Limit:
case T_ModifyTable:
case T_Append:
case T_RecursiveUnion:
return false;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.257 2009/10/08 02:39:21 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.258 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -217,6 +217,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result = makeNode(PlannedStmt);
result->commandType = parse->commandType;
result->hasReturning = (parse->returningList != NIL);
result->canSetTag = parse->canSetTag;
result->transientPlan = glob->transientPlan;
result->planTree = top_plan;
@ -226,7 +227,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->intoClause = parse->intoClause;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->returningLists = root->returningLists;
result->rowMarks = parse->rowMarks;
result->relationOids = glob->relationOids;
result->invalItems = glob->invalItems;
@ -478,7 +478,39 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
rt_fetch(parse->resultRelation, parse->rtable)->inh)
plan = inheritance_planner(root);
else
{
plan = grouping_planner(root, tuple_fraction);
/* If it's not SELECT, we need a ModifyTable node */
if (parse->commandType != CMD_SELECT)
{
/*
* Deal with the RETURNING clause if any. It's convenient to pass
* the returningList through setrefs.c now rather than at top
* level (if we waited, handling inherited UPDATE/DELETE would be
* much harder).
*/
List *returningLists;
if (parse->returningList)
{
List *rlist;
Assert(parse->resultRelation);
rlist = set_returning_clause_references(root->glob,
parse->returningList,
plan,
parse->resultRelation);
returningLists = list_make1(rlist);
}
else
returningLists = NIL;
plan = (Plan *) make_modifytable(parse->commandType,
copyObject(root->resultRelations),
list_make1(plan),
returningLists);
}
}
/*
* If any subplans were generated, or if we're inside a subplan, build
@ -625,9 +657,7 @@ preprocess_qual_conditions(PlannerInfo *root, Node *jtnode)
* is an inheritance set. Source inheritance is expanded at the bottom of the
* plan tree (see allpaths.c), but target inheritance has to be expanded at
* the top. The reason is that for UPDATE, each target relation needs a
* different targetlist matching its own column set. Also, for both UPDATE
* and DELETE, the executor needs the Append plan node at the top, else it
* can't keep track of which table is the current target table. Fortunately,
* different targetlist matching its own column set. Fortunately,
* the UPDATE/DELETE target can never be the nullable side of an outer join,
* so it's OK to generate the plan this way.
*
@ -642,7 +672,7 @@ inheritance_planner(PlannerInfo *root)
List *resultRelations = NIL;
List *returningLists = NIL;
List *rtable = NIL;
List *tlist = NIL;
List *tlist;
PlannerInfo subroot;
ListCell *l;
@ -662,7 +692,6 @@ inheritance_planner(PlannerInfo *root)
subroot.parse = (Query *)
adjust_appendrel_attrs((Node *) parse,
appinfo);
subroot.returningLists = NIL;
subroot.init_plans = NIL;
/* We needn't modify the child's append_rel_list */
/* There shouldn't be any OJ info to translate, as yet */
@ -680,12 +709,9 @@ inheritance_planner(PlannerInfo *root)
if (is_dummy_plan(subplan))
continue;
/* Save rtable and tlist from first rel for use below */
/* Save rtable from first rel for use below */
if (subplans == NIL)
{
rtable = subroot.parse->rtable;
tlist = subplan->targetlist;
}
subplans = lappend(subplans, subplan);
@ -698,20 +724,24 @@ inheritance_planner(PlannerInfo *root)
/* Build list of per-relation RETURNING targetlists */
if (parse->returningList)
{
Assert(list_length(subroot.returningLists) == 1);
returningLists = list_concat(returningLists,
subroot.returningLists);
List *rlist;
rlist = set_returning_clause_references(root->glob,
subroot.parse->returningList,
subplan,
appinfo->child_relid);
returningLists = lappend(returningLists, rlist);
}
}
root->resultRelations = resultRelations;
root->returningLists = returningLists;
/* Mark result as unordered (probably unnecessary) */
root->query_pathkeys = NIL;
/*
* If we managed to exclude every child rel, return a dummy plan
* If we managed to exclude every child rel, return a dummy plan;
* it doesn't even need a ModifyTable node.
*/
if (subplans == NIL)
{
@ -738,11 +768,11 @@ inheritance_planner(PlannerInfo *root)
*/
parse->rtable = rtable;
/* Suppress Append if there's only one surviving child rel */
if (list_length(subplans) == 1)
return (Plan *) linitial(subplans);
return (Plan *) make_append(subplans, true, tlist);
/* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */
return (Plan *) make_modifytable(parse->commandType,
copyObject(root->resultRelations),
subplans,
returningLists);
}
/*--------------------
@ -1569,25 +1599,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
count_est);
}
/*
* Deal with the RETURNING clause if any. It's convenient to pass the
* returningList through setrefs.c now rather than at top level (if we
* waited, handling inherited UPDATE/DELETE would be much harder).
*/
if (parse->returningList)
{
List *rlist;
Assert(parse->resultRelation);
rlist = set_returning_clause_references(root->glob,
parse->returningList,
result_plan,
parse->resultRelation);
root->returningLists = list_make1(rlist);
}
else
root->returningLists = NIL;
/* 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.150 2009/06/11 14:48:59 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.151 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -442,6 +442,29 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
fix_scan_expr(glob, splan->resconstantqual, rtoffset);
}
break;
case T_ModifyTable:
{
ModifyTable *splan = (ModifyTable *) plan;
/*
* planner.c already called set_returning_clause_references,
* so we should not process either the targetlist or the
* returningLists.
*/
Assert(splan->plan.qual == NIL);
foreach(l, splan->resultRelations)
{
lfirst_int(l) += rtoffset;
}
foreach(l, splan->plans)
{
lfirst(l) = set_plan_refs(glob,
(Plan *) lfirst(l),
rtoffset);
}
}
break;
case T_Append:
{
Append *splan = (Append *) plan;
@ -1600,7 +1623,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
*
* If the query involves more than just the result table, we have to
* adjust any Vars that refer to other tables to reference junk tlist
* entries in the top plan's targetlist. Vars referencing the result
* entries in the top subplan's targetlist. Vars referencing the result
* table should be left alone, however (the executor will evaluate them
* using the actual heap tuple, after firing triggers if any). In the
* adjusted RETURNING list, result-table Vars will still have their
@ -1610,8 +1633,8 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
* glob->relationOids.
*
* 'rlist': the RETURNING targetlist to be fixed
* 'topplan': the top Plan node for the query (not yet passed through
* set_plan_references)
* 'topplan': the top subplan node that will be just below the ModifyTable
* node (note it's not yet passed through set_plan_references)
* 'resultRelation': RT index of the associated result relation
*
* Note: we assume that result relations will have rtoffset zero, that is,

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.153 2009/09/12 22:12:04 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.154 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1937,6 +1937,23 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
((WorkTableScan *) plan)->wtParam);
break;
case T_ModifyTable:
{
ListCell *l;
finalize_primnode((Node *) ((ModifyTable *) plan)->returningLists,
&context);
foreach(l, ((ModifyTable *) plan)->plans)
{
context.paramids =
bms_add_members(context.paramids,
finalize_plan(root,
(Plan *) lfirst(l),
valid_params));
}
}
break;
case T_Append:
{
ListCell *l;

View File

@ -22,7 +22,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.174 2009/09/02 17:52:24 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.175 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -448,7 +448,7 @@ generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
/*
* Append the child results together.
*/
plan = (Plan *) make_append(planlist, false, tlist);
plan = (Plan *) make_append(planlist, tlist);
/*
* For UNION ALL, we just need the Append plan. For UNION, need to add
@ -539,7 +539,7 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
/*
* Append the child results together.
*/
plan = (Plan *) make_append(planlist, false, tlist);
plan = (Plan *) make_append(planlist, tlist);
/* Identify the grouping semantics */
groupList = generate_setop_grouplist(op, tlist);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.131 2009/06/11 14:49:02 momjian Exp $
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.132 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -338,7 +338,7 @@ ChoosePortalStrategy(List *stmts)
{
if (++nSetTag > 1)
return PORTAL_MULTI_QUERY; /* no need to look further */
if (pstmt->returningLists == NIL)
if (!pstmt->hasReturning)
return PORTAL_MULTI_QUERY; /* no need to look further */
}
}
@ -414,8 +414,8 @@ FetchStatementTargetList(Node *stmt)
pstmt->utilityStmt == NULL &&
pstmt->intoClause == NULL)
return pstmt->planTree->targetlist;
if (pstmt->returningLists)
return (List *) linitial(pstmt->returningLists);
if (pstmt->hasReturning)
return pstmt->planTree->targetlist;
return NIL;
}
if (IsA(stmt, FetchStmt))
@ -570,9 +570,9 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal);
Assert(IsA(pstmt, PlannedStmt));
Assert(pstmt->returningLists);
Assert(pstmt->hasReturning);
portal->tupDesc =
ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
ExecCleanTypeFromTL(pstmt->planTree->targetlist,
false);
}

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.308 2009/10/09 21:02:55 petere Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.309 2009/10/10 01:43:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -3346,11 +3346,12 @@ static void
push_plan(deparse_namespace *dpns, Plan *subplan)
{
/*
* We special-case Append to pretend that the first child plan is the
* OUTER referent; otherwise normal.
* We special-case ModifyTable to pretend that the first child plan is the
* OUTER referent; otherwise normal. This is to support RETURNING lists
* containing references to non-target relations.
*/
if (IsA(subplan, Append))
dpns->outer_plan = (Plan *) linitial(((Append *) subplan)->appendplans);
if (IsA(subplan, ModifyTable))
dpns->outer_plan = (Plan *) linitial(((ModifyTable *) subplan)->plans);
else
dpns->outer_plan = outerPlan(subplan);

View File

@ -35,7 +35,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.28 2009/07/14 15:37:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.29 2009/10/10 01:43:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -880,8 +880,8 @@ PlanCacheComputeResultDesc(List *stmt_list)
if (IsA(node, PlannedStmt))
{
pstmt = (PlannedStmt *) node;
Assert(pstmt->returningLists);
return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
Assert(pstmt->hasReturning);
return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
}
/* other cases shouldn't happen, but return NULL */
break;

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/include/commands/trigger.h,v 1.75 2009/07/29 20:56:20 tgl Exp $
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.76 2009/10/10 01:43:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -139,6 +139,7 @@ extern void ExecBSDeleteTriggers(EState *estate,
extern void ExecASDeleteTriggers(EState *estate,
ResultRelInfo *relinfo);
extern bool ExecBRDeleteTriggers(EState *estate,
PlanState *subplanstate,
ResultRelInfo *relinfo,
ItemPointer tupleid);
extern void ExecARDeleteTriggers(EState *estate,
@ -149,6 +150,7 @@ extern void ExecBSUpdateTriggers(EState *estate,
extern void ExecASUpdateTriggers(EState *estate,
ResultRelInfo *relinfo);
extern HeapTuple ExecBRUpdateTriggers(EState *estate,
PlanState *subplanstate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
HeapTuple newtuple);

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.160 2009/09/27 21:10:53 tgl Exp $
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.161 2009/10/10 01:43:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -167,6 +167,7 @@ extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate);
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
PlanState *subplanstate,
ItemPointer tid, TransactionId priorXmax);
extern PlanState *ExecGetActivePlanTree(QueryDesc *queryDesc);
extern DestReceiver *CreateIntoRelDestReceiver(void);

View File

@ -0,0 +1,23 @@
/*-------------------------------------------------------------------------
*
* nodeModifyTable.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/nodeModifyTable.h,v 1.1 2009/10/10 01:43:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef NODEMODIFYTABLE_H
#define NODEMODIFYTABLE_H
#include "nodes/execnodes.h"
extern ModifyTableState *ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags);
extern TupleTableSlot *ExecModifyTable(ModifyTableState *node);
extern void ExecEndModifyTable(ModifyTableState *node);
extern void ExecReScanModifyTable(ModifyTableState *node, ExprContext *exprCtxt);
#endif /* NODEMODIFYTABLE_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.208 2009/09/27 20:09:58 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.209 2009/10/10 01:43:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -339,7 +339,7 @@ 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; /* currently active junk filter */
JunkFilter *es_junkFilter; /* top-level junk filter, if any */
/* Stuff used for firing triggers: */
List *es_trig_target_relations; /* trigger-only ResultRelInfos */
@ -975,13 +975,25 @@ typedef struct ResultState
bool rs_checkqual; /* do we need to check the qual? */
} ResultState;
/* ----------------
* ModifyTableState information
* ----------------
*/
typedef struct ModifyTableState
{
PlanState ps; /* its first field is NodeTag */
CmdType operation;
PlanState **mt_plans; /* subplans (one per target rel) */
int mt_nplans; /* number of plans in the array */
int mt_whichplan; /* which one is being executed (0..n-1) */
bool fireBSTriggers; /* do we need to fire stmt triggers? */
} ModifyTableState;
/* ----------------
* AppendState information
*
* nplans how many plans are in the list
* nplans how many plans are in the array
* whichplan which plan is being executed (0 .. n-1)
* firstplan first plan to execute (usually 0)
* lastplan last plan to execute (usually n-1)
* ----------------
*/
typedef struct AppendState
@ -990,8 +1002,6 @@ typedef struct AppendState
PlanState **appendplans; /* array of PlanStates for my inputs */
int as_nplans;
int as_whichplan;
int as_firstplan;
int as_lastplan;
} AppendState;
/* ----------------

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.228 2009/10/08 02:39:24 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.229 2009/10/10 01:43:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -43,6 +43,7 @@ typedef enum NodeTag
*/
T_Plan = 100,
T_Result,
T_ModifyTable,
T_Append,
T_RecursiveUnion,
T_BitmapAnd,
@ -81,6 +82,7 @@ typedef enum NodeTag
*/
T_PlanState = 200,
T_ResultState,
T_ModifyTableState,
T_AppendState,
T_RecursiveUnionState,
T_BitmapAndState,

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.110 2009/06/11 14:49:11 momjian Exp $
* $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.111 2009/10/10 01:43:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -38,6 +38,8 @@ typedef struct PlannedStmt
CmdType commandType; /* select|insert|update|delete */
bool hasReturning; /* is it insert|update|delete RETURNING? */
bool canSetTag; /* do I set the command result tag? */
bool transientPlan; /* redo plan when TransactionXmin changes? */
@ -57,18 +59,6 @@ typedef struct PlannedStmt
Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */
/*
* If the query has a returningList then the planner will store a list of
* processed targetlists (one per result relation) here. We must have a
* separate RETURNING targetlist for each result rel because column
* numbers may vary within an inheritance tree. In the targetlists, Vars
* referencing the result relation will have their original varno and
* varattno, while Vars referencing other rels will be converted to have
* varno OUTER and varattno referencing a resjunk entry in the top plan
* node's targetlist.
*/
List *returningLists; /* list of lists of TargetEntry, or NIL */
List *rowMarks; /* a list of RowMarkClause's */
List *relationOids; /* OIDs of relations the plan depends on */
@ -164,22 +154,30 @@ typedef struct Result
Node *resconstantqual;
} Result;
/* ----------------
* ModifyTable node -
* Apply rows produced by subplan(s) to result table(s),
* by inserting, updating, or deleting.
* ----------------
*/
typedef struct ModifyTable
{
Plan plan;
CmdType operation; /* INSERT, UPDATE, or DELETE */
List *resultRelations; /* integer list of RT indexes */
List *plans; /* plan(s) producing source data */
List *returningLists; /* per-target-table RETURNING tlists */
} ModifyTable;
/* ----------------
* Append node -
* Generate the concatenation of the results of sub-plans.
*
* Append nodes are sometimes used to switch between several result relations
* (when the target of an UPDATE or DELETE is an inheritance set). Such a
* node will have isTarget true. The Append executor is then responsible
* for updating the executor state to point at the correct target relation
* whenever it switches subplans.
* ----------------
*/
typedef struct Append
{
Plan plan;
List *appendplans;
bool isTarget;
} Append;
/* ----------------

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.175 2009/09/17 20:49:29 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.176 2009/10/10 01:43:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -140,8 +140,6 @@ typedef struct PlannerInfo
List *resultRelations; /* integer list of RT indexes, or NIL */
List *returningLists; /* list of lists of TargetEntry, or NIL */
List *init_plans; /* init SubPlans for query */
List *cte_plan_ids; /* per-CTE-item list of subplan IDs */

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.118 2009/06/11 14:49:11 momjian Exp $
* $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.119 2009/10/10 01:43:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -41,7 +41,7 @@ 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);
extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
extern Append *make_append(List *appendplans, List *tlist);
extern RecursiveUnion *make_recursive_union(List *tlist,
Plan *lefttree, Plan *righttree, int wtParam,
List *distinctList, long numGroups);
@ -74,6 +74,8 @@ extern SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
long numGroups, double outputRows);
extern Result *make_result(PlannerInfo *root, List *tlist,
Node *resconstantqual, Plan *subplan);
extern ModifyTable *make_modifytable(CmdType operation, List *resultRelations,
List *subplans, List *returningLists);
extern bool is_projection_capable_plan(Plan *plan);
/*