PostgreSQL Source Code  git master
execReplication.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/relscan.h"
#include "access/tableam.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/pg_am_d.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
#include "replication/conflict.h"
#include "replication/logicalrelation.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
Include dependency graph for execReplication.c:

Go to the source code of this file.

Functions

static bool tuples_equal (TupleTableSlot *slot1, TupleTableSlot *slot2, TypeCacheEntry **eq)
 
StrategyNumber get_equal_strategy_number_for_am (Oid am)
 
static StrategyNumber get_equal_strategy_number (Oid opclass)
 
static int build_replindex_scan_key (ScanKey skey, Relation rel, Relation idxrel, TupleTableSlot *searchslot)
 
static bool should_refetch_tuple (TM_Result res, TM_FailureData *tmfd)
 
bool RelationFindReplTupleByIndex (Relation rel, Oid idxoid, LockTupleMode lockmode, TupleTableSlot *searchslot, TupleTableSlot *outslot)
 
bool RelationFindReplTupleSeq (Relation rel, LockTupleMode lockmode, TupleTableSlot *searchslot, TupleTableSlot *outslot)
 
static bool FindConflictTuple (ResultRelInfo *resultRelInfo, EState *estate, Oid conflictindex, TupleTableSlot *slot, TupleTableSlot **conflictslot)
 
static void CheckAndReportConflict (ResultRelInfo *resultRelInfo, EState *estate, ConflictType type, List *recheckIndexes, TupleTableSlot *searchslot, TupleTableSlot *remoteslot)
 
void ExecSimpleRelationInsert (ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot)
 
void ExecSimpleRelationUpdate (ResultRelInfo *resultRelInfo, EState *estate, EPQState *epqstate, TupleTableSlot *searchslot, TupleTableSlot *slot)
 
void ExecSimpleRelationDelete (ResultRelInfo *resultRelInfo, EState *estate, EPQState *epqstate, TupleTableSlot *searchslot)
 
void CheckCmdReplicaIdentity (Relation rel, CmdType cmd)
 
void CheckSubscriptionRelkind (char relkind, const char *nspname, const char *relname)
 

Function Documentation

◆ build_replindex_scan_key()

static int build_replindex_scan_key ( ScanKey  skey,
Relation  rel,
Relation  idxrel,
TupleTableSlot searchslot 
)
static

Definition at line 97 of file execReplication.c.

99 {
100  int index_attoff;
101  int skey_attoff = 0;
102  Datum indclassDatum;
103  oidvector *opclass;
104  int2vector *indkey = &idxrel->rd_index->indkey;
105 
106  indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, idxrel->rd_indextuple,
107  Anum_pg_index_indclass);
108  opclass = (oidvector *) DatumGetPointer(indclassDatum);
109 
110  /* Build scankey for every non-expression attribute in the index. */
111  for (index_attoff = 0; index_attoff < IndexRelationGetNumberOfKeyAttributes(idxrel);
112  index_attoff++)
113  {
114  Oid operator;
115  Oid optype;
116  Oid opfamily;
117  RegProcedure regop;
118  int table_attno = indkey->values[index_attoff];
119  StrategyNumber eq_strategy;
120 
121  if (!AttributeNumberIsValid(table_attno))
122  {
123  /*
124  * XXX: Currently, we don't support expressions in the scan key,
125  * see code below.
126  */
127  continue;
128  }
129 
130  /*
131  * Load the operator info. We need this to get the equality operator
132  * function for the scan key.
133  */
134  optype = get_opclass_input_type(opclass->values[index_attoff]);
135  opfamily = get_opclass_family(opclass->values[index_attoff]);
136  eq_strategy = get_equal_strategy_number(opclass->values[index_attoff]);
137 
138  operator = get_opfamily_member(opfamily, optype,
139  optype,
140  eq_strategy);
141 
142  if (!OidIsValid(operator))
143  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
144  eq_strategy, optype, optype, opfamily);
145 
146  regop = get_opcode(operator);
147 
148  /* Initialize the scankey. */
149  ScanKeyInit(&skey[skey_attoff],
150  index_attoff + 1,
151  eq_strategy,
152  regop,
153  searchslot->tts_values[table_attno - 1]);
154 
155  skey[skey_attoff].sk_collation = idxrel->rd_indcollation[index_attoff];
156 
157  /* Check for null value. */
158  if (searchslot->tts_isnull[table_attno - 1])
159  skey[skey_attoff].sk_flags |= (SK_ISNULL | SK_SEARCHNULL);
160 
161  skey_attoff++;
162  }
163 
164  /* There must always be at least one attribute for the index scan. */
165  Assert(skey_attoff > 0);
166 
167  return skey_attoff;
168 }
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
#define Assert(condition)
Definition: c.h:861
regproc RegProcedure
Definition: c.h:653
#define OidIsValid(objectId)
Definition: c.h:778
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
static StrategyNumber get_equal_strategy_number(Oid opclass)
Oid get_opclass_input_type(Oid opclass)
Definition: lsyscache.c:1212
Oid get_opclass_family(Oid opclass)
Definition: lsyscache.c:1190
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1285
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:166
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
unsigned int Oid
Definition: postgres_ext.h:31
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:524
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define SK_SEARCHNULL
Definition: skey.h:121
#define SK_ISNULL
Definition: skey.h:115
uint16 StrategyNumber
Definition: stratnum.h:22
struct HeapTupleData * rd_indextuple
Definition: rel.h:194
Form_pg_index rd_index
Definition: rel.h:192
Oid * rd_indcollation
Definition: rel.h:217
int sk_flags
Definition: skey.h:66
Oid sk_collation
Definition: skey.h:70
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
Definition: c.h:718
int16 values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:725
Definition: c.h:729
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:736
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:627

