From d74191ec28332152f1ecdc4d089a6d9744f9bf4f Mon Sep 17 00:00:00 2001
From: Yugo Nagata <nagata@sraoss.co.jp>
Date: Tue, 1 Jul 2025 18:36:01 +0900
Subject: [PATCH v6 4/4] Improve error reporting for concurrent catalog object
 creation with same key

Previously, when multiple sessions attempted to create an object with the
same key, (e.g. CREATE TABLE with the same table name) concurrently, DDL
commands could fail with an error:

 ERROR:  duplicate key value violates unique constraint ....

This commit improves the behavior by reporting a more user-facing error
message in such cases, making it easier for users to understand the cause
of the failure.
---
 src/backend/catalog/indexing.c      | 22 +++++++++++++++++++++-
 src/backend/executor/execIndexing.c | 19 +++++++++++++++++++
 src/include/executor/executor.h     |  5 +++++
 3 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index 25c4b6bdc87..2919b27960c 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -91,7 +91,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple,
 	 * table/index.
 	 */
 #ifndef USE_ASSERT_CHECKING
-	if (HeapTupleIsHeapOnly(heapTuple) && !onlySummarized)
+	if (HeapTupleIsHeapOenly(heapTuple) && !onlySummarized)
 		return;
 #endif
 
@@ -164,6 +164,26 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple,
 					   values,
 					   isnull);
 
+		/* Check if a concurrent command inserted an entry with the same key */
+		if (index->rd_index->indisunique && IsCatalogRelation(heapRelation))
+		{
+			bool satisfied;
+			EState	*estate = CreateExecutorState();
+
+			BuildSpeculativeIndexInfo(index, indexInfo);
+			sartisfied = check_unique_constraint(heapRelation,
+												 index, indexInfo,
+												 &(heapTuple->t_self), values, isnull,
+												 estate);
+
+			if (!satisfied)
+				ereport(ERROR,
+						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						 errmsg("operation failed due to a concurrent command"),
+						 errdetail("Another command inserted the key in unique index %s in a concurrent session.",
+								   RelationGetRelationName(index))));
+		}
+
 		/*
 		 * The index AM does the rest.
 		 */
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index bdf862b2406..889573feb6b 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -965,6 +965,25 @@ check_exclusion_constraint(Relation heap, Relation index,
 												CEOUC_WAIT, false, NULL);
 }
 
+/*
+ * Check for violation of a unique constraint
+ *
+ * This is a dumbed down version of check_exclusion_or_unique_constraint
+ * for external callers. They don't need all the special modes.
+ */
+bool
+check_unique_constraint(Relation heap, Relation index,
+						   IndexInfo *indexInfo,
+						   ItemPointer tupleid,
+						   const Datum *values, const bool *isnull,
+						   EState *estate)
+{
+	return check_exclusion_or_unique_constraint(heap, index, indexInfo, tupleid,
+												values, isnull,
+												estate, false,
+												CEOUC_WAIT, true, NULL);
+}
+
 /*
  * Check existing tuple's index values to see if it really matches the
  * exclusion condition against the new_values.  Returns true if conflict.
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 104b059544d..2653e85e5ea 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -749,6 +749,11 @@ extern void check_exclusion_constraint(Relation heap, Relation index,
 									   ItemPointer tupleid,
 									   const Datum *values, const bool *isnull,
 									   EState *estate, bool newIndex);
+extern bool check_unique_constraint(Relation heap, Relation index,
+									IndexInfo *indexInfo,
+									ItemPointer tupleid,
+									const Datum *values, const bool *isnull,
+									EState *estate);
 
 /*
  * prototypes from functions in execReplication.c
-- 
2.43.0

