PostgreSQL Source Code git master
Loading...
Searching...
No Matches
conflict.c File Reference
#include "postgres.h"
#include "access/commit_ts.h"
#include "access/genam.h"
#include "access/tableam.h"
#include "catalog/heap.h"
#include "catalog/pg_am.h"
#include "catalog/pg_namespace.h"
#include "catalog/toasting.h"
#include "executor/executor.h"
#include "pgstat.h"
#include "replication/conflict.h"
#include "replication/worker_internal.h"
#include "storage/lmgr.h"
#include "utils/lsyscache.h"
Include dependency graph for conflict.c:

Go to the source code of this file.

Data Structures

struct  ConflictLogColumnDef
 

Macros

#define NUM_CONFLICT_ATTRS   ((AttrNumber) lengthof(ConflictLogSchema))
 

Typedefs

typedef struct ConflictLogColumnDef ConflictLogColumnDef
 

Functions

 StaticAssertDecl (lengthof(ConflictLogDestNames)==CONFLICT_LOG_DEST_ALL+1, "ConflictLogDestNames length mismatch")
 
static int errcode_apply_conflict (ConflictType type)
 
static void errdetail_apply_conflict (EState *estate, ResultRelInfo *relinfo, ConflictType type, TupleTableSlot *searchslot, TupleTableSlot *localslot, TupleTableSlot *remoteslot, Oid indexoid, TransactionId localxmin, ReplOriginId localorigin, TimestampTz localts, StringInfo err_msg)
 
static void get_tuple_desc (EState *estate, ResultRelInfo *relinfo, ConflictType type, char **key_desc, TupleTableSlot *localslot, char **local_desc, TupleTableSlot *remoteslot, char **remote_desc, TupleTableSlot *searchslot, char **search_desc, Oid indexoid)
 
static charbuild_index_value_desc (EState *estate, Relation localrel, TupleTableSlot *slot, Oid indexoid)
 
static TupleDesc create_conflict_log_table_tupdesc (void)
 
Oid create_conflict_log_table (Oid subid, char *subname, Oid subowner)
 
ConflictLogDest GetConflictLogDest (const char *dest)
 
bool GetTupleTransactionInfo (TupleTableSlot *localslot, TransactionId *xmin, ReplOriginId *localorigin, TimestampTz *localts)
 
void ReportApplyConflict (EState *estate, ResultRelInfo *relinfo, int elevel, ConflictType type, TupleTableSlot *searchslot, TupleTableSlot *remoteslot, List *conflicttuples)
 
void InitConflictIndexes (ResultRelInfo *relInfo)
 
static void append_tuple_value_detail (StringInfo buf, List *tuple_values)
 

Variables

const char *const ConflictLogDestNames []
 
static const ConflictLogColumnDef ConflictLogSchema []
 
static const char *const ConflictTypeNames []
 

Macro Definition Documentation

◆ NUM_CONFLICT_ATTRS

#define NUM_CONFLICT_ATTRS   ((AttrNumber) lengthof(ConflictLogSchema))

Definition at line 82 of file conflict.c.

Typedef Documentation

◆ ConflictLogColumnDef

Function Documentation

◆ append_tuple_value_detail()

static void append_tuple_value_detail ( StringInfo  buf,
List tuple_values 
)
static

Definition at line 367 of file conflict.c.

368{
369 bool first = true;
370
371 Assert(buf != NULL && tuple_values != NIL);
372
374 {
375 /*
376 * Skip if the value is NULL. This means the current user does not
377 * have enough permissions to see all columns in the table. See
378 * get_tuple_desc().
379 */
380 if (!tuple_value)
381 continue;
382
383 /* standard SQL punctuation, not translated */
384 if (!first)
386
388 first = false;
389 }
390}
#define Assert(condition)
Definition c.h:1002
#define NIL
Definition pg_list.h:68
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
static char buf[DEFAULT_XLOG_SEG_SIZE]
static int fb(int x)
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230

References appendStringInfoString(), Assert, buf, fb(), foreach_ptr, and NIL.

Referenced by errdetail_apply_conflict().

◆ build_index_value_desc()

static char * build_index_value_desc ( EState estate,
Relation  localrel,
TupleTableSlot slot,
Oid  indexoid 
)
static

Definition at line 779 of file conflict.c.

