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?
Regards, Vignesh
From 73f670b9206e3f0240d56435f838c67f3e9fb681 Mon Sep 17 00:00:00 2001 From: vignesh <vignes...@gmail.com> Date: Mon, 26 Apr 2021 09:44:23 +0530 Subject: [PATCH] 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..1239b0cafd 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"), + errhint("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"), + errhint("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..6772bdbce9 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"), + errhint("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"), + errhint("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..804082116b 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"), + errhint("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"), + errhint("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..740e8169fe 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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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..f6095ab279 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"), + errhint("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..3403029f3e 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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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..de46df71ca 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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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..e3f652df4c 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"), + errhint("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..6a02b4be16 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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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"), + errhint("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..f3dc3eafe0 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"), + errhint("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"), + errhint("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"), + errhint("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..039ea52ef5 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 +HINT: 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 +HINT: 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..5f87c403ce 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 +HINT: 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