mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03: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:
@ -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())
|
||||
|
Reference in New Issue
Block a user