781{
782 char *index_value;
785 bool isnull[INDEX_MAX_KEYS];
786 TupleTableSlot *tableslot = slot;
787
788 if (!tableslot)
789 return NULL;
790
792
793 indexDesc = index_open(indexoid, NoLock);
794
795 /*
796 * If the slot is a virtual slot, copy it into a heap tuple slot as
797 * FormIndexDatum only works with heap tuple slots.
798 */
799 if (TTS_IS_VIRTUAL(slot))
800 {
801 tableslot = table_slot_create(localrel, &estate->es_tupleTable);
802 tableslot = ExecCopySlot(tableslot, slot);
803 }
804
805 /*
806 * Initialize ecxt_scantuple for potential use in FormIndexDatum when
807 * index expressions are present.
808 */
809 GetPerTupleExprContext(estate)->ecxt_scantuple = tableslot;
810
811 /*
812 * The values/nulls arrays passed to BuildIndexValueDescription should be
813 * the results of FormIndexDatum, which are the "raw" input to the index
814 * AM.
815 */
816 FormIndexDatum(BuildIndexInfo(indexDesc), tableslot, estate, values, isnull);
817
819
821
822 return index_value;
823}
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define GetPerTupleExprContext(estate)
Definition executor.h:665
char * BuildIndexValueDescription(Relation indexRelation, const Datum *values, const bool *isnull)
Definition genam.c:178
IndexInfo * BuildIndexInfo(Relation index)
Definition index.c:2446
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition index.c:2760
void index_close(Relation relation, LOCKMODE lockmode)
Definition indexam.c:178
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition indexam.c:134
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:351
#define NoLock
Definition lockdefs.h:34
#define RowExclusiveLock
Definition lockdefs.h:38
#define INDEX_MAX_KEYS
uint64_t Datum
Definition postgres.h:70
List * es_tupleTable
Definition execnodes.h:749
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition tableam.c:92
#define TTS_IS_VIRTUAL(slot)
Definition tuptable.h:253
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:544

References Assert, BuildIndexInfo(), BuildIndexValueDescription(), CheckRelationOidLockedByMe(), EState::es_tupleTable, ExecCopySlot(), fb(), FormIndexDatum(), GetPerTupleExprContext, index_close(), INDEX_MAX_KEYS, index_open(), NoLock, RowExclusiveLock, table_slot_create(), TTS_IS_VIRTUAL, and values.

Referenced by get_tuple_desc().

◆ create_conflict_log_table()

Oid create_conflict_log_table ( Oid  subid,
char subname,
Oid  subowner 
)

Definition at line 147 of file conflict.c.

148{
149 TupleDesc tupdesc;
150 Oid relid;
151 char relname[NAMEDATALEN];
152
153 snprintf(relname, NAMEDATALEN, "pg_conflict_log_%u", subid);
154
155 /* Build the tuple descriptor for the new table. */
157
158 /* Create conflict log table. */
161 0, /* tablespace */
162 InvalidOid, /* relid */
163 InvalidOid, /* reltypeid */
164 InvalidOid, /* reloftypeid */
165 subowner,
167 tupdesc,
168 NIL,
171 false, /* shared_relation */
172 false, /* mapped_relation */
174 (Datum) 0, /* reloptions */
175 false, /* use_user_acl */
176 false, /* allow_system_table_mods */
177 true, /* is_internal */
178 InvalidOid, /* relrewrite */
179 NULL); /* typaddress */
180 Assert(OidIsValid(relid));
181
182 /* Release tuple descriptor memory. */
183 FreeTupleDesc(tupdesc);
184
185 /*
186 * We must bump the command counter to make the newly-created relation
187 * tuple visible for opening.
188 */
190
191 /*
192 * Create a TOAST table for the conflict log to support out-of-line
193 * storage of large json data.
194 */
196
198 (errmsg("created conflict log table \"%s\" for subscription \"%s\"",
200 subname)));
201
202 return relid;
203}
#define OidIsValid(objectId)
Definition c.h:917
static TupleDesc create_conflict_log_table_tupdesc(void)
Definition conflict.c:118
#define NOTICE
Definition elog.h:36
#define ereport(elevel,...)
Definition elog.h:152
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition heap.c:1140
char * get_qualified_objname(Oid nspid, char *objname)
Definition lsyscache.c:3720
static char * errmsg
NameData relname
Definition pg_class.h:40
#define NAMEDATALEN
NameData subname
#define snprintf
Definition port.h:261
#define InvalidOid
unsigned int Oid
@ ONCOMMIT_NOOP
Definition primnodes.h:59
void NewRelationCreateToastTable(Oid relOid, Datum reloptions)
Definition toasting.c:72
void FreeTupleDesc(TupleDesc tupdesc)
Definition tupdesc.c:569
void CommandCounterIncrement(void)
Definition xact.c:1130

