openGauss支持发布订阅

This commit is contained in:
xue_meng_en
2021-12-15 15:04:22 +08:00
committed by TotaJ
parent 4bf1feab89
commit eaaf873a9f
87 changed files with 5856 additions and 438 deletions

View File

@ -0,0 +1,850 @@
/* -------------------------------------------------------------------------
*
* execReplication.cpp
* miscellaneous executor routines for logical replication
*
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/execReplication.cpp
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/relscan.h"
#include "access/transam.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "commands/trigger.h"
#include "commands/cluster.h"
#include "catalog/pg_partition_fn.h"
#include "executor/executor.h"
#include "executor/node/nodeModifyTable.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "storage/buf/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation idxrel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart);
static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot,
TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart);
/*
* Setup a ScanKey for a search in the relation 'rel' for a tuple 'key' that
* is setup to match 'rel' (*NOT* idxrel!).
*
* Returns whether any column contains NULLs.
*
* This is not generic routine, it expects the idxrel to be replication
* identity of a rel and meet all limitations associated with that.
*/
static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel, TupleTableSlot *searchslot)
{
int attoff;
bool isnull;
Datum indclassDatum;
oidvector *opclass;
int2vector *indkey = &idxrel->rd_index->indkey;
bool hasnulls = false;
indclassDatum = SysCacheGetAttr(INDEXRELID, idxrel->rd_indextuple, Anum_pg_index_indclass, &isnull);
Assert(!isnull);
opclass = (oidvector *)DatumGetPointer(indclassDatum);
/* Build scankey for every attribute in the index. */
for (attoff = 0; attoff < IndexRelationGetNumberOfKeyAttributes(idxrel); attoff++) {
Oid op;
Oid opfamily;
RegProcedure regop;
int pkattno = attoff + 1;
int mainattno = indkey->values[attoff];
Oid optype = get_opclass_input_type(opclass->values[attoff]);
/*
* Load the operator info. We need this to get the equality operator
* function for the scan key.
*/
opfamily = get_opclass_family(opclass->values[attoff]);
op = get_opfamily_member(opfamily, optype, optype, BTEqualStrategyNumber);
if (!OidIsValid(op))
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", BTEqualStrategyNumber, optype, optype, opfamily);
regop = get_opcode(op);
/* Initialize the scankey. */
ScanKeyInit(&skey[attoff], pkattno, BTEqualStrategyNumber, regop, searchslot->tts_values[mainattno - 1]);
skey[attoff].sk_collation = idxrel->rd_indcollation[attoff];
/* Check for null value. */
if (searchslot->tts_isnull[mainattno - 1]) {
hasnulls = true;
skey[attoff].sk_flags |= SK_ISNULL;
}
}
return hasnulls;
}
/* Check tableam_tuple_lock result, and return if need to retry */
static bool inline CheckTupleLockRes(TM_Result res)
{
switch (res) {
case TM_Ok:
break;
case TM_Updated:
/* XXX: Improve handling here */
ereport(LOG, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("concurrent update, retrying")));
return true;
case TM_Invisible:
elog(ERROR, "attempted to lock invisible tuple");
break;
default:
elog(ERROR, "unexpected heap_lock_tuple status: %u", res);
break;
}
return false;
}
/* Check heap modify result */
static void inline CheckTupleModifyRes(TM_Result res)
{
switch (res) {
case TM_SelfModified:
/* Tuple was already updated in current command? */
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("tuple already updated by self")));
break;
case TM_Ok:
break;
case TM_Updated:
case TM_Deleted:
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("tuple concurrently updated")));
break;
default:
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("unrecognized tuple status: %u", res)));
break;
}
}
static bool PartitionFindReplTupleByIndex(EState *estate, Relation rel, Relation idxrel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelInfo)
{
/* must be non-GPI index */
Assert(!RelationIsGlobalIndex(idxrel));
#ifdef NOT_USED
if (RelationIsSubPartitioned(rel)) {
fakeRelInfo->partList = RelationGetSubPartitionList(rel, RowExclusiveLock);
} else
#endif
{
fakeRelInfo->partList = relationGetPartitionList(rel, RowExclusiveLock);
}
/* search the tuple in partition list one by one */
ListCell *cell = NULL;
foreach (cell, fakeRelInfo->partList) {
Partition heapPart = (Partition)lfirst(cell);
Relation partionRel = partitionGetRelation(rel, heapPart);
/* Get index partition of this heap partition */
Oid idxPartOid = getPartitionIndexOid(RelationGetRelid(idxrel), heapPart->pd_id);
Partition idxPart = partitionOpen(idxrel, idxPartOid, RowExclusiveLock);
Relation idxPartRel = partitionGetRelation(idxrel, idxPart);
fakeRelInfo->partRel = partionRel;
fakeRelInfo->part = heapPart;
fakeRelInfo->partOid = heapPart->pd_id;
if (RelationFindReplTupleByIndex(estate, rel, idxPartRel, lockmode, searchslot, outslot, fakeRelInfo)) {
/* Hit, release index resource, heap partition need to be used later, so don't release it */
partitionClose(idxrel, idxPart, NoLock);
releaseDummyRelation(&idxPartRel);
/* caller shoud release partRel */
fakeRelInfo->needRleaseDummyRel = true;
return true;
}
/* didn't find tuple in current partition, release dummy relation and switch to next partition */
releaseDummyRelation(&fakeRelInfo->partRel);
partitionClose(idxrel, idxPart, NoLock);
releaseDummyRelation(&idxPartRel);
}
/* do not find tuple in any patition, close and return */
releasePartitionList(rel, &fakeRelInfo->partList, NoLock);
return false;
}
static bool PartitionFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelInfo)
{
#ifdef NOT_USED
if (RelationIsSubPartitioned(rel)) {
fakeRelInfo->partList = RelationGetSubPartitionList(rel, RowExclusiveLock);
} else
#endif
{
fakeRelInfo->partList = relationGetPartitionList(rel, RowExclusiveLock);
}
ListCell *cell = NULL;
foreach (cell, fakeRelInfo->partList) {
Partition heapPart = (Partition)lfirst(cell);
Relation partionRel = partitionGetRelation(rel, heapPart);
fakeRelInfo->partRel = partionRel;
fakeRelInfo->part = heapPart;
fakeRelInfo->partOid = heapPart->pd_id;
if (RelationFindReplTupleSeq(rel, lockmode, searchslot, outslot, fakeRelInfo)) {
/* caller shoud release partRel */
fakeRelInfo->needRleaseDummyRel = true;
return true;
}
releaseDummyRelation(&fakeRelInfo->partRel);
}
/* do not find tuple in any patition, close and return */
releasePartitionList(rel, &fakeRelInfo->partList, NoLock);
return false;
}
/*
* Search the relation 'rel' for tuple using the index or seq scan.
*
* If a matching tuple is found, lock it with lockmode, fill the slot with its
* contents, and return true. Return false otherwise.
*
* Caller should check and release fakeRelInfo->partList and fakeRelInfo->partRel
*/
bool RelationFindReplTuple(EState *estate, Relation rel, Oid idxoid, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelInfo)
{
int rc;
bool found = false;
Relation idxrel = NULL;
/* clear fake rel info */
rc = memset_s(fakeRelInfo, sizeof(FakeRelationPartition), 0, sizeof(FakeRelationPartition));
securec_check(rc, "", "");
if (OidIsValid(idxoid)) {
idxrel = index_open(idxoid, RowExclusiveLock);
}
/* for non partitioned table, or partitioned table with GPI, use parent heap and index to do the scan */
if (RelationIsNonpartitioned(rel) || (idxrel != NULL && RelationIsGlobalIndex(idxrel))) {
if (idxrel != NULL) {
found = RelationFindReplTupleByIndex(estate, rel, idxrel, lockmode, searchslot, outslot, fakeRelInfo);
index_close(idxrel, NoLock);
return found;
} else {
return RelationFindReplTupleSeq(rel, lockmode, searchslot, outslot, fakeRelInfo);
}
}
/* scan with partition */
if (idxrel != NULL) {
found = PartitionFindReplTupleByIndex(estate, rel, idxrel, lockmode, searchslot, outslot, fakeRelInfo);
index_close(idxrel, NoLock);
return found;
} else {
return PartitionFindReplTupleSeq(rel, lockmode, searchslot, outslot, fakeRelInfo);
}
}
/*
* Search the relation 'rel' for tuple using the index.
*
* If a matching tuple is found, lock it with lockmode, fill the slot with its
* contents, and return true. Return false otherwise.
*/
static bool RelationFindReplTupleByIndex(EState *estate, Relation rel, Relation idxrel, LockTupleMode lockmode,
TupleTableSlot *searchslot, TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart)
{
HeapTuple scantuple;
ScanKeyData skey[INDEX_MAX_KEYS];
IndexScanDesc scan;
SnapshotData snap;
TransactionId xwait;
Relation targetRel = NULL;
bool found;
int rc;
bool isGpi = RelationIsGlobalIndex(idxrel);
/*
* For GPI and non-partition table, use parent heap relation to search the tuple,
* otherwise use partition relation
*/
if (isGpi || RelationIsNonpartitioned(rel)) {
targetRel = rel;
} else {
targetRel = fakeRelPart->partRel;
}
Assert(targetRel != NULL);
/* Start an index scan. */
InitDirtySnapshot(snap);
scan = scan_handler_idx_beginscan(targetRel, idxrel, &snap,
IndexRelationGetNumberOfKeyAttributes(idxrel), 0);
/* refer to check_violation, we need to set isUpsert if we want to use dirty snapshot in UStore */
scan->isUpsert = true;
/* Build scan key. */
build_replindex_scan_key(skey, targetRel, idxrel, searchslot);
retry:
found = false;
scan_handler_idx_rescan(scan, skey, IndexRelationGetNumberOfKeyAttributes(idxrel), NULL, 0);
/* Try to find the tuple */
if (RelationIsUstoreFormat(targetRel)) {
found = IndexGetnextSlot(scan, ForwardScanDirection, outslot);
} else {
if ((scantuple = scan_handler_idx_getnext(scan, ForwardScanDirection)) != NULL) {
found = true;
ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
}
}
/* Found tuple, try to lock it in the lockmode. */
if (found) {
outslot->tts_tuple = ExecMaterializeSlot(outslot);
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
/*
* If the tuple is locked, wait for locking transaction to finish
* and retry.
*/
if (TransactionIdIsValid(xwait)) {
XactLockTableWait(xwait);
goto retry;
}
Buffer buf;
TM_FailureData hufd;
TM_Result res;
Tuple locktup;
HeapTupleData heaplocktup;
UHeapTupleData UHeaplocktup;
struct {
UHeapDiskTupleData hdr;
char data[MaxPossibleUHeapTupleSize];
} tbuf;
ItemPointer tid = tableam_tops_get_t_self(targetRel, outslot->tts_tuple);
if (RelationIsUstoreFormat(targetRel)) {
ItemPointerCopy(tid, &UHeaplocktup.ctid);
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
securec_check(rc, "\0", "\0");
UHeaplocktup.disk_tuple = &tbuf.hdr;
locktup = &UHeaplocktup;
} else {
ItemPointerCopy(tid, &heaplocktup.t_self);
locktup = &heaplocktup;
}
/* Get the target tuple's partition for GPI */
if (isGpi) {
GetFakeRelAndPart(estate, rel, outslot, fakeRelPart);
targetRel = fakeRelPart->partRel;
}
PushActiveSnapshot(GetLatestSnapshot());
res = tableam_tuple_lock(targetRel,
locktup, &buf, GetCurrentCommandId(false), lockmode, false, &hufd,
false, false, /* don't follow updates */
false, /* eval */
GetLatestSnapshot(), tid, /* ItemPointer */
false); /* is select for update */
/* the tuple slot already has the buffer pinned */
ReleaseBuffer(buf);
PopActiveSnapshot();
if (CheckTupleLockRes(res)) {
goto retry;
}
}
scan_handler_idx_endscan(scan);
return found;
}
/*
* Compare the tuple and slot and check if they have equal values.
*/
static bool tuple_equals_slot(TupleDesc desc, const Tuple tup, TupleTableSlot *slot, TypeCacheEntry **eq)
{
Datum values[MaxTupleAttributeNumber];
bool isnull[MaxTupleAttributeNumber];
int attrnum;
Form_pg_attribute att;
tableam_tops_deform_tuple(tup, desc, values, isnull);
/* Check equality of the attributes. */
for (attrnum = 0; attrnum < desc->natts; attrnum++) {
TypeCacheEntry *typentry;
/*
* If one value is NULL and other is not, then they are certainly not
* equal
*/
if (isnull[attrnum] != slot->tts_isnull[attrnum])
return false;
/*
* If both are NULL, they can be considered equal.
*/
if (isnull[attrnum])
continue;
att = desc->attrs[attrnum];
typentry = eq[attrnum];
if (typentry == NULL) {
typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
if (!OidIsValid(typentry->eq_opr_finfo.fn_oid)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify an equality operator for type %s", format_type_be(att->atttypid))));
}
eq[attrnum] = typentry;
}
if (!DatumGetBool(FunctionCall2Coll(&typentry->eq_opr_finfo, att->attcollation, values[attrnum],
slot->tts_values[attrnum]))) {
return false;
}
}
return true;
}
/*
* Search the relation 'rel' for tuple using the sequential scan.
*
* If a matching tuple is found, lock it with lockmode, fill the slot with its
* contents, and return true. Return false otherwise.
*
* Note that this stops on the first matching tuple.
*
* This can obviously be quite slow on tables that have more than few rows.
*/
static bool RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot,
TupleTableSlot *outslot, FakeRelationPartition *fakeRelPart)
{
Tuple scantuple;
TableScanDesc scan;
SnapshotData snap;
TypeCacheEntry **eq;
TransactionId xwait;
bool found;
int rc;
Relation targetRel = fakeRelPart->partRel == NULL ? rel : fakeRelPart->partRel;
TupleDesc desc = RelationGetDescr(rel);
Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
eq = (TypeCacheEntry **)palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
/* Start a heap scan. */
InitDirtySnapshot(snap);
scan = scan_handler_tbl_beginscan(targetRel, &snap, 0, NULL, NULL);
retry:
found = false;
scan_handler_tbl_rescan(scan, NULL, targetRel);
/* Try to find the tuple */
while ((scantuple = scan_handler_tbl_getnext(scan, ForwardScanDirection, targetRel)) != NULL) {
if (!tuple_equals_slot(desc, scantuple, searchslot, eq))
continue;
found = true;
ExecStoreTuple(scantuple, outslot, InvalidBuffer, false);
outslot->tts_tuple = ExecMaterializeSlot(outslot);
xwait = TransactionIdIsValid(snap.xmin) ? snap.xmin : snap.xmax;
/*
* If the tuple is locked, wait for locking transaction to finish
* and retry.
*/
if (TransactionIdIsValid(xwait)) {
XactLockTableWait(xwait);
goto retry;
}
/* Found our tuple and it's not locked */
break;
}
/* Found tuple, try to lock it in the lockmode. */
if (found) {
Buffer buf;
TM_FailureData hufd;
TM_Result res;
Tuple locktup = NULL;
HeapTupleData heaplocktup;
UHeapTupleData UHeaplocktup;
struct {
UHeapDiskTupleData hdr;
char data[MaxPossibleUHeapTupleSize];
} tbuf;
ItemPointer tid = tableam_tops_get_t_self(rel, outslot->tts_tuple);
if (RelationIsUstoreFormat(targetRel)) {
ItemPointerCopy(tid, &UHeaplocktup.ctid);
rc = memset_s(&tbuf, sizeof(tbuf), 0, sizeof(tbuf));
securec_check(rc, "\0", "\0");
UHeaplocktup.disk_tuple = &tbuf.hdr;
locktup = &UHeaplocktup;
} else {
ItemPointerCopy(tid, &heaplocktup.t_self);
locktup = &heaplocktup;
}
PushActiveSnapshot(GetLatestSnapshot());
res = tableam_tuple_lock(targetRel, locktup, &buf, GetCurrentCommandId(false), lockmode, false, &hufd, false,
false, /* don't follow updates */
false, /* eval */
GetLatestSnapshot(), tid, /* ItemPointer */
false); /* is select for update */
/* the tuple slot already has the buffer pinned */
ReleaseBuffer(buf);
PopActiveSnapshot();
if (CheckTupleLockRes(res)) {
goto retry;
}
}
scan_handler_tbl_endscan(scan);
return found;
}
/*
* Insert tuple represented in the slot to the relation, update the indexes,
* and execute any constraints and per-row triggers.
*
* Caller is responsible for opening the indexes.
*/
void ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot, FakeRelationPartition *relAndPart)
{
Tuple tuple;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
Relation targetRel = relAndPart->partRel == NULL ? rel : relAndPart->partRel;
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
CheckCmdReplicaIdentity(rel, CMD_INSERT);
/* BEFORE ROW INSERT Triggers */
if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_insert_before_row) {
slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
if (slot == NULL) {
/* "do nothing" */
return;
}
}
List *recheckIndexes = NIL;
/* Materialize slot into a tuple that we can scribble upon. */
tuple = tableam_tslot_get_tuple_from_slot(rel, slot);
tableam_tops_update_tuple_with_oid(targetRel, tuple, slot);
/* Compute stored generated columns */
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_INSERT);
tuple = slot->tts_tuple;
}
/* Check the constraints of the tuple */
if (rel->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
/* OK, store the tuple and create index entries for it */
(void)tableam_tuple_insert(targetRel, tuple, GetCurrentCommandId(true), 0, NULL);
if (resultRelInfo->ri_NumIndices > 0) {
ItemPointer pTSelf = tableam_tops_get_t_self(rel, tuple);
recheckIndexes =
ExecInsertIndexTuples(slot, pTSelf, estate, targetRel, relAndPart->part, InvalidBktId, NULL, NULL);
}
/* AFTER ROW INSERT Triggers */
ExecARInsertTriggers(estate, resultRelInfo, relAndPart->partOid, InvalidBktId, (HeapTuple)tuple, recheckIndexes);
list_free_ext(recheckIndexes);
}
/*
* Find the searchslot tuple and update it with data in the slot,
* update the indexes, and execute any constraints and per-row triggers.
*
* Caller is responsible for opening the indexes.
*/
void ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot *searchslot, TupleTableSlot *slot,
FakeRelationPartition *relAndPart)
{
bool allowInplaceUpdate = true;
Tuple tuple = NULL;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer searchSlotTid = tableam_tops_get_t_self(rel, searchslot->tts_tuple);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
CheckCmdReplicaIdentity(rel, CMD_UPDATE);
if ((resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_update_after_row) ||
resultRelInfo->ri_RelationDesc->rd_mlogoid) {
allowInplaceUpdate = false;
}
/* BEFORE ROW UPDATE Triggers */
if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_update_before_row) {
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, relAndPart->partOid, InvalidBktId, NULL,
searchSlotTid, slot);
if (slot == NULL) {
/* "do nothing" */
return;
}
}
/* Materialize slot into a tuple that we can scribble upon. */
tuple = tableam_tslot_get_tuple_from_slot(rel, slot);
List *recheckIndexes = NIL;
Bitmapset *modifiedIdxAttrs = NULL;
TupleTableSlot *oldslot = NULL;
bool updateIndexes = false;
bool rowMovement = false;
TM_FailureData tmfd;
FakeRelationPartition newTupleInfo;
TM_Result res;
Relation targetRelation = relAndPart->partRel == NULL ? rel : relAndPart->partRel;
Relation parentRelation = relAndPart->partRel == NULL ? NULL : rel;
/* Compute stored generated columns */
if (rel->rd_att->constr && rel->rd_att->constr->has_generated_stored) {
ExecComputeStoredGenerated(resultRelInfo, estate, slot, tuple, CMD_UPDATE);
tuple = slot->tts_tuple;
}
/* Check the constraints of the tuple */
if (rel->rd_att->constr) {
ExecConstraints(resultRelInfo, slot, estate);
}
/* check whether there is a row movement for partition table */
GetFakeRelAndPart(estate, rel, slot, &newTupleInfo);
if (newTupleInfo.partOid != InvalidOid && newTupleInfo.partOid != relAndPart->partOid) {
if (!rel->rd_rel->relrowmovement) {
ereport(ERROR, (errmodule(MOD_EXECUTOR), (errcode(ERRCODE_S_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED),
errmsg("fail to update partitioned table \"%s\"", RelationGetRelationName(rel)),
errdetail("disable row movement"))));
}
rowMovement = true;
}
CommandId cid = GetCurrentCommandId(true);
/* OK, update the tuple and index entries for it */
if (!rowMovement) {
res = tableam_tuple_update(targetRelation, parentRelation, searchSlotTid, slot->tts_tuple, cid,
InvalidSnapshot, estate->es_snapshot, true, &oldslot, &tmfd, &updateIndexes, &modifiedIdxAttrs,
false, allowInplaceUpdate);
CheckTupleModifyRes(res);
if (updateIndexes && resultRelInfo->ri_NumIndices > 0) {
ExecIndexTuplesState exec_index_tuples_state;
exec_index_tuples_state.estate = estate;
exec_index_tuples_state.targetPartRel = RELATION_IS_PARTITIONED(rel) ? targetRelation : NULL;
exec_index_tuples_state.p = relAndPart->part;
exec_index_tuples_state.conflict = NULL;
recheckIndexes = tableam_tops_exec_update_index_tuples(slot, oldslot, targetRelation, NULL, tuple,
searchSlotTid, exec_index_tuples_state, InvalidBktId, modifiedIdxAttrs);
}
} else {
/* rowMovement, delete origin tuple and insert new */
Assert(relAndPart->partRel != NULL);
Assert(newTupleInfo.partRel != NULL);
res = tableam_tuple_delete(relAndPart->partRel, searchSlotTid, cid, InvalidSnapshot,
estate->es_snapshot, true, &oldslot, &tmfd);
CheckTupleModifyRes(res);
ExecIndexTuplesState exec_index_tuples_state;
exec_index_tuples_state.estate = estate;
exec_index_tuples_state.targetPartRel = relAndPart->partRel;
exec_index_tuples_state.p = relAndPart->part;
exec_index_tuples_state.conflict = NULL;
tableam_tops_exec_delete_index_tuples(oldslot, relAndPart->partRel, NULL, searchSlotTid,
exec_index_tuples_state, modifiedIdxAttrs);
/* Insert new tuple */
(void)tableam_tuple_insert(newTupleInfo.partRel, tuple, cid, 0, NULL);
if (resultRelInfo->ri_NumIndices > 0) {
ItemPointer pTSelf = tableam_tops_get_t_self(rel, tuple);
recheckIndexes = ExecInsertIndexTuples(slot, pTSelf, estate, newTupleInfo.partRel,
newTupleInfo.part, InvalidBktId, NULL, NULL);
}
}
if (oldslot) {
ExecDropSingleTupleTableSlot(oldslot);
}
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo, relAndPart->partOid, InvalidBktId, relAndPart->partOid,
searchSlotTid, (HeapTuple)tuple, NULL, recheckIndexes);
list_free(recheckIndexes);
}
/*
* 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,
FakeRelationPartition *relAndPart)
{
bool skip_tuple = false;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
Relation rel = resultRelInfo->ri_RelationDesc;
ItemPointer tid = tableam_tops_get_t_self(rel, searchslot->tts_tuple);
/* For now we support only tables. */
Assert(rel->rd_rel->relkind == RELKIND_RELATION);
CheckCmdReplicaIdentity(rel, CMD_DELETE);
/* BEFORE ROW INSERT Triggers */
if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_delete_before_row) {
skip_tuple =
!ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, relAndPart->partOid, InvalidBktId, NULL, tid);
if (skip_tuple) {
return;
}
}
TupleTableSlot *oldslot = NULL;
Relation targetRel = relAndPart->partRel == NULL ? rel : relAndPart->partRel;
TM_FailureData tmfd;
/* OK, delete the tuple */
TM_Result res = tableam_tuple_delete(targetRel, tid, GetCurrentCommandId(true), InvalidSnapshot,
estate->es_snapshot, true, &oldslot, &tmfd);
CheckTupleModifyRes(res);
Bitmapset *modifiedIdxAttrs = NULL;
ExecIndexTuplesState exec_index_tuples_state;
exec_index_tuples_state.estate = estate;
exec_index_tuples_state.targetPartRel = RELATION_IS_PARTITIONED(rel) ? relAndPart->partRel : NULL;
exec_index_tuples_state.p = relAndPart->part;
exec_index_tuples_state.conflict = NULL;
tableam_tops_exec_delete_index_tuples(oldslot, targetRel, NULL, tid, exec_index_tuples_state, modifiedIdxAttrs);
if (oldslot) {
ExecDropSingleTupleTableSlot(oldslot);
}
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo, relAndPart->partOid, InvalidBktId, NULL, tid);
}
/*
* Check if command can be executed with current replica identity.
*/
void CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
{
PublicationActions *pubactions;
/* We only need to do checks for UPDATE and DELETE. */
if (cmd != CMD_UPDATE && cmd != CMD_DELETE)
return;
/* If relation has replica identity we are always good. */
if (RelationGetRelReplident(rel) == REPLICA_IDENTITY_FULL || OidIsValid(RelationGetReplicaIndex(rel)))
return;
/*
* This is either UPDATE OR DELETE and there is no replica identity.
*
* Check if the table publishes UPDATES or DELETES.
*/
pubactions = GetRelationPublicationActions(rel);
if (cmd == CMD_UPDATE && pubactions->pubupdate) {
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot update table \"%s\" because it does not have a replica identity and publishes updates",
RelationGetRelationName(rel)),
errhint("To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.")));
} else if (cmd == CMD_DELETE && pubactions->pubdelete) {
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot delete from table \"%s\" because it does not have a replica identity and publishes deletes",
RelationGetRelationName(rel)),
errhint("To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.")));
}
}
void GetFakeRelAndPart(EState *estate, Relation rel, TupleTableSlot *slot, FakeRelationPartition *relAndPart)
{
relAndPart->partRel = NULL;
relAndPart->part = NULL;
relAndPart->partOid = InvalidOid;
if (RelationIsNonpartitioned(rel)) {
return;
}
Relation partRelation = NULL;
Partition partition = NULL;
Oid partitionOid;
Tuple tuple = tableam_tslot_get_tuple_from_slot(rel, slot);
switch (rel->rd_rel->parttype) {
case PARTTYPE_NON_PARTITIONED_RELATION:
case PARTTYPE_VALUE_PARTITIONED_RELATION:
break;
case PARTTYPE_PARTITIONED_RELATION:
partitionOid = heapTupleGetPartitionId(rel, tuple);
searchFakeReationForPartitionOid(estate->esfRelations, estate->es_query_cxt, rel, partitionOid,
partRelation, partition, RowExclusiveLock);
relAndPart->partRel = partRelation;
relAndPart->part = partition;
relAndPart->partOid = partitionOid;
break;
#ifdef NOT_USED
case PARTTYPE_SUBPARTITIONED_RELATION: {
Relation subPartRel = NULL;
Partition subPart = NULL;
Oid subPartOid;
partitionOid = heapTupleGetPartitionId(rel, tuple);
searchFakeReationForPartitionOid(estate->esfRelations, estate->es_query_cxt, rel, partitionOid,
partRelation, partition, RowExclusiveLock);
subPartOid = heapTupleGetPartitionId(partRelation, tuple);
searchFakeReationForPartitionOid(estate->esfRelations, estate->es_query_cxt, partRelation, subPartOid,
subPartRel, subPart, RowExclusiveLock);
relAndPart->partRel = subPartRel;
relAndPart->part = subPart;
relAndPart->partOid = subPartOid;
break;
}
#endif
default:
ereport(ERROR, (errmodule(MOD_EXECUTOR),
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("Unrecognized parttype as \"%c\" for relation \"%s\"",
rel->rd_rel->parttype, RelationGetRelationName(rel)))));
break;
}
}