On Mon, Apr 26, 2021 at 5:49 PM Bharath Rupireddy
<bharath.rupireddyforpostg...@gmail.com> wrote:
>
> On Mon, Apr 26, 2021 at 5:29 PM vignesh C <vignes...@gmail.com> wrote:
> >
> > Hi,
> >
> > While reviewing one of the logical replication patches, I found that
> > we do not include hint messages to display the actual option which has
> > been specified more than once in case of redundant option error. I
> > felt including this will help in easily identifying the error, users
> > will not have to search through the statement to identify where the
> > actual error is present. Attached a patch for the same.
> > Thoughts?
>
> +1. A more detailed error will be useful in a rare scenario like users
> have specified duplicate options along with a lot of other options,
> they will know for which option the error is thrown by the server.
>
> Instead of adding errhint or errdetail, how about just changing the
> error message itself to something like "option \"%s\" specified more
> than once" or "parameter \"%s\" specified more than once" like we have
> in other places in the code?
>

Both seemed fine but I preferred using errdetail as I felt it is
slightly better for the details to appear in a new line.

> Comments on the patch:
>
> 1) generally errhint and errdetail messages should end with a period
> ".", please see their usage in other places.
> +                         errhint("Option \"streaming\" specified more
> than once")));
>

Modified it.

> 2) I think it should be errdetail instead of errhint, because you are
> giving more information about the error,  but not hinting user how to
> overcome it. If you had to say something like "Remove duplicate
> \"password\" option." or "The \"password\" option is specified more
> than once. Remove all the duplicates.", then it makes sense to use
> errhint.

Modified it.

Attached patch for the same.

Regards,
Vignesh
From 19b33693873a9c6201a139370649f605978863f8 Mon Sep 17 00:00:00 2001
From: vignesh <vignes...@gmail.com>
Date: Mon, 26 Apr 2021 18:40:36 +0530
Subject: [PATCH v2] Enhance error message.

Enhanced error message, so that the user can easily identify the error.
---
 src/backend/commands/copy.c                 |  3 +-
 src/backend/commands/foreigncmds.c          |  6 ++--
 src/backend/commands/functioncmds.c         |  6 ++--
 src/backend/commands/publicationcmds.c      |  6 ++--
 src/backend/commands/subscriptioncmds.c     | 28 +++++++++++------
 src/backend/commands/tablecmds.c            |  3 +-
 src/backend/commands/typecmds.c             | 18 +++++++----
 src/backend/commands/user.c                 | 33 ++++++++++++++-------
 src/backend/parser/parse_utilcmd.c          |  3 +-
 src/backend/replication/pgoutput/pgoutput.c | 15 ++++++----
 src/backend/replication/walsender.c         |  9 ++++--
 src/test/regress/expected/copy2.out         |  2 ++
 src/test/regress/expected/foreign_data.out  |  2 ++
 src/test/regress/expected/publication.out   |  1 +
 14 files changed, 92 insertions(+), 43 deletions(-)

diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 8265b981eb..77a50652cc 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -469,7 +469,8 @@ ProcessCopyOptions(ParseState *pstate,
 			if (opts_out->force_null)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 parser_errposition(pstate, defel->location)));
 			if (defel->arg && IsA(defel->arg, List))
 				opts_out->force_null = castNode(List, defel->arg);
 			else
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index eb7103fd3b..83b969a5f8 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -536,7 +536,8 @@ parse_func_options(List *func_options,
 			if (*handler_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"handler\" specified more than once.")));
 			*handler_given = true;
 			*fdwhandler = lookup_fdw_handler_func(def);
 		}
@@ -545,7 +546,8 @@ parse_func_options(List *func_options,
 			if (*validator_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"validator\" specified more than once.")));
 			*validator_given = true;
 			*fdwvalidator = lookup_fdw_validator_func(def);
 		}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 9548287217..5508fad48f 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -2065,7 +2065,8 @@ ExecuteDoStmt(DoStmt *stmt, bool atomic)
 			if (as_item)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"as\" specified more than once.")));
 			as_item = defel;
 		}
 		else if (strcmp(defel->defname, "language") == 0)