References Assert, CommandCounterIncrement(), create_conflict_log_table_tupdesc(), ereport, errmsg, fb(), FreeTupleDesc(), get_qualified_objname(), heap_create_with_catalog(), InvalidOid, NAMEDATALEN, NewRelationCreateToastTable(), NIL, NOTICE, OidIsValid, ONCOMMIT_NOOP, relname, snprintf, and subname.

Referenced by alter_sub_conflict_log_dest().

◆ create_conflict_log_table_tupdesc()

static TupleDesc create_conflict_log_table_tupdesc ( void  )
static

Definition at line 118 of file conflict.c.

119{
120 TupleDesc tupdesc;
121
123
124 for (int i = 0; i < NUM_CONFLICT_ATTRS; i++)
125 TupleDescInitEntry(tupdesc, i + 1,
127 ConflictLogSchema[i].atttypid,
128 -1, 0);
129
130 TupleDescFinalize(tupdesc);
131
132 return tupdesc;
133}
static const ConflictLogColumnDef ConflictLogSchema[]
Definition conflict.c:67
#define NUM_CONFLICT_ATTRS
Definition conflict.c:82
int i
Definition isn.c:77
NameData attname
TupleDesc CreateTemplateTupleDesc(int natts)
Definition tupdesc.c:165
void TupleDescFinalize(TupleDesc tupdesc)
Definition tupdesc.c:511
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition tupdesc.c:909

References attname, ConflictLogSchema, CreateTemplateTupleDesc(), i, NUM_CONFLICT_ATTRS, TupleDescFinalize(), and TupleDescInitEntry().

Referenced by create_conflict_log_table().

◆ errcode_apply_conflict()

static int errcode_apply_conflict ( ConflictType  type)
static

Definition at line 342 of file conflict.c.

343{
344 switch (type)
345 {
346 case CT_INSERT_EXISTS:
347 case CT_UPDATE_EXISTS:
356 }
357
358 Assert(false);
359 return 0; /* silence compiler warning */
360}
@ CT_UPDATE_DELETED
Definition conflict.h:43
@ CT_MULTIPLE_UNIQUE_CONFLICTS
Definition conflict.h:55
@ CT_DELETE_MISSING
Definition conflict.h:52
@ CT_UPDATE_ORIGIN_DIFFERS
Definition conflict.h:37
@ CT_INSERT_EXISTS
Definition conflict.h:34
@ CT_UPDATE_EXISTS
Definition conflict.h:40
@ CT_UPDATE_MISSING
Definition conflict.h:46
@ CT_DELETE_ORIGIN_DIFFERS
Definition conflict.h:49
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition pgbench.c:77
const char * type

References Assert, CT_DELETE_MISSING, CT_DELETE_ORIGIN_DIFFERS, CT_INSERT_EXISTS, CT_MULTIPLE_UNIQUE_CONFLICTS, CT_UPDATE_DELETED, CT_UPDATE_EXISTS, CT_UPDATE_MISSING, CT_UPDATE_ORIGIN_DIFFERS, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, fb(), and type.

Referenced by ReportApplyConflict().

◆ errdetail_apply_conflict()

static void errdetail_apply_conflict ( EState estate,
ResultRelInfo relinfo,
ConflictType  type,
TupleTableSlot searchslot,
TupleTableSlot localslot,
TupleTableSlot remoteslot,
Oid  indexoid,
TransactionId  localxmin,
ReplOriginId  localorigin,
TimestampTz  localts,
StringInfo  err_msg 
)
static

Definition at line 403 of file conflict.c.