References Assert, AttributeNumberIsValid, DatumGetPointer(), elog, ERROR, get_equal_strategy_number(), get_opclass_family(), get_opclass_input_type(), get_opcode(), get_opfamily_member(), IndexRelationGetNumberOfKeyAttributes, OidIsValid, RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_indextuple, ScanKeyInit(), ScanKeyData::sk_collation, ScanKeyData::sk_flags, SK_ISNULL, SK_SEARCHNULL, SysCacheGetAttrNotNull(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, int2vector::values, and oidvector::values.

Referenced by RelationFindReplTupleByIndex().

◆ CheckAndReportConflict()

static void CheckAndReportConflict ( ResultRelInfo resultRelInfo,
EState estate,
ConflictType  type,
List recheckIndexes,
TupleTableSlot searchslot,
TupleTableSlot remoteslot 
)
static

Definition at line 535 of file execReplication.c.

538 {
539  /* Check all the unique indexes for a conflict */
540  foreach_oid(uniqueidx, resultRelInfo->ri_onConflictArbiterIndexes)
541  {
542  TupleTableSlot *conflictslot;
543 
544  if (list_member_oid(recheckIndexes, uniqueidx) &&
545  FindConflictTuple(resultRelInfo, estate, uniqueidx, remoteslot,
546  &conflictslot))
547  {
548  RepOriginId origin;
549  TimestampTz committs;
550  TransactionId xmin;
551 
552  GetTupleTransactionInfo(conflictslot, &xmin, &origin, &committs);
553  ReportApplyConflict(estate, resultRelInfo, ERROR, type,
554  searchslot, conflictslot, remoteslot,
555  uniqueidx, xmin, origin, committs);
556  }
557  }
558 }
uint32 TransactionId
Definition: c.h:655
void ReportApplyConflict(EState *estate, ResultRelInfo *relinfo, int elevel, ConflictType type, TupleTableSlot *searchslot, TupleTableSlot *localslot, TupleTableSlot *remoteslot, Oid indexoid, TransactionId localxmin, RepOriginId localorigin, TimestampTz localts)
Definition: conflict.c:107
bool GetTupleTransactionInfo(TupleTableSlot *localslot, TransactionId *xmin, RepOriginId *localorigin, TimestampTz *localts)
Definition: conflict.c:61
int64 TimestampTz
Definition: timestamp.h:39
static bool FindConflictTuple(ResultRelInfo *resultRelInfo, EState *estate, Oid conflictindex, TupleTableSlot *slot, TupleTableSlot **conflictslot)
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
#define foreach_oid(var, lst)
Definition: pg_list.h:471
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:550
const char * type
uint16 RepOriginId
Definition: xlogdefs.h:65

References ERROR, FindConflictTuple(), foreach_oid, GetTupleTransactionInfo(), list_member_oid(), ReportApplyConflict(), ResultRelInfo::ri_onConflictArbiterIndexes, and type.

Referenced by ExecSimpleRelationInsert(), and ExecSimpleRelationUpdate().

◆ CheckCmdReplicaIdentity()

void CheckCmdReplicaIdentity ( Relation  rel,
CmdType  cmd 
)

Definition at line 772 of file execReplication.c.

773 {
774  PublicationDesc pubdesc;
775 
776  /*
777  * Skip checking the replica identity for partitioned tables, because the
778  * operations are actually performed on the leaf partitions.
779  */
780  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
781  return;
782 
783  /* We only need to do checks for UPDATE and DELETE. */
784  if (cmd != CMD_UPDATE && cmd != CMD_DELETE)
785  return;
786 
787  /*
788  * It is only safe to execute UPDATE/DELETE when all columns, referenced
789  * in the row filters from publications which the relation is in, are
790  * valid - i.e. when all referenced columns are part of REPLICA IDENTITY
791  * or the table does not publish UPDATEs or DELETEs.
792  *
793  * XXX We could optimize it by first checking whether any of the
794  * publications have a row filter for this relation. If not and relation
795  * has replica identity then we can avoid building the descriptor but as
796  * this happens only one time it doesn't seem worth the additional
797  * complexity.
798  */
799  RelationBuildPublicationDesc(rel, &pubdesc);
800  if (cmd == CMD_UPDATE && !pubdesc.rf_valid_for_update)
801  ereport(ERROR,
802  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
803  errmsg("cannot update table \"%s\"",
805  errdetail("Column used in the publication WHERE expression is not part of the replica identity.")));
806  else if (cmd == CMD_UPDATE && !pubdesc.cols_valid_for_update)
807  ereport(ERROR,
808  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
809  errmsg("cannot update table \"%s\"",
811  errdetail("Column list used by the publication does not cover the replica identity.")));
812  else if (cmd == CMD_DELETE && !pubdesc.rf_valid_for_delete)
813  ereport(ERROR,
814  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
815  errmsg("cannot delete from table \"%s\"",
817  errdetail("Column used in the publication WHERE expression is not part of the replica identity.")));
818  else if (cmd == CMD_DELETE && !pubdesc.cols_valid_for_delete)
819  ereport(ERROR,
820  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
821  errmsg("cannot delete from table \"%s\"",
823  errdetail("Column list used by the publication does not cover the replica identity.")));
824 
825  /* If relation has replica identity we are always good. */
827  return;
828 
829  /* REPLICA IDENTITY FULL is also good for UPDATE/DELETE. */
830  if (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
831  return;
832 
833  /*
834  * This is UPDATE/DELETE and there is no replica identity.
835  *
836  * Check if the table publishes UPDATES or DELETES.
837  */
838  if (cmd == CMD_UPDATE && pubdesc.pubactions.pubupdate)
839  ereport(ERROR,
840  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
841  errmsg("cannot update table \"%s\" because it does not have a replica identity and publishes updates",
843  errhint("To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.")));
844  else if (cmd == CMD_DELETE && pubdesc.pubactions.pubdelete)
845  ereport(ERROR,
846  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
847  errmsg("cannot delete from table \"%s\" because it does not have a replica identity and publishes deletes",
849  errhint("To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.")));
850 }
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errhint(const char *fmt,...)
Definition: elog.c:1317
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ereport(elevel,...)
Definition: elog.h:149
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_UPDATE
Definition: nodes.h:266
#define RelationGetRelationName(relation)
Definition: rel.h:539
void RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
Definition: relcache.c:5732
Oid RelationGetReplicaIndex(Relation relation)
Definition: relcache.c:5016
PublicationActions pubactions
bool cols_valid_for_delete
bool cols_valid_for_update
Form_pg_class rd_rel
Definition: rel.h:111

References CMD_DELETE, CMD_UPDATE, PublicationDesc::cols_valid_for_delete, PublicationDesc::cols_valid_for_update, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, OidIsValid, PublicationDesc::pubactions, PublicationActions::pubdelete, PublicationActions::pubupdate, RelationData::rd_rel, RelationBuildPublicationDesc(), RelationGetRelationName, RelationGetReplicaIndex(), PublicationDesc::rf_valid_for_delete, and PublicationDesc::rf_valid_for_update.

Referenced by CheckValidResultRel(), ExecSimpleRelationDelete(), ExecSimpleRelationInsert(), and ExecSimpleRelationUpdate().

◆ CheckSubscriptionRelkind()

void CheckSubscriptionRelkind ( char  relkind,
const char *  nspname,
const char *  relname 
)

Definition at line 859 of file execReplication.c.

861 {
862  if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
863  ereport(ERROR,
864  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
865  errmsg("cannot use relation \"%s.%s\" as logical replication target",
866  nspname, relname),
868 }
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
NameData relname
Definition: pg_class.h:38

References ereport, errcode(), errdetail_relkind_not_supported(), errmsg(), ERROR, and relname.

Referenced by AlterSubscription_refresh(), apply_handle_tuple_routing(), CreateSubscription(), and logicalrep_rel_open().

◆ ExecSimpleRelationDelete()

void ExecSimpleRelationDelete ( ResultRelInfo resultRelInfo,
EState estate,
EPQState epqstate,
TupleTableSlot searchslot 
)

Definition at line 739 of file execReplication.c.

742 {
743  bool skip_tuple = false;
744  Relation rel = resultRelInfo->ri_RelationDesc;
745  ItemPointer tid = &searchslot->tts_tid;
746 
748 
749  /* BEFORE ROW DELETE Triggers */
750  if (resultRelInfo->ri_TrigDesc &&
751  resultRelInfo->ri_TrigDesc->trig_delete_before_row)
752  {
753  skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
754  tid, NULL, NULL, NULL, NULL);
755  }
756 
757  if (!skip_tuple)
758  {
759  /* OK, delete the tuple */
760  simple_table_tuple_delete(rel, tid, estate->es_snapshot);
761 
762  /* AFTER ROW DELETE Triggers */
763  ExecARDeleteTriggers(estate, resultRelInfo,
764  tid, NULL, NULL, false);
765  }
766 }
void CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
Snapshot es_snapshot
Definition: execnodes.h:632
Relation ri_RelationDesc
Definition: execnodes.h:459
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:492
bool trig_delete_before_row
Definition: reltrigger.h:66
ItemPointerData tts_tid
Definition: tuptable.h:129
void simple_table_tuple_delete(Relation rel, ItemPointer tid, Snapshot snapshot)
Definition: tableam.c:290
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition: trigger.c:2775
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot, TM_Result *tmresult, TM_FailureData *tmfd)
Definition: trigger.c:2684

References CheckCmdReplicaIdentity(), CMD_DELETE, EState::es_snapshot, ExecARDeleteTriggers(), ExecBRDeleteTriggers(), ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, simple_table_tuple_delete(), TriggerDesc::trig_delete_before_row, and TupleTableSlot::tts_tid.

Referenced by apply_handle_delete_internal(), and apply_handle_tuple_routing().

◆ ExecSimpleRelationInsert()

void ExecSimpleRelationInsert ( ResultRelInfo resultRelInfo,
EState estate,
TupleTableSlot slot 
)

Definition at line 567 of file execReplication.c.

569 {
570  bool skip_tuple = false;
571  Relation rel = resultRelInfo->ri_RelationDesc;
572 
573  /* For now we support only tables. */
574  Assert(rel->rd_rel->relkind == RELKIND_RELATION);
575 
577 
578  /* BEFORE ROW INSERT Triggers */
579  if (resultRelInfo->ri_TrigDesc &&
580  resultRelInfo->ri_TrigDesc->trig_insert_before_row)
581  {
582  if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
583  skip_tuple = true; /* "do nothing" */
584  }
585 
586  if (!skip_tuple)
587  {
588  List *recheckIndexes = NIL;
589  List *conflictindexes;
590  bool conflict = false;
591 
592  /* Compute stored generated columns */
593  if (rel->rd_att->constr &&
595  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
596  CMD_INSERT);
597 
598  /* Check the constraints of the tuple */
599  if (rel->rd_att->constr)
600  ExecConstraints(resultRelInfo, slot, estate);
601  if (rel->rd_rel->relispartition)
602  ExecPartitionCheck(resultRelInfo, slot, estate, true);
603 
604  /* OK, store the tuple and create index entries for it */
605  simple_table_tuple_insert(resultRelInfo->ri_RelationDesc, slot);
606 
607  conflictindexes = resultRelInfo->ri_onConflictArbiterIndexes;
608 
609  if (resultRelInfo->ri_NumIndices > 0)
610  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
611  slot, estate, false,
612  conflictindexes ? true : false,
613  &conflict,
614  conflictindexes, false);
615 
616  /*
617  * Checks the conflict indexes to fetch the conflicting local tuple
618  * and reports the conflict. We perform this check here, instead of
619  * performing an additional index scan before the actual insertion and
620  * reporting the conflict if any conflicting tuples are found. This is
621  * to avoid the overhead of executing the extra scan for each INSERT
622  * operation, even when no conflict arises, which could introduce
623  * significant overhead to replication, particularly in cases where
624  * conflicts are rare.
625  *
626  * XXX OTOH, this could lead to clean-up effort for dead tuples added
627  * in heap and index in case of conflicts. But as conflicts shouldn't
628  * be a frequent thing so we preferred to save the performance
629  * overhead of extra scan before each insertion.
630  */
631  if (conflict)
632  CheckAndReportConflict(resultRelInfo, estate, CT_INSERT_EXISTS,
633  recheckIndexes, NULL, slot);
634 
635  /* AFTER ROW INSERT Triggers */
636  ExecARInsertTriggers(estate, resultRelInfo, slot,
637  recheckIndexes, NULL);
638 
639  /*
640  * XXX we should in theory pass a TransitionCaptureState object to the
641  * above to capture transition tuples, but after statement triggers
642  * don't actually get fired by replication yet anyway
643  */
644 
645  list_free(recheckIndexes);
646  }
647 }
@ CT_INSERT_EXISTS
Definition: conflict.h:27
List * ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool update, bool noDupErr, bool *specConflict, List *arbiterIndexes, bool onlySummarizing)
Definition: execIndexing.c:303
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1796
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1920
static void CheckAndReportConflict(ResultRelInfo *resultRelInfo, EState *estate, ConflictType type, List *recheckIndexes, TupleTableSlot *searchslot, TupleTableSlot *remoteslot)
void list_free(List *list)
Definition: list.c:1546
void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
@ CMD_INSERT
Definition: nodes.h:267
#define NIL
Definition: pg_list.h:68
Definition: pg_list.h:54
TupleDesc rd_att
Definition: rel.h:112
int ri_NumIndices
Definition: execnodes.h:462
bool trig_insert_before_row
Definition: reltrigger.h:56
bool has_generated_stored
Definition: tupdesc.h:45
TupleConstr * constr
Definition: tupdesc.h:85
void simple_table_tuple_insert(Relation rel, TupleTableSlot *slot)
Definition: tableam.c:276
bool ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2459
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2535

