mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-06 15:24:56 +08:00
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:
parent
b865d27582
commit
8a5849b7ff
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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 \
|
||||
|
@ -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...
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
1005
src/backend/executor/nodeModifyTable.c
Normal file
1005
src/backend/executor/nodeModifyTable.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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())
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
6
src/backend/utils/cache/plancache.c
vendored
6
src/backend/utils/cache/plancache.c
vendored
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
23
src/include/executor/nodeModifyTable.h
Normal file
23
src/include/executor/nodeModifyTable.h
Normal 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 */
|
@ -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;
|
||||
|
||||
/* ----------------
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
/* ----------------
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user