409{
412 char *origin_name;
413 char *key_desc = NULL;
414 char *local_desc = NULL;
415 char *remote_desc = NULL;
416 char *search_desc = NULL;
417
418 /* Get key, replica identity, remote, and local value data */
423 indexoid);
424
427
428 /* Construct a detailed message describing the type of conflict */
429 switch (type)
430 {
431 case CT_INSERT_EXISTS:
432 case CT_UPDATE_EXISTS:
434 Assert(OidIsValid(indexoid) &&
436
437 if (err_msg->len == 0)
438 {
441
442 if (tuple_buf.len)
443 appendStringInfo(&err_detail, _("Could not apply remote change: %s.\n"),
444 tuple_buf.data);
445 else
446 appendStringInfo(&err_detail, _("Could not apply remote change.\n"));
447
448
450 }
451
454
455 if (localts)
456 {
458 {
459 if (tuple_buf.len)
460 appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified locally in transaction %u at %s: %s."),
461 get_rel_name(indexoid),
463 tuple_buf.data);
464 else
465 appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified locally in transaction %u at %s."),
466 get_rel_name(indexoid),
468 }
469 else if (replorigin_by_oid(localorigin, true, &origin_name))
470 {
471 if (tuple_buf.len)
472 appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified by origin \"%s\" in transaction %u at %s: %s."),
473 get_rel_name(indexoid), origin_name,
475 tuple_buf.data);
476 else
477 appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified by origin \"%s\" in transaction %u at %s."),
478 get_rel_name(indexoid), origin_name,
480 }
481
482 /*
483 * The origin that modified this row has been removed. This
484 * can happen if the origin was created by a different apply
485 * worker and its associated subscription and origin were
486 * dropped after updating the row, or if the origin was
487 * manually dropped by the user.
488 */
489 else
490 {
491 if (tuple_buf.len)
492 appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified by a non-existent origin in transaction %u at %s: %s."),
493 get_rel_name(indexoid),
495 tuple_buf.data);
496 else
497 appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified by a non-existent origin in transaction %u at %s."),
498 get_rel_name(indexoid),
500 }
501 }
502 else
503 {
504 if (tuple_buf.len)
505 appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified in transaction %u: %s."),
506 get_rel_name(indexoid), localxmin,
507 tuple_buf.data);
508 else
509 appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified in transaction %u."),
510 get_rel_name(indexoid), localxmin);
511 }
512
513 break;
514
518 search_desc));
519
521 {
522 if (tuple_buf.len)
523 appendStringInfo(&err_detail, _("Updating the row that was modified locally in transaction %u at %s: %s."),
525 tuple_buf.data);
526 else
527 appendStringInfo(&err_detail, _("Updating the row that was modified locally in transaction %u at %s."),
529 }
530 else if (replorigin_by_oid(localorigin, true, &origin_name))
531 {
532 if (tuple_buf.len)
533 appendStringInfo(&err_detail, _("Updating the row that was modified by a different origin \"%s\" in transaction %u at %s: %s."),
534 origin_name, localxmin,
536 tuple_buf.data);
537 else
538 appendStringInfo(&err_detail, _("Updating the row that was modified by a different origin \"%s\" in transaction %u at %s."),
539 origin_name, localxmin,
541 }
542
543 /* The origin that modified this row has been removed. */
544 else
545 {
546 if (tuple_buf.len)
547 appendStringInfo(&err_detail, _("Updating the row that was modified by a non-existent origin in transaction %u at %s: %s."),
549 tuple_buf.data);
550 else
551 appendStringInfo(&err_detail, _("Updating the row that was modified by a non-existent origin in transaction %u at %s."),
553 }
554
555 break;
556
560
561 if (tuple_buf.len)
562 appendStringInfo(&err_detail, _("Could not find the row to be updated: %s.\n"),
563 tuple_buf.data);
564 else
565 appendStringInfo(&err_detail, _("Could not find the row to be updated.\n"));
566
567 if (localts)
568 {
570 appendStringInfo(&err_detail, _("The row to be updated was deleted locally in transaction %u at %s"),
572 else if (replorigin_by_oid(localorigin, true, &origin_name))
573 appendStringInfo(&err_detail, _("The row to be updated was deleted by a different origin \"%s\" in transaction %u at %s"),
574 origin_name, localxmin, timestamptz_to_str(localts));
575
576 /* The origin that modified this row has been removed. */
577 else
578 appendStringInfo(&err_detail, _("The row to be updated was deleted by a non-existent origin in transaction %u at %s"),
580 }
581 else
582 appendStringInfoString(&err_detail, _("The row to be updated was deleted"));
583
584 break;
585
589
590 if (tuple_buf.len)
591 appendStringInfo(&err_detail, _("Could not find the row to be updated: %s."),
592 tuple_buf.data);
593 else
594 appendStringInfo(&err_detail, _("Could not find the row to be updated."));
595
596 break;
597
601 search_desc));
602
604 {
605 if (tuple_buf.len)
606 appendStringInfo(&err_detail, _("Deleting the row that was modified locally in transaction %u at %s: %s."),
608 tuple_buf.data);
609 else
610 appendStringInfo(&err_detail, _("Deleting the row that was modified locally in transaction %u at %s."),
612 }
613 else if (replorigin_by_oid(localorigin, true, &origin_name))
614 {
615 if (tuple_buf.len)
616 appendStringInfo(&err_detail, _("Deleting the row that was modified by a different origin \"%s\" in transaction %u at %s: %s."),
617 origin_name, localxmin,
619 tuple_buf.data);
620 else
621 appendStringInfo(&err_detail, _("Deleting the row that was modified by a different origin \"%s\" in transaction %u at %s."),
622 origin_name, localxmin,
624 }
625
626 /* The origin that modified this row has been removed. */
627 else
628 {
629 if (tuple_buf.len)
630 appendStringInfo(&err_detail, _("Deleting the row that was modified by a non-existent origin in transaction %u at %s: %s."),
632 tuple_buf.data);
633 else
634 appendStringInfo(&err_detail, _("Deleting the row that was modified by a non-existent origin in transaction %u at %s."),
636 }
637
638 break;
639
643
644 if (tuple_buf.len)
645 appendStringInfo(&err_detail, _("Could not find the row to be deleted: %s."),
646 tuple_buf.data);
647 else
648 appendStringInfo(&err_detail, _("Could not find the row to be deleted."));
649
650 break;
651 }
652
653 Assert(err_detail.len > 0);
654
655 /*
656 * Insert a blank line to visually separate the new detail line from the
657 * existing ones.
658 */
659 if (err_msg->len > 0)
660 appendStringInfoChar(err_msg, '\n');
661
662 appendStringInfoString(err_msg, err_detail.data);
663}
const char * timestamptz_to_str(TimestampTz t)
Definition timestamp.c:1870
static void append_tuple_value_detail(StringInfo buf, List *tuple_values)
Definition conflict.c:367
static void get_tuple_desc(EState *estate, ResultRelInfo *relinfo, ConflictType type, char **key_desc, TupleTableSlot *localslot, char **local_desc, TupleTableSlot *remoteslot, char **remote_desc, TupleTableSlot *searchslot, char **search_desc, Oid indexoid)
Definition conflict.c:673
#define _(x)
Definition elog.c:96
char * get_rel_name(Oid relid)
Definition lsyscache.c:2242
bool replorigin_by_oid(ReplOriginId roident, bool missing_ok, char **roname)
Definition origin.c:513
#define InvalidReplOriginId
Definition origin.h:33
#define list_make1(x1)
Definition pg_list.h:244
#define list_make3(x1, x2, x3)
Definition pg_list.h:248
#define list_make2(x1, x2)
Definition pg_list.h:246
void resetStringInfo(StringInfo str)
Definition stringinfo.c:126
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242
void initStringInfo(StringInfo str)
Definition stringinfo.c:97

References _, append_tuple_value_detail(), appendStringInfo(), appendStringInfoChar(), appendStringInfoString(), Assert, CheckRelationOidLockedByMe(), CT_DELETE_MISSING, CT_DELETE_ORIGIN_DIFFERS, CT_INSERT_EXISTS, CT_MULTIPLE_UNIQUE_CONFLICTS, CT_UPDATE_DELETED, CT_UPDATE_EXISTS, CT_UPDATE_MISSING, CT_UPDATE_ORIGIN_DIFFERS, StringInfoData::data, fb(), get_rel_name(), get_tuple_desc(), initStringInfo(), InvalidReplOriginId, StringInfoData::len, list_make1, list_make2, list_make3, OidIsValid, replorigin_by_oid(), resetStringInfo(), RowExclusiveLock, timestamptz_to_str(), and type.

Referenced by ReportApplyConflict().

◆ get_tuple_desc()

static void get_tuple_desc ( EState estate,
ResultRelInfo relinfo,
ConflictType  type,
char **  key_desc,
TupleTableSlot localslot,
char **  local_desc,
TupleTableSlot remoteslot,
char **  remote_desc,
TupleTableSlot searchslot,
char **  search_desc,
Oid  indexoid 
)
static

Definition at line 673 of file conflict.c.

679{
680 Relation localrel = relinfo->ri_RelationDesc;
681 Oid relid = RelationGetRelid(localrel);
682 TupleDesc tupdesc = RelationGetDescr(localrel);
683 char *desc = NULL;
684
687
688 /*
689 * Report the conflicting key values in the case of a unique constraint
690 * violation.
691 */
694 {
695 Assert(OidIsValid(indexoid) && localslot);
696
697 desc = build_index_value_desc(estate, localrel, localslot,
698 indexoid);
699
700 if (desc)
701 *key_desc = psprintf(_("key %s"), desc);
702 }
703
704 if (localslot)
705 {
706 /*
707 * The 'modifiedCols' only applies to the new tuple, hence we pass
708 * NULL for the local row.
709 */
710 desc = ExecBuildSlotValueDescription(relid, localslot, tupdesc,
711 NULL, 64);
712
713 if (desc)
714 *local_desc = psprintf(_("local row %s"), desc);
715 }
716
717 if (remoteslot)
718 {
720
721 /*
722 * Although logical replication doesn't maintain the bitmap for the
723 * columns being inserted, we still use it to create 'modifiedCols'
724 * for consistency with other calls to ExecBuildSlotValueDescription.
725 *
726 * Note that generated columns are formed locally on the subscriber.
727 */
729 ExecGetUpdatedCols(relinfo, estate));
731 tupdesc, modifiedCols,
732 64);
733
734 if (desc)
735 *remote_desc = psprintf(_("remote row %s"), desc);
736 }
737
738 if (searchslot)
739 {
740 /*
741 * Note that while index other than replica identity may be used (see
742 * IsIndexUsableForReplicaIdentityFull for details) to find the tuple
743 * when applying update or delete, such an index scan may not result
744 * in a unique tuple and we still compare the complete tuple in such
745 * cases, thus such indexes are not used here.
746 */
748
750
751 /*
752 * If the table has a valid replica identity index, build the index
753 * key value string. Otherwise, construct the full tuple value for
754 * REPLICA IDENTITY FULL cases.
755 */
757 desc = build_index_value_desc(estate, localrel, searchslot, replica_index);
758 else
759 desc = ExecBuildSlotValueDescription(relid, searchslot, tupdesc, NULL, 64);
760
761 if (desc)
762 {
764 *search_desc = psprintf(_("replica identity %s"), desc);
765 else
766 *search_desc = psprintf(_("replica identity full %s"), desc);
767 }
768 }
769}
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:252
static char * build_index_value_desc(EState *estate, Relation localrel, TupleTableSlot *slot, Oid indexoid)
Definition conflict.c:779
char * ExecBuildSlotValueDescription(Oid reloid, TupleTableSlot *slot, TupleDesc tupdesc, Bitmapset *modifiedCols, int maxfieldlen)
Definition execMain.c:2457
Bitmapset * ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate)
Definition execUtils.c:1387
Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition execUtils.c:1408
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
#define RelationGetRelid(relation)
Definition rel.h:516
#define RelationGetDescr(relation)
Definition rel.h:542
Oid GetRelationIdentityOrPK(Relation rel)
Definition relation.c:904

References _, Assert, bms_union(), build_index_value_desc(), CT_INSERT_EXISTS, CT_MULTIPLE_UNIQUE_CONFLICTS, CT_UPDATE_EXISTS, ExecBuildSlotValueDescription(), ExecGetInsertedCols(), ExecGetUpdatedCols(), fb(), GetRelationIdentityOrPK(), OidIsValid, psprintf(), RelationGetDescr, RelationGetRelid, and type.

Referenced by errdetail_apply_conflict().

◆ GetConflictLogDest()

ConflictLogDest GetConflictLogDest ( const char dest)

Definition at line 210 of file conflict.c.

211{
212 /* NULL defaults to LOG. */
213 if (dest == NULL || pg_strcasecmp(dest, "log") == 0)
215
216 if (pg_strcasecmp(dest, "table") == 0)
218
219 if (pg_strcasecmp(dest, "all") == 0)
221
222 /* Unrecognized string. */
225 errmsg("unrecognized conflict_log_destination value: \"%s\"", dest),
226 errhint("Valid values are \"log\", \"table\", and \"all\".")));
227}
@ CONFLICT_LOG_DEST_TABLE
Definition conflict.h:92
@ CONFLICT_LOG_DEST_ALL
Definition conflict.h:93
@ CONFLICT_LOG_DEST_LOG
Definition conflict.h:91
int errhint(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
int pg_strcasecmp(const char *s1, const char *s2)

References CONFLICT_LOG_DEST_ALL, CONFLICT_LOG_DEST_LOG, CONFLICT_LOG_DEST_TABLE, ereport, errcode(), errhint(), errmsg, ERROR, fb(), and pg_strcasecmp().

Referenced by AlterSubscription(), and parse_subscription_options().

◆ GetTupleTransactionInfo()

bool GetTupleTransactionInfo ( TupleTableSlot localslot,
TransactionId xmin,
ReplOriginId localorigin,
TimestampTz localts 
)

Definition at line 236 of file conflict.c.

238{
240 bool isnull;
241
243 &isnull);
245 Assert(!isnull);
246
247 /*
248 * The commit timestamp data is not available if track_commit_timestamp is
249 * disabled.
250 */
252 {
254 *localts = 0;
255 return false;
256 }
257
259}
bool track_commit_timestamp
Definition commit_ts.c:121
bool TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts, ReplOriginId *nodeid)
Definition commit_ts.c:283
static TransactionId DatumGetTransactionId(Datum X)
Definition postgres.h:282
#define MinTransactionIdAttributeNumber
Definition sysattr.h:22
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:438