References Assert, CheckAndReportConflict(), CheckCmdReplicaIdentity(), CMD_INSERT, TupleDescData::constr, CT_INSERT_EXISTS, ExecARInsertTriggers(), ExecBRInsertTriggers(), ExecComputeStoredGenerated(), ExecConstraints(), ExecInsertIndexTuples(), ExecPartitionCheck(), TupleConstr::has_generated_stored, list_free(), NIL, RelationData::rd_att, RelationData::rd_rel, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, simple_table_tuple_insert(), and TriggerDesc::trig_insert_before_row.

Referenced by apply_handle_insert_internal().

◆ ExecSimpleRelationUpdate()

void ExecSimpleRelationUpdate ( ResultRelInfo resultRelInfo,
EState estate,
EPQState epqstate,
TupleTableSlot searchslot,
TupleTableSlot slot 
)

Definition at line 656 of file execReplication.c.

659 {
660  bool skip_tuple = false;
661  Relation rel = resultRelInfo->ri_RelationDesc;
662  ItemPointer tid = &(searchslot->tts_tid);
663 
664  /*
665  * We support only non-system tables, with
666  * check_publication_add_relation() accountable.
667  */
668  Assert(rel->rd_rel->relkind == RELKIND_RELATION);
669  Assert(!IsCatalogRelation(rel));
670 
672 
673  /* BEFORE ROW UPDATE Triggers */
674  if (resultRelInfo->ri_TrigDesc &&
675  resultRelInfo->ri_TrigDesc->trig_update_before_row)
676  {
677  if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
678  tid, NULL, slot, NULL, NULL))
679  skip_tuple = true; /* "do nothing" */
680  }
681 
682  if (!skip_tuple)
683  {
684  List *recheckIndexes = NIL;
685  TU_UpdateIndexes update_indexes;
686  List *conflictindexes;
687  bool conflict = false;
688 
689  /* Compute stored generated columns */
690  if (rel->rd_att->constr &&
692  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
693  CMD_UPDATE);
694 
695  /* Check the constraints of the tuple */
696  if (rel->rd_att->constr)
697  ExecConstraints(resultRelInfo, slot, estate);
698  if (rel->rd_rel->relispartition)
699  ExecPartitionCheck(resultRelInfo, slot, estate, true);
700 
701  simple_table_tuple_update(rel, tid, slot, estate->es_snapshot,
702  &update_indexes);
703 
704  conflictindexes = resultRelInfo->ri_onConflictArbiterIndexes;
705 
706  if (resultRelInfo->ri_NumIndices > 0 && (update_indexes != TU_None))
707  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
708  slot, estate, true,
709  conflictindexes ? true : false,
710  &conflict, conflictindexes,
711  (update_indexes == TU_Summarizing));
712 
713  /*
714  * Refer to the comments above the call to CheckAndReportConflict() in
715  * ExecSimpleRelationInsert to understand why this check is done at
716  * this point.
717  */
718  if (conflict)
719  CheckAndReportConflict(resultRelInfo, estate, CT_UPDATE_EXISTS,
720  recheckIndexes, searchslot, slot);
721 
722  /* AFTER ROW UPDATE Triggers */
723  ExecARUpdateTriggers(estate, resultRelInfo,
724  NULL, NULL,
725  tid, NULL, slot,
726  recheckIndexes, NULL, false);
727 
728  list_free(recheckIndexes);
729  }
730 }
bool IsCatalogRelation(Relation relation)
Definition: catalog.c:103
@ CT_UPDATE_EXISTS
Definition: conflict.h:33
bool trig_update_before_row
Definition: reltrigger.h:61
void simple_table_tuple_update(Relation rel, ItemPointer otid, TupleTableSlot *slot, Snapshot snapshot, TU_UpdateIndexes *update_indexes)
Definition: tableam.c:335
TU_UpdateIndexes
Definition: tableam.h:118
@ TU_Summarizing
Definition: tableam.h:126
@ TU_None
Definition: tableam.h:120
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, TM_Result *tmresult, TM_FailureData *tmfd)
Definition: trigger.c:2935
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition: trigger.c:3098

