Message wording improvements

Use "row" instead of "tuple" for user-facing information for
logical replication conflicts.
REL_18_STABLE
Peter Eisentraut 2 weeks ago
parent e76738e597
commit 9d115b9e11
  1. 28
      doc/src/sgml/logical-replication.sgml
  2. 6
      src/backend/executor/execReplication.c
  3. 26
      src/backend/replication/logical/conflict.c
  4. 4
      src/include/replication/conflict.h
  5. 4
      src/test/subscription/t/001_rep_changes.pl
  6. 8
      src/test/subscription/t/013_partition.pl
  7. 2
      src/test/subscription/t/029_on_error.pl
  8. 4
      src/test/subscription/t/030_origin.pl
  9. 16
      src/test/subscription/t/035_conflicts.pl

@ -1808,7 +1808,7 @@ Publications:
<term><literal>update_missing</literal></term> <term><literal>update_missing</literal></term>
<listitem> <listitem>
<para> <para>
The tuple to be updated was not found. The update will simply be The row to be updated was not found. The update will simply be
skipped in this scenario. skipped in this scenario.
</para> </para>
</listitem> </listitem>
@ -1829,7 +1829,7 @@ Publications:
<term><literal>delete_missing</literal></term> <term><literal>delete_missing</literal></term>
<listitem> <listitem>
<para> <para>
The tuple to be deleted was not found. The delete will simply be The row to be deleted was not found. The delete will simply be
skipped in this scenario. skipped in this scenario.
</para> </para>
</listitem> </listitem>
@ -1863,8 +1863,8 @@ DETAIL: <replaceable class="parameter">detailed_explanation</replaceable>.
<phrase>where <replaceable class="parameter">detail_values</replaceable> is one of:</phrase> <phrase>where <replaceable class="parameter">detail_values</replaceable> is one of:</phrase>
<literal>Key</literal> (<replaceable>column_name</replaceable> <optional>, ...</optional>)=(<replaceable>column_value</replaceable> <optional>, ...</optional>) <literal>Key</literal> (<replaceable>column_name</replaceable> <optional>, ...</optional>)=(<replaceable>column_value</replaceable> <optional>, ...</optional>)
<literal>existing local tuple</literal> <optional>(<replaceable>column_name</replaceable> <optional>, ...</optional>)=</optional>(<replaceable>column_value</replaceable> <optional>, ...</optional>) <literal>existing local row</literal> <optional>(<replaceable>column_name</replaceable> <optional>, ...</optional>)=</optional>(<replaceable>column_value</replaceable> <optional>, ...</optional>)
<literal>remote tuple</literal> <optional>(<replaceable>column_name</replaceable> <optional>, ...</optional>)=</optional>(<replaceable>column_value</replaceable> <optional>, ...</optional>) <literal>remote row</literal> <optional>(<replaceable>column_name</replaceable> <optional>, ...</optional>)=</optional>(<replaceable>column_value</replaceable> <optional>, ...</optional>)
<literal>replica identity</literal> {(<replaceable>column_name</replaceable> <optional>, ...</optional>)=(<replaceable>column_value</replaceable> <optional>, ...</optional>) | full <optional>(<replaceable>column_name</replaceable> <optional>, ...</optional>)=</optional>(<replaceable>column_value</replaceable> <optional>, ...</optional>)} <literal>replica identity</literal> {(<replaceable>column_name</replaceable> <optional>, ...</optional>)=(<replaceable>column_value</replaceable> <optional>, ...</optional>) | full <optional>(<replaceable>column_name</replaceable> <optional>, ...</optional>)=</optional>(<replaceable>column_value</replaceable> <optional>, ...</optional>)}
</synopsis> </synopsis>
@ -1898,32 +1898,32 @@ DETAIL: <replaceable class="parameter">detailed_explanation</replaceable>.
<para> <para>
<replaceable class="parameter">detailed_explanation</replaceable> includes <replaceable class="parameter">detailed_explanation</replaceable> includes
the origin, transaction ID, and commit timestamp of the transaction that the origin, transaction ID, and commit timestamp of the transaction that
modified the existing local tuple, if available. modified the existing local row, if available.
</para> </para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
The <literal>Key</literal> section includes the key values of the local The <literal>Key</literal> section includes the key values of the local
tuple that violated a unique constraint for row that violated a unique constraint for
<literal>insert_exists</literal>, <literal>update_exists</literal> or <literal>insert_exists</literal>, <literal>update_exists</literal> or
<literal>multiple_unique_conflicts</literal> conflicts. <literal>multiple_unique_conflicts</literal> conflicts.
</para> </para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
The <literal>existing local tuple</literal> section includes the local The <literal>existing local row</literal> section includes the local
tuple if its origin differs from the remote tuple for row if its origin differs from the remote row for
<literal>update_origin_differs</literal> or <literal>delete_origin_differs</literal> <literal>update_origin_differs</literal> or <literal>delete_origin_differs</literal>
conflicts, or if the key value conflicts with the remote tuple for conflicts, or if the key value conflicts with the remote row for
<literal>insert_exists</literal>, <literal>update_exists</literal> or <literal>insert_exists</literal>, <literal>update_exists</literal> or
<literal>multiple_unique_conflicts</literal> conflicts. <literal>multiple_unique_conflicts</literal> conflicts.
</para> </para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
The <literal>remote tuple</literal> section includes the new tuple from The <literal>remote row</literal> section includes the new row from
the remote insert or update operation that caused the conflict. Note that the remote insert or update operation that caused the conflict. Note that
for an update operation, the column value of the new tuple will be null for an update operation, the column value of the new row will be null
if the value is unchanged and toasted. if the value is unchanged and toasted.
</para> </para>
</listitem> </listitem>
@ -1931,7 +1931,7 @@ DETAIL: <replaceable class="parameter">detailed_explanation</replaceable>.
<para> <para>
The <literal>replica identity</literal> section includes the replica The <literal>replica identity</literal> section includes the replica
identity key values that were used to search for the existing local identity key values that were used to search for the existing local
tuple to be updated or deleted. This may include the full tuple value row to be updated or deleted. This may include the full row value
if the local relation is marked with if the local relation is marked with
<link linkend="sql-altertable-replica-identity-full"><literal>REPLICA IDENTITY FULL</literal></link>. <link linkend="sql-altertable-replica-identity-full"><literal>REPLICA IDENTITY FULL</literal></link>.
</para> </para>
@ -1939,7 +1939,7 @@ DETAIL: <replaceable class="parameter">detailed_explanation</replaceable>.
<listitem> <listitem>
<para> <para>
<replaceable class="parameter">column_name</replaceable> is the column name. <replaceable class="parameter">column_name</replaceable> is the column name.
For <literal>existing local tuple</literal>, <literal>remote tuple</literal>, For <literal>existing local row</literal>, <literal>remote row</literal>,
and <literal>replica identity full</literal> cases, column names are and <literal>replica identity full</literal> cases, column names are
logged only if the user lacks the privilege to access all columns of logged only if the user lacks the privilege to access all columns of
the table. If column names are present, they appear in the same order the table. If column names are present, they appear in the same order
@ -1996,7 +1996,7 @@ DETAIL: <replaceable class="parameter">detailed_explanation</replaceable>.
<screen> <screen>
ERROR: conflict detected on relation "public.test": conflict=insert_exists ERROR: conflict detected on relation "public.test": conflict=insert_exists
DETAIL: Key already exists in unique index "t_pkey", which was modified locally in transaction 740 at 2024-06-26 10:47:04.727375+08. DETAIL: Key already exists in unique index "t_pkey", which was modified locally in transaction 740 at 2024-06-26 10:47:04.727375+08.
Key (c)=(1); existing local tuple (1, 'local'); remote tuple (1, 'remote'). Key (c)=(1); existing local row (1, 'local'); remote row (1, 'remote').
CONTEXT: processing remote data for replication origin "pg_16395" during "INSERT" for replication target relation "public.test" in transaction 725 finished at 0/14C0378 CONTEXT: processing remote data for replication origin "pg_16395" during "INSERT" for replication target relation "public.test" in transaction 725 finished at 0/14C0378
</screen> </screen>
The LSN of the transaction that contains the change violating the constraint and The LSN of the transaction that contains the change violating the constraint and

