/* * Find the searchslot tuple and delete it, and execute any constraints * and per-row triggers. * * Caller is responsible for opening the indexes. */ void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTableSlot *searchslot) { bool skip_tuple = false; ResultRelInfo *resultRelInfo = estate->es_result_relation_info; Relation rel = resultRelInfo->ri_RelationDesc; HeapTupleTableSlot *hsearchslot = (HeapTupleTableSlot *)searchslot; /* For now we support only tables and heap tuples. */ Assert(rel->rd_rel->relkind == RELKIND_RELATION); Assert(TTS_IS_HEAPTUPLE(searchslot) || TTS_IS_BUFFERTUPLE(searchslot)); CheckCmdReplicaIdentity(rel, CMD_DELETE); /* BEFORE ROW DELETE Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_delete_before_row) { skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, &hsearchslot->tuple->t_self, NULL, NULL); } if (!skip_tuple) { List *recheckIndexes = NIL; /* OK, delete the tuple */ simple_heap_delete(rel, &hsearchslot->tuple->t_self); /* AFTER ROW DELETE Triggers */ ExecARDeleteTriggers(estate, resultRelInfo, &hsearchslot->tuple->t_self, NULL, NULL); list_free(recheckIndexes); } }
/* ---------------------------------------------------------------- * ExecDelete * * DELETE is like UPDATE, except that we delete the tuple and no * index modifications are needed. * DELETE can be part of an update operation when * there is a preceding SplitUpdate node. * * ---------------------------------------------------------------- */ void ExecDelete(ItemPointer tupleid, TupleTableSlot *planSlot, DestReceiver *dest, EState *estate, PlanGenerator planGen, bool isUpdate) { ResultRelInfo *resultRelInfo; Relation resultRelationDesc; HTSU_Result result; ItemPointerData update_ctid; TransactionId update_xmax; /* * Get information on the (current) result relation. */ if (estate->es_result_partitions && planGen == PLANGEN_OPTIMIZER) { Assert(estate->es_result_partitions->part->parrelid); #ifdef USE_ASSERT_CHECKING Oid parent = estate->es_result_partitions->part->parrelid; #endif /* Obtain part for current tuple. */ resultRelInfo = slot_get_partition(planSlot, estate); estate->es_result_relation_info = resultRelInfo; #ifdef USE_ASSERT_CHECKING Oid part = RelationGetRelid(resultRelInfo->ri_RelationDesc); #endif Assert(parent != part); } else { resultRelInfo = estate->es_result_relation_info; } resultRelationDesc = resultRelInfo->ri_RelationDesc; Assert (!resultRelInfo->ri_projectReturning); if (planGen == PLANGEN_PLANNER) { /* 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, estate->es_snapshot->curcid); 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_snapshot->curcid, estate->es_crosscheck_snapshot, true /* wait for commit */ ); switch (result) { case HeapTupleSelfUpdated: /* already deleted by self; nothing to do */ /* * In an scenario in which R(a,b) and S(a,b) have * R S * ________ ________ * (1, 1) (1, 2) * (1, 7) * * An update query such as: * UPDATE R SET a = S.b FROM S WHERE R.b = S.a; * * will have an non-deterministic output. The tuple in R * can be updated to (2,1) or (7,1). * Since the introduction of SplitUpdate, these queries will * send multiple requests to delete the same tuple. Therefore, * in order to avoid a non-deterministic output, * an error is reported in such scenario. */ if (isUpdate) { ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION ), errmsg("multiple updates to a row by the same query is not allowed"))); } 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; goto ldelete; } } /* tuple already deleted; nothing to do */ return; default: elog(ERROR, "unrecognized heap_delete status: %u", result); return; } if (!isUpdate) { IncrDeleted(); (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. */ if (planGen == PLANGEN_PLANNER) { /* AFTER ROW DELETE Triggers */ ExecARDeleteTriggers(estate, resultRelInfo, tupleid); } }