References Assert, CheckAndReportConflict(), CheckCmdReplicaIdentity(), CMD_UPDATE, TupleDescData::constr, CT_UPDATE_EXISTS, EState::es_snapshot, ExecARUpdateTriggers(), ExecBRUpdateTriggers(), ExecComputeStoredGenerated(), ExecConstraints(), ExecInsertIndexTuples(), ExecPartitionCheck(), TupleConstr::has_generated_stored, IsCatalogRelation(), list_free(), NIL, RelationData::rd_att, RelationData::rd_rel, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, simple_table_tuple_update(), TriggerDesc::trig_update_before_row, TupleTableSlot::tts_tid, TU_None, and TU_Summarizing.

Referenced by apply_handle_tuple_routing(), and apply_handle_update_internal().

◆ FindConflictTuple()

static bool FindConflictTuple ( ResultRelInfo resultRelInfo,
EState estate,
Oid  conflictindex,
TupleTableSlot slot,
TupleTableSlot **  conflictslot 
)
static

Definition at line 487 of file execReplication.c.

490 {
491  Relation rel = resultRelInfo->ri_RelationDesc;
492  ItemPointerData conflictTid;
493  TM_FailureData tmfd;
494  TM_Result res;
495 
496  *conflictslot = NULL;
497 
498 retry:
499  if (ExecCheckIndexConstraints(resultRelInfo, slot, estate,
500  &conflictTid, &slot->tts_tid,
501  list_make1_oid(conflictindex)))
502  {
503  if (*conflictslot)
504  ExecDropSingleTupleTableSlot(*conflictslot);
505 
506  *conflictslot = NULL;
507  return false;
508  }
509 
510  *conflictslot = table_slot_create(rel, NULL);
511 
513 
514  res = table_tuple_lock(rel, &conflictTid, GetLatestSnapshot(),
515  *conflictslot,
516  GetCurrentCommandId(false),
519  0 /* don't follow updates */ ,
520  &tmfd);
521 
523 
524  if (should_refetch_tuple(res, &tmfd))
525  goto retry;
526 
527  return true;
528 }
bool ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, ItemPointer conflictTid, ItemPointer tupleid, List *arbiterIndexes)
Definition: execIndexing.c:536
static bool should_refetch_tuple(TM_Result res, TM_FailureData *tmfd)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1341
@ LockWaitBlock
Definition: lockoptions.h:39
@ LockTupleShare
Definition: lockoptions.h:54
#define list_make1_oid(x1)
Definition: pg_list.h:242
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:291
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:648
void PopActiveSnapshot(void)
Definition: snapmgr.c:743
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
TM_Result
Definition: tableam.h:80
static TM_Result table_tuple_lock(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
Definition: tableam.h:1580
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:828

References ExecCheckIndexConstraints(), ExecDropSingleTupleTableSlot(), GetCurrentCommandId(), GetLatestSnapshot(), list_make1_oid, LockTupleShare, LockWaitBlock, PopActiveSnapshot(), PushActiveSnapshot(), res, ResultRelInfo::ri_RelationDesc, should_refetch_tuple(), table_slot_create(), table_tuple_lock(), and TupleTableSlot::tts_tid.

Referenced by CheckAndReportConflict().

◆ get_equal_strategy_number()

static StrategyNumber get_equal_strategy_number ( Oid  opclass)
static

Definition at line 76 of file execReplication.c.

77 {
78  Oid am = get_opclass_method(opclass);
79 
81 }
StrategyNumber get_equal_strategy_number_for_am(Oid am)
Oid get_opclass_method(Oid opclass)
Definition: lsyscache.c:1260

References get_equal_strategy_number_for_am(), and get_opclass_method().

Referenced by build_replindex_scan_key().

◆ get_equal_strategy_number_for_am()

StrategyNumber get_equal_strategy_number_for_am ( Oid  am)

Definition at line 50 of file execReplication.c.

51 {
52  int ret;
53 
54  switch (am)
55  {
56  case BTREE_AM_OID:
58  break;
59  case HASH_AM_OID:
61  break;
62  default:
63  /* XXX: Only Btree and Hash indexes are supported */
64  ret = InvalidStrategy;
65  break;
66  }
67 
68  return ret;
69 }
#define InvalidStrategy
Definition: stratnum.h:24
#define HTEqualStrategyNumber
Definition: stratnum.h:41
#define BTEqualStrategyNumber
Definition: stratnum.h:31

References BTEqualStrategyNumber, HTEqualStrategyNumber, and InvalidStrategy.

Referenced by get_equal_strategy_number(), and IsIndexUsableForReplicaIdentityFull().

◆ RelationFindReplTupleByIndex()

bool RelationFindReplTupleByIndex ( Relation  rel,
Oid  idxoid,
LockTupleMode  lockmode,
TupleTableSlot searchslot,
TupleTableSlot outslot 
)

Definition at line 222 of file execReplication.c.

226 {
228  int skey_attoff;
229  IndexScanDesc scan;
230  SnapshotData snap;
231  TransactionId xwait;
232  Relation idxrel;
233  bool found;
234  TypeCacheEntry **eq = NULL;
235  bool isIdxSafeToSkipDuplicates;
236 
237  /* Open the index. */
238  idxrel = index_open(idxoid, RowExclusiveLock);
239 
240  isIdxSafeToSkipDuplicates = (GetRelationIdentityOrPK(rel) == idxoid);
241 
242  InitDirtySnapshot(snap);
243 
244  /* Build scan key. */
245  skey_attoff = build_replindex_scan_key(skey, rel, idxrel, searchslot);
246 
247  /* Start an index scan. */
248  scan = index_beginscan(rel, idxrel, &snap, skey_attoff, 0);
249 
250 retry:
251  found = false;
252 
253  index_rescan(scan, skey, skey_attoff, NULL, 0);
254 
255  /* Try to find the tuple */
256  while (index_getnext_slot(scan, ForwardScanDirection, outslot))
257  {
258  /*
259  * Avoid expensive equality check if the index is primary key or
260  * replica identity index.
261  */
262  if (!isIdxSafeToSkipDuplicates)
263  {
264  if (eq == NULL)
265  eq = palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
266 
267  if (!tuples_equal(outslot, searchslot, eq))
268  continue;
269  }
270 
271  ExecMaterializeSlot(outslot);
272 
273  xwait = TransactionIdIsValid(snap.xmin) ?
274  snap.xmin : snap.xmax;
275 
276  /*
277  * If the tuple is locked, wait for locking transaction to finish and
278  * retry.
279  */
280  if (TransactionIdIsValid(xwait))
281  {
282  XactLockTableWait(xwait, NULL, NULL, XLTW_None);
283  goto retry;
284  }
285 
286  /* Found our tuple and it's not locked */
287  found = true;
288  break;
289  }
290 
291  /* Found tuple, try to lock it in the lockmode. */
292  if (found)
293  {
294  TM_FailureData tmfd;
295  TM_Result res;
296 
298 
299  res = table_tuple_lock(rel, &(outslot->tts_tid), GetLatestSnapshot(),
300  outslot,
301  GetCurrentCommandId(false),
302  lockmode,
304  0 /* don't follow updates */ ,
305  &tmfd);
306 
308 
309  if (should_refetch_tuple(res, &tmfd))
310  goto retry;
311  }
312 
313  index_endscan(scan);
314 
315  /* Don't release lock until commit. */
316  index_close(idxrel, NoLock);
317 
318  return found;
319 }
static bool tuples_equal(TupleTableSlot *slot1, TupleTableSlot *slot2, TypeCacheEntry **eq)
static int build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel, TupleTableSlot *searchslot)
bool index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
Definition: indexam.c:675
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, int norderbys)
Definition: indexam.c:256
void index_endscan(IndexScanDesc scan)
Definition: indexam.c:378
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition: indexam.c:352
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:657
@ XLTW_None
Definition: lmgr.h:26
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
void * palloc0(Size size)
Definition: mcxt.c:1347
#define INDEX_MAX_KEYS
@ ForwardScanDirection
Definition: sdir.h:28
#define InitDirtySnapshot(snapshotdata)
Definition: snapmgr.h:40
Oid GetRelationIdentityOrPK(Relation rel)
Definition: relation.c:851
TransactionId xmin
Definition: snapshot.h:157
TransactionId xmax
Definition: snapshot.h:158
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:123
#define TransactionIdIsValid(xid)
Definition: transam.h:41
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:472