@ -609,10 +609,10 @@ ExecSimpleRelationInsert(ResultRelInfo *resultRelInfo,
conflictindexes, false); conflictindexes, false);
/* /*
* Checks the conflict indexes to fetch the conflicting local tuple * Checks the conflict indexes to fetch the conflicting local row and
* and reports the conflict. We perform this check here, instead of * reports the conflict. We perform this check here, instead of
* performing an additional index scan before the actual insertion and * performing an additional index scan before the actual insertion and
* reporting the conflict if any conflicting tuples are found. This is * reporting the conflict if any conflicting rows are found. This is
* to avoid the overhead of executing the extra scan for each INSERT * to avoid the overhead of executing the extra scan for each INSERT
* operation, even when no conflict arises, which could introduce * operation, even when no conflict arises, which could introduce
* significant overhead to replication, particularly in cases where * significant overhead to replication, particularly in cases where

@ -54,7 +54,7 @@ static char *build_index_value_desc(EState *estate, Relation localrel,
/* /*
* Get the xmin and commit timestamp data (origin and timestamp) associated * Get the xmin and commit timestamp data (origin and timestamp) associated
* with the provided local tuple. * with the provided local row.
* *
* Return true if the commit timestamp data was found, false otherwise. * Return true if the commit timestamp data was found, false otherwise.
*/ */
@ -88,12 +88,12 @@ GetTupleTransactionInfo(TupleTableSlot *localslot, TransactionId *xmin,
* This function is used to report a conflict while applying replication * This function is used to report a conflict while applying replication
* changes. * changes.
* *
* 'searchslot' should contain the tuple used to search the local tuple to be * 'searchslot' should contain the tuple used to search the local row to be
* updated or deleted. * updated or deleted.
* *
* 'remoteslot' should contain the remote new tuple, if any. * 'remoteslot' should contain the remote new tuple, if any.
* *
* conflicttuples is a list of local tuples that caused the conflict and the * conflicttuples is a list of local rows that caused the conflict and the
* conflict related information. See ConflictTupleInfo. * conflict related information. See ConflictTupleInfo.
* *
* The caller must ensure that all the indexes passed in ConflictTupleInfo are * The caller must ensure that all the indexes passed in ConflictTupleInfo are
@ -189,9 +189,9 @@ errcode_apply_conflict(ConflictType type)
* *
* The DETAIL line comprises of two parts: * The DETAIL line comprises of two parts:
* 1. Explanation of the conflict type, including the origin and commit * 1. Explanation of the conflict type, including the origin and commit
* timestamp of the existing local tuple. * timestamp of the existing local row.
* 2. Display of conflicting key, existing local tuple, remote new tuple, and * 2. Display of conflicting key, existing local row, remote new row, and
* replica identity columns, if any. The remote old tuple is excluded as its * replica identity columns, if any. The remote old row is excluded as its
* information is covered in the replica identity columns. * information is covered in the replica identity columns.
*/ */
static void static void
@ -291,7 +291,7 @@ errdetail_apply_conflict(EState *estate, ResultRelInfo *relinfo,
localslot, remoteslot, indexoid); localslot, remoteslot, indexoid);
/* /*
* Next, append the key values, existing local tuple, remote tuple and * Next, append the key values, existing local row, remote row, and
* replica identity columns after the message. * replica identity columns after the message.
*/ */
if (val_desc) if (val_desc)
@ -309,7 +309,7 @@ errdetail_apply_conflict(EState *estate, ResultRelInfo *relinfo,
/* /*
* Helper function to build the additional details for conflicting key, * Helper function to build the additional details for conflicting key,
* existing local tuple, remote tuple, and replica identity columns. * existing local row, remote row, and replica identity columns.
* *
* If the return value is NULL, it indicates that the current user lacks * If the return value is NULL, it indicates that the current user lacks
* permissions to view the columns involved. * permissions to view the columns involved.
@ -351,7 +351,7 @@ build_tuple_value_details(EState *estate, ResultRelInfo *relinfo,
{ {
/* /*
* The 'modifiedCols' only applies to the new tuple, hence we pass * The 'modifiedCols' only applies to the new tuple, hence we pass
* NULL for the existing local tuple. * NULL for the existing local row.
*/ */
desc = ExecBuildSlotValueDescription(relid, localslot, tupdesc, desc = ExecBuildSlotValueDescription(relid, localslot, tupdesc,
NULL, 64); NULL, 64);
@ -361,12 +361,12 @@ build_tuple_value_details(EState *estate, ResultRelInfo *relinfo,
if (tuple_value.len > 0) if (tuple_value.len > 0)
{ {
appendStringInfoString(&tuple_value, "; "); appendStringInfoString(&tuple_value, "; ");
appendStringInfo(&tuple_value, _("existing local tuple %s"), appendStringInfo(&tuple_value, _("existing local row %s"),
desc); desc);
} }
else else
{ {
appendStringInfo(&tuple_value, _("Existing local tuple %s"), appendStringInfo(&tuple_value, _("Existing local row %s"),
desc); desc);
} }
} }
@ -393,11 +393,11 @@ build_tuple_value_details(EState *estate, ResultRelInfo *relinfo,
if (tuple_value.len > 0) if (tuple_value.len > 0)
{ {
appendStringInfoString(&tuple_value, "; "); appendStringInfoString(&tuple_value, "; ");
appendStringInfo(&tuple_value, _("remote tuple %s"), desc); appendStringInfo(&tuple_value, _("remote row %s"), desc);
} }
else else
{ {
appendStringInfo(&tuple_value, _("Remote tuple %s"), desc); appendStringInfo(&tuple_value, _("Remote row %s"), desc);
} }
} }
} }