References Assert, DatumGetTransactionId(), fb(), InvalidReplOriginId, MinTransactionIdAttributeNumber, slot_getsysattr(), track_commit_timestamp, and TransactionIdGetCommitTsData().

Referenced by apply_handle_delete_internal(), apply_handle_tuple_routing(), apply_handle_update_internal(), and CheckAndReportConflict().

◆ InitConflictIndexes()

void InitConflictIndexes ( ResultRelInfo relInfo)

Definition at line 312 of file conflict.c.

313{
315
316 for (int i = 0; i < relInfo->ri_NumIndices; i++)
317 {
318 Relation indexRelation = relInfo->ri_IndexRelationDescs[i];
319
320 if (indexRelation == NULL)
321 continue;
322
323 /* Detect conflict only for unique indexes */
324 if (!relInfo->ri_IndexRelationInfo[i]->ii_Unique)
325 continue;
326
327 /* Don't support conflict detection for deferrable index */
328 if (!indexRelation->rd_index->indimmediate)
329 continue;
330
332 RelationGetRelid(indexRelation));
333 }
334
335 relInfo->ri_onConflictArbiterIndexes = uniqueIndexes;
336}
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
Definition pg_list.h:54
Form_pg_index rd_index
Definition rel.h:192

References fb(), i, lappend_oid(), NIL, RelationData::rd_index, and RelationGetRelid.