References build_replindex_scan_key(), ExecMaterializeSlot(), ForwardScanDirection, GetCurrentCommandId(), GetLatestSnapshot(), GetRelationIdentityOrPK(), index_beginscan(), index_close(), index_endscan(), index_getnext_slot(), INDEX_MAX_KEYS, index_open(), index_rescan(), InitDirtySnapshot, LockWaitBlock, TupleDescData::natts, NoLock, palloc0(), PopActiveSnapshot(), PushActiveSnapshot(), res, RowExclusiveLock, should_refetch_tuple(), table_tuple_lock(), TransactionIdIsValid, TupleTableSlot::tts_tid, TupleTableSlot::tts_tupleDescriptor, tuples_equal(), XactLockTableWait(), XLTW_None, SnapshotData::xmax, and SnapshotData::xmin.

Referenced by FindReplTupleInLocalRel().

◆ RelationFindReplTupleSeq()

bool RelationFindReplTupleSeq ( Relation  rel,
LockTupleMode  lockmode,
TupleTableSlot searchslot,
TupleTableSlot outslot 
)

Definition at line 398 of file execReplication.c.

400 {
401  TupleTableSlot *scanslot;
402  TableScanDesc scan;
403  SnapshotData snap;
404  TypeCacheEntry **eq;
405  TransactionId xwait;
406  bool found;
408 
409  Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
410 
411  eq = palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
412 
413  /* Start a heap scan. */
414  InitDirtySnapshot(snap);
415  scan = table_beginscan(rel, &snap, 0, NULL);
416  scanslot = table_slot_create(rel, NULL);
417 
418 retry:
419  found = false;
420 
421  table_rescan(scan, NULL);
422 
423  /* Try to find the tuple */
424  while (table_scan_getnextslot(scan, ForwardScanDirection, scanslot))
425  {
426  if (!tuples_equal(scanslot, searchslot, eq))
427  continue;
428 
429  found = true;
430  ExecCopySlot(outslot, scanslot);
431 
432  xwait = TransactionIdIsValid(snap.xmin) ?
433  snap.xmin : snap.xmax;
434 
435  /*
436  * If the tuple is locked, wait for locking transaction to finish and
437  * retry.
438  */
439  if (TransactionIdIsValid(xwait))
440  {
441  XactLockTableWait(xwait, NULL, NULL, XLTW_None);
442  goto retry;
443  }
444 
445  /* Found our tuple and it's not locked */
446  break;
447  }
448 
449  /* Found tuple, try to lock it in the lockmode. */
450  if (found)
451  {
452  TM_FailureData tmfd;
453  TM_Result res;
454 
456 
457  res = table_tuple_lock(rel, &(outslot->tts_tid), GetLatestSnapshot(),
458  outslot,
459  GetCurrentCommandId(false),
460  lockmode,
462  0 /* don't follow updates */ ,
463  &tmfd);
464 
466 
467  if (should_refetch_tuple(res, &tmfd))
468  goto retry;
469  }
470 
471  table_endscan(scan);
473 
474  return found;
475 }
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:185
#define RelationGetDescr(relation)
Definition: rel.h:531
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:908
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1019
static void table_rescan(TableScanDesc scan, struct ScanKeyData *key)
Definition: tableam.h:1028
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1055
bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
Definition: tupdesc.c:419
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:509