@ -54,7 +54,7 @@ typedef enum
#define CONFLICT_NUM_TYPES (CT_MULTIPLE_UNIQUE_CONFLICTS + 1) #define CONFLICT_NUM_TYPES (CT_MULTIPLE_UNIQUE_CONFLICTS + 1)
/* /*
* Information for the existing local tuple that caused the conflict. * Information for the existing local row that caused the conflict.
*/ */
typedef struct ConflictTupleInfo typedef struct ConflictTupleInfo
{ {
@ -66,7 +66,7 @@ typedef struct ConflictTupleInfo
* the conflict */ * the conflict */
RepOriginId origin; /* origin identifier of the modification */ RepOriginId origin; /* origin identifier of the modification */
TimestampTz ts; /* timestamp of when the modification on the TimestampTz ts; /* timestamp of when the modification on the
* conflicting local tuple occurred */ * conflicting local row occurred */
} ConflictTupleInfo; } ConflictTupleInfo;
extern bool GetTupleTransactionInfo(TupleTableSlot *localslot, extern bool GetTupleTransactionInfo(TupleTableSlot *localslot,

@ -365,10 +365,10 @@ $node_publisher->wait_for_catchup('tap_sub');
my $logfile = slurp_file($node_subscriber->logfile, $log_location); my $logfile = slurp_file($node_subscriber->logfile, $log_location);
ok( $logfile =~ ok( $logfile =~
qr/conflict detected on relation "public.tab_full_pk": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated.*\n.*Remote tuple \(1, quux\); replica identity \(a\)=\(1\)/m, qr/conflict detected on relation "public.tab_full_pk": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated.*\n.*Remote row \(1, quux\); replica identity \(a\)=\(1\)/m,
'update target row is missing'); 'update target row is missing');
ok( $logfile =~ ok( $logfile =~
qr/conflict detected on relation "public.tab_full": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated.*\n.*Remote tuple \(26\); replica identity full \(25\)/m, qr/conflict detected on relation "public.tab_full": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated.*\n.*Remote row \(26\); replica identity full \(25\)/m,
'update target row is missing'); 'update target row is missing');
ok( $logfile =~ ok( $logfile =~
qr/conflict detected on relation "public.tab_full_pk": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted.*\n.*Replica identity \(a\)=\(2\)/m, qr/conflict detected on relation "public.tab_full_pk": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted.*\n.*Replica identity \(a\)=\(2\)/m,

@ -368,7 +368,7 @@ $node_publisher->wait_for_catchup('sub2');
my $logfile = slurp_file($node_subscriber1->logfile(), $log_location); my $logfile = slurp_file($node_subscriber1->logfile(), $log_location);
ok( $logfile =~ ok( $logfile =~
qr/conflict detected on relation "public.tab1_2_2": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated.*\n.*Remote tuple \(null, 4, quux\); replica identity \(a\)=\(4\)/, qr/conflict detected on relation "public.tab1_2_2": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated.*\n.*Remote row \(null, 4, quux\); replica identity \(a\)=\(4\)/,
'update target row is missing in tab1_2_2'); 'update target row is missing in tab1_2_2');
ok( $logfile =~ ok( $logfile =~
qr/conflict detected on relation "public.tab1_1": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted.*\n.*Replica identity \(a\)=\(1\)/, qr/conflict detected on relation "public.tab1_1": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted.*\n.*Replica identity \(a\)=\(1\)/,
@ -781,7 +781,7 @@ $node_publisher->wait_for_catchup('sub2');
$logfile = slurp_file($node_subscriber1->logfile(), $log_location); $logfile = slurp_file($node_subscriber1->logfile(), $log_location);
ok( $logfile =~ ok( $logfile =~
qr/conflict detected on relation "public.tab2_1": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated.*\n.*Remote tuple \(pub_tab2, quux, 5\); replica identity \(a\)=\(5\)/, qr/conflict detected on relation "public.tab2_1": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated.*\n.*Remote row \(pub_tab2, quux, 5\); replica identity \(a\)=\(5\)/,
'update target row is missing in tab2_1'); 'update target row is missing in tab2_1');
ok( $logfile =~ ok( $logfile =~
qr/conflict detected on relation "public.tab2_1": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted.*\n.*Replica identity \(a\)=\(1\)/, qr/conflict detected on relation "public.tab2_1": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted.*\n.*Replica identity \(a\)=\(1\)/,
@ -802,8 +802,8 @@ $node_publisher->wait_for_catchup('sub_viaroot');
$logfile = slurp_file($node_subscriber1->logfile(), $log_location); $logfile = slurp_file($node_subscriber1->logfile(), $log_location);
ok( $logfile =~ ok( $logfile =~
qr/conflict detected on relation "public.tab2_1": conflict=update_origin_differs.*\n.*DETAIL:.* Updating the row that was modified locally in transaction [0-9]+ at .*\n.*Existing local tuple \(yyy, null, 3\); remote tuple \(pub_tab2, quux, 3\); replica identity \(a\)=\(3\)/, qr/conflict detected on relation "public.tab2_1": conflict=update_origin_differs.*\n.*DETAIL:.* Updating the row that was modified locally in transaction [0-9]+ at .*\n.*Existing local row \(yyy, null, 3\); remote row \(pub_tab2, quux, 3\); replica identity \(a\)=\(3\)/,
'updating a tuple that was modified by a different origin'); 'updating a row that was modified by a different origin');
# The remaining tests no longer test conflict detection. # The remaining tests no longer test conflict detection.
$node_subscriber1->append_conf('postgresql.conf', $node_subscriber1->append_conf('postgresql.conf',

@ -30,7 +30,7 @@ sub test_skip_lsn
# ERROR with its CONTEXT when retrieving this information. # ERROR with its CONTEXT when retrieving this information.
my $contents = slurp_file($node_subscriber->logfile, $offset); my $contents = slurp_file($node_subscriber->logfile, $offset);
$contents =~ $contents =~
qr/conflict detected on relation "public.tbl".*\n.*DETAIL:.* Key already exists in unique index "tbl_pkey", modified by .*origin.* transaction \d+ at .*\n.*Key \(i\)=\(\d+\); existing local tuple .*; remote tuple .*\n.*CONTEXT:.* for replication target relation "public.tbl" in transaction \d+, finished at ([[:xdigit:]]+\/[[:xdigit:]]+)/m qr/conflict detected on relation "public.tbl".*\n.*DETAIL:.* Key already exists in unique index "tbl_pkey", modified by .*origin.* transaction \d+ at .*\n.*Key \(i\)=\(\d+\); existing local row .*; remote row .*\n.*CONTEXT:.* for replication target relation "public.tbl" in transaction \d+, finished at ([[:xdigit:]]+\/[[:xdigit:]]+)/m
or die "could not get error-LSN"; or die "could not get error-LSN";
my $lsn = $1; my $lsn = $1;

@ -163,7 +163,7 @@ is($result, qq(32), 'The node_A data replicated to node_B');
$node_C->safe_psql('postgres', "UPDATE tab SET a = 33 WHERE a = 32;"); $node_C->safe_psql('postgres', "UPDATE tab SET a = 33 WHERE a = 32;");
$node_B->wait_for_log( $node_B->wait_for_log(
qr/conflict detected on relation "public.tab": conflict=update_origin_differs.*\n.*DETAIL:.* Updating the row that was modified by a different origin ".*" in transaction [0-9]+ at .*\n.*Existing local tuple \(32\); remote tuple \(33\); replica identity \(a\)=\(32\)/ qr/conflict detected on relation "public.tab": conflict=update_origin_differs.*\n.*DETAIL:.* Updating the row that was modified by a different origin ".*" in transaction [0-9]+ at .*\n.*Existing local row \(32\); remote row \(33\); replica identity \(a\)=\(32\)/
); );
$node_B->safe_psql('postgres', "DELETE FROM tab;"); $node_B->safe_psql('postgres', "DELETE FROM tab;");
@ -179,7 +179,7 @@ is($result, qq(33), 'The node_A data replicated to node_B');
$node_C->safe_psql('postgres', "DELETE FROM tab WHERE a = 33;"); $node_C->safe_psql('postgres', "DELETE FROM tab WHERE a = 33;");
$node_B->wait_for_log( $node_B->wait_for_log(
qr/conflict detected on relation "public.tab": conflict=delete_origin_differs.*\n.*DETAIL:.* Deleting the row that was modified by a different origin ".*" in transaction [0-9]+ at .*\n.*Existing local tuple \(33\); replica identity \(a\)=\(33\)/ qr/conflict detected on relation "public.tab": conflict=delete_origin_differs.*\n.*DETAIL:.* Deleting the row that was modified by a different origin ".*" in transaction [0-9]+ at .*\n.*Existing local row \(33\); replica identity \(a\)=\(33\)/
); );
# The remaining tests no longer test conflict detection. # The remaining tests no longer test conflict detection.

@ -79,11 +79,11 @@ $node_publisher->safe_psql('postgres',
$node_subscriber->wait_for_log( $node_subscriber->wait_for_log(
qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.* qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.*
.*Key already exists in unique index \"conf_tab_pkey\".* .*Key already exists in unique index \"conf_tab_pkey\".*
.*Key \(a\)=\(2\); existing local tuple \(2, 2, 2\); remote tuple \(2, 3, 4\).* .*Key \(a\)=\(2\); existing local row \(2, 2, 2\); remote row \(2, 3, 4\).*
.*Key already exists in unique index \"conf_tab_b_key\".* .*Key already exists in unique index \"conf_tab_b_key\".*
.*Key \(b\)=\(3\); existing local tuple \(3, 3, 3\); remote tuple \(2, 3, 4\).* .*Key \(b\)=\(3\); existing local row \(3, 3, 3\); remote row \(2, 3, 4\).*
.*Key already exists in unique index \"conf_tab_c_key\".* .*Key already exists in unique index \"conf_tab_c_key\".*
.*Key \(c\)=\(4\); existing local tuple \(4, 4, 4\); remote tuple \(2, 3, 4\)./, .*Key \(c\)=\(4\); existing local row \(4, 4, 4\); remote row \(2, 3, 4\)./,
$log_offset); $log_offset);
pass('multiple_unique_conflicts detected during insert'); pass('multiple_unique_conflicts detected during insert');
@ -111,11 +111,11 @@ $node_publisher->safe_psql('postgres',
$node_subscriber->wait_for_log( $node_subscriber->wait_for_log(
qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.* qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.*
.*Key already exists in unique index \"conf_tab_pkey\".* .*Key already exists in unique index \"conf_tab_pkey\".*
.*Key \(a\)=\(6\); existing local tuple \(6, 6, 6\); remote tuple \(6, 7, 8\).* .*Key \(a\)=\(6\); existing local row \(6, 6, 6\); remote row \(6, 7, 8\).*
.*Key already exists in unique index \"conf_tab_b_key\".* .*Key already exists in unique index \"conf_tab_b_key\".*
.*Key \(b\)=\(7\); existing local tuple \(7, 7, 7\); remote tuple \(6, 7, 8\).* .*Key \(b\)=\(7\); existing local row \(7, 7, 7\); remote row \(6, 7, 8\).*
.*Key already exists in unique index \"conf_tab_c_key\".* .*Key already exists in unique index \"conf_tab_c_key\".*
.*Key \(c\)=\(8\); existing local tuple \(8, 8, 8\); remote tuple \(6, 7, 8\)./, .*Key \(c\)=\(8\); existing local row \(8, 8, 8\); remote row \(6, 7, 8\)./,
$log_offset); $log_offset);
pass('multiple_unique_conflicts detected during update'); pass('multiple_unique_conflicts detected during update');
@ -139,9 +139,9 @@ $node_publisher->safe_psql('postgres',
$node_subscriber->wait_for_log( $node_subscriber->wait_for_log(
qr/conflict detected on relation \"public.conf_tab_2_p1\": conflict=multiple_unique_conflicts.* qr/conflict detected on relation \"public.conf_tab_2_p1\": conflict=multiple_unique_conflicts.*
.*Key already exists in unique index \"conf_tab_2_p1_pkey\".* .*Key already exists in unique index \"conf_tab_2_p1_pkey\".*
.*Key \(a\)=\(55\); existing local tuple \(55, 2, 3\); remote tuple \(55, 2, 3\).* .*Key \(a\)=\(55\); existing local row \(55, 2, 3\); remote row \(55, 2, 3\).*
.*Key already exists in unique index \"conf_tab_2_p1_a_b_key\".* .*Key already exists in unique index \"conf_tab_2_p1_a_b_key\".*
.*Key \(a, b\)=\(55, 2\); existing local tuple \(55, 2, 3\); remote tuple \(55, 2, 3\)./, .*Key \(a, b\)=\(55, 2\); existing local row \(55, 2, 3\); remote row \(55, 2, 3\)./,
$log_offset); $log_offset);
pass('multiple_unique_conflicts detected on a leaf partition during insert'); pass('multiple_unique_conflicts detected on a leaf partition during insert');

Loading…
Cancel
Save