pg_import_system_collations-fixes.patch
text/x-diff
Filename: pg_import_system_collations-fixes.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| doc/src/sgml/func.sgml | 0 | 0 |
| src/backend/catalog/pg_collation.c | 0 | 0 |
| src/backend/commands/collationcmds.c | 0 | 0 |
| src/bin/initdb/initdb.c | 0 | 0 |
| src/include/catalog/pg_collation_fn.h | 0 | 0 |
| src/include/catalog/pg_proc.h | 0 | 0 |
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index e073f7b..bd2eaaa 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** postgres=# SELECT * FROM pg_walfile_name
*** 19711,19717 ****
<row>
<entry>
<indexterm><primary>pg_import_system_collations</primary></indexterm>
! <literal><function>pg_import_system_collations(<parameter>if_not_exists</> <type>boolean</>, <parameter>schema</> <type>regnamespace</>)</function></literal>
</entry>
<entry><type>void</type></entry>
<entry>Import operating system collations</entry>
--- 19711,19717 ----
<row>
<entry>
<indexterm><primary>pg_import_system_collations</primary></indexterm>
! <literal><function>pg_import_system_collations(<parameter>schema</> <type>regnamespace</>)</function></literal>
</entry>
<entry><type>void</type></entry>
<entry>Import operating system collations</entry>
*************** postgres=# SELECT * FROM pg_walfile_name
*** 19730,19747 ****
</para>
<para>
! <function>pg_import_system_collations</> populates the system
! catalog <literal>pg_collation</literal> with collations based on all the
! locales it finds on the operating system. This is
what <command>initdb</command> uses;
see <xref linkend="collation-managing"> for more details. If additional
locales are installed into the operating system later on, this function
! can be run again to add collations for the new locales. In that case, the
! parameter <parameter>if_not_exists</parameter> should be set to true to
! skip over existing collations. The <parameter>schema</parameter>
! parameter would typically be <literal>pg_catalog</literal>, but that is
! not a requirement. (Collation objects based on locales that are no longer
! present on the operating system are never removed by this function.)
</para>
</sect2>
--- 19730,19748 ----
</para>
<para>
! <function>pg_import_system_collations</> adds collations to the system
! catalog <literal>pg_collation</literal> based on all the
! locales it finds in the operating system. This is
what <command>initdb</command> uses;
see <xref linkend="collation-managing"> for more details. If additional
locales are installed into the operating system later on, this function
! can be run again to add collations for the new locales. Locales that
! match existing entries in <literal>pg_collation</literal> will be skipped.
! (But collation objects based on locales that are no longer
! present in the operating system are not removed by this function.)
! The <parameter>schema</parameter> parameter would typically
! be <literal>pg_catalog</literal>, but that is not a requirement;
! the collations could be installed into some other schema as well.
</para>
</sect2>
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index 6b1b72f..bf66bde 100644
*** a/src/backend/catalog/pg_collation.c
--- b/src/backend/catalog/pg_collation.c
***************
*** 37,42 ****
--- 37,47 ----
* CollationCreate
*
* Add a new tuple to pg_collation.
+ *
+ * if_not_exists: if true, don't fail on duplicate name, just print a notice
+ * and return InvalidOid.
+ * quiet: if true, don't fail on duplicate name, just silently return
+ * InvalidOid (overrides if_not_exists).
*/
Oid
CollationCreate(const char *collname, Oid collnamespace,
*************** CollationCreate(const char *collname, Oi
*** 45,51 ****
int32 collencoding,
const char *collcollate, const char *collctype,
const char *collversion,
! bool if_not_exists)
{
Relation rel;
TupleDesc tupDesc;
--- 50,57 ----
int32 collencoding,
const char *collcollate, const char *collctype,
const char *collversion,
! bool if_not_exists,
! bool quiet)
{
Relation rel;
TupleDesc tupDesc;
*************** CollationCreate(const char *collname, Oi
*** 77,83 ****
Int32GetDatum(collencoding),
ObjectIdGetDatum(collnamespace)))
{
! if (if_not_exists)
{
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_OBJECT),
--- 83,91 ----
Int32GetDatum(collencoding),
ObjectIdGetDatum(collnamespace)))
{
! if (quiet)
! return InvalidOid;
! else if (if_not_exists)
{
ereport(NOTICE,
(errcode(ERRCODE_DUPLICATE_OBJECT),
*************** CollationCreate(const char *collname, Oi
*** 119,125 ****
Int32GetDatum(-1),
ObjectIdGetDatum(collnamespace))))
{
! if (if_not_exists)
{
heap_close(rel, NoLock);
ereport(NOTICE,
--- 127,135 ----
Int32GetDatum(-1),
ObjectIdGetDatum(collnamespace))))
{
! if (quiet)
! return InvalidOid;
! else if (if_not_exists)
{
heap_close(rel, NoLock);
ereport(NOTICE,
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index bdfd739..ec56320 100644
*** a/src/backend/commands/collationcmds.c
--- b/src/backend/commands/collationcmds.c
***************
*** 37,42 ****
--- 37,50 ----
#include "utils/syscache.h"
+ typedef struct
+ {
+ char *localename; /* name of locale, as per "locale -a" */
+ char *alias; /* shortened alias for same */
+ int enc; /* encoding */
+ } CollAliasData;
+
+
/*
* CREATE COLLATION
*/
*************** DefineCollation(ParseState *pstate, List
*** 196,202 ****
collcollate,
collctype,
collversion,
! if_not_exists);
if (!OidIsValid(newoid))
return InvalidObjectAddress;
--- 204,211 ----
collcollate,
collctype,
collversion,
! if_not_exists,
! false); /* not quiet */
if (!OidIsValid(newoid))
return InvalidObjectAddress;
*************** pg_collation_actual_version(PG_FUNCTION_
*** 344,356 ****
}
/*
* "Normalize" a libc locale name, stripping off encoding tags such as
* ".utf8" (e.g., "en_US.utf8" -> "en_US", but "br_FR.iso885915@euro"
* -> "br_FR@euro"). Return true if a new, different name was
* generated.
*/
- pg_attribute_unused()
static bool
normalize_libc_locale_name(char *new, const char *old)
{
--- 353,370 ----
}
+ /* will we use "locale -a" in pg_import_system_collations? */
+ #if defined(HAVE_LOCALE_T) && !defined(WIN32)
+ #define READ_LOCALE_A_OUTPUT
+ #endif
+
+ #ifdef READ_LOCALE_A_OUTPUT
/*
* "Normalize" a libc locale name, stripping off encoding tags such as
* ".utf8" (e.g., "en_US.utf8" -> "en_US", but "br_FR.iso885915@euro"
* -> "br_FR@euro"). Return true if a new, different name was
* generated.
*/
static bool
normalize_libc_locale_name(char *new, const char *old)
{
*************** normalize_libc_locale_name(char *new, co
*** 379,384 ****
--- 393,412 ----
return changed;
}
+ /*
+ * qsort comparator for CollAliasData items
+ */
+ static int
+ cmpaliases(const void *a, const void *b)
+ {
+ const CollAliasData *ca = (const CollAliasData *) a;
+ const CollAliasData *cb = (const CollAliasData *) b;
+
+ /* comparing localename is enough because other fields are derived */
+ return strcmp(ca->localename, cb->localename);
+ }
+ #endif /* READ_LOCALE_A_OUTPUT */
+
#ifdef USE_ICU
static char *
*************** get_icu_locale_comment(const char *local
*** 420,455 ****
#endif /* USE_ICU */
Datum
pg_import_system_collations(PG_FUNCTION_ARGS)
{
! bool if_not_exists = PG_GETARG_BOOL(0);
! Oid nspid = PG_GETARG_OID(1);
! #if defined(HAVE_LOCALE_T) && !defined(WIN32)
FILE *locale_a_handle;
char localebuf[NAMEDATALEN]; /* we assume ASCII so this is fine */
int count = 0;
! List *aliaslist = NIL;
! List *localelist = NIL;
! List *enclist = NIL;
! ListCell *lca,
! *lcl,
! *lce;
#endif
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to import system collations"))));
! #if !(defined(HAVE_LOCALE_T) && !defined(WIN32)) && !defined(USE_ICU)
! /* silence compiler warnings */
! (void) if_not_exists;
! (void) nspid;
! #endif
- #if defined(HAVE_LOCALE_T) && !defined(WIN32)
locale_a_handle = OpenPipeStream("locale -a", "r");
if (locale_a_handle == NULL)
ereport(ERROR,
--- 448,486 ----
#endif /* USE_ICU */
+ /*
+ * pg_import_system_collations: add known system collations to pg_collation
+ */
Datum
pg_import_system_collations(PG_FUNCTION_ARGS)
{
! Oid nspid = PG_GETARG_OID(0);
! #ifdef READ_LOCALE_A_OUTPUT
FILE *locale_a_handle;
char localebuf[NAMEDATALEN]; /* we assume ASCII so this is fine */
int count = 0;
! CollAliasData *aliases;
! int naliases,
! maxaliases,
! i;
#endif
+ /* silence compiler warning if we have no locale implementation at all */
+ (void) nspid;
+
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to import system collations"))));
! /* Load collations known to libc, using "locale -a" to enumerate them */
! #ifdef READ_LOCALE_A_OUTPUT
! /* expansible array of aliases */
! maxaliases = 100;
! aliases = (CollAliasData *) palloc(maxaliases * sizeof(CollAliasData));
! naliases = 0;
locale_a_handle = OpenPipeStream("locale -a", "r");
if (locale_a_handle == NULL)
ereport(ERROR,
*************** pg_import_system_collations(PG_FUNCTION_
*** 459,465 ****
while (fgets(localebuf, sizeof(localebuf), locale_a_handle))
{
- int i;
size_t len;
int enc;
bool skip;
--- 490,495 ----
*************** pg_import_system_collations(PG_FUNCTION_
*** 507,519 ****
if (enc == PG_SQL_ASCII)
continue; /* C/POSIX are already in the catalog */
count++;
CollationCreate(localebuf, nspid, GetUserId(), COLLPROVIDER_LIBC, enc,
localebuf, localebuf,
get_collation_actual_version(COLLPROVIDER_LIBC, localebuf),
! if_not_exists);
CommandCounterIncrement();
/*
--- 537,559 ----
if (enc == PG_SQL_ASCII)
continue; /* C/POSIX are already in the catalog */
+ /* count valid locales found in operating system */
count++;
+ /*
+ * Create a collation named the same as the locale, but quietly doing
+ * nothing if it already exists. This is the behavior we need even at
+ * initdb time, because some implementations of "locale -a" can report
+ * the same locale name more than once. And it's convenient for later
+ * import runs, too, since you just about always want to add on new
+ * locales without a lot of chatter about existing ones.
+ */
CollationCreate(localebuf, nspid, GetUserId(), COLLPROVIDER_LIBC, enc,
localebuf, localebuf,
get_collation_actual_version(COLLPROVIDER_LIBC, localebuf),
! true, true);
+ /* Must do CCI between inserts to handle duplicates correctly */
CommandCounterIncrement();
/*
*************** pg_import_system_collations(PG_FUNCTION_
*** 527,559 ****
*/
if (normalize_libc_locale_name(alias, localebuf))
{
! aliaslist = lappend(aliaslist, pstrdup(alias));
! localelist = lappend(localelist, pstrdup(localebuf));
! enclist = lappend_int(enclist, enc);
}
}
ClosePipeStream(locale_a_handle);
! /* Now try to add any aliases we created */
! forthree(lca, aliaslist, lcl, localelist, lce, enclist)
{
! char *alias = (char *) lfirst(lca);
! char *locale = (char *) lfirst(lcl);
! int enc = lfirst_int(lce);
CollationCreate(alias, nspid, GetUserId(), COLLPROVIDER_LIBC, enc,
locale, locale,
get_collation_actual_version(COLLPROVIDER_LIBC, locale),
! true);
CommandCounterIncrement();
}
if (count == 0)
ereport(WARNING,
(errmsg("no usable system locales were found")));
#endif /* not HAVE_LOCALE_T && not WIN32 */
#ifdef USE_ICU
if (!is_encoding_supported_by_icu(GetDatabaseEncoding()))
{
--- 567,622 ----
*/
if (normalize_libc_locale_name(alias, localebuf))
{
! if (naliases >= maxaliases)
! {
! maxaliases *= 2;
! aliases = (CollAliasData *)
! repalloc(aliases, maxaliases * sizeof(CollAliasData));
! }
! aliases[naliases].localename = pstrdup(localebuf);
! aliases[naliases].alias = pstrdup(alias);
! aliases[naliases].enc = enc;
! naliases++;
}
}
ClosePipeStream(locale_a_handle);
! /*
! * Before processing the aliases, sort them by locale name. The point
! * here is that if "locale -a" gives us multiple locale names with the
! * same encoding and base name, say "en_US.utf8" and "en_US.utf-8", we
! * want to pick a deterministic one of them. First in ASCII sort order is
! * a good enough rule. (Before PG 10, the code corresponding to this
! * logic in initdb.c had an additional ordering rule, to prefer the locale
! * name exactly matching the alias, if any. We don't need to consider
! * that here, because we would have already created such a pg_collation
! * entry above, and that one will win.)
! */
! if (naliases > 1)
! qsort((void *) aliases, naliases, sizeof(CollAliasData), cmpaliases);
!
! /* Now add aliases, ignoring any that match pre-existing entries */
! for (i = 0; i < naliases; i++)
{
! char *locale = aliases[i].localename;
! char *alias = aliases[i].alias;
! int enc = aliases[i].enc;
CollationCreate(alias, nspid, GetUserId(), COLLPROVIDER_LIBC, enc,
locale, locale,
get_collation_actual_version(COLLPROVIDER_LIBC, locale),
! true, true);
CommandCounterIncrement();
}
+ /* Give a warning if "locale -a" seems to be malfunctioning */
if (count == 0)
ereport(WARNING,
(errmsg("no usable system locales were found")));
#endif /* not HAVE_LOCALE_T && not WIN32 */
+ /* Load collations known to ICU */
#ifdef USE_ICU
if (!is_encoding_supported_by_icu(GetDatabaseEncoding()))
{
*************** pg_import_system_collations(PG_FUNCTION_
*** 591,597 ****
nspid, GetUserId(), COLLPROVIDER_ICU, -1,
collcollate, collcollate,
get_collation_actual_version(COLLPROVIDER_ICU, collcollate),
! if_not_exists);
CreateComments(collid, CollationRelationId, 0,
get_icu_locale_comment(name));
--- 654,661 ----
nspid, GetUserId(), COLLPROVIDER_ICU, -1,
collcollate, collcollate,
get_collation_actual_version(COLLPROVIDER_ICU, collcollate),
! true, true);
! CommandCounterIncrement();
CreateComments(collid, CollationRelationId, 0,
get_icu_locale_comment(name));
*************** pg_import_system_collations(PG_FUNCTION_
*** 618,624 ****
nspid, GetUserId(), COLLPROVIDER_ICU, -1,
collcollate, collcollate,
get_collation_actual_version(COLLPROVIDER_ICU, collcollate),
! if_not_exists);
CreateComments(collid, CollationRelationId, 0,
get_icu_locale_comment(localeid));
}
--- 682,690 ----
nspid, GetUserId(), COLLPROVIDER_ICU, -1,
collcollate, collcollate,
get_collation_actual_version(COLLPROVIDER_ICU, collcollate),
! true, true);
! CommandCounterIncrement();
!
CreateComments(collid, CollationRelationId, 0,
get_icu_locale_comment(localeid));
}
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 0f22e6d..20c7d07 100644
*** a/src/bin/initdb/initdb.c
--- b/src/bin/initdb/initdb.c
*************** setup_description(FILE *cmdfd)
*** 1627,1633 ****
static void
setup_collation(FILE *cmdfd)
{
! PG_CMD_PUTS("SELECT pg_import_system_collations(if_not_exists => false, schema => 'pg_catalog');\n\n");
/* Add an SQL-standard name */
PG_CMD_PRINTF3("INSERT INTO pg_collation (collname, collnamespace, collowner, collprovider, collencoding, collcollate, collctype) VALUES ('ucs_basic', 'pg_catalog'::regnamespace, %u, '%c', %d, 'C', 'C');\n\n", BOOTSTRAP_SUPERUSERID, COLLPROVIDER_LIBC, PG_UTF8);
--- 1627,1633 ----
static void
setup_collation(FILE *cmdfd)
{
! PG_CMD_PUTS("SELECT pg_import_system_collations('pg_catalog');\n\n");
/* Add an SQL-standard name */
PG_CMD_PRINTF3("INSERT INTO pg_collation (collname, collnamespace, collowner, collprovider, collencoding, collcollate, collctype) VALUES ('ucs_basic', 'pg_catalog'::regnamespace, %u, '%c', %d, 'C', 'C');\n\n", BOOTSTRAP_SUPERUSERID, COLLPROVIDER_LIBC, PG_UTF8);
diff --git a/src/include/catalog/pg_collation_fn.h b/src/include/catalog/pg_collation_fn.h
index 26b8f0d..0ef3138 100644
*** a/src/include/catalog/pg_collation_fn.h
--- b/src/include/catalog/pg_collation_fn.h
*************** extern Oid CollationCreate(const char *c
*** 20,26 ****
int32 collencoding,
const char *collcollate, const char *collctype,
const char *collversion,
! bool if_not_exists);
extern void RemoveCollationById(Oid collationOid);
#endif /* PG_COLLATION_FN_H */
--- 20,27 ----
int32 collencoding,
const char *collcollate, const char *collctype,
const char *collversion,
! bool if_not_exists,
! bool quiet);
extern void RemoveCollationById(Oid collationOid);
#endif /* PG_COLLATION_FN_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 1191b4a..282a2ac 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("pg_controldata recovery state inf
*** 5462,5472 ****
DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
DESCR("pg_controldata init state information as a function");
! DATA(insert OID = 3445 ( pg_import_system_collations PGNSP PGUID 12 100 0 0 0 f f f f t f v r 2 0 2278 "16 4089" _null_ _null_ "{if_not_exists,schema}" _null_ _null_ pg_import_system_collations _null_ _null_ _null_ ));
DESCR("import collations from operating system");
DATA(insert OID = 3448 ( pg_collation_actual_version PGNSP PGUID 12 100 0 0 0 f f f f t f v s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_collation_actual_version _null_ _null_ _null_ ));
! DESCR("import collations from operating system");
/* system management/monitoring related functions */
DATA(insert OID = 3353 ( pg_ls_logdir PGNSP PGUID 12 10 20 0 0 f f f f t t v s 0 0 2249 "" "{25,20,1184}" "{o,o,o}" "{name,size,modification}" _null_ _null_ pg_ls_logdir _null_ _null_ _null_ ));
--- 5462,5473 ----
DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
DESCR("pg_controldata init state information as a function");
! /* collation management functions */
! DATA(insert OID = 3445 ( pg_import_system_collations PGNSP PGUID 12 100 0 0 0 f f f f t f v r 1 0 2278 "4089" _null_ _null_ _null_ _null_ _null_ pg_import_system_collations _null_ _null_ _null_ ));
DESCR("import collations from operating system");
DATA(insert OID = 3448 ( pg_collation_actual_version PGNSP PGUID 12 100 0 0 0 f f f f t f v s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_collation_actual_version _null_ _null_ _null_ ));
! DESCR("get actual version of collation from operating system");
/* system management/monitoring related functions */
DATA(insert OID = 3353 ( pg_ls_logdir PGNSP PGUID 12 10 20 0 0 f f f f t t v s 0 0 2249 "" "{25,20,1184}" "{o,o,o}" "{name,size,modification}" _null_ _null_ pg_ls_logdir _null_ _null_ _null_ ));