References Assert, equalTupleDescs(), ExecCopySlot(), ExecDropSingleTupleTableSlot(), ForwardScanDirection, GetCurrentCommandId(), GetLatestSnapshot(), InitDirtySnapshot, LockWaitBlock, TupleDescData::natts, palloc0(), PG_USED_FOR_ASSERTS_ONLY, PopActiveSnapshot(), PushActiveSnapshot(), RelationGetDescr, res, should_refetch_tuple(), table_beginscan(), table_endscan(), table_rescan(), table_scan_getnextslot(), table_slot_create(), table_tuple_lock(), TransactionIdIsValid, TupleTableSlot::tts_tid, TupleTableSlot::tts_tupleDescriptor, tuples_equal(), XactLockTableWait(), XLTW_None, SnapshotData::xmax, and SnapshotData::xmin.

Referenced by FindReplTupleInLocalRel().

◆ should_refetch_tuple()

static bool should_refetch_tuple ( TM_Result  res,
TM_FailureData tmfd 
)
static

Definition at line 177 of file execReplication.c.

178 {
179  bool refetch = false;
180 
181  switch (res)
182  {
183  case TM_Ok:
184  break;
185  case TM_Updated:
186  /* XXX: Improve handling here */
188  ereport(LOG,
190  errmsg("tuple to be locked was already moved to another partition due to concurrent update, retrying")));
191  else
192  ereport(LOG,
194  errmsg("concurrent update, retrying")));
195  refetch = true;
196  break;
197  case TM_Deleted:
198  /* XXX: Improve handling here */
199  ereport(LOG,
201  errmsg("concurrent delete, retrying")));
202  refetch = true;
203  break;
204  case TM_Invisible:
205  elog(ERROR, "attempted to lock invisible tuple");
206  break;
207  default:
208  elog(ERROR, "unexpected table_tuple_lock status: %u", res);
209  break;
210  }
211 
212  return refetch;
213 }
#define LOG
Definition: elog.h:31
static bool ItemPointerIndicatesMovedPartitions(const ItemPointerData *pointer)
Definition: itemptr.h:197
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition: pgbench.c:76
ItemPointerData ctid
Definition: tableam.h:150
@ TM_Ok
Definition: tableam.h:85
@ TM_Deleted
Definition: tableam.h:100
@ TM_Updated
Definition: tableam.h:97
@ TM_Invisible
Definition: tableam.h:88

