From 005bdc7582f014efe1aaad7c97c1b1cb48eb63e9 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Date: Fri, 16 Jan 2026 18:05:46 +0900
Subject: [PATCH v4 2/2] Fix primary error message for conflicts

---
 doc/src/sgml/logical-replication.sgml      | 10 +++++-----
 src/backend/replication/logical/conflict.c |  6 +++---
 src/test/subscription/t/001_rep_changes.pl |  6 +++---
 src/test/subscription/t/013_partition.pl   | 14 +++++++-------
 src/test/subscription/t/029_on_error.pl    |  2 +-
 src/test/subscription/t/030_origin.pl      |  4 ++--
 src/test/subscription/t/035_conflicts.pl   | 14 +++++++-------
 7 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml
index 403a9032f11..9f8a7fb0b3d 100644
--- a/doc/src/sgml/logical-replication.sgml
+++ b/doc/src/sgml/logical-replication.sgml
@@ -2120,7 +2120,7 @@ Publications:
   <para>
    The log format for logical replication conflicts is as follows:
 <synopsis>
-LOG:  conflict detected on relation "<replaceable>schemaname</replaceable>.<replaceable>tablename</replaceable>": conflict=<replaceable>conflict_type</replaceable>
+LOG:  conflict <replaceable>conflict_type</replaceable> detected on relation "<replaceable>schemaname</replaceable>.<replaceable>tablename</replaceable>"
 DETAIL:  <replaceable class="parameter">detailed_explanation</replaceable>[: <replaceable class="parameter">detail_values</replaceable> [, ... ]].
 
 <phrase>where <replaceable class="parameter">detail_values</replaceable> is one of:</phrase>
@@ -2139,14 +2139,14 @@ DETAIL:  <replaceable class="parameter">detailed_explanation</replaceable>[: <re
        <itemizedlist>
         <listitem>
          <para>
-         <replaceable>schemaname</replaceable>.<replaceable>tablename</replaceable>
-         identifies the local relation involved in the conflict.
+         <replaceable>conflict_type</replaceable> is the type of conflict that occurred
+         (e.g., <literal>insert_exists</literal>, <literal>update_exists</literal>).
          </para>
         </listitem>
         <listitem>
          <para>
-         <replaceable>conflict_type</replaceable> is the type of conflict that occurred
-         (e.g., <literal>insert_exists</literal>, <literal>update_exists</literal>).
+         <replaceable>schemaname</replaceable>.<replaceable>tablename</replaceable>
+         identifies the local relation involved in the conflict.
          </para>
         </listitem>
        </itemizedlist>
diff --git a/src/backend/replication/logical/conflict.c b/src/backend/replication/logical/conflict.c
index 0aab1763511..8335628a23c 100644
--- a/src/backend/replication/logical/conflict.c
+++ b/src/backend/replication/logical/conflict.c
@@ -124,10 +124,10 @@ ReportApplyConflict(EState *estate, ResultRelInfo *relinfo, int elevel,
 
 	ereport(elevel,
 			errcode_apply_conflict(type),
-			errmsg("conflict detected on relation \"%s.%s\": conflict=%s",
+			errmsg("conflict %s detected on relation \"%s.%s\"",
+				   ConflictTypeNames[type],
 				   get_namespace_name(RelationGetNamespace(localrel)),
-				   RelationGetRelationName(localrel),
-				   ConflictTypeNames[type]),
+				   RelationGetRelationName(localrel)),
 			errdetail_internal("%s", err_detail.data));
 }
 
diff --git a/src/test/subscription/t/001_rep_changes.pl b/src/test/subscription/t/001_rep_changes.pl
index d7e62e4d488..466e5ba46cb 100644
--- a/src/test/subscription/t/001_rep_changes.pl
+++ b/src/test/subscription/t/001_rep_changes.pl
@@ -366,15 +366,15 @@ $node_publisher->wait_for_catchup('tap_sub');
 my $logfile = slurp_file($node_subscriber->logfile, $log_location);
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab_full_pk": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated: remote row \(1, quux\), replica identity \(a\)=\(1\)/m,
+	qr/conflict update_missing detected on relation "public.tab_full_pk".*\n.*DETAIL:.* Could not find the row to be updated: remote row \(1, quux\), replica identity \(a\)=\(1\)/m,
 	'update target row is missing');
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab_full": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated: remote row \(26\), replica identity full \(25\)/m,
+	qr/conflict update_missing detected on relation "public.tab_full".*\n.*DETAIL:.* Could not find the row to be updated: remote row \(26\), replica identity full \(25\)/m,
 	'update target row is missing');
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab_full_pk": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(2\)/m,
+	qr/conflict delete_missing detected on relation "public.tab_full_pk".*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(2\)/m,
 	'delete target row is missing');
 
 $node_subscriber->append_conf('postgresql.conf',
diff --git a/src/test/subscription/t/013_partition.pl b/src/test/subscription/t/013_partition.pl
index 234d4f003b7..64fa09cf08b 100644
--- a/src/test/subscription/t/013_partition.pl
+++ b/src/test/subscription/t/013_partition.pl
@@ -369,19 +369,19 @@ $node_publisher->wait_for_catchup('sub2');
 my $logfile = slurp_file($node_subscriber1->logfile(), $log_location);
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab1_2_2": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated: remote row \(null, 4, quux\), replica identity \(a\)=\(4\)/,
+	qr/conflict update_missing detected on relation "public.tab1_2_2".*\n.*DETAIL:.* Could not find the row to be updated: remote row \(null, 4, quux\), replica identity \(a\)=\(4\)/,
 	'update target row is missing in tab1_2_2');
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab1_1": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(1\)/,
+	qr/conflict delete_missing detected on relation "public.tab1_1".*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(1\)/,
 	'delete target row is missing in tab1_1');
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab1_2_2": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(4\)/,
+	qr/conflict delete_missing detected on relation "public.tab1_2_2".*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(4\)/,
 	'delete target row is missing in tab1_2_2');
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab1_def": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(10\)/,
+	qr/conflict delete_missing detected on relation "public.tab1_def".*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(10\)/,
 	'delete target row is missing in tab1_def');
 
 # Tests for replication using root table identity and schema
