diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c new file mode 100644 index ebb0994..40e2626 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -86,19 +86,47 @@ DefineCollation(ParseState *pstate, List DefElem **defelp; if (strcmp(defel->defname, "from") == 0) + { + if (fromEl) + errorDuplicateDefElem(defel, pstate); defelp = &fromEl; + } else if (strcmp(defel->defname, "locale") == 0) + { + if (localeEl) + errorDuplicateDefElem(defel, pstate); defelp = &localeEl; + } else if (strcmp(defel->defname, "lc_collate") == 0) + { + if (lccollateEl) + errorDuplicateDefElem(defel, pstate); defelp = &lccollateEl; + } else if (strcmp(defel->defname, "lc_ctype") == 0) + { + if (lcctypeEl) + errorDuplicateDefElem(defel, pstate); defelp = &lcctypeEl; + } else if (strcmp(defel->defname, "provider") == 0) + { + if (providerEl) + errorDuplicateDefElem(defel, pstate); defelp = &providerEl; + } else if (strcmp(defel->defname, "deterministic") == 0) + { + if (deterministicEl) + errorDuplicateDefElem(defel, pstate); defelp = &deterministicEl; + } else if (strcmp(defel->defname, "version") == 0) + { + if (versionEl) + errorDuplicateDefElem(defel, pstate); defelp = &versionEl; + } else { ereport(ERROR, @@ -112,11 +140,17 @@ DefineCollation(ParseState *pstate, List *defelp = defel; } - if ((localeEl && (lccollateEl || lcctypeEl)) - || (fromEl && list_length(parameters) != 1)) + if (localeEl && (lccollateEl || lcctypeEl)) ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("conflicting or redundant options"))); + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + errdetail("LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE.")); + + if (fromEl && list_length(parameters) != 1) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + errdetail("FROM cannot be specified together with any other options.")); if (fromEl) { diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c new file mode 100644 index aafd755..8dd260d --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -359,3 +359,21 @@ errorConflictingDefElem(DefElem *defel, errmsg("conflicting or redundant options"), parser_errposition(pstate, defel->location)); } + +/* + * Raise an error about a duplicate DefElem. + * + * This is similar to errorConflictingDefElem(), except that it is intended for + * an option that the user explicitly specified more than once. This should + * only be used if defel->defname is guaranteed to match the user-entered + * option name, otherwise the detail text might be confusing. + */ +void +errorDuplicateDefElem(DefElem *defel, ParseState *pstate) +{ + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + errdetail("Option \"%s\" specified more than once.", defel->defname), + parser_errposition(pstate, defel->location)); +} diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h new file mode 100644 index f84d099..91743ca --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -154,5 +154,6 @@ extern TypeName *defGetTypeName(DefElem extern int defGetTypeLength(DefElem *def); extern List *defGetStringList(DefElem *def); extern void errorConflictingDefElem(DefElem *defel, ParseState *pstate) pg_attribute_noreturn(); +extern void errorDuplicateDefElem(DefElem *defel, ParseState *pstate) pg_attribute_noreturn(); #endif /* DEFREM_H */ diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out new file mode 100644 index 0b60b55..c08d8f2 --- a/src/test/regress/expected/collate.out +++ b/src/test/regress/expected/collate.out @@ -701,6 +701,65 @@ View definition: SELECT ss.c1 + 1 AS c1p FROM ( SELECT 4 AS c1) ss; +-- Check conflicting or duplicate options in CREATE COLLATION +-- FROM +CREATE COLLATION coll_dup_chk (FROM = "C", FROM = "X"); +ERROR: conflicting or redundant options +LINE 1: CREATE COLLATION coll_dup_chk (FROM = "C", FROM = "X"); + ^ +DETAIL: Option "from" specified more than once. +-- FROM conflicts with any other option +CREATE COLLATION coll_dup_chk (FROM = "C", VERSION = "1"); +ERROR: conflicting or redundant options +DETAIL: FROM cannot be specified together with any other options. +-- LC_COLLATE +CREATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LC_COLLATE = "NONSENSE", LC_CTYPE = "POSIX"); +ERROR: conflicting or redundant options +LINE 1: ...ATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LC_COLLATE... + ^ +DETAIL: Option "lc_collate" specified more than once. +-- LC_CTYPE +CREATE COLLATION coll_dup_chk (LC_CTYPE = "POSIX", LC_CTYPE = "NONSENSE", LC_COLLATE = "POSIX"); +ERROR: conflicting or redundant options +LINE 1: ...REATE COLLATION coll_dup_chk (LC_CTYPE = "POSIX", LC_CTYPE =... + ^ +DETAIL: Option "lc_ctype" specified more than once. +-- PROVIDER +CREATE COLLATION coll_dup_chk (PROVIDER = icu, PROVIDER = NONSENSE, LC_COLLATE = "POSIX", LC_CTYPE = "POSIX"); +ERROR: conflicting or redundant options +LINE 1: CREATE COLLATION coll_dup_chk (PROVIDER = icu, PROVIDER = NO... + ^ +DETAIL: Option "provider" specified more than once. +-- LOCALE +CREATE COLLATION case_sensitive (LOCALE = '', LOCALE = "NONSENSE"); +ERROR: conflicting or redundant options +LINE 1: CREATE COLLATION case_sensitive (LOCALE = '', LOCALE = "NONS... + ^ +DETAIL: Option "locale" specified more than once. +-- DETERMINISTIC +CREATE COLLATION coll_dup_chk (DETERMINISTIC = TRUE, DETERMINISTIC = NONSENSE, LOCALE = ''); +ERROR: conflicting or redundant options +LINE 1: ...ATE COLLATION coll_dup_chk (DETERMINISTIC = TRUE, DETERMINIS... + ^ +DETAIL: Option "deterministic" specified more than once. +-- VERSION +CREATE COLLATION coll_dup_chk (VERSION = '1', VERSION = "NONSENSE", LOCALE = ''); +ERROR: conflicting or redundant options +LINE 1: CREATE COLLATION coll_dup_chk (VERSION = '1', VERSION = "NON... + ^ +DETAIL: Option "version" specified more than once. +-- LOCALE conflicts with LC_COLLATE and LC_CTYPE +CREATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LC_CTYPE = "POSIX", LOCALE = ''); +ERROR: conflicting or redundant options +DETAIL: LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE. +-- LOCALE conflicts with LC_COLLATE +CREATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LOCALE = ''); +ERROR: conflicting or redundant options +DETAIL: LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE. +-- LOCALE conflicts with LC_CTYPE +CREATE COLLATION coll_dup_chk (LC_CTYPE = "POSIX", LOCALE = ''); +ERROR: conflicting or redundant options +DETAIL: LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE. -- -- Clean up. Many of these table names will be re-used if the user is -- trying to run any platform-specific collation tests later, so we diff --git a/src/test/regress/sql/collate.sql b/src/test/regress/sql/collate.sql new file mode 100644 index 30f811b..87c31d4 --- a/src/test/regress/sql/collate.sql +++ b/src/test/regress/sql/collate.sql @@ -272,6 +272,29 @@ SELECT c1+1 AS c1p FROM (SELECT ('4' COLLATE "C")::INT AS c1) ss; \d+ collate_on_int +-- Check conflicting or duplicate options in CREATE COLLATION +-- FROM +CREATE COLLATION coll_dup_chk (FROM = "C", FROM = "X"); +-- FROM conflicts with any other option +CREATE COLLATION coll_dup_chk (FROM = "C", VERSION = "1"); +-- LC_COLLATE +CREATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LC_COLLATE = "NONSENSE", LC_CTYPE = "POSIX"); +-- LC_CTYPE +CREATE COLLATION coll_dup_chk (LC_CTYPE = "POSIX", LC_CTYPE = "NONSENSE", LC_COLLATE = "POSIX"); +-- PROVIDER +CREATE COLLATION coll_dup_chk (PROVIDER = icu, PROVIDER = NONSENSE, LC_COLLATE = "POSIX", LC_CTYPE = "POSIX"); +-- LOCALE +CREATE COLLATION case_sensitive (LOCALE = '', LOCALE = "NONSENSE"); +-- DETERMINISTIC +CREATE COLLATION coll_dup_chk (DETERMINISTIC = TRUE, DETERMINISTIC = NONSENSE, LOCALE = ''); +-- VERSION +CREATE COLLATION coll_dup_chk (VERSION = '1', VERSION = "NONSENSE", LOCALE = ''); +-- LOCALE conflicts with LC_COLLATE and LC_CTYPE +CREATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LC_CTYPE = "POSIX", LOCALE = ''); +-- LOCALE conflicts with LC_COLLATE +CREATE COLLATION coll_dup_chk (LC_COLLATE = "POSIX", LOCALE = ''); +-- LOCALE conflicts with LC_CTYPE +CREATE COLLATION coll_dup_chk (LC_CTYPE = "POSIX", LOCALE = ''); -- -- Clean up. Many of these table names will be re-used if the user is