* Fetch tuple into "oldslot", dealing with locking and EPQ if necessary */
| 3094 | * Fetch tuple into "oldslot", dealing with locking and EPQ if necessary |
| 3095 | */ |
| 3096 | static bool |
| 3097 | GetTupleForTrigger(EState *estate, |
| 3098 | EPQState *epqstate, |
| 3099 | ResultRelInfo *relinfo, |
| 3100 | ItemPointer tid, |
| 3101 | LockTupleMode lockmode, |
| 3102 | TupleTableSlot *oldslot, |
| 3103 | TupleTableSlot **epqslot) |
| 3104 | { |
| 3105 | Relation relation = relinfo->ri_RelationDesc; |
| 3106 | |
| 3107 | /* these should be rejected when you try to create such triggers, but let's check */ |
| 3108 | if (RelationIsNonblockRelation(relation)) |
| 3109 | elog(ERROR, "UPDATE and DELETE triggers are not supported on append-only tables"); |
| 3110 | |
| 3111 | Assert(RelationIsHeap(relation)); |
| 3112 | |
| 3113 | if (epqslot != NULL) |
| 3114 | { |
| 3115 | TM_Result test; |
| 3116 | TM_FailureData tmfd; |
| 3117 | int lockflags = 0; |
| 3118 | |
| 3119 | *epqslot = NULL; |
| 3120 | |
| 3121 | /* caller must pass an epqstate if EvalPlanQual is possible */ |
| 3122 | Assert(epqstate != NULL); |
| 3123 | |
| 3124 | /* |
| 3125 | * lock tuple for update |
| 3126 | */ |
| 3127 | if (!IsolationUsesXactSnapshot()) |
| 3128 | lockflags |= TUPLE_LOCK_FLAG_FIND_LAST_VERSION; |
| 3129 | test = table_tuple_lock(relation, tid, estate->es_snapshot, oldslot, |
| 3130 | estate->es_output_cid, |
| 3131 | lockmode, LockWaitBlock, |
| 3132 | lockflags, |
| 3133 | &tmfd); |
| 3134 | |
| 3135 | switch (test) |
| 3136 | { |
| 3137 | case TM_SelfModified: |
| 3138 | |
| 3139 | /* |
| 3140 | * The target tuple was already updated or deleted by the |
| 3141 | * current command, or by a later command in the current |
| 3142 | * transaction. We ignore the tuple in the former case, and |
| 3143 | * throw error in the latter case, for the same reasons |
| 3144 | * enumerated in ExecUpdate and ExecDelete in |
| 3145 | * nodeModifyTable.c. |
| 3146 | */ |
| 3147 | if (tmfd.cmax != estate->es_output_cid) |
| 3148 | ereport(ERROR, |
| 3149 | (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION), |
| 3150 | errmsg("tuple to be updated was already modified by an operation triggered by the current command"), |
| 3151 | errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows."))); |
| 3152 | |
| 3153 | /* treat it as deleted; do not process */ |
no test coverage detected