# # # patch "ChangeLog" # from [63b7efc47b89d714ed924eb4913381a77ca99ca2] # to [712f4bb83602e7af1ce14ed4f8c3d86116861832] # # patch "tests/test_a_merge_8/correct" # from [5f0b7aad22f9002748fba840d9bc612413887b8c] # to [11d1b0fc244c106e4b1e615cf8a9d47996cb1752] # # patch "tests/test_a_merge_8/left" # from [3ad78dd8d181bd08c699214692923da58cdcdda4] # to [2218e9827eff22bde55cac4e48749c33d8231876] # # patch "tests/test_a_merge_8/parent" # from [300798a2fc26fbf1e3b63d2e313a823d9226f183] # to [635502b5dd25e4891825e8f584e1f423c8432223] # # patch "tests/test_a_merge_8/right" # from [13cb3ac0b7c84eaf7aa7d8ba22670bd3cd22060a] # to [e2c3c1e5c4db85c53581122fad7cf83d835635e6] # ============================================================ --- ChangeLog 63b7efc47b89d714ed924eb4913381a77ca99ca2 +++ ChangeLog 712f4bb83602e7af1ce14ed4f8c3d86116861832 @@ -1,3 +1,8 @@ +2007-02-09 Markus Schiltknecht + + * tests/test_a_merge_8/{parent,left,right,correct}: simplified + the test case. + 2007-02-08 Derek Scherger * cmd_merging.cc (get_content_paths): new function ============================================================ --- tests/test_a_merge_8/correct 5f0b7aad22f9002748fba840d9bc612413887b8c +++ tests/test_a_merge_8/correct 11d1b0fc244c106e4b1e615cf8a9d47996cb1752 @@ -1,432 +1,4 @@ -#include "postgres.h" - -#include "access/heapam.h" -#include "access/reloptions.h" -#include "access/transam.h" -#include "access/xact.h" -#include "catalog/heap.h" -#include "catalog/namespace.h" -#include "catalog/toasting.h" -#include "commands/tablespace.h" -#include "commands/trigger.h" -#include "executor/execdebug.h" -#include "executor/instrument.h" -#include "executor/nodeSubplan.h" -#include "miscadmin.h" -#include "optimizer/clauses.h" -#include "parser/parse_clause.h" -#include "parser/parsetree.h" -#include "storage/smgr.h" -#include "utils/acl.h" -#include "utils/lsyscache.h" -#include "utils/memutils.h" - - -typedef struct evalPlanQual -{ - Index rti; - EState *estate; - PlanState *planstate; - struct evalPlanQual *next; /* stack of active PlanQual plans */ - struct evalPlanQual *free; /* list of free PlanQual plans */ -} evalPlanQual; - -/* decls for local routines only used within this module */ -static void InitPlan(QueryDesc *queryDesc, int eflags); -static void initResultRelInfo(ResultRelInfo *resultRelInfo, - Index resultRelationIndex, - List *rangeTable, - CmdType operation, - bool doInstrument); -static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, - CmdType operation, - 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 ExecCheckRTEPerms(RangeTblEntry *rte); -static void ExecCheckXactReadOnly(Query *parsetree); -static void EvalPlanQualStart(evalPlanQual *epq, EState *estate, - evalPlanQual *priorepq); -static void EvalPlanQualStop(evalPlanQual *epq); -static void OpenIntoRel(QueryDesc *queryDesc); -static void CloseIntoRel(QueryDesc *queryDesc); -static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo); -static void intorel_receive(TupleTableSlot *slot, DestReceiver *self); -static void intorel_shutdown(DestReceiver *self); -static void intorel_destroy(DestReceiver *self); - -/* end of local decls */ - - /* ---------------------------------------------------------------- - * ExecutorStart - * - * This routine must be called at the beginning of any execution of any - * query plan - * - * Takes a QueryDesc previously created by CreateQueryDesc (it's not real - * clear why we bother to separate the two functions, but...). The tupDesc - * field of the QueryDesc is filled in to describe the tuples that will be - * returned, and the internal fields (estate and planstate) are set up. - * - * eflags contains flag bits as described in executor.h. - * - * NB: the CurrentMemoryContext when this is called will become the parent - * of the per-query context used for this Executor invocation. - * ---------------------------------------------------------------- - */ -void -ExecutorStart(QueryDesc *queryDesc, int eflags) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks: queryDesc must not be started already */ - Assert(queryDesc != NULL); - Assert(queryDesc->estate == NULL); - - /* - * If the transaction is read-only, we need to check if any writes are - * planned to non-temporary tables. EXPLAIN is considered read-only. - */ - if (XactReadOnly && !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) - ExecCheckXactReadOnly(queryDesc->parsetree); - - /* - * Build EState, switch into per-query memory context for startup. - */ - estate = CreateExecutorState(); - queryDesc->estate = estate; - - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * Fill in parameters, if any, from queryDesc - */ - estate->es_param_list_info = queryDesc->params; - - if (queryDesc->plantree->nParamExec > 0) - estate->es_param_exec_vals = (ParamExecData *) - palloc0(queryDesc->plantree->nParamExec * sizeof(ParamExecData)); - - /* - * Copy other important information into the EState - */ - estate->es_snapshot = queryDesc->snapshot; - estate->es_crosscheck_snapshot = queryDesc->crosscheck_snapshot; - estate->es_instrument = queryDesc->doInstrument; - - /* - * Initialize the plan state tree - */ - InitPlan(queryDesc, eflags); - - MemoryContextSwitchTo(oldcontext); -} - -/* ---------------------------------------------------------------- - * ExecutorRun - * - * This is the main routine of the executor module. It accepts - * the query descriptor from the traffic cop and executes the - * query plan. - * - * ExecutorStart must have been called already. - * - * If direction is NoMovementScanDirection then nothing is done - * except to start up/shut down the destination. Otherwise, - * we retrieve up to 'count' tuples in the specified direction. - * - * Note: count = 0 is interpreted as no portal limit, i.e., run to - * completion. - * - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecutorRun(QueryDesc *queryDesc, - ScanDirection direction, long count) -{ - EState *estate; - CmdType operation; - DestReceiver *dest; - bool sendTuples; - TupleTableSlot *result; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* - * Switch into per-query memory context - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * extract information from the query descriptor and the query feature. - */ - operation = queryDesc->operation; - dest = queryDesc->dest; - - /* - * startup tuple receiver, if we will be emitting tuples - */ - estate->es_processed = 0; - estate->es_lastoid = InvalidOid; - - sendTuples = (operation == CMD_SELECT || - queryDesc->parsetree->returningList); - - if (sendTuples) - (*dest->rStartup) (dest, operation, queryDesc->tupDesc); - - /* - * run plan - */ - if (ScanDirectionIsNoMovement(direction)) - result = NULL; - else - result = ExecutePlan(estate, - queryDesc->planstate, - operation, - count, - direction, - dest); - - /* - * shutdown tuple receiver, if we started it - */ - if (sendTuples) - (*dest->rShutdown) (dest); - - MemoryContextSwitchTo(oldcontext); - - return result; -} - -/* ---------------------------------------------------------------- - * ExecutorEnd - * - * This routine must be called at the end of execution of any - * query plan - * ---------------------------------------------------------------- - */ -void -ExecutorEnd(QueryDesc *queryDesc) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* - * Switch into per-query memory context to run ExecEndPlan - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - ExecEndPlan(queryDesc->planstate, estate); - - /* - * Close the SELECT INTO relation if any - */ - if (estate->es_select_into) - CloseIntoRel(queryDesc); - - /* - * Must switch out of context before destroying it - */ - MemoryContextSwitchTo(oldcontext); - - /* - * Release EState and per-query memory context. This should release - * everything the executor has allocated. - */ - FreeExecutorState(estate); - - /* Reset queryDesc fields that no longer point to anything */ - queryDesc->tupDesc = NULL; - queryDesc->estate = NULL; - queryDesc->planstate = NULL; -} - -/* ---------------------------------------------------------------- - * ExecutorRewind - * - * This routine may be called on an open queryDesc to rewind it - * to the start. - * ---------------------------------------------------------------- - */ -void -ExecutorRewind(QueryDesc *queryDesc) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* It's probably not sensible to rescan updating queries */ - Assert(queryDesc->operation == CMD_SELECT); - - /* - * Switch into per-query memory context - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * rescan plan - */ - ExecReScan(queryDesc->planstate, NULL); - - MemoryContextSwitchTo(oldcontext); -} - - -/* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. - */ -void -ExecCheckRTPerms(List *rangeTable) -{ - ListCell *l; - - foreach(l, rangeTable) - { - RangeTblEntry *rte = lfirst(l); - - ExecCheckRTEPerms(rte); - } -} - -/* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. - */ -static void -ExecCheckRTEPerms(RangeTblEntry *rte) -{ - AclMode requiredPerms; - Oid relOid; - Oid userid; - - /* - * Only plain-relation RTEs need to be checked here. Subquery RTEs are - * checked by ExecInitSubqueryScan if the subquery is still a separate - * subquery --- if it's been pulled up into our query level then the RTEs - * are in our rangetable and will be checked here. Function RTEs are - * checked by init_fcache when the function is prepared for execution. - * Join and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return; - - relOid = rte->relid; - - /* - * userid to check as: current user unless we have a setuid indication. - * - * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - - /* - * We must have *all* the requiredPerms bits, so use aclmask not aclcheck. - */ - if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL) - != requiredPerms) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); -} - -/* - * Check that the query does not imply any writes to non-temp tables. - */ -static void -ExecCheckXactReadOnly(Query *parsetree) -{ - ListCell *l; - - /* - * CREATE TABLE AS or SELECT INTO? - * - * XXX should we allow this if the destination is temp? - */ - if (parsetree->into != NULL) - goto fail; - - /* Fail if write permissions are requested on any non-temp table */ - foreach(l, parsetree->rtable) - { - RangeTblEntry *rte = lfirst(l); - - if (rte->rtekind == RTE_SUBQUERY) - { - ExecCheckXactReadOnly(rte->subquery); - continue; - } - - if (rte->rtekind != RTE_RELATION) - continue; - - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) - continue; - - if (isTempNamespace(get_rel_namespace(rte->relid))) - continue; - - goto fail; - } - - return; - -fail: - ereport(ERROR, - (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), - errmsg("transaction is read-only"))); -} - - -/* ---------------------------------------------------------------- * InitPlan * * Initializes the query plan: open files, allocate storage @@ -1338,9 +910,6 @@ ExecInsert(TupleTableSlot *slot, /* BEFORE ROW INSERT Triggers */ if (resultRelInfo->ri_TrigDesc && -#ifdef REPLICATION - (txn_type != REPLICATED_REMOTE) && -#endif resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) { HeapTuple newtuple; @@ -1371,12 +940,7 @@ ExecInsert(TupleTableSlot *slot, /* * Check the constraints of the tuple */ -#ifndef REPLICATION if (resultRelationDesc->rd_att->constr) -#else - if ((txn_type != REPLICATED_REMOTE) && - (resultRelationDesc->rd_att->constr)) -#endif ExecConstraints(resultRelInfo, slot, estate); /* @@ -1400,11 +964,6 @@ ExecInsert(TupleTableSlot *slot, if (resultRelInfo->ri_NumIndices > 0) ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); -#ifdef REPLICATION - if (txn_type != REPLICATED_REMOTE) - { -#endif - /* AFTER ROW INSERT Triggers */ ExecARInsertTriggers(estate, resultRelInfo, tuple); @@ -1412,22 +971,6 @@ ExecInsert(TupleTableSlot *slot, if (resultRelInfo->ri_projectReturning) ExecProcessReturning(resultRelInfo->ri_projectReturning, slot, planSlot, dest); - -#ifdef REPLICATION - } - - if ( txn_type == REPLICATED_LOCAL ) - { - Oid resultRelationOid; - TupleCollection *tcoll; - - tcoll = &(((QueryInfo *) CurrentWriteSet->currQuery)->tcoll); - resultRelationOid = RelationGetRelid(resultRelationDesc); - if(resultRelationOid == tcoll->rel->relOid) - WriteSetCollectTuple(tupleid, slot, CurrentWriteSet->currQuery, - estate->es_snapshot); - } -#endif } /* ---------------------------------------------------------------- @@ -1457,9 +1000,6 @@ ExecDelete(ItemPointer tupleid, /* BEFORE ROW DELETE Triggers */ if (resultRelInfo->ri_TrigDesc && -#ifdef REPLICATION - (txn_type != REPLICATED_REMOTE) && -#endif resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_DELETE] > 0) { bool dodelete; @@ -1505,13 +1045,8 @@ ldelete:; &update_ctid, &update_xmax, estate->es_snapshot->curcid, estate->es_crosscheck_snapshot, -#ifndef REPLICATION true /* wait for commit */ ); -#else - /* remote transaction don't wait */ - (txn_type != REPLICATED_REMOTE)); -#endif - + //THIS LINE IS NEEDED TO TRIGGER THE BUG switch (result) { case HeapTupleSelfUpdated: @@ -1521,37 +1056,7 @@ ldelete:; case HeapTupleMayBeUpdated: break; -#ifdef REPLICATION - case HeapTupleBeingUpdated: - if (txn_type == REPLICATED_REMOTE) - { - /* - * A running local transaction has a lock on the tuple. Abort - * that local transaction and return, signaling that we must - * wait until the other transaction releases the lock. - */ -#ifdef RMGR_DEBUG - elog(DEBUG5, - "ExecDelete: need to terminate a local transaction %d", update_xmax); -#endif - TxnToAbort = update_xmax; - return; - } -#ifdef RMGR_DEBUG - else - /* should be impossible */ - Assert(result != HeapTupleBeingUpdated); -#endif -#endif - case HeapTupleUpdated: -#ifdef RMGR_DEBUG - if (txn_type == REPLICATED_REMOTE) - { - elog(DEBUG5, "ExecDelete: a concurrent update has committed before. Abort this transaction."); - //FIXME - } -#endif if (IsXactIsoLevelSerializable) ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), @@ -1621,172 +1126,3 @@ ldelete:; 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; - - /* - * 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, - estate->es_snapshot->curcid); - - 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_snapshot->curcid, - 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, - estate->es_snapshot->curcid); - 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; - } - - IncrReplaced(); - (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 (resultRelInfo->ri_NumIndices > 0) - ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); - - /* AFTER ROW UPDATE Triggers */ - ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple); - - /* Process RETURNING if present */ - if (resultRelInfo->ri_projectReturning) - ExecProcessReturning(resultRelInfo->ri_projectReturning, - slot, planSlot, dest); -} ============================================================ --- tests/test_a_merge_8/left 3ad78dd8d181bd08c699214692923da58cdcdda4 +++ tests/test_a_merge_8/left 2218e9827eff22bde55cac4e48749c33d8231876 @@ -1,432 +1,4 @@ -#include "postgres.h" - -#include "access/heapam.h" -#include "access/reloptions.h" -#include "access/transam.h" -#include "access/xact.h" -#include "catalog/heap.h" -#include "catalog/namespace.h" -#include "catalog/toasting.h" -#include "commands/tablespace.h" -#include "commands/trigger.h" -#include "executor/execdebug.h" -#include "executor/instrument.h" -#include "executor/nodeSubplan.h" -#include "miscadmin.h" -#include "optimizer/clauses.h" -#include "parser/parse_clause.h" -#include "parser/parsetree.h" -#include "storage/smgr.h" -#include "utils/acl.h" -#include "utils/lsyscache.h" -#include "utils/memutils.h" - - -typedef struct evalPlanQual -{ - Index rti; - EState *estate; - PlanState *planstate; - struct evalPlanQual *next; /* stack of active PlanQual plans */ - struct evalPlanQual *free; /* list of free PlanQual plans */ -} evalPlanQual; - -/* decls for local routines only used within this module */ -static void InitPlan(QueryDesc *queryDesc, int eflags); -static void initResultRelInfo(ResultRelInfo *resultRelInfo, - Index resultRelationIndex, - List *rangeTable, - CmdType operation, - bool doInstrument); -static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, - CmdType operation, - 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 ExecCheckRTEPerms(RangeTblEntry *rte); -static void ExecCheckXactReadOnly(Query *parsetree); -static void EvalPlanQualStart(evalPlanQual *epq, EState *estate, - evalPlanQual *priorepq); -static void EvalPlanQualStop(evalPlanQual *epq); -static void OpenIntoRel(QueryDesc *queryDesc); -static void CloseIntoRel(QueryDesc *queryDesc); -static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo); -static void intorel_receive(TupleTableSlot *slot, DestReceiver *self); -static void intorel_shutdown(DestReceiver *self); -static void intorel_destroy(DestReceiver *self); - -/* end of local decls */ - - /* ---------------------------------------------------------------- - * ExecutorStart - * - * This routine must be called at the beginning of any execution of any - * query plan - * - * Takes a QueryDesc previously created by CreateQueryDesc (it's not real - * clear why we bother to separate the two functions, but...). The tupDesc - * field of the QueryDesc is filled in to describe the tuples that will be - * returned, and the internal fields (estate and planstate) are set up. - * - * eflags contains flag bits as described in executor.h. - * - * NB: the CurrentMemoryContext when this is called will become the parent - * of the per-query context used for this Executor invocation. - * ---------------------------------------------------------------- - */ -void -ExecutorStart(QueryDesc *queryDesc, int eflags) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks: queryDesc must not be started already */ - Assert(queryDesc != NULL); - Assert(queryDesc->estate == NULL); - - /* - * If the transaction is read-only, we need to check if any writes are - * planned to non-temporary tables. EXPLAIN is considered read-only. - */ - if (XactReadOnly && !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) - ExecCheckXactReadOnly(queryDesc->parsetree); - - /* - * Build EState, switch into per-query memory context for startup. - */ - estate = CreateExecutorState(); - queryDesc->estate = estate; - - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * Fill in parameters, if any, from queryDesc - */ - estate->es_param_list_info = queryDesc->params; - - if (queryDesc->plantree->nParamExec > 0) - estate->es_param_exec_vals = (ParamExecData *) - palloc0(queryDesc->plantree->nParamExec * sizeof(ParamExecData)); - - /* - * Copy other important information into the EState - */ - estate->es_snapshot = queryDesc->snapshot; - estate->es_crosscheck_snapshot = queryDesc->crosscheck_snapshot; - estate->es_instrument = queryDesc->doInstrument; - - /* - * Initialize the plan state tree - */ - InitPlan(queryDesc, eflags); - - MemoryContextSwitchTo(oldcontext); -} - -/* ---------------------------------------------------------------- - * ExecutorRun - * - * This is the main routine of the executor module. It accepts - * the query descriptor from the traffic cop and executes the - * query plan. - * - * ExecutorStart must have been called already. - * - * If direction is NoMovementScanDirection then nothing is done - * except to start up/shut down the destination. Otherwise, - * we retrieve up to 'count' tuples in the specified direction. - * - * Note: count = 0 is interpreted as no portal limit, i.e., run to - * completion. - * - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecutorRun(QueryDesc *queryDesc, - ScanDirection direction, long count) -{ - EState *estate; - CmdType operation; - DestReceiver *dest; - bool sendTuples; - TupleTableSlot *result; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* - * Switch into per-query memory context - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * extract information from the query descriptor and the query feature. - */ - operation = queryDesc->operation; - dest = queryDesc->dest; - - /* - * startup tuple receiver, if we will be emitting tuples - */ - estate->es_processed = 0; - estate->es_lastoid = InvalidOid; - - sendTuples = (operation == CMD_SELECT || - queryDesc->parsetree->returningList); - - if (sendTuples) - (*dest->rStartup) (dest, operation, queryDesc->tupDesc); - - /* - * run plan - */ - if (ScanDirectionIsNoMovement(direction)) - result = NULL; - else - result = ExecutePlan(estate, - queryDesc->planstate, - operation, - count, - direction, - dest); - - /* - * shutdown tuple receiver, if we started it - */ - if (sendTuples) - (*dest->rShutdown) (dest); - - MemoryContextSwitchTo(oldcontext); - - return result; -} - -/* ---------------------------------------------------------------- - * ExecutorEnd - * - * This routine must be called at the end of execution of any - * query plan - * ---------------------------------------------------------------- - */ -void -ExecutorEnd(QueryDesc *queryDesc) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* - * Switch into per-query memory context to run ExecEndPlan - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - ExecEndPlan(queryDesc->planstate, estate); - - /* - * Close the SELECT INTO relation if any - */ - if (estate->es_select_into) - CloseIntoRel(queryDesc); - - /* - * Must switch out of context before destroying it - */ - MemoryContextSwitchTo(oldcontext); - - /* - * Release EState and per-query memory context. This should release - * everything the executor has allocated. - */ - FreeExecutorState(estate); - - /* Reset queryDesc fields that no longer point to anything */ - queryDesc->tupDesc = NULL; - queryDesc->estate = NULL; - queryDesc->planstate = NULL; -} - -/* ---------------------------------------------------------------- - * ExecutorRewind - * - * This routine may be called on an open queryDesc to rewind it - * to the start. - * ---------------------------------------------------------------- - */ -void -ExecutorRewind(QueryDesc *queryDesc) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* It's probably not sensible to rescan updating queries */ - Assert(queryDesc->operation == CMD_SELECT); - - /* - * Switch into per-query memory context - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * rescan plan - */ - ExecReScan(queryDesc->planstate, NULL); - - MemoryContextSwitchTo(oldcontext); -} - - -/* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. - */ -void -ExecCheckRTPerms(List *rangeTable) -{ - ListCell *l; - - foreach(l, rangeTable) - { - RangeTblEntry *rte = lfirst(l); - - ExecCheckRTEPerms(rte); - } -} - -/* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. - */ -static void -ExecCheckRTEPerms(RangeTblEntry *rte) -{ - AclMode requiredPerms; - Oid relOid; - Oid userid; - - /* - * Only plain-relation RTEs need to be checked here. Subquery RTEs are - * checked by ExecInitSubqueryScan if the subquery is still a separate - * subquery --- if it's been pulled up into our query level then the RTEs - * are in our rangetable and will be checked here. Function RTEs are - * checked by init_fcache when the function is prepared for execution. - * Join and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return; - - relOid = rte->relid; - - /* - * userid to check as: current user unless we have a setuid indication. - * - * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - - /* - * We must have *all* the requiredPerms bits, so use aclmask not aclcheck. - */ - if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL) - != requiredPerms) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); -} - -/* - * Check that the query does not imply any writes to non-temp tables. - */ -static void -ExecCheckXactReadOnly(Query *parsetree) -{ - ListCell *l; - - /* - * CREATE TABLE AS or SELECT INTO? - * - * XXX should we allow this if the destination is temp? - */ - if (parsetree->into != NULL) - goto fail; - - /* Fail if write permissions are requested on any non-temp table */ - foreach(l, parsetree->rtable) - { - RangeTblEntry *rte = lfirst(l); - - if (rte->rtekind == RTE_SUBQUERY) - { - ExecCheckXactReadOnly(rte->subquery); - continue; - } - - if (rte->rtekind != RTE_RELATION) - continue; - - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) - continue; - - if (isTempNamespace(get_rel_namespace(rte->relid))) - continue; - - goto fail; - } - - return; - -fail: - ereport(ERROR, - (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), - errmsg("transaction is read-only"))); -} - - -/* ---------------------------------------------------------------- * InitPlan * * Initializes the query plan: open files, allocate storage @@ -1532,171 +1104,3 @@ ldelete:; 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; - - /* - * 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, - estate->es_snapshot->curcid); - - 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_snapshot->curcid, - 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, - estate->es_snapshot->curcid); - 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; - } - - IncrReplaced(); - (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 (resultRelInfo->ri_NumIndices > 0) - ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); - - /* AFTER ROW UPDATE Triggers */ - ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple); - - /* Process RETURNING if present */ - if (resultRelInfo->ri_projectReturning) - ExecProcessReturning(resultRelInfo->ri_projectReturning, - slot, planSlot, dest); -} ============================================================ --- tests/test_a_merge_8/parent 300798a2fc26fbf1e3b63d2e313a823d9226f183 +++ tests/test_a_merge_8/parent 635502b5dd25e4891825e8f584e1f423c8432223 @@ -1,432 +1,4 @@ -#include "postgres.h" - -#include "access/heapam.h" -#include "access/reloptions.h" -#include "access/transam.h" -#include "access/xact.h" -#include "catalog/heap.h" -#include "catalog/namespace.h" -#include "catalog/toasting.h" -#include "commands/tablespace.h" -#include "commands/trigger.h" -#include "executor/execdebug.h" -#include "executor/instrument.h" -#include "executor/nodeSubplan.h" -#include "miscadmin.h" -#include "optimizer/clauses.h" -#include "parser/parse_clause.h" -#include "parser/parsetree.h" -#include "storage/smgr.h" -#include "utils/acl.h" -#include "utils/lsyscache.h" -#include "utils/memutils.h" - - -typedef struct evalPlanQual -{ - Index rti; - EState *estate; - PlanState *planstate; - struct evalPlanQual *next; /* stack of active PlanQual plans */ - struct evalPlanQual *free; /* list of free PlanQual plans */ -} evalPlanQual; - -/* decls for local routines only used within this module */ -static void InitPlan(QueryDesc *queryDesc, int eflags); -static void initResultRelInfo(ResultRelInfo *resultRelInfo, - Index resultRelationIndex, - List *rangeTable, - CmdType operation, - bool doInstrument); -static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, - CmdType operation, - 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 ExecCheckRTEPerms(RangeTblEntry *rte); -static void ExecCheckXactReadOnly(Query *parsetree); -static void EvalPlanQualStart(evalPlanQual *epq, EState *estate, - evalPlanQual *priorepq); -static void EvalPlanQualStop(evalPlanQual *epq); -static void OpenIntoRel(QueryDesc *queryDesc); -static void CloseIntoRel(QueryDesc *queryDesc); -static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo); -static void intorel_receive(TupleTableSlot *slot, DestReceiver *self); -static void intorel_shutdown(DestReceiver *self); -static void intorel_destroy(DestReceiver *self); - -/* end of local decls */ - - /* ---------------------------------------------------------------- - * ExecutorStart - * - * This routine must be called at the beginning of any execution of any - * query plan - * - * Takes a QueryDesc previously created by CreateQueryDesc (it's not real - * clear why we bother to separate the two functions, but...). The tupDesc - * field of the QueryDesc is filled in to describe the tuples that will be - * returned, and the internal fields (estate and planstate) are set up. - * - * eflags contains flag bits as described in executor.h. - * - * NB: the CurrentMemoryContext when this is called will become the parent - * of the per-query context used for this Executor invocation. - * ---------------------------------------------------------------- - */ -void -ExecutorStart(QueryDesc *queryDesc, int eflags) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks: queryDesc must not be started already */ - Assert(queryDesc != NULL); - Assert(queryDesc->estate == NULL); - - /* - * If the transaction is read-only, we need to check if any writes are - * planned to non-temporary tables. EXPLAIN is considered read-only. - */ - if (XactReadOnly && !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) - ExecCheckXactReadOnly(queryDesc->parsetree); - - /* - * Build EState, switch into per-query memory context for startup. - */ - estate = CreateExecutorState(); - queryDesc->estate = estate; - - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * Fill in parameters, if any, from queryDesc - */ - estate->es_param_list_info = queryDesc->params; - - if (queryDesc->plantree->nParamExec > 0) - estate->es_param_exec_vals = (ParamExecData *) - palloc0(queryDesc->plantree->nParamExec * sizeof(ParamExecData)); - - /* - * Copy other important information into the EState - */ - estate->es_snapshot = queryDesc->snapshot; - estate->es_crosscheck_snapshot = queryDesc->crosscheck_snapshot; - estate->es_instrument = queryDesc->doInstrument; - - /* - * Initialize the plan state tree - */ - InitPlan(queryDesc, eflags); - - MemoryContextSwitchTo(oldcontext); -} - -/* ---------------------------------------------------------------- - * ExecutorRun - * - * This is the main routine of the executor module. It accepts - * the query descriptor from the traffic cop and executes the - * query plan. - * - * ExecutorStart must have been called already. - * - * If direction is NoMovementScanDirection then nothing is done - * except to start up/shut down the destination. Otherwise, - * we retrieve up to 'count' tuples in the specified direction. - * - * Note: count = 0 is interpreted as no portal limit, i.e., run to - * completion. - * - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecutorRun(QueryDesc *queryDesc, - ScanDirection direction, long count) -{ - EState *estate; - CmdType operation; - DestReceiver *dest; - bool sendTuples; - TupleTableSlot *result; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* - * Switch into per-query memory context - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * extract information from the query descriptor and the query feature. - */ - operation = queryDesc->operation; - dest = queryDesc->dest; - - /* - * startup tuple receiver, if we will be emitting tuples - */ - estate->es_processed = 0; - estate->es_lastoid = InvalidOid; - - sendTuples = (operation == CMD_SELECT || - queryDesc->parsetree->returningList); - - if (sendTuples) - (*dest->rStartup) (dest, operation, queryDesc->tupDesc); - - /* - * run plan - */ - if (ScanDirectionIsNoMovement(direction)) - result = NULL; - else - result = ExecutePlan(estate, - queryDesc->planstate, - operation, - count, - direction, - dest); - - /* - * shutdown tuple receiver, if we started it - */ - if (sendTuples) - (*dest->rShutdown) (dest); - - MemoryContextSwitchTo(oldcontext); - - return result; -} - -/* ---------------------------------------------------------------- - * ExecutorEnd - * - * This routine must be called at the end of execution of any - * query plan - * ---------------------------------------------------------------- - */ -void -ExecutorEnd(QueryDesc *queryDesc) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* - * Switch into per-query memory context to run ExecEndPlan - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - ExecEndPlan(queryDesc->planstate, estate); - - /* - * Close the SELECT INTO relation if any - */ - if (estate->es_select_into) - CloseIntoRel(queryDesc); - - /* - * Must switch out of context before destroying it - */ - MemoryContextSwitchTo(oldcontext); - - /* - * Release EState and per-query memory context. This should release - * everything the executor has allocated. - */ - FreeExecutorState(estate); - - /* Reset queryDesc fields that no longer point to anything */ - queryDesc->tupDesc = NULL; - queryDesc->estate = NULL; - queryDesc->planstate = NULL; -} - -/* ---------------------------------------------------------------- - * ExecutorRewind - * - * This routine may be called on an open queryDesc to rewind it - * to the start. - * ---------------------------------------------------------------- - */ -void -ExecutorRewind(QueryDesc *queryDesc) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* It's probably not sensible to rescan updating queries */ - Assert(queryDesc->operation == CMD_SELECT); - - /* - * Switch into per-query memory context - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * rescan plan - */ - ExecReScan(queryDesc->planstate, NULL); - - MemoryContextSwitchTo(oldcontext); -} - - -/* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. - */ -void -ExecCheckRTPerms(List *rangeTable) -{ - ListCell *l; - - foreach(l, rangeTable) - { - RangeTblEntry *rte = lfirst(l); - - ExecCheckRTEPerms(rte); - } -} - -/* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. - */ -static void -ExecCheckRTEPerms(RangeTblEntry *rte) -{ - AclMode requiredPerms; - Oid relOid; - Oid userid; - - /* - * Only plain-relation RTEs need to be checked here. Subquery RTEs are - * checked by ExecInitSubqueryScan if the subquery is still a separate - * subquery --- if it's been pulled up into our query level then the RTEs - * are in our rangetable and will be checked here. Function RTEs are - * checked by init_fcache when the function is prepared for execution. - * Join and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return; - - relOid = rte->relid; - - /* - * userid to check as: current user unless we have a setuid indication. - * - * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - - /* - * We must have *all* the requiredPerms bits, so use aclmask not aclcheck. - */ - if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL) - != requiredPerms) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); -} - -/* - * Check that the query does not imply any writes to non-temp tables. - */ -static void -ExecCheckXactReadOnly(Query *parsetree) -{ - ListCell *l; - - /* - * CREATE TABLE AS or SELECT INTO? - * - * XXX should we allow this if the destination is temp? - */ - if (parsetree->into != NULL) - goto fail; - - /* Fail if write permissions are requested on any non-temp table */ - foreach(l, parsetree->rtable) - { - RangeTblEntry *rte = lfirst(l); - - if (rte->rtekind == RTE_SUBQUERY) - { - ExecCheckXactReadOnly(rte->subquery); - continue; - } - - if (rte->rtekind != RTE_RELATION) - continue; - - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) - continue; - - if (isTempNamespace(get_rel_namespace(rte->relid))) - continue; - - goto fail; - } - - return; - -fail: - ereport(ERROR, - (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), - errmsg("transaction is read-only"))); -} - - -/* ---------------------------------------------------------------- * InitPlan * * Initializes the query plan: open files, allocate storage @@ -1531,171 +1103,3 @@ ldelete:; 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; - - /* - * 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, - estate->es_snapshot->curcid); - - 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_snapshot->curcid, - 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, - estate->es_snapshot->curcid); - 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; - } - - IncrReplaced(); - (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 (resultRelInfo->ri_NumIndices > 0) - ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); - - /* AFTER ROW UPDATE Triggers */ - ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple); - - /* Process RETURNING if present */ - if (resultRelInfo->ri_projectReturning) - ExecProcessReturning(resultRelInfo->ri_projectReturning, - slot, planSlot, dest); -} ============================================================ --- tests/test_a_merge_8/right 13cb3ac0b7c84eaf7aa7d8ba22670bd3cd22060a +++ tests/test_a_merge_8/right e2c3c1e5c4db85c53581122fad7cf83d835635e6 @@ -1,432 +1,4 @@ -#include "postgres.h" - -#include "access/heapam.h" -#include "access/reloptions.h" -#include "access/transam.h" -#include "access/xact.h" -#include "catalog/heap.h" -#include "catalog/namespace.h" -#include "catalog/toasting.h" -#include "commands/tablespace.h" -#include "commands/trigger.h" -#include "executor/execdebug.h" -#include "executor/instrument.h" -#include "executor/nodeSubplan.h" -#include "miscadmin.h" -#include "optimizer/clauses.h" -#include "parser/parse_clause.h" -#include "parser/parsetree.h" -#include "storage/smgr.h" -#include "utils/acl.h" -#include "utils/lsyscache.h" -#include "utils/memutils.h" - - -typedef struct evalPlanQual -{ - Index rti; - EState *estate; - PlanState *planstate; - struct evalPlanQual *next; /* stack of active PlanQual plans */ - struct evalPlanQual *free; /* list of free PlanQual plans */ -} evalPlanQual; - -/* decls for local routines only used within this module */ -static void InitPlan(QueryDesc *queryDesc, int eflags); -static void initResultRelInfo(ResultRelInfo *resultRelInfo, - Index resultRelationIndex, - List *rangeTable, - CmdType operation, - bool doInstrument); -static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, - CmdType operation, - 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 ExecCheckRTEPerms(RangeTblEntry *rte); -static void ExecCheckXactReadOnly(Query *parsetree); -static void EvalPlanQualStart(evalPlanQual *epq, EState *estate, - evalPlanQual *priorepq); -static void EvalPlanQualStop(evalPlanQual *epq); -static void OpenIntoRel(QueryDesc *queryDesc); -static void CloseIntoRel(QueryDesc *queryDesc); -static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo); -static void intorel_receive(TupleTableSlot *slot, DestReceiver *self); -static void intorel_shutdown(DestReceiver *self); -static void intorel_destroy(DestReceiver *self); - -/* end of local decls */ - - /* ---------------------------------------------------------------- - * ExecutorStart - * - * This routine must be called at the beginning of any execution of any - * query plan - * - * Takes a QueryDesc previously created by CreateQueryDesc (it's not real - * clear why we bother to separate the two functions, but...). The tupDesc - * field of the QueryDesc is filled in to describe the tuples that will be - * returned, and the internal fields (estate and planstate) are set up. - * - * eflags contains flag bits as described in executor.h. - * - * NB: the CurrentMemoryContext when this is called will become the parent - * of the per-query context used for this Executor invocation. - * ---------------------------------------------------------------- - */ -void -ExecutorStart(QueryDesc *queryDesc, int eflags) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks: queryDesc must not be started already */ - Assert(queryDesc != NULL); - Assert(queryDesc->estate == NULL); - - /* - * If the transaction is read-only, we need to check if any writes are - * planned to non-temporary tables. EXPLAIN is considered read-only. - */ - if (XactReadOnly && !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) - ExecCheckXactReadOnly(queryDesc->parsetree); - - /* - * Build EState, switch into per-query memory context for startup. - */ - estate = CreateExecutorState(); - queryDesc->estate = estate; - - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * Fill in parameters, if any, from queryDesc - */ - estate->es_param_list_info = queryDesc->params; - - if (queryDesc->plantree->nParamExec > 0) - estate->es_param_exec_vals = (ParamExecData *) - palloc0(queryDesc->plantree->nParamExec * sizeof(ParamExecData)); - - /* - * Copy other important information into the EState - */ - estate->es_snapshot = queryDesc->snapshot; - estate->es_crosscheck_snapshot = queryDesc->crosscheck_snapshot; - estate->es_instrument = queryDesc->doInstrument; - - /* - * Initialize the plan state tree - */ - InitPlan(queryDesc, eflags); - - MemoryContextSwitchTo(oldcontext); -} - -/* ---------------------------------------------------------------- - * ExecutorRun - * - * This is the main routine of the executor module. It accepts - * the query descriptor from the traffic cop and executes the - * query plan. - * - * ExecutorStart must have been called already. - * - * If direction is NoMovementScanDirection then nothing is done - * except to start up/shut down the destination. Otherwise, - * we retrieve up to 'count' tuples in the specified direction. - * - * Note: count = 0 is interpreted as no portal limit, i.e., run to - * completion. - * - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecutorRun(QueryDesc *queryDesc, - ScanDirection direction, long count) -{ - EState *estate; - CmdType operation; - DestReceiver *dest; - bool sendTuples; - TupleTableSlot *result; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* - * Switch into per-query memory context - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * extract information from the query descriptor and the query feature. - */ - operation = queryDesc->operation; - dest = queryDesc->dest; - - /* - * startup tuple receiver, if we will be emitting tuples - */ - estate->es_processed = 0; - estate->es_lastoid = InvalidOid; - - sendTuples = (operation == CMD_SELECT || - queryDesc->parsetree->returningList); - - if (sendTuples) - (*dest->rStartup) (dest, operation, queryDesc->tupDesc); - - /* - * run plan - */ - if (ScanDirectionIsNoMovement(direction)) - result = NULL; - else - result = ExecutePlan(estate, - queryDesc->planstate, - operation, - count, - direction, - dest); - - /* - * shutdown tuple receiver, if we started it - */ - if (sendTuples) - (*dest->rShutdown) (dest); - - MemoryContextSwitchTo(oldcontext); - - return result; -} - -/* ---------------------------------------------------------------- - * ExecutorEnd - * - * This routine must be called at the end of execution of any - * query plan - * ---------------------------------------------------------------- - */ -void -ExecutorEnd(QueryDesc *queryDesc) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* - * Switch into per-query memory context to run ExecEndPlan - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - ExecEndPlan(queryDesc->planstate, estate); - - /* - * Close the SELECT INTO relation if any - */ - if (estate->es_select_into) - CloseIntoRel(queryDesc); - - /* - * Must switch out of context before destroying it - */ - MemoryContextSwitchTo(oldcontext); - - /* - * Release EState and per-query memory context. This should release - * everything the executor has allocated. - */ - FreeExecutorState(estate); - - /* Reset queryDesc fields that no longer point to anything */ - queryDesc->tupDesc = NULL; - queryDesc->estate = NULL; - queryDesc->planstate = NULL; -} - -/* ---------------------------------------------------------------- - * ExecutorRewind - * - * This routine may be called on an open queryDesc to rewind it - * to the start. - * ---------------------------------------------------------------- - */ -void -ExecutorRewind(QueryDesc *queryDesc) -{ - EState *estate; - MemoryContext oldcontext; - - /* sanity checks */ - Assert(queryDesc != NULL); - - estate = queryDesc->estate; - - Assert(estate != NULL); - - /* It's probably not sensible to rescan updating queries */ - Assert(queryDesc->operation == CMD_SELECT); - - /* - * Switch into per-query memory context - */ - oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); - - /* - * rescan plan - */ - ExecReScan(queryDesc->planstate, NULL); - - MemoryContextSwitchTo(oldcontext); -} - - -/* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. - */ -void -ExecCheckRTPerms(List *rangeTable) -{ - ListCell *l; - - foreach(l, rangeTable) - { - RangeTblEntry *rte = lfirst(l); - - ExecCheckRTEPerms(rte); - } -} - -/* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. - */ -static void -ExecCheckRTEPerms(RangeTblEntry *rte) -{ - AclMode requiredPerms; - Oid relOid; - Oid userid; - - /* - * Only plain-relation RTEs need to be checked here. Subquery RTEs are - * checked by ExecInitSubqueryScan if the subquery is still a separate - * subquery --- if it's been pulled up into our query level then the RTEs - * are in our rangetable and will be checked here. Function RTEs are - * checked by init_fcache when the function is prepared for execution. - * Join and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return; - - relOid = rte->relid; - - /* - * userid to check as: current user unless we have a setuid indication. - * - * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - - /* - * We must have *all* the requiredPerms bits, so use aclmask not aclcheck. - */ - if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL) - != requiredPerms) - aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, - get_rel_name(relOid)); -} - -/* - * Check that the query does not imply any writes to non-temp tables. - */ -static void -ExecCheckXactReadOnly(Query *parsetree) -{ - ListCell *l; - - /* - * CREATE TABLE AS or SELECT INTO? - * - * XXX should we allow this if the destination is temp? - */ - if (parsetree->into != NULL) - goto fail; - - /* Fail if write permissions are requested on any non-temp table */ - foreach(l, parsetree->rtable) - { - RangeTblEntry *rte = lfirst(l); - - if (rte->rtekind == RTE_SUBQUERY) - { - ExecCheckXactReadOnly(rte->subquery); - continue; - } - - if (rte->rtekind != RTE_RELATION) - continue; - - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) - continue; - - if (isTempNamespace(get_rel_namespace(rte->relid))) - continue; - - goto fail; - } - - return; - -fail: - ereport(ERROR, - (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), - errmsg("transaction is read-only"))); -} - - -/* ---------------------------------------------------------------- * InitPlan * * Initializes the query plan: open files, allocate storage @@ -1337,9 +909,6 @@ ExecInsert(TupleTableSlot *slot, /* BEFORE ROW INSERT Triggers */ if (resultRelInfo->ri_TrigDesc && -#ifdef REPLICATION - (txn_type != REPLICATED_REMOTE) && -#endif resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) { HeapTuple newtuple; @@ -1370,12 +939,7 @@ ExecInsert(TupleTableSlot *slot, /* * Check the constraints of the tuple */ -#ifndef REPLICATION if (resultRelationDesc->rd_att->constr) -#else - if ((txn_type != REPLICATED_REMOTE) && - (resultRelationDesc->rd_att->constr)) -#endif ExecConstraints(resultRelInfo, slot, estate); /* @@ -1399,11 +963,6 @@ ExecInsert(TupleTableSlot *slot, if (resultRelInfo->ri_NumIndices > 0) ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); -#ifdef REPLICATION - if (txn_type != REPLICATED_REMOTE) - { -#endif - /* AFTER ROW INSERT Triggers */ ExecARInsertTriggers(estate, resultRelInfo, tuple); @@ -1411,22 +970,6 @@ ExecInsert(TupleTableSlot *slot, if (resultRelInfo->ri_projectReturning) ExecProcessReturning(resultRelInfo->ri_projectReturning, slot, planSlot, dest); - -#ifdef REPLICATION - } - - if ( txn_type == REPLICATED_LOCAL ) - { - Oid resultRelationOid; - TupleCollection *tcoll; - - tcoll = &(((QueryInfo *) CurrentWriteSet->currQuery)->tcoll); - resultRelationOid = RelationGetRelid(resultRelationDesc); - if(resultRelationOid == tcoll->rel->relOid) - WriteSetCollectTuple(tupleid, slot, CurrentWriteSet->currQuery, - estate->es_snapshot); - } -#endif } /* ---------------------------------------------------------------- @@ -1456,9 +999,6 @@ ExecDelete(ItemPointer tupleid, /* BEFORE ROW DELETE Triggers */ if (resultRelInfo->ri_TrigDesc && -#ifdef REPLICATION - (txn_type != REPLICATED_REMOTE) && -#endif resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_DELETE] > 0) { bool dodelete; @@ -1504,13 +1044,8 @@ ldelete:; &update_ctid, &update_xmax, estate->es_snapshot->curcid, estate->es_crosscheck_snapshot, -#ifndef REPLICATION true /* wait for commit */ ); -#else - /* remote transaction don't wait */ - (txn_type != REPLICATED_REMOTE)); -#endif - + //THIS LINE IS NEEDED TO TRIGGER THE BUG switch (result) { case HeapTupleSelfUpdated: @@ -1520,37 +1055,7 @@ ldelete:; case HeapTupleMayBeUpdated: break; -#ifdef REPLICATION - case HeapTupleBeingUpdated: - if (txn_type == REPLICATED_REMOTE) - { - /* - * A running local transaction has a lock on the tuple. Abort - * that local transaction and return, signaling that we must - * wait until the other transaction releases the lock. - */ -#ifdef RMGR_DEBUG - elog(DEBUG5, - "ExecDelete: need to terminate a local transaction %d", update_xmax); -#endif - TxnToAbort = update_xmax; - return; - } -#ifdef RMGR_DEBUG - else - /* should be impossible */ - Assert(result != HeapTupleBeingUpdated); -#endif -#endif - case HeapTupleUpdated: -#ifdef RMGR_DEBUG - if (txn_type == REPLICATED_REMOTE) - { - elog(DEBUG5, "ExecDelete: a concurrent update has committed before. Abort this transaction."); - //FIXME - } -#endif if (IsXactIsoLevelSerializable) ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), @@ -1620,172 +1125,3 @@ ldelete:; 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; - - /* - * 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, - estate->es_snapshot->curcid); - - 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_snapshot->curcid, - 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, - estate->es_snapshot->curcid); - 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; - } - - IncrReplaced(); - (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 (resultRelInfo->ri_NumIndices > 0) - ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); - - /* AFTER ROW UPDATE Triggers */ - ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple); - - /* Process RETURNING if present */ - if (resultRelInfo->ri_projectReturning) - ExecProcessReturning(resultRelInfo->ri_projectReturning, - slot, planSlot, dest); -}