@@ -2073,7 +2074,8 @@ ExecuteDoStmt(DoStmt *stmt, bool atomic)
 			if (language_item)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"language\" specified more than once.")));
 			language_item = defel;
 		}
 		else
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index 95c253c8e0..50a876a533 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -87,7 +87,8 @@ parse_publication_options(List *options,
 			if (*publish_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"publish\" specified more than once.")));
 
 			/*
 			 * If publish option was given only the explicitly listed actions
@@ -130,7 +131,8 @@ parse_publication_options(List *options,
 			if (*publish_via_partition_root_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"publish_via_partition_root\" specified more than once.")));
 			*publish_via_partition_root_given = true;
 			*publish_via_partition_root = defGetBoolean(defel);
 		}
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 517c8edd3b..fd9fdb08d8 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -121,7 +121,8 @@ parse_subscription_options(List *options,
 			if (connect_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"connect\" specified more than once.")));
 
 			connect_given = true;
 			*connect = defGetBoolean(defel);
@@ -131,7 +132,8 @@ parse_subscription_options(List *options,
 			if (*enabled_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"enabled\" specified more than once.")));
 
 			*enabled_given = true;
 			*enabled = defGetBoolean(defel);
@@ -141,7 +143,8 @@ parse_subscription_options(List *options,
 			if (create_slot_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"create_slot\" specified more than once.")));
 
 			create_slot_given = true;
 			*create_slot = defGetBoolean(defel);
@@ -151,7 +154,8 @@ parse_subscription_options(List *options,
 			if (*slot_name_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"slot_name\" specified more than once.")));
 
 			*slot_name_given = true;
 			*slot_name = defGetString(defel);
@@ -165,7 +169,8 @@ parse_subscription_options(List *options,
 			if (copy_data_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"copy_data\" specified more than once.")));
 
 			copy_data_given = true;
 			*copy_data = defGetBoolean(defel);
@@ -176,7 +181,8 @@ parse_subscription_options(List *options,
 			if (*synchronous_commit)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"synchronous_commit\" specified more than once.")));
 
 			*synchronous_commit = defGetString(defel);
 
@@ -190,7 +196,8 @@ parse_subscription_options(List *options,
 			if (refresh_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"refresh\" specified more than once.")));
 
 			refresh_given = true;
 			*refresh = defGetBoolean(defel);
@@ -200,7 +207,9 @@ parse_subscription_options(List *options,
 			if (*binary_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"binary\" specified more than once.")));
+
 
 			*binary_given = true;
 			*binary = defGetBoolean(defel);
@@ -210,7 +219,8 @@ parse_subscription_options(List *options,
 			if (*streaming_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"streaming\" specified more than once.")));
 
 			*streaming_given = true;
 			*streaming = defGetBoolean(defel);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7d00f4eb25..3371040ba2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7539,7 +7539,8 @@ ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmod
 			if (generatedEl)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"generated\" specified more than once.")));
 			generatedEl = defel;
 		}
 		else
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 036fa69d17..bbfc06113e 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1413,7 +1413,8 @@ DefineRange(CreateRangeStmt *stmt)
 			if (OidIsValid(rangeSubtype))
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"subtype\" specified more than once.")));
 			/* we can look up the subtype name immediately */
 			rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel));
 		}
@@ -1422,7 +1423,8 @@ DefineRange(CreateRangeStmt *stmt)
 			if (rangeSubOpclassName != NIL)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"subtype_opclass\" specified more than once.")));
 			rangeSubOpclassName = defGetQualifiedName(defel);
 		}
 		else if (strcmp(defel->defname, "collation") == 0)
@@ -1430,7 +1432,8 @@ DefineRange(CreateRangeStmt *stmt)
 			if (rangeCollationName != NIL)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"collation\" specified more than once.")));
 			rangeCollationName = defGetQualifiedName(defel);
 		}
 		else if (strcmp(defel->defname, "canonical") == 0)
@@ -1438,7 +1441,8 @@ DefineRange(CreateRangeStmt *stmt)
 			if (rangeCanonicalName != NIL)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"canonical\" specified more than once.")));
 			rangeCanonicalName = defGetQualifiedName(defel);
 		}
 		else if (strcmp(defel->defname, "subtype_diff") == 0)