Referenced by apply_handle_insert_internal(), apply_handle_tuple_routing(), and apply_handle_update_internal().

◆ ReportApplyConflict()

void ReportApplyConflict ( EState estate,
ResultRelInfo relinfo,
int  elevel,
ConflictType  type,
TupleTableSlot searchslot,
TupleTableSlot remoteslot,
List conflicttuples 
)

Definition at line 277 of file conflict.c.

280{
281 Relation localrel = relinfo->ri_RelationDesc;
283
285
286 /* Form errdetail message by combining conflicting tuples information. */
290 conflicttuple->indexoid,
291 conflicttuple->xmin,
292 conflicttuple->origin,
293 conflicttuple->ts,
294 &err_detail);
295
297
298 ereport(elevel,
300 errmsg("conflict detected on relation \"%s.%s\": conflict=%s",
302 RelationGetRelationName(localrel),
304 errdetail_internal("%s", err_detail.data));
305}
Subscription * MySubscription
Definition worker.c:484
static const char *const ConflictTypeNames[]
Definition conflict.c:84
static void errdetail_apply_conflict(EState *estate, ResultRelInfo *relinfo, ConflictType type, TupleTableSlot *searchslot, TupleTableSlot *localslot, TupleTableSlot *remoteslot, Oid indexoid, TransactionId localxmin, ReplOriginId localorigin, TimestampTz localts, StringInfo err_msg)
Definition conflict.c:403
static int errcode_apply_conflict(ConflictType type)
Definition conflict.c:342
int int errdetail_internal(const char *fmt,...) pg_attribute_printf(1
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
void pgstat_report_subscription_conflict(Oid subid, ConflictType type)
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationGetNamespace(relation)
Definition rel.h:557

References ConflictTypeNames, ereport, errcode_apply_conflict(), errdetail_apply_conflict(), errdetail_internal(), errmsg, fb(), foreach_ptr, get_namespace_name(), initStringInfo(), MySubscription, Subscription::oid, pgstat_report_subscription_conflict(), RelationGetNamespace, RelationGetRelationName, and type.

Referenced by apply_handle_delete_internal(), apply_handle_tuple_routing(), apply_handle_update_internal(), and CheckAndReportConflict().

◆ StaticAssertDecl()

StaticAssertDecl ( lengthof(ConflictLogDestNames = =CONFLICT_LOG_DEST_ALL+1,
"ConflictLogDestNames length mismatch"   
)

Variable Documentation

◆ ConflictLogDestNames

const char* const ConflictLogDestNames[]
Initial value:

Definition at line 34 of file conflict.c.

34 {
35 [CONFLICT_LOG_DEST_LOG] = "log",
36 [CONFLICT_LOG_DEST_TABLE] = "table",
37 [CONFLICT_LOG_DEST_ALL] = "all"
38};

Referenced by AlterSubscription().

◆ ConflictLogSchema

const ConflictLogColumnDef ConflictLogSchema[]
static
Initial value:
= {
{.attname = "relid", .atttypid = OIDOID},
{.attname = "schemaname", .atttypid = TEXTOID},
{.attname = "relname", .atttypid = TEXTOID},
{.attname = "conflict_type", .atttypid = TEXTOID},
{.attname = "remote_xid", .atttypid = XIDOID},
{.attname = "remote_commit_lsn", .atttypid = LSNOID},
{.attname = "remote_commit_ts", .atttypid = TIMESTAMPTZOID},
{.attname = "remote_origin", .atttypid = TEXTOID},
{.attname = "replica_identity_full", .atttypid = BOOLOID},
{.attname = "replica_identity", .atttypid = JSONOID},
{.attname = "remote_tuple", .atttypid = JSONOID},
{.attname = "local_conflicts", .atttypid = JSONARRAYOID}
}

Definition at line 67 of file conflict.c.

67 {
68 {.attname = "relid", .atttypid = OIDOID},
69 {.attname = "schemaname", .atttypid = TEXTOID},
70 {.attname = "relname", .atttypid = TEXTOID},
71 {.attname = "conflict_type", .atttypid = TEXTOID},
72 {.attname = "remote_xid", .atttypid = XIDOID},
73 {.attname = "remote_commit_lsn", .atttypid = LSNOID},
74 {.attname = "remote_commit_ts", .atttypid = TIMESTAMPTZOID},
75 {.attname = "remote_origin", .atttypid = TEXTOID},
76 {.attname = "replica_identity_full", .atttypid = BOOLOID},
77 {.attname = "replica_identity", .atttypid = JSONOID},
78 {.attname = "remote_tuple", .atttypid = JSONOID},
79 {.attname = "local_conflicts", .atttypid = JSONARRAYOID}
80};

Referenced by create_conflict_log_table_tupdesc().

◆ ConflictTypeNames

const char* const ConflictTypeNames[]
static
Initial value:
= {
[CT_INSERT_EXISTS] = "insert_exists",
[CT_UPDATE_ORIGIN_DIFFERS] = "update_origin_differs",
[CT_UPDATE_EXISTS] = "update_exists",
[CT_UPDATE_MISSING] = "update_missing",
[CT_DELETE_ORIGIN_DIFFERS] = "delete_origin_differs",
[CT_UPDATE_DELETED] = "update_deleted",
[CT_DELETE_MISSING] = "delete_missing",
[CT_MULTIPLE_UNIQUE_CONFLICTS] = "multiple_unique_conflicts"
}

Definition at line 84 of file conflict.c.

84 {
85 [CT_INSERT_EXISTS] = "insert_exists",
86 [CT_UPDATE_ORIGIN_DIFFERS] = "update_origin_differs",
87 [CT_UPDATE_EXISTS] = "update_exists",
88 [CT_UPDATE_MISSING] = "update_missing",
89 [CT_DELETE_ORIGIN_DIFFERS] = "delete_origin_differs",
90 [CT_UPDATE_DELETED] = "update_deleted",
91 [CT_DELETE_MISSING] = "delete_missing",
92 [CT_MULTIPLE_UNIQUE_CONFLICTS] = "multiple_unique_conflicts"
93};

Referenced by ReportApplyConflict().