aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execMain.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/execMain.c')
-rw-r--r--src/backend/executor/execMain.c949
1 files changed, 94 insertions, 855 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 82b05dc4d1b..7c788ea6df4 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,13 +26,12 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.331 2009/10/08 22:34:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.332 2009/10/10 01:43:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include "access/heapam.h"
#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/transam.h"
@@ -44,17 +43,13 @@
#include "commands/trigger.h"
#include "executor/execdebug.h"
#include "executor/instrument.h"
-#include "executor/nodeSubplan.h"
#include "miscadmin.h"
-#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
-#include "storage/smgr.h"
#include "utils/acl.h"
-#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
@@ -77,35 +72,20 @@ typedef struct evalPlanQual
/* decls for local routines only used within this module */
static void InitPlan(QueryDesc *queryDesc, int eflags);
-static void ExecCheckPlanOutput(Relation resultRel, List *targetList);
static void ExecEndPlan(PlanState *planstate, EState *estate);
static void ExecutePlan(EState *estate, PlanState *planstate,
CmdType operation,
+ bool sendTuples,
long numberTuples,
ScanDirection direction,
DestReceiver *dest);
-static void ExecSelect(TupleTableSlot *slot,
- DestReceiver *dest, EState *estate);
-static void ExecInsert(TupleTableSlot *slot, ItemPointer tupleid,
- TupleTableSlot *planSlot,
- DestReceiver *dest, EState *estate);
-static void ExecDelete(ItemPointer tupleid,
- TupleTableSlot *planSlot,
- DestReceiver *dest, EState *estate);
-static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
- TupleTableSlot *planSlot,
- DestReceiver *dest, EState *estate);
-static void ExecProcessReturning(ProjectionInfo *projectReturning,
- TupleTableSlot *tupleSlot,
- TupleTableSlot *planSlot,
- DestReceiver *dest);
static TupleTableSlot *EvalPlanQualNext(EState *estate);
static void EndEvalPlanQual(EState *estate);
static void ExecCheckRTPerms(List *rangeTable);
static void ExecCheckRTEPerms(RangeTblEntry *rte);
static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
- evalPlanQual *priorepq);
+ Plan *planTree, evalPlanQual *priorepq);
static void EvalPlanQualStop(evalPlanQual *epq);
static void OpenIntoRel(QueryDesc *queryDesc);
static void CloseIntoRel(QueryDesc *queryDesc);
@@ -297,7 +277,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate->es_lastoid = InvalidOid;
sendTuples = (operation == CMD_SELECT ||
- queryDesc->plannedstmt->returningLists);
+ queryDesc->plannedstmt->hasReturning);
if (sendTuples)
(*dest->rStartup) (dest, operation, queryDesc->tupDesc);
@@ -309,6 +289,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
ExecutePlan(estate,
queryDesc->planstate,
operation,
+ sendTuples,
count,
direction,
dest);
@@ -668,7 +649,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
estate->es_range_table = rangeTable;
/*
- * initialize result relation stuff
+ * initialize result relation stuff, and open/lock the result rels.
+ *
+ * We must do this before initializing the plan tree, else we might
+ * try to do a lock upgrade if a result rel is also a source rel.
*/
if (plannedstmt->resultRelations)
{
@@ -697,8 +681,8 @@ InitPlan(QueryDesc *queryDesc, int eflags)
}
estate->es_result_relations = resultRelInfos;
estate->es_num_result_relations = numResultRelations;
- /* Initialize to first or only result rel */
- estate->es_result_relation_info = resultRelInfos;
+ /* es_result_relation_info is NULL except when within ModifyTable */
+ estate->es_result_relation_info = NULL;
}
else
{
@@ -755,12 +739,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
}
/*
- * Initialize the executor's tuple table. Also, if it's not a SELECT,
- * set up a tuple table slot for use for trigger output tuples.
+ * Initialize the executor's tuple table to empty.
*/
estate->es_tupleTable = NIL;
- if (operation != CMD_SELECT)
- estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate);
+ estate->es_trig_tuple_slot = NULL;
/* mark EvalPlanQual not active */
estate->es_plannedstmt = plannedstmt;
@@ -814,214 +796,70 @@ InitPlan(QueryDesc *queryDesc, int eflags)
tupType = ExecGetResultType(planstate);
/*
- * Initialize the junk filter if needed. SELECT and INSERT queries need a
- * filter if there are any junk attrs in the tlist. UPDATE and DELETE
- * always need a filter, since there's always a junk 'ctid' attribute
- * present --- no need to look first.
- *
- * This section of code is also a convenient place to verify that the
- * output of an INSERT or UPDATE matches the target table(s).
+ * Initialize the junk filter if needed. SELECT queries need a
+ * filter if there are any junk attrs in the top-level tlist.
*/
+ if (operation == CMD_SELECT)
{
bool junk_filter_needed = false;
ListCell *tlist;
- switch (operation)
+ foreach(tlist, plan->targetlist)
{
- case CMD_SELECT:
- case CMD_INSERT:
- foreach(tlist, plan->targetlist)
- {
- TargetEntry *tle = (TargetEntry *) lfirst(tlist);
+ TargetEntry *tle = (TargetEntry *) lfirst(tlist);
- if (tle->resjunk)
- {
- junk_filter_needed = true;
- break;
- }
- }
- break;
- case CMD_UPDATE:
- case CMD_DELETE:
+ if (tle->resjunk)
+ {
junk_filter_needed = true;
break;
- default:
- break;
+ }
}
if (junk_filter_needed)
{
- /*
- * If there are multiple result relations, each one needs its own
- * junk filter. Note this is only possible for UPDATE/DELETE, so
- * we can't be fooled by some needing a filter and some not.
- */
- if (list_length(plannedstmt->resultRelations) > 1)
- {
- PlanState **appendplans;
- int as_nplans;
- ResultRelInfo *resultRelInfo;
-
- /* Top plan had better be an Append here. */
- Assert(IsA(plan, Append));
- Assert(((Append *) plan)->isTarget);
- Assert(IsA(planstate, AppendState));
- appendplans = ((AppendState *) planstate)->appendplans;
- as_nplans = ((AppendState *) planstate)->as_nplans;
- Assert(as_nplans == estate->es_num_result_relations);
- resultRelInfo = estate->es_result_relations;
- for (i = 0; i < as_nplans; i++)
- {
- PlanState *subplan = appendplans[i];
- JunkFilter *j;
-
- if (operation == CMD_UPDATE)
- ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
- subplan->plan->targetlist);
-
- j = ExecInitJunkFilter(subplan->plan->targetlist,
- resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
- ExecInitExtraTupleSlot(estate));
-
- /*
- * Since it must be UPDATE/DELETE, there had better be a
- * "ctid" junk attribute in the tlist ... but ctid could
- * be at a different resno for each result relation. We
- * look up the ctid resnos now and save them in the
- * junkfilters.
- */
- j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
- if (!AttributeNumberIsValid(j->jf_junkAttNo))
- elog(ERROR, "could not find junk ctid column");
- resultRelInfo->ri_junkFilter = j;
- resultRelInfo++;
- }
-
- /*
- * Set active junkfilter too; at this point ExecInitAppend has
- * already selected an active result relation...
- */
- estate->es_junkFilter =
- estate->es_result_relation_info->ri_junkFilter;
-
- /*
- * We currently can't support rowmarks in this case, because
- * the associated junk CTIDs might have different resnos in
- * different subplans.
- */
- if (estate->es_rowMarks)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("SELECT FOR UPDATE/SHARE is not supported within a query with multiple result relations")));
- }
- else
- {
- /* Normal case with just one JunkFilter */
- JunkFilter *j;
-
- if (operation == CMD_INSERT || operation == CMD_UPDATE)
- ExecCheckPlanOutput(estate->es_result_relation_info->ri_RelationDesc,
- planstate->plan->targetlist);
+ JunkFilter *j;
- j = ExecInitJunkFilter(planstate->plan->targetlist,
- tupType->tdhasoid,
- ExecInitExtraTupleSlot(estate));
- estate->es_junkFilter = j;
- if (estate->es_result_relation_info)
- estate->es_result_relation_info->ri_junkFilter = j;
+ j = ExecInitJunkFilter(planstate->plan->targetlist,
+ tupType->tdhasoid,
+ ExecInitExtraTupleSlot(estate));
+ estate->es_junkFilter = j;
- if (operation == CMD_SELECT)
- {
- /* For SELECT, want to return the cleaned tuple type */
- tupType = j->jf_cleanTupType;
- }
- else if (operation == CMD_UPDATE || operation == CMD_DELETE)
- {
- /* For UPDATE/DELETE, find the ctid junk attr now */
- j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
- if (!AttributeNumberIsValid(j->jf_junkAttNo))
- elog(ERROR, "could not find junk ctid column");
- }
+ /* Want to return the cleaned tuple type */
+ tupType = j->jf_cleanTupType;
- /* For SELECT FOR UPDATE/SHARE, find the junk attrs now */
- foreach(l, estate->es_rowMarks)
+ /* For SELECT FOR UPDATE/SHARE, find the junk attrs now */
+ foreach(l, estate->es_rowMarks)
+ {
+ ExecRowMark *erm = (ExecRowMark *) lfirst(l);
+ char resname[32];
+
+ /* always need the ctid */
+ snprintf(resname, sizeof(resname), "ctid%u",
+ erm->prti);
+ erm->ctidAttNo = ExecFindJunkAttribute(j, resname);
+ if (!AttributeNumberIsValid(erm->ctidAttNo))
+ elog(ERROR, "could not find junk \"%s\" column",
+ resname);
+ /* if child relation, need tableoid too */
+ if (erm->rti != erm->prti)
{
- ExecRowMark *erm = (ExecRowMark *) lfirst(l);
- char resname[32];
-
- /* always need the ctid */
- snprintf(resname, sizeof(resname), "ctid%u",
+ snprintf(resname, sizeof(resname), "tableoid%u",
erm->prti);
- erm->ctidAttNo = ExecFindJunkAttribute(j, resname);
- if (!AttributeNumberIsValid(erm->ctidAttNo))
+ erm->toidAttNo = ExecFindJunkAttribute(j, resname);
+ if (!AttributeNumberIsValid(erm->toidAttNo))
elog(ERROR, "could not find junk \"%s\" column",
resname);
- /* if child relation, need tableoid too */
- if (erm->rti != erm->prti)
- {
- snprintf(resname, sizeof(resname), "tableoid%u",
- erm->prti);
- erm->toidAttNo = ExecFindJunkAttribute(j, resname);
- if (!AttributeNumberIsValid(erm->toidAttNo))
- elog(ERROR, "could not find junk \"%s\" column",
- resname);
- }
}
}
}
else
{
- if (operation == CMD_INSERT)
- ExecCheckPlanOutput(estate->es_result_relation_info->ri_RelationDesc,
- planstate->plan->targetlist);
-
estate->es_junkFilter = NULL;
if (estate->es_rowMarks)
elog(ERROR, "SELECT FOR UPDATE/SHARE, but no junk columns");
}
}
- /*
- * Initialize RETURNING projections if needed.
- */
- if (plannedstmt->returningLists)
- {
- TupleTableSlot *slot;
- ExprContext *econtext;
- ResultRelInfo *resultRelInfo;
-
- /*
- * We set QueryDesc.tupDesc to be the RETURNING rowtype in this case.
- * We assume all the sublists will generate the same output tupdesc.
- */
- tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists),
- false);
-
- /* Set up a slot for the output of the RETURNING projection(s) */
- slot = ExecInitExtraTupleSlot(estate);
- ExecSetSlotDescriptor(slot, tupType);
- /* Need an econtext too */
- econtext = CreateExprContext(estate);
-
- /*
- * Build a projection for each result rel. Note that any SubPlans in
- * the RETURNING lists get attached to the topmost plan node.
- */
- Assert(list_length(plannedstmt->returningLists) == estate->es_num_result_relations);
- resultRelInfo = estate->es_result_relations;
- foreach(l, plannedstmt->returningLists)
- {
- List *rlist = (List *) lfirst(l);
- List *rliststate;
-
- rliststate = (List *) ExecInitExpr((Expr *) rlist, planstate);
- resultRelInfo->ri_projectReturning =
- ExecBuildProjectionInfo(rliststate, econtext, slot,
- resultRelInfo->ri_RelationDesc->rd_att);
- resultRelInfo++;
- }
- }
-
queryDesc->tupDesc = tupType;
queryDesc->planstate = planstate;
@@ -1123,75 +961,6 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
}
/*
- * Verify that the tuples to be produced by INSERT or UPDATE match the
- * target relation's rowtype
- *
- * We do this to guard against stale plans. If plan invalidation is
- * functioning properly then we should never get a failure here, but better
- * safe than sorry. Note that this is called after we have obtained lock
- * on the target rel, so the rowtype can't change underneath us.
- *
- * The plan output is represented by its targetlist, because that makes
- * handling the dropped-column case easier.
- */
-static void
-ExecCheckPlanOutput(Relation resultRel, List *targetList)
-{
- TupleDesc resultDesc = RelationGetDescr(resultRel);
- int attno = 0;
- ListCell *lc;
-
- foreach(lc, targetList)
- {
- TargetEntry *tle = (TargetEntry *) lfirst(lc);
- Form_pg_attribute attr;
-
- if (tle->resjunk)
- continue; /* ignore junk tlist items */
-
- if (attno >= resultDesc->natts)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("table row type and query-specified row type do not match"),
- errdetail("Query has too many columns.")));
- attr = resultDesc->attrs[attno++];
-
- if (!attr->attisdropped)
- {
- /* Normal case: demand type match */
- if (exprType((Node *) tle->expr) != attr->atttypid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("table row type and query-specified row type do not match"),
- errdetail("Table has type %s at ordinal position %d, but query expects %s.",
- format_type_be(attr->atttypid),
- attno,
- format_type_be(exprType((Node *) tle->expr)))));
- }
- else
- {
- /*
- * For a dropped column, we can't check atttypid (it's likely 0).
- * In any case the planner has most likely inserted an INT4 null.
- * What we insist on is just *some* NULL constant.
- */
- if (!IsA(tle->expr, Const) ||
- !((Const *) tle->expr)->constisnull)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("table row type and query-specified row type do not match"),
- errdetail("Query provides a value for a dropped column at ordinal position %d.",
- attno)));
- }
- }
- if (attno != resultDesc->natts)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("table row type and query-specified row type do not match"),
- errdetail("Query has too few columns.")));
-}
-
-/*
* ExecGetTriggerResultRel
*
* Get a ResultRelInfo for a trigger target relation. Most of the time,
@@ -1281,11 +1050,13 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
* recognize how far down the requirement really goes, but for now we just
* make all plan nodes do the same thing if the top level forces the choice.
*
- * We assume that estate->es_result_relation_info is already set up to
- * describe the target relation. Note that in an UPDATE that spans an
- * inheritance tree, some of the target relations may have OIDs and some not.
- * We have to make the decisions on a per-relation basis as we initialize
- * each of the child plans of the topmost Append plan.
+ * We assume that if we are generating tuples for INSERT or UPDATE,
+ * estate->es_result_relation_info is already set up to describe the target
+ * relation. Note that in an UPDATE that spans an inheritance tree, some of
+ * the target relations may have OIDs and some not. We have to make the
+ * decisions on a per-relation basis as we initialize each of the subplans of
+ * the ModifyTable node, so ModifyTable has to set es_result_relation_info
+ * while initializing each subplan.
*
* SELECT INTO is even uglier, because we don't have the INTO relation's
* descriptor available when this code runs; we have to look aside at a
@@ -1294,27 +1065,25 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
bool
ExecContextForcesOids(PlanState *planstate, bool *hasoids)
{
- if (planstate->state->es_select_into)
- {
- *hasoids = planstate->state->es_into_oids;
- return true;
- }
- else
+ ResultRelInfo *ri = planstate->state->es_result_relation_info;
+
+ if (ri != NULL)
{
- ResultRelInfo *ri = planstate->state->es_result_relation_info;
+ Relation rel = ri->ri_RelationDesc;
- if (ri != NULL)
+ if (rel != NULL)
{
- Relation rel = ri->ri_RelationDesc;
-
- if (rel != NULL)
- {
- *hasoids = rel->rd_rel->relhasoids;
- return true;
- }
+ *hasoids = rel->rd_rel->relhasoids;
+ return true;
}
}
+ if (planstate->state->es_select_into)
+ {
+ *hasoids = planstate->state->es_into_oids;
+ return true;
+ }
+
return false;
}
@@ -1416,6 +1185,7 @@ static void
ExecutePlan(EState *estate,
PlanState *planstate,
CmdType operation,
+ bool sendTuples,
long numberTuples,
ScanDirection direction,
DestReceiver *dest)
@@ -1423,8 +1193,6 @@ ExecutePlan(EState *estate,
JunkFilter *junkfilter;
TupleTableSlot *planSlot;
TupleTableSlot *slot;
- ItemPointer tupleid = NULL;
- ItemPointerData tuple_ctid;
long current_tuple_count;
/*
@@ -1438,25 +1206,6 @@ ExecutePlan(EState *estate,
estate->es_direction = direction;
/*
- * Process BEFORE EACH STATEMENT triggers
- */
- switch (operation)
- {
- case CMD_UPDATE:
- ExecBSUpdateTriggers(estate, estate->es_result_relation_info);
- break;
- case CMD_DELETE:
- ExecBSDeleteTriggers(estate, estate->es_result_relation_info);
- break;
- case CMD_INSERT:
- ExecBSInsertTriggers(estate, estate->es_result_relation_info);
- break;
- default:
- /* do nothing */
- break;
- }
-
- /*
* Loop until we've processed the proper number of tuples from the plan.
*/
for (;;)
@@ -1578,6 +1327,7 @@ lnext: ;
/* updated, so look at updated version */
newSlot = EvalPlanQual(estate,
erm->rti,
+ planstate,
&update_ctid,
update_xmax);
if (!TupIsNull(newSlot))
@@ -1605,61 +1355,25 @@ lnext: ;
}
/*
- * extract the 'ctid' junk attribute.
- */
- if (operation == CMD_UPDATE || operation == CMD_DELETE)
- {
- Datum datum;
- bool isNull;
-
- datum = ExecGetJunkAttribute(slot, junkfilter->jf_junkAttNo,
- &isNull);
- /* shouldn't ever get a null result... */
- if (isNull)
- elog(ERROR, "ctid is NULL");
-
- tupleid = (ItemPointer) DatumGetPointer(datum);
- tuple_ctid = *tupleid; /* make sure we don't free the ctid!! */
- tupleid = &tuple_ctid;
- }
-
- /*
- * Create a new "clean" tuple with all junk attributes removed. We
- * don't need to do this for DELETE, however (there will in fact
- * be no non-junk attributes in a DELETE!)
+ * Create a new "clean" tuple with all junk attributes removed.
*/
- if (operation != CMD_DELETE)
- slot = ExecFilterJunk(junkfilter, slot);
+ slot = ExecFilterJunk(junkfilter, slot);
}
/*
- * now that we have a tuple, do the appropriate thing with it.. either
- * send it to the output destination, add it to a relation someplace,
- * delete it from a relation, or modify some of its attributes.
+ * If we are supposed to send the tuple somewhere, do so.
+ * (In practice this is probably always the case at this point.)
*/
- switch (operation)
- {
- case CMD_SELECT:
- ExecSelect(slot, dest, estate);
- break;
+ if (sendTuples)
+ (*dest->receiveSlot) (slot, dest);
- case CMD_INSERT:
- ExecInsert(slot, tupleid, planSlot, dest, estate);
- break;
-
- case CMD_DELETE:
- ExecDelete(tupleid, planSlot, dest, estate);
- break;
-
- case CMD_UPDATE:
- ExecUpdate(slot, tupleid, planSlot, dest, estate);
- break;
-
- default:
- elog(ERROR, "unrecognized operation code: %d",
- (int) operation);
- break;
- }
+ /*
+ * Count tuples processed, if this is a SELECT. (For other operation
+ * types, the ModifyTable plan node must count the appropriate
+ * events.)
+ */
+ if (operation == CMD_SELECT)
+ (estate->es_processed)++;
/*
* check our tuple count.. if we've processed the proper number then
@@ -1670,453 +1384,8 @@ lnext: ;
if (numberTuples && numberTuples == current_tuple_count)
break;
}
-
- /*
- * Process AFTER EACH STATEMENT triggers
- */
- switch (operation)
- {
- case CMD_UPDATE:
- ExecASUpdateTriggers(estate, estate->es_result_relation_info);
- break;
- case CMD_DELETE:
- ExecASDeleteTriggers(estate, estate->es_result_relation_info);
- break;
- case CMD_INSERT:
- ExecASInsertTriggers(estate, estate->es_result_relation_info);
- break;
- default:
- /* do nothing */
- break;
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecSelect
- *
- * SELECTs are easy.. we just pass the tuple to the appropriate
- * output function.
- * ----------------------------------------------------------------
- */
-static void
-ExecSelect(TupleTableSlot *slot,
- DestReceiver *dest,
- EState *estate)
-{
- (*dest->receiveSlot) (slot, dest);
- (estate->es_processed)++;
-}
-
-/* ----------------------------------------------------------------
- * ExecInsert
- *
- * INSERTs are trickier.. we have to insert the tuple into
- * the base relation and insert appropriate tuples into the
- * index relations.
- * ----------------------------------------------------------------
- */
-static void
-ExecInsert(TupleTableSlot *slot,
- ItemPointer tupleid,
- TupleTableSlot *planSlot,
- DestReceiver *dest,
- EState *estate)
-{
- HeapTuple tuple;
- ResultRelInfo *resultRelInfo;
- Relation resultRelationDesc;
- Oid newId;
- List *recheckIndexes = NIL;
-
- /*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecMaterializeSlot(slot);
-
- /*
- * get information on the (current) result relation
- */
- resultRelInfo = estate->es_result_relation_info;
- resultRelationDesc = resultRelInfo->ri_RelationDesc;
-
- /*
- * If the result relation has OIDs, force the tuple's OID to zero so that
- * heap_insert will assign a fresh OID. Usually the OID already will be
- * zero at this point, but there are corner cases where the plan tree can
- * return a tuple extracted literally from some table with the same
- * rowtype.
- *
- * XXX if we ever wanted to allow users to assign their own OIDs to new
- * rows, this'd be the place to do it. For the moment, we make a point of
- * doing this before calling triggers, so that a user-supplied trigger
- * could hack the OID if desired.
- */
- if (resultRelationDesc->rd_rel->relhasoids)
- HeapTupleSetOid(tuple, InvalidOid);
-
- /* BEFORE ROW INSERT Triggers */
- if (resultRelInfo->ri_TrigDesc &&
- resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
- {
- HeapTuple newtuple;
-
- newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);
-
- if (newtuple == NULL) /* "do nothing" */
- return;
-
- if (newtuple != tuple) /* modified by Trigger(s) */
- {
- /*
- * Put the modified tuple into a slot for convenience of routines
- * below. We assume the tuple was allocated in per-tuple memory
- * context, and therefore will go away by itself. The tuple table
- * slot should not try to clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
-
- if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor)
- ExecSetSlotDescriptor(newslot, slot->tts_tupleDescriptor);
- ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
- slot = newslot;
- tuple = newtuple;
- }
- }
-
- /*
- * Check the constraints of the tuple
- */
- if (resultRelationDesc->rd_att->constr)
- ExecConstraints(resultRelInfo, slot, estate);
-
- /*
- * insert the tuple
- *
- * Note: heap_insert returns the tid (location) of the new tuple in the
- * t_self field.
- */
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_output_cid, 0, NULL);
-
- (estate->es_processed)++;
- estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
-
- /*
- * insert index entries for tuple
- */
- if (resultRelInfo->ri_NumIndices > 0)
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false);
-
- /* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes);
-
- /* Process RETURNING if present */
- if (resultRelInfo->ri_projectReturning)
- ExecProcessReturning(resultRelInfo->ri_projectReturning,
- slot, planSlot, dest);
-}
-
-/* ----------------------------------------------------------------
- * ExecDelete
- *
- * DELETE is like UPDATE, except that we delete the tuple and no
- * index modifications are needed
- * ----------------------------------------------------------------
- */
-static void
-ExecDelete(ItemPointer tupleid,
- TupleTableSlot *planSlot,
- DestReceiver *dest,
- EState *estate)
-{
- ResultRelInfo *resultRelInfo;
- Relation resultRelationDesc;
- HTSU_Result result;
- ItemPointerData update_ctid;
- TransactionId update_xmax;
-
- /*
- * get information on the (current) result relation
- */
- resultRelInfo = estate->es_result_relation_info;
- resultRelationDesc = resultRelInfo->ri_RelationDesc;
-
- /* BEFORE ROW DELETE Triggers */
- if (resultRelInfo->ri_TrigDesc &&
- resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_DELETE] > 0)
- {
- bool dodelete;
-
- dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid);
-
- if (!dodelete) /* "do nothing" */
- return;
- }
-
- /*
- * delete the tuple
- *
- * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that
- * the row to be deleted is visible to that snapshot, and throw a can't-
- * serialize error if not. This is a special-case behavior needed for
- * referential integrity updates in serializable transactions.
- */
-ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
- &update_ctid, &update_xmax,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ );
- switch (result)
- {
- case HeapTupleSelfUpdated:
- /* already deleted by self; nothing to do */
- return;
-
- case HeapTupleMayBeUpdated:
- break;
-
- case HeapTupleUpdated:
- if (IsXactIsoLevelSerializable)
- ereport(ERROR,
- (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
- errmsg("could not serialize access due to concurrent update")));
- else if (!ItemPointerEquals(tupleid, &update_ctid))
- {
- TupleTableSlot *epqslot;
-
- epqslot = EvalPlanQual(estate,
- resultRelInfo->ri_RangeTableIndex,
- &update_ctid,
- update_xmax);
- if (!TupIsNull(epqslot))
- {
- *tupleid = update_ctid;
- goto ldelete;
- }
- }
- /* tuple already deleted; nothing to do */
- return;
-
- default:
- elog(ERROR, "unrecognized heap_delete status: %u", result);
- return;
- }
-
- (estate->es_processed)++;
-
- /*
- * Note: Normally one would think that we have to delete index tuples
- * associated with the heap tuple now...
- *
- * ... but in POSTGRES, we have no need to do this because VACUUM will
- * take care of it later. We can't delete index tuples immediately
- * anyway, since the tuple is still visible to other transactions.
- */
-
- /* AFTER ROW DELETE Triggers */
- ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
-
- /* Process RETURNING if present */
- if (resultRelInfo->ri_projectReturning)
- {
- /*
- * We have to put the target tuple into a slot, which means first we
- * gotta fetch it. We can use the trigger tuple slot.
- */
- TupleTableSlot *slot = estate->es_trig_tuple_slot;
- HeapTupleData deltuple;
- Buffer delbuffer;
-
- deltuple.t_self = *tupleid;
- if (!heap_fetch(resultRelationDesc, SnapshotAny,
- &deltuple, &delbuffer, false, NULL))
- elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
-
- if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
- ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
-
- ExecProcessReturning(resultRelInfo->ri_projectReturning,
- slot, planSlot, dest);
-
- ExecClearTuple(slot);
- ReleaseBuffer(delbuffer);
- }
}
-/* ----------------------------------------------------------------
- * ExecUpdate
- *
- * note: we can't run UPDATE queries with transactions
- * off because UPDATEs are actually INSERTs and our
- * scan will mistakenly loop forever, updating the tuple
- * it just inserted.. This should be fixed but until it
- * is, we don't want to get stuck in an infinite loop
- * which corrupts your database..
- * ----------------------------------------------------------------
- */
-static void
-ExecUpdate(TupleTableSlot *slot,
- ItemPointer tupleid,
- TupleTableSlot *planSlot,
- DestReceiver *dest,
- EState *estate)
-{
- HeapTuple tuple;
- ResultRelInfo *resultRelInfo;
- Relation resultRelationDesc;
- HTSU_Result result;
- ItemPointerData update_ctid;
- TransactionId update_xmax;
- List *recheckIndexes = NIL;
-
- /*
- * abort the operation if not running transactions
- */
- if (IsBootstrapProcessingMode())
- elog(ERROR, "cannot UPDATE during bootstrap");
-
- /*
- * get the heap tuple out of the tuple table slot, making sure we have a
- * writable copy
- */
- tuple = ExecMaterializeSlot(slot);
-
- /*
- * get information on the (current) result relation
- */
- resultRelInfo = estate->es_result_relation_info;
- resultRelationDesc = resultRelInfo->ri_RelationDesc;
-
- /* BEFORE ROW UPDATE Triggers */
- if (resultRelInfo->ri_TrigDesc &&
- resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
- {
- HeapTuple newtuple;
-
- newtuple = ExecBRUpdateTriggers(estate, resultRelInfo,
- tupleid, tuple);
-
- if (newtuple == NULL) /* "do nothing" */
- return;
-
- if (newtuple != tuple) /* modified by Trigger(s) */
- {
- /*
- * Put the modified tuple into a slot for convenience of routines
- * below. We assume the tuple was allocated in per-tuple memory
- * context, and therefore will go away by itself. The tuple table
- * slot should not try to clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
-
- if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor)
- ExecSetSlotDescriptor(newslot, slot->tts_tupleDescriptor);
- ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
- slot = newslot;
- tuple = newtuple;
- }
- }
-
- /*
- * Check the constraints of the tuple
- *
- * If we generate a new candidate tuple after EvalPlanQual testing, we
- * must loop back here and recheck constraints. (We don't need to redo
- * triggers, however. If there are any BEFORE triggers then trigger.c
- * will have done heap_lock_tuple to lock the correct tuple, so there's no
- * need to do them again.)
- */
-lreplace:;
- if (resultRelationDesc->rd_att->constr)
- ExecConstraints(resultRelInfo, slot, estate);
-
- /*
- * replace the heap tuple
- *
- * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that
- * the row to be updated is visible to that snapshot, and throw a can't-
- * serialize error if not. This is a special-case behavior needed for
- * referential integrity updates in serializable transactions.
- */
- result = heap_update(resultRelationDesc, tupleid, tuple,
- &update_ctid, &update_xmax,
- estate->es_output_cid,
- estate->es_crosscheck_snapshot,
- true /* wait for commit */ );
- switch (result)
- {
- case HeapTupleSelfUpdated:
- /* already deleted by self; nothing to do */
- return;
-
- case HeapTupleMayBeUpdated:
- break;
-
- case HeapTupleUpdated:
- if (IsXactIsoLevelSerializable)
- ereport(ERROR,
- (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
- errmsg("could not serialize access due to concurrent update")));
- else if (!ItemPointerEquals(tupleid, &update_ctid))
- {
- TupleTableSlot *epqslot;
-
- epqslot = EvalPlanQual(estate,
- resultRelInfo->ri_RangeTableIndex,
- &update_ctid,
- update_xmax);
- if (!TupIsNull(epqslot))
- {
- *tupleid = update_ctid;
- slot = ExecFilterJunk(estate->es_junkFilter, epqslot);
- tuple = ExecMaterializeSlot(slot);
- goto lreplace;
- }
- }
- /* tuple already deleted; nothing to do */
- return;
-
- default:
- elog(ERROR, "unrecognized heap_update status: %u", result);
- return;
- }
-
- (estate->es_processed)++;
-
- /*
- * Note: instead of having to update the old index tuples associated with
- * the heap tuple, all we do is form and insert new index tuples. This is
- * because UPDATEs are actually DELETEs and INSERTs, and index tuple
- * deletion is done later by VACUUM (see notes in ExecDelete). All we do
- * here is insert new index tuples. -cim 9/27/89
- */
-
- /*
- * insert index entries for tuple
- *
- * Note: heap_update returns the tid (location) of the new tuple in the
- * t_self field.
- *
- * If it's a HOT update, we mustn't insert new index entries.
- */
- if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
- recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
- estate, false);
-
- /* AFTER ROW UPDATE Triggers */
- ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple,
- recheckIndexes);
-
- /* Process RETURNING if present */
- if (resultRelInfo->ri_projectReturning)
- ExecProcessReturning(resultRelInfo->ri_projectReturning,
- slot, planSlot, dest);
-}
/*
* ExecRelCheck --- check that tuple meets constraints for result relation
@@ -2218,42 +1487,6 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
}
/*
- * ExecProcessReturning --- evaluate a RETURNING list and send to dest
- *
- * projectReturning: RETURNING projection info for current result rel
- * tupleSlot: slot holding tuple actually inserted/updated/deleted
- * planSlot: slot holding tuple returned by top plan node
- * dest: where to send the output
- */
-static void
-ExecProcessReturning(ProjectionInfo *projectReturning,
- TupleTableSlot *tupleSlot,
- TupleTableSlot *planSlot,
- DestReceiver *dest)
-{
- ExprContext *econtext = projectReturning->pi_exprContext;
- TupleTableSlot *retSlot;
-
- /*
- * Reset per-tuple memory context to free any expression evaluation
- * storage allocated in the previous cycle.
- */
- ResetExprContext(econtext);
-
- /* Make tuple and any needed join variables available to ExecProject */
- econtext->ecxt_scantuple = tupleSlot;
- econtext->ecxt_outertuple = planSlot;
-
- /* Compute the RETURNING expressions */
- retSlot = ExecProject(projectReturning, NULL);
-
- /* Send to dest */
- (*dest->receiveSlot) (retSlot, dest);
-
- ExecClearTuple(retSlot);
-}
-
-/*
* Check a modified tuple to see if we want to process its updated version
* under READ COMMITTED rules.
*
@@ -2261,6 +1494,7 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
*
* estate - executor state data
* rti - rangetable index of table containing tuple
+ * subplanstate - portion of plan tree that needs to be re-evaluated
* *tid - t_ctid from the outdated tuple (ie, next updated version)
* priorXmax - t_xmax from the outdated tuple
*
@@ -2272,6 +1506,7 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
*/
TupleTableSlot *
EvalPlanQual(EState *estate, Index rti,
+ PlanState *subplanstate,
ItemPointer tid, TransactionId priorXmax)
{
evalPlanQual *epq;
@@ -2526,10 +1761,10 @@ EvalPlanQual(EState *estate, Index rti,
*
* Note: if we were re-using PlanQual plans via ExecReScan, we'd need to
* instead copy down changeable state from the top plan (including
- * es_result_relation_info, es_junkFilter) and reset locally changeable
+ * es_result_relation_info) and reset locally changeable
* state in the epq (including es_param_exec_vals, es_evTupleNull).
*/
- EvalPlanQualStart(epq, estate, epq->next);
+ EvalPlanQualStart(epq, estate, subplanstate->plan, epq->next);
/*
* free old RTE' tuple, if any, and store target tuple where relation's
@@ -2628,7 +1863,8 @@ EndEvalPlanQual(EState *estate)
* the top-level estate rather than initializing it fresh.
*/
static void
-EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
+EvalPlanQualStart(evalPlanQual *epq, EState *estate, Plan *planTree,
+ evalPlanQual *priorepq)
{
EState *epqstate;
int rtsize;
@@ -2690,6 +1926,9 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
* Initialize private state information for each SubPlan. We must do this
* before running ExecInitNode on the main query tree, since
* ExecInitSubPlan expects to be able to find these entries.
+ * Some of the SubPlans might not be used in the part of the plan tree
+ * we intend to run, but since it's not easy to tell which, we just
+ * initialize them all.
*/
Assert(epqstate->es_subplanstates == NIL);
foreach(l, estate->es_plannedstmt->subplans)
@@ -2704,11 +1943,11 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
}
/*
- * Initialize the private state information for all the nodes in the query
- * tree. This opens files, allocates storage and leaves us ready to start
- * processing tuples.
+ * Initialize the private state information for all the nodes in the
+ * part of the plan tree we need to run. This opens files, allocates
+ * storage and leaves us ready to start processing tuples.
*/
- epq->planstate = ExecInitNode(estate->es_plannedstmt->planTree, epqstate, 0);
+ epq->planstate = ExecInitNode(planTree, epqstate, 0);
MemoryContextSwitchTo(oldcontext);
}