@@ -1446,7 +1450,8 @@ DefineRange(CreateRangeStmt *stmt)
 			if (rangeSubtypeDiffName != NIL)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"subtype_diff\" specified more than once.")));
 			rangeSubtypeDiffName = defGetQualifiedName(defel);
 		}
 		else if (strcmp(defel->defname, "multirange_type_name") == 0)
@@ -1454,7 +1459,8 @@ DefineRange(CreateRangeStmt *stmt)
 			if (multirangeTypeName != NULL)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"multirange_type_name\" specified more than once.")));
 			/* we can look up the subtype name immediately */
 			multirangeNamespace = QualifiedNameGetCreationNamespace(defGetQualifiedName(defel),
 																	&multirangeTypeName);
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 65bb733958..5f4274327d 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -579,7 +579,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (dpassword)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"password\" specified more than once.")));
 			dpassword = defel;
 		}
 		else if (strcmp(defel->defname, "superuser") == 0)
@@ -587,7 +588,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (dissuper)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"superuser\" specified more than once.")));
 			dissuper = defel;
 		}
 		else if (strcmp(defel->defname, "inherit") == 0)
@@ -595,7 +597,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (dinherit)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"inherit\" specified more than once.")));
 			dinherit = defel;
 		}
 		else if (strcmp(defel->defname, "createrole") == 0)
@@ -603,7 +606,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (dcreaterole)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"createrole\" specified more than once.")));
 			dcreaterole = defel;
 		}
 		else if (strcmp(defel->defname, "createdb") == 0)
@@ -611,7 +615,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (dcreatedb)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"createdb\" specified more than once.")));
 			dcreatedb = defel;
 		}
 		else if (strcmp(defel->defname, "canlogin") == 0)
@@ -619,7 +624,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (dcanlogin)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"canlogin\" specified more than once.")));
 			dcanlogin = defel;
 		}
 		else if (strcmp(defel->defname, "isreplication") == 0)
@@ -627,7 +633,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (disreplication)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"isreplication\" specified more than once.")));
 			disreplication = defel;
 		}
 		else if (strcmp(defel->defname, "connectionlimit") == 0)
@@ -635,7 +642,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (dconnlimit)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"connectionlimit\" specified more than once.")));
 			dconnlimit = defel;
 		}
 		else if (strcmp(defel->defname, "rolemembers") == 0 &&
@@ -644,7 +652,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (drolemembers)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"rolemembers\" specified more than once.")));
 			drolemembers = defel;
 		}
 		else if (strcmp(defel->defname, "validUntil") == 0)
@@ -652,7 +661,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (dvalidUntil)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"validUntil\" specified more than once.")));
 			dvalidUntil = defel;
 		}
 		else if (strcmp(defel->defname, "bypassrls") == 0)
@@ -660,7 +670,8 @@ AlterRole(AlterRoleStmt *stmt)
 			if (dbypassRLS)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"bypassrls\" specified more than once.")));
 			dbypassRLS = defel;
 		}
 		else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 9dd30370da..7565450820 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -402,7 +402,8 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
 			if (nameEl)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"sequence_name\" specified more than once.")));
 			nameEl = defel;
 			nameEl_idx = foreach_current_index(option);
 		}
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index f68348dcf4..1a28c6d2ce 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -189,7 +189,8 @@ parse_output_parameters(List *options, PGOutputData *data)
 			if (protocol_version_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"proto_version\" specified more than once.")));
 			protocol_version_given = true;
 
 			if (!scanint8(strVal(defel->arg), true, &parsed))
@@ -210,7 +211,8 @@ parse_output_parameters(List *options, PGOutputData *data)
 			if (publication_names_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"publication_names\" specified more than once.")));
 			publication_names_given = true;
 
 			if (!SplitIdentifierString(strVal(defel->arg), ',',
@@ -224,7 +226,8 @@ parse_output_parameters(List *options, PGOutputData *data)
 			if (binary_option_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"binary\" specified more than once.")));
 			binary_option_given = true;
 
 			data->binary = defGetBoolean(defel);