References TM_FailureData::ctid, elog, ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errmsg(), ERROR, ItemPointerIndicatesMovedPartitions(), LOG, res, TM_Deleted, TM_Invisible, TM_Ok, and TM_Updated.

Referenced by FindConflictTuple(), RelationFindReplTupleByIndex(), and RelationFindReplTupleSeq().

◆ tuples_equal()

static bool tuples_equal ( TupleTableSlot slot1,
TupleTableSlot slot2,
TypeCacheEntry **  eq 
)
static

Definition at line 325 of file execReplication.c.

327 {
328  int attrnum;
329 
330  Assert(slot1->tts_tupleDescriptor->natts ==
331  slot2->tts_tupleDescriptor->natts);
332 
333  slot_getallattrs(slot1);
334  slot_getallattrs(slot2);
335 
336  /* Check equality of the attributes. */
337  for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
338  {
339  Form_pg_attribute att;
340  TypeCacheEntry *typentry;
341 
342  att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
343 
344  /*
345  * Ignore dropped and generated columns as the publisher doesn't send
346  * those
347  */
348  if (att->attisdropped || att->attgenerated)
349  continue;
350 
351  /*
352  * If one value is NULL and other is not, then they are certainly not
353  * equal
354  */
355  if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
356  return false;
357 
358  /*
359  * If both are NULL, they can be considered equal.
360  */
361  if (slot1->tts_isnull[attrnum] || slot2->tts_isnull[attrnum])
362  continue;
363 
364  typentry = eq[attrnum];
365  if (typentry == NULL)
366  {
367  typentry = lookup_type_cache(att->atttypid,
369  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
370  ereport(ERROR,
371  (errcode(ERRCODE_UNDEFINED_FUNCTION),
372  errmsg("could not identify an equality operator for type %s",
373  format_type_be(att->atttypid))));
374  eq[attrnum] = typentry;
375  }
376 
378  att->attcollation,
379  slot1->tts_values[attrnum],
380  slot2->tts_values[attrnum])))
381  return false;
382  }
383 
384  return true;
385 }
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
static bool DatumGetBool(Datum X)
Definition: postgres.h:90
Oid fn_oid
Definition: fmgr.h:59
FmgrInfo eq_opr_finfo
Definition: typcache.h:75
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:368
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:356
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:142

References Assert, DatumGetBool(), TypeCacheEntry::eq_opr_finfo, ereport, errcode(), errmsg(), ERROR, FmgrInfo::fn_oid, format_type_be(), FunctionCall2Coll(), lookup_type_cache(), TupleDescData::natts, OidIsValid, slot_getallattrs(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_tupleDescriptor, TupleTableSlot::tts_values, TupleDescAttr, and TYPECACHE_EQ_OPR_FINFO.

Referenced by RelationFindReplTupleByIndex(), and RelationFindReplTupleSeq().