@@ -786,11 +786,11 @@ $node_publisher->wait_for_catchup('sub2');
 $logfile = slurp_file($node_subscriber1->logfile(), $log_location);
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab2_1": conflict=update_missing.*\n.*DETAIL:.* Could not find the row to be updated: remote row \(pub_tab2, quux, 5\), replica identity \(a\)=\(5\)/,
+	qr/conflict update_missing detected on relation "public.tab2_1".*\n.*DETAIL:.* Could not find the row to be updated: remote row \(pub_tab2, quux, 5\), replica identity \(a\)=\(5\)/,
 	'update target row is missing in tab2_1');
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab2_1": conflict=delete_missing.*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(1\)/,
+	qr/conflict delete_missing detected on relation "public.tab2_1".*\n.*DETAIL:.* Could not find the row to be deleted: replica identity \(a\)=\(1\)/,
 	'delete target row is missing in tab2_1');
 
 # Enable the track_commit_timestamp to detect the conflict when attempting
@@ -809,7 +809,7 @@ $node_publisher->wait_for_catchup('sub_viaroot');
 $logfile = slurp_file($node_subscriber1->logfile(), $log_location);
 like(
 	$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 .*: local row \(yyy, null, 3\), remote row \(pub_tab2, quux, 3\), replica identity \(a\)=\(3\)./,
+	qr/conflict update_origin_differs detected on relation "public.tab2_1".*\n.*DETAIL:.* Updating the row that was modified locally in transaction [0-9]+ at .*: local row \(yyy, null, 3\), remote row \(pub_tab2, quux, 3\), replica identity \(a\)=\(3\)./,
 	'updating a row that was modified by a different origin');
 
 # The remaining tests no longer test conflict detection.
diff --git a/src/test/subscription/t/029_on_error.pl b/src/test/subscription/t/029_on_error.pl
index 7d68759b6cd..5232c6f8d93 100644
--- a/src/test/subscription/t/029_on_error.pl
+++ b/src/test/subscription/t/029_on_error.pl
@@ -30,7 +30,7 @@ sub test_skip_lsn
 	# ERROR with its CONTEXT when retrieving this information.
 	my $contents = slurp_file($node_subscriber->logfile, $offset);
 	$contents =~
-	  qr/conflict detected on relation "public.tbl".*\n.*DETAIL:.* Could not apply remote change.*\n.*Key already exists in unique index "tbl_pkey", modified by .*origin.* in transaction \d+ at .*: key .*, local row .*\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:.* Could not apply remote change.*\n.*Key already exists in unique index "tbl_pkey", modified by .*origin.* in transaction \d+ at .*: key .*, local row .*\n.*CONTEXT:.* for replication target relation "public.tbl" in transaction \d+, finished at ([[:xdigit:]]+\/[[:xdigit:]]+)/m
 	  or die "could not get error-LSN";
 	my $lsn = $1;
 
diff --git a/src/test/subscription/t/030_origin.pl b/src/test/subscription/t/030_origin.pl
index 5076ebe609b..813bf4827d0 100644
--- a/src/test/subscription/t/030_origin.pl
+++ b/src/test/subscription/t/030_origin.pl
@@ -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_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 .*: local row \(32\), remote row \(33\), replica identity \(a\)=\(32\)./
+	qr/conflict update_origin_differs detected on relation "public.tab".*\n.*DETAIL:.* Updating the row that was modified by a different origin ".*" in transaction [0-9]+ at .*: local row \(32\), remote row \(33\), replica identity \(a\)=\(32\)./
 );
 
 $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_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 .*: local row \(33\), replica identity \(a\)=\(33\).*/
+	qr/conflict delete_origin_differs detected on relation "public.tab".*\n.*DETAIL:.* Deleting the row that was modified by a different origin ".*" in transaction [0-9]+ at .*: local row \(33\), replica identity \(a\)=\(33\).*/
 );
 
 # The remaining tests no longer test conflict detection.
diff --git a/src/test/subscription/t/035_conflicts.pl b/src/test/subscription/t/035_conflicts.pl
index 426ad74cf33..3d585626778 100644
--- a/src/test/subscription/t/035_conflicts.pl
+++ b/src/test/subscription/t/035_conflicts.pl
@@ -77,7 +77,7 @@ $node_publisher->safe_psql('postgres',
 
 # Confirm that this causes an error on the subscriber
 $node_subscriber->wait_for_log(
-	qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.*
+	qr/conflict multiple_unique_conflicts detected on relation \"public.conf_tab\".*
 .*Could not apply remote change: remote row \(2, 3, 4\).*
 .*Key already exists in unique index \"conf_tab_pkey\", modified in transaction .*: key \(a\)=\(2\), local row \(2, 2, 2\).*
 .*Key already exists in unique index \"conf_tab_b_key\", modified in transaction .*: key \(b\)=\(3\), local row \(3, 3, 3\).*
@@ -107,7 +107,7 @@ $node_publisher->safe_psql('postgres',
 
 # Confirm that this causes an error on the subscriber
 $node_subscriber->wait_for_log(
-	qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.*
+	qr/conflict multiple_unique_conflicts detected on relation \"public.conf_tab\".*
 .*Could not apply remote change: remote row \(6, 7, 8\), replica identity \(a\)=\(5\).*
 .*Key already exists in unique index \"conf_tab_pkey\", modified in transaction .*: key \(a\)=\(6\), local row \(6, 6, 6\).*
 .*Key already exists in unique index \"conf_tab_b_key\", modified in transaction .*: key \(b\)=\(7\), local row \(7, 7, 7\).*
@@ -133,7 +133,7 @@ $node_publisher->safe_psql('postgres',
 	"INSERT INTO conf_tab_2 VALUES (55,2,3);");
 
 $node_subscriber->wait_for_log(
-	qr/conflict detected on relation \"public.conf_tab_2_p1\": conflict=multiple_unique_conflicts.*
+	qr/conflict multiple_unique_conflicts detected on relation \"public.conf_tab_2_p1\".*
 .*Could not apply remote change: remote row \(55, 2, 3\).*
 .*Key already exists in unique index \"conf_tab_2_p1_pkey\", modified in transaction .*: key \(a\)=\(55\), local row \(55, 2, 3\).*
 .*Key already exists in unique index \"conf_tab_2_p1_a_b_key\", modified in transaction .*: key \(a, b\)=\(55, 2\), local row \(55, 2, 3\)./,
@@ -313,7 +313,7 @@ $node_A->wait_for_catchup($subname_BA);
 my $logfile = slurp_file($node_B->logfile(), $log_location);
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab": conflict=delete_origin_differs.*
+	qr/conflict delete_origin_differs detected on relation "public.tab".*
 .*DETAIL:.* Deleting the row that was modified locally in transaction [0-9]+ at .*: local row \(1, 3\), replica identity \(a\)=\(1\)./,
 	'delete target row was modified in tab');
 
@@ -326,7 +326,7 @@ $node_B->wait_for_catchup($subname_AB);
 $logfile = slurp_file($node_A->logfile(), $log_location);
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab": conflict=update_deleted.*
+	qr/conflict update_deleted detected on relation "public.tab".*
 .*DETAIL:.* Could not find the row to be updated: remote row \(1, 3\), replica identity \(a\)=\(1\).
 .*The row to be updated was deleted locally in transaction [0-9]+ at .*/,
 	'update target row was deleted in tab');
@@ -374,7 +374,7 @@ $node_B->wait_for_catchup($subname_AB);
 $logfile = slurp_file($node_A->logfile(), $log_location);
 like(
 	$logfile,
-	qr/conflict detected on relation "public.tab": conflict=update_deleted.*
+	qr/conflict update_deleted detected on relation "public.tab".*
 .*DETAIL:.* Could not find the row to be updated: remote row \(2, 4\), replica identity full \(2, 2\).*
 .*The row to be updated was deleted locally in transaction [0-9]+ at .*/,
 	'update target row was deleted in tab');
@@ -533,7 +533,7 @@ if ($injection_points_supported != 0)
 	$logfile = slurp_file($node_A->logfile(), $log_location);
 	like(
 		$logfile,
-		qr/conflict detected on relation "public.tab": conflict=update_deleted.*
+		qr/conflict update_deleted detected on relation "public.tab".*
 .*DETAIL:.* Could not find the row to be updated: remote row \(1, 2\), replica identity full \(1, 1\).*
 .*The row to be updated was deleted locally in transaction [0-9]+ at .*/,
 		'update target row was deleted in tab');
-- 
2.47.3