@@ -234,7 +237,8 @@ parse_output_parameters(List *options, PGOutputData *data)
 			if (messages_option_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"messages\" specified more than once.")));
 			messages_option_given = true;
 
 			data->messages = defGetBoolean(defel);
@@ -244,7 +248,8 @@ parse_output_parameters(List *options, PGOutputData *data)
 			if (streaming_given)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"streaming\" specified more than once.")));
 			streaming_given = true;
 
 			data->streaming = defGetBoolean(defel);
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 52fe9aba66..6b436c6174 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -882,7 +882,8 @@ parseCreateReplSlotOptions(CreateReplicationSlotCmd *cmd,
 			if (snapshot_action_given || cmd->kind != REPLICATION_KIND_LOGICAL)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"export_snapshot\" specified more than once.")));
 
 			snapshot_action_given = true;
 			*snapshot_action = defGetBoolean(defel) ? CRS_EXPORT_SNAPSHOT :
@@ -893,7 +894,8 @@ parseCreateReplSlotOptions(CreateReplicationSlotCmd *cmd,
 			if (snapshot_action_given || cmd->kind != REPLICATION_KIND_LOGICAL)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"use_snapshot\" specified more than once.")));
 
 			snapshot_action_given = true;
 			*snapshot_action = CRS_USE_SNAPSHOT;
@@ -903,7 +905,8 @@ parseCreateReplSlotOptions(CreateReplicationSlotCmd *cmd,
 			if (reserve_wal_given || cmd->kind != REPLICATION_KIND_PHYSICAL)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("conflicting or redundant options")));
+						 errmsg("conflicting or redundant options"),
+						 errdetail("Option \"reserve_wal\" specified more than once.")));
 
 			reserve_wal_given = true;
 			*reserve_wal = true;
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index c64f0719e7..5f3685e9ef 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -67,6 +67,8 @@ LINE 1: COPY x from stdin (force_not_null (a), force_not_null (b));
                                                ^
 COPY x from stdin (force_null (a), force_null (b));
 ERROR:  conflicting or redundant options
+LINE 1: COPY x from stdin (force_null (a), force_null (b));
+                                           ^
 COPY x from stdin (convert_selectively (a), convert_selectively (b));
 ERROR:  conflicting or redundant options
 LINE 1: COPY x from stdin (convert_selectively (a), convert_selectiv...
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 5385f98a0f..75ee3b8a75 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -95,6 +95,7 @@ CREATE FOREIGN DATA WRAPPER test_fdw HANDLER invalid_fdw_handler;  -- ERROR
 ERROR:  function invalid_fdw_handler must return type fdw_handler
 CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler HANDLER invalid_fdw_handler;  -- ERROR
 ERROR:  conflicting or redundant options
+DETAIL:  Option "handler" specified more than once.
 CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler;
 DROP FOREIGN DATA WRAPPER test_fdw;
 -- ALTER FOREIGN DATA WRAPPER
@@ -201,6 +202,7 @@ ALTER FOREIGN DATA WRAPPER foo HANDLER invalid_fdw_handler;  -- ERROR
 ERROR:  function invalid_fdw_handler must return type fdw_handler
 ALTER FOREIGN DATA WRAPPER foo HANDLER test_fdw_handler HANDLER anything;  -- ERROR
 ERROR:  conflicting or redundant options
+DETAIL:  Option "handler" specified more than once.
 ALTER FOREIGN DATA WRAPPER foo HANDLER test_fdw_handler;
 WARNING:  changing the foreign-data wrapper handler can change behavior of existing foreign tables
 DROP FUNCTION invalid_fdw_handler();
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index 63d6ab7a4e..9f34d032af 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -27,6 +27,7 @@ CREATE PUBLICATION testpub_xxx WITH (publish = 'cluster, vacuum');
 ERROR:  unrecognized "publish" value: "cluster"
 CREATE PUBLICATION testpub_xxx WITH (publish_via_partition_root = 'true', publish_via_partition_root = '0');
 ERROR:  conflicting or redundant options
+DETAIL:  Option "publish_via_partition_root" specified more than once.
 \dRp
                                               List of publications
         Name        |          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
-- 
2.25.1

Reply via email to