From d2ca145b6fc493596e2e23f0099084ca765435f6 Mon Sep 17 00:00:00 2001 From: heikki Date: Tue, 23 Sep 2008 09:20:39 +0000 Subject: [PATCH] Make LC_COLLATE and LC_CTYPE database-level settings. Collation and ctype are now more like encoding, stored in new datcollate and datctype columns in pg_database. This is a stripped-down version of Radek Strnad's patch, with further changes by me. --- doc/src/sgml/catalogs.sgml | 14 +++++ doc/src/sgml/charset.sgml | 71 +++++++++++-------------- doc/src/sgml/indices.sgml | 4 +- doc/src/sgml/ref/create_database.sgml | 43 +++++++++++++-- doc/src/sgml/ref/initdb.sgml | 39 ++++++++------ doc/src/sgml/ref/pg_controldata.sgml | 2 +- doc/src/sgml/ref/pg_resetxlog.sgml | 12 ++--- doc/src/sgml/ref/select.sgml | 3 +- doc/src/sgml/ref/show.sgml | 8 +-- doc/src/sgml/runtime.sgml | 11 ++-- doc/src/sgml/textsearch.sgml | 2 +- src/backend/access/transam/xlog.c | 43 +-------------- src/backend/commands/dbcommands.c | 92 +++++++++++++++++++++++++++++---- src/backend/parser/gram.y | 19 ++++++- src/backend/parser/keywords.c | 1 + src/backend/postmaster/postmaster.c | 8 +-- src/backend/utils/adt/pg_locale.c | 41 ++++++++++----- src/backend/utils/init/postinit.c | 24 +++++++++ src/bin/initdb/initdb.c | 14 +++-- src/bin/pg_controldata/pg_controldata.c | 7 --- src/bin/pg_dump/pg_dump.c | 38 +++++++++++++- src/bin/pg_dump/pg_dumpall.c | 39 ++++++++++++-- src/bin/pg_resetxlog/pg_resetxlog.c | 22 -------- src/bin/psql/describe.c | 13 +++-- src/bin/scripts/createdb.c | 17 ++++++ src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_control.h | 12 +---- src/include/catalog/pg_database.h | 24 +++++---- src/include/utils/pg_locale.h | 1 + src/interfaces/ecpg/preproc/preproc.y | 2 +- 30 files changed, 410 insertions(+), 218 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 70030925d2..5e1347f166 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -2150,6 +2150,20 @@ + datcollate + name + + LC_COLLATE for this database + + + + datctype + name + + LC_CTYPE for this database + + + datistemplate bool diff --git a/doc/src/sgml/charset.sgml b/doc/src/sgml/charset.sgml index f484db878c..4ec78303f7 100644 --- a/doc/src/sgml/charset.sgml +++ b/doc/src/sgml/charset.sgml @@ -130,23 +130,23 @@ initdb --locale=sv_SE The nature of some locale categories is that their value has to be - fixed for the lifetime of a database cluster. That is, once - initdb has run, you cannot change them anymore. - LC_COLLATE and LC_CTYPE are - those categories. They affect the sort order of indexes, so they - must be kept fixed, or indexes on text columns will become corrupt. - PostgreSQL enforces this by recording - the values of LC_COLLATE and LC_CTYPE that are - seen by initdb. The server automatically adopts - those two values when it is started. + fixed when the database is created. You can use different settings + for different databases, but once a database is created, you cannot + change them for that database anymore. LC_COLLATE + and LC_CTYPE are those categories. They affect + the sort order of indexes, so they must be kept fixed, or indexes on + text columns will become corrupt. The default values for these + categories are defined when initdb is run, and + those values are used when new databases are created, unless + specified otherwise in the CREATE DATABASE command. The other locale categories can be changed as desired whenever the server is running by setting the run-time configuration variables that have the same name as the locale categories (see for details). The defaults that are - chosen by initdb are actually only written into + linkend="runtime-config-client-format"> for details). The defaults + that are chosen by initdb are actually only written into the configuration file postgresql.conf to serve as defaults when the server is started. If you delete these assignments from postgresql.conf then the @@ -261,7 +261,7 @@ initdb --locale=sv_SE Check that PostgreSQL is actually using the locale - that you think it is. LC_COLLATE and LC_CTYPE + that you think it is. The default LC_COLLATE and LC_CTYPE settings are determined at initdb time and cannot be changed without repeating initdb. Other locale settings including LC_MESSAGES and LC_MONETARY @@ -319,17 +319,11 @@ initdb --locale=sv_SE - An important restriction, however, is that each database character set - must be compatible with the server's LC_CTYPE setting. + An important restriction, however, is that each database's character set + must be compatible with the database's LC_CTYPE setting. When LC_CTYPE is C or POSIX, any character set is allowed, but for other settings of LC_CTYPE there is only one character set that will work correctly. - Since the LC_CTYPE setting is frozen by initdb, the - apparent flexibility to use different encodings in different databases - of a cluster is more theoretical than real, except when you select - C or POSIX locale (thus disabling any real locale - awareness). It is likely that these mechanisms will be revisited in future - versions of PostgreSQL. @@ -734,19 +728,19 @@ initdb -E EUC_JP - If you have selected C or POSIX locale, - you can create a database with a different character set: + You can specify a non-default encoding at database creation time, + provided that the encoding is compatible with the selected locale: -createdb -E EUC_KR korean +createdb -E EUC_KR -T template0 --lc-collate=ko_KR.euckr --lc-ctype=ko_KR.euckr korean This will create a database named korean that - uses the character set EUC_KR. Another way to - accomplish this is to use this SQL command: + uses the character set EUC_KR, and locale ko_KR. + Another way to accomplish this is to use this SQL command: -CREATE DATABASE korean WITH ENCODING 'EUC_KR'; +CREATE DATABASE korean WITH ENCODING 'EUC_KR' COLLATE='ko_KR.euckr' CTYPE='ko_KR.euckr' TEMPLATE=template0; The encoding for a database is stored in the system catalog @@ -756,20 +750,17 @@ CREATE DATABASE korean WITH ENCODING 'EUC_KR'; $ psql -l - List of databases - Database | Owner | Encoding ----------------+---------+--------------- - euc_cn | t-ishii | EUC_CN - euc_jp | t-ishii | EUC_JP - euc_kr | t-ishii | EUC_KR - euc_tw | t-ishii | EUC_TW - mule_internal | t-ishii | MULE_INTERNAL - postgres | t-ishii | EUC_JP - regression | t-ishii | SQL_ASCII - template1 | t-ishii | EUC_JP - test | t-ishii | EUC_JP - utf8 | t-ishii | UTF8 -(9 rows) + List of databases + Name | Owner | Encoding | Collation | Ctype | Access Privileges +-----------+----------+-----------+-------------+-------------+------------------------------------- + clocaledb | hlinnaka | SQL_ASCII | C | C | + englishdb | hlinnaka | UTF8 | en_GB.UTF8 | en_GB.UTF8 | + japanese | hlinnaka | UTF8 | ja_JP.UTF8 | ja_JP.UTF8 | + korean | hlinnaka | EUC_KR | ko_KR.euckr | ko_KR.euckr | + postgres | hlinnaka | UTF8 | fi_FI.UTF8 | fi_FI.UTF8 | + template0 | hlinnaka | UTF8 | fi_FI.UTF8 | fi_FI.UTF8 | {=c/hlinnaka,hlinnaka=CTc/hlinnaka} + template1 | hlinnaka | UTF8 | fi_FI.UTF8 | fi_FI.UTF8 | {=c/hlinnaka,hlinnaka=CTc/hlinnaka} +(7 rows) diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml index 2d4a7bfefc..f02a9579d1 100644 --- a/doc/src/sgml/indices.sgml +++ b/doc/src/sgml/indices.sgml @@ -157,7 +157,7 @@ CREATE INDEX test1_id_index ON test1 (id); if the pattern is a constant and is anchored to the beginning of the string — for example, col LIKE 'foo%' or col ~ '^foo', but not - col LIKE '%bar'. However, if your server does not + col LIKE '%bar'. However, if your database does not use the C locale you will need to create the index with a special operator class to support indexing of pattern-matching queries. See below. It is also possible to use @@ -922,7 +922,7 @@ CREATE INDEX name ON table according to the locale-specific collation rules. This makes these operator classes suitable for use by queries involving pattern matching expressions (LIKE or POSIX - regular expressions) when the server does not use the standard + regular expressions) when the database does not use the standard C locale. As an example, you might index a varchar column like this: diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index 95350c4a1b..a343f2568f 100644 --- a/doc/src/sgml/ref/create_database.sgml +++ b/doc/src/sgml/ref/create_database.sgml @@ -24,6 +24,8 @@ CREATE DATABASE name [ [ WITH ] [ OWNER [=] dbowner ] [ TEMPLATE [=] template ] [ ENCODING [=] encoding ] + [ COLLATE [=] collate ] + [ CTYPE [=] ctype ] [ TABLESPACE [=] tablespace ] [ CONNECTION LIMIT [=] connlimit ] ] @@ -113,6 +115,29 @@ CREATE DATABASE name + collate + + + Collation order (LC_COLLATE) to use in the new database. + This affects the sort order applied to strings, e.g in queries with + ORDER BY, as well as the order used in indexes on text columns. + The default is to use the collation order of the template database. + See below for additional restrictions. + + + + + ctype + + + Character classification (LC_CTYPE) to use in the new + database. This affects the categorization of characters, e.g. lower, + upper and digit. The default is to use the character classification of + the template database. See below for additional restrictions. + + + + tablespace @@ -180,13 +205,11 @@ CREATE DATABASE name - Any character set encoding specified for the new database must be - compatible with the server's LC_CTYPE locale setting. + The character set encoding specified for the new database must be + compatible with the chosen COLLATE and CTYPE settings. If LC_CTYPE is C (or equivalently POSIX), then all encodings are allowed, but for other - locale settings there is only one encoding that will work properly, - and so the apparent freedom to specify an encoding is illusory if - you didn't initialize the database cluster in C locale. + locale settings there is only one encoding that will work properly. CREATE DATABASE will allow superusers to specify SQL_ASCII encoding regardless of the locale setting, but this choice is deprecated and may result in misbehavior of @@ -195,6 +218,16 @@ CREATE DATABASE name + The COLLATE and CTYPE settings must match + those of the template database, except when template0 is used as + template. This is because COLLATE and CTYPE + affects the ordering in indexes, so that any indexes copied from the + template database would be invalid in the new database with different + settings. template0, however, is known to not + contain any indexes that would be affected. + + + The CONNECTION LIMIT option is only enforced approximately; if two new sessions start at about the same time when just one connection slot remains for the database, it is possible that diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml index 10a9d1c77d..0e846bca99 100644 --- a/doc/src/sgml/ref/initdb.sgml +++ b/doc/src/sgml/ref/initdb.sgml @@ -76,25 +76,34 @@ PostgreSQL documentation initdb initializes the database cluster's default - locale and character set encoding. The collation order - (LC_COLLATE) and character set classes - (LC_CTYPE, e.g. upper, lower, digit) are fixed for all - databases and cannot be changed. Collation orders other than - C or POSIX also have a performance penalty. - For these reasons it is important to choose the right locale when - running initdb. The remaining locale categories - can be changed later when the server is started. All server locale - values (lc_*) can be displayed via SHOW ALL. + locale and character set encoding. The character set encoding, + collation order (LC_COLLATE) and character set classes + (LC_CTYPE, e.g. upper, lower, digit) can be set separately + for a database when it is created. initdb determines + those settings for the template1 database, which will + serve as the default for all other databases. + + + + To alter the default collation order or character set classes, use the + and options. + Collation orders other than C or POSIX also have + a performance penalty. For these reasons it is important to choose the + right locale when running initdb. + + + + The remaining locale categories can be changed later when the server + is started. You can also use to set the + default for all locale categories, including collation order and + character set classes. All server locale values (lc_*) can + be displayed via SHOW ALL. More details can be found in . - The character set encoding can be set separately for a database when - it is created. initdb determines the encoding for - the template1 database, which will serve as the - default for all other databases. To alter the default encoding use - the option. More details can be found in - . + To alter the default encoding, use the . + More details can be found in . diff --git a/doc/src/sgml/ref/pg_controldata.sgml b/doc/src/sgml/ref/pg_controldata.sgml index b379df047f..7f50b7be7a 100644 --- a/doc/src/sgml/ref/pg_controldata.sgml +++ b/doc/src/sgml/ref/pg_controldata.sgml @@ -30,7 +30,7 @@ PostgreSQL documentation Description pg_controldata prints information initialized during - initdb, such as the catalog version and server locale. + initdb, such as the catalog version. It also shows information about write-ahead logging and checkpoint processing. This information is cluster-wide, and not specific to any one database. diff --git a/doc/src/sgml/ref/pg_resetxlog.sgml b/doc/src/sgml/ref/pg_resetxlog.sgml index 193345583c..b4306e6ccc 100644 --- a/doc/src/sgml/ref/pg_resetxlog.sgml +++ b/doc/src/sgml/ref/pg_resetxlog.sgml @@ -62,14 +62,10 @@ PostgreSQL documentation by specifying the -f (force) switch. In this case plausible values will be substituted for the missing data. Most of the fields can be expected to match, but manual assistance might be needed for the next OID, - next transaction ID and epoch, next multitransaction ID and offset, - WAL starting address, and database locale fields. - The first six of these can be set using the switches discussed below. - pg_resetxlog's own environment is the source for its - guess at the locale fields; take care that LANG and so forth - match the environment that initdb was run in. - If you are not able to determine correct values for all these fields, - -f can still be used, but + next transaction ID and epoch, next multitransaction ID and offset, and + WAL starting address fields. These fields can be set using the switches + discussed below. If you are not able to determine correct values for all + these fields, -f can still be used, but the recovered database must be treated with even more suspicion than usual: an immediate dump and reload is imperative. Do not execute any data-modifying operations in the database before you dump, diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index e463d15450..a1be7cb786 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -747,8 +747,7 @@ SELECT name FROM distributors ORDER BY code; Character-string data is sorted according to the locale-specific - collation order that was established when the database cluster - was initialized. + collation order that was established when the database was created. diff --git a/doc/src/sgml/ref/show.sgml b/doc/src/sgml/ref/show.sgml index 3d238c313d..00fc0de7f0 100644 --- a/doc/src/sgml/ref/show.sgml +++ b/doc/src/sgml/ref/show.sgml @@ -82,8 +82,8 @@ SHOW ALL Shows the database's locale setting for collation (text ordering). At present, this parameter can be shown but not - set, because the setting is determined at - initdb time. + set, because the setting is determined at database creation + time. @@ -94,8 +94,8 @@ SHOW ALL Shows the database's locale setting for character classification. At present, this parameter can be shown but - not set, because the setting is determined at - initdb time. + not set, because the setting is determined at database creation + time. diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 5ce6163d11..4b7f5eb6c2 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -145,11 +145,12 @@ postgres$ initdb -D /usr/local/pgsql/data Normally, it will just take the locale settings in the environment and apply them to the initialized database. It is possible to specify a different locale for the database; more information about - that can be found in . The sort order used - within a particular database cluster is set by - initdb and cannot be changed later, short of - dumping all data, rerunning initdb, and reloading - the data. There is also a performance impact for using locales + that can be found in . The default sort order used + within the particular database cluster is set by + initdb, and while you can create new databases using + different sort order, the order used in the template databases that initdb + creates cannot be changed without dropping and recreating them. + There is also a performance impact for using locales other than C or POSIX. Therefore, it is important to make this choice correctly the first time. diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml index e48f82bb1a..98ac7f8c45 100644 --- a/doc/src/sgml/textsearch.sgml +++ b/doc/src/sgml/textsearch.sgml @@ -1896,7 +1896,7 @@ LIMIT 10; - The parser's notion of a letter is determined by the server's + The parser's notion of a letter is determined by the database's locale setting, specifically lc_ctype. Words containing only the basic ASCII letters are reported as a separate token type, since it is sometimes useful to distinguish them. In most European diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 5645271171..844aba6200 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -46,7 +46,7 @@ #include "storage/smgr.h" #include "storage/spin.h" #include "utils/builtins.h" -#include "utils/pg_locale.h" +#include "utils/guc.h" #include "utils/ps_status.h" @@ -3847,7 +3847,6 @@ WriteControlFile(void) { int fd; char buffer[PG_CONTROL_SIZE]; /* need not be aligned */ - char *localeptr; /* * Initialize version and compatibility-check fields @@ -3876,18 +3875,6 @@ WriteControlFile(void) ControlFile->float4ByVal = FLOAT4PASSBYVAL; ControlFile->float8ByVal = FLOAT8PASSBYVAL; - ControlFile->localeBuflen = LOCALE_NAME_BUFLEN; - localeptr = setlocale(LC_COLLATE, NULL); - if (!localeptr) - ereport(PANIC, - (errmsg("invalid LC_COLLATE setting"))); - StrNCpy(ControlFile->lc_collate, localeptr, LOCALE_NAME_BUFLEN); - localeptr = setlocale(LC_CTYPE, NULL); - if (!localeptr) - ereport(PANIC, - (errmsg("invalid LC_CTYPE setting"))); - StrNCpy(ControlFile->lc_ctype, localeptr, LOCALE_NAME_BUFLEN); - /* Contents are protected with a CRC */ INIT_CRC32(ControlFile->crc); COMP_CRC32(ControlFile->crc, @@ -4126,34 +4113,6 @@ ReadControlFile(void) " but the server was compiled without USE_FLOAT8_BYVAL."), errhint("It looks like you need to recompile or initdb."))); #endif - - if (ControlFile->localeBuflen != LOCALE_NAME_BUFLEN) - ereport(FATAL, - (errmsg("database files are incompatible with server"), - errdetail("The database cluster was initialized with LOCALE_NAME_BUFLEN %d," - " but the server was compiled with LOCALE_NAME_BUFLEN %d.", - ControlFile->localeBuflen, LOCALE_NAME_BUFLEN), - errhint("It looks like you need to recompile or initdb."))); - if (pg_perm_setlocale(LC_COLLATE, ControlFile->lc_collate) == NULL) - ereport(FATAL, - (errmsg("database files are incompatible with operating system"), - errdetail("The database cluster was initialized with LC_COLLATE \"%s\"," - " which is not recognized by setlocale().", - ControlFile->lc_collate), - errhint("It looks like you need to initdb or install locale support."))); - if (pg_perm_setlocale(LC_CTYPE, ControlFile->lc_ctype) == NULL) - ereport(FATAL, - (errmsg("database files are incompatible with operating system"), - errdetail("The database cluster was initialized with LC_CTYPE \"%s\"," - " which is not recognized by setlocale().", - ControlFile->lc_ctype), - errhint("It looks like you need to initdb or install locale support."))); - - /* Make the fixed locale settings visible as GUC variables, too */ - SetConfigOption("lc_collate", ControlFile->lc_collate, - PGC_INTERNAL, PGC_S_OVERRIDE); - SetConfigOption("lc_ctype", ControlFile->lc_ctype, - PGC_INTERNAL, PGC_S_OVERRIDE); } void diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 4dd6262d79..c3b465367e 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -53,6 +53,7 @@ #include "utils/fmgroids.h" #include "utils/guc.h" #include "utils/lsyscache.h" +#include "utils/pg_locale.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -69,7 +70,7 @@ static bool get_db_info(const char *name, LOCKMODE lockmode, Oid *dbIdP, Oid *ownerIdP, int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, Oid *dbLastSysOidP, TransactionId *dbFrozenXidP, - Oid *dbTablespace); + Oid *dbTablespace, char **dbCollate, char **dbCtype); static bool have_createdb_privilege(void); static void remove_dbtablespaces(Oid db_id); static bool check_db_file_conflict(Oid db_id); @@ -87,6 +88,8 @@ createdb(const CreatedbStmt *stmt) Oid src_dboid; Oid src_owner; int src_encoding; + char *src_collate; + char *src_ctype; bool src_istemplate; bool src_allowconn; Oid src_lastsysoid; @@ -104,10 +107,14 @@ createdb(const CreatedbStmt *stmt) DefElem *downer = NULL; DefElem *dtemplate = NULL; DefElem *dencoding = NULL; + DefElem *dcollate = NULL; + DefElem *dctype = NULL; DefElem *dconnlimit = NULL; char *dbname = stmt->dbname; char *dbowner = NULL; const char *dbtemplate = NULL; + char *dbcollate = NULL; + char *dbctype = NULL; int encoding = -1; int dbconnlimit = -1; int ctype_encoding; @@ -152,6 +159,22 @@ createdb(const CreatedbStmt *stmt) errmsg("conflicting or redundant options"))); dencoding = defel; } + else if (strcmp(defel->defname, "collate") == 0) + { + if (dcollate) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dcollate = defel; + } + else if (strcmp(defel->defname, "ctype") == 0) + { + if (dctype) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dctype = defel; + } else if (strcmp(defel->defname, "connectionlimit") == 0) { if (dconnlimit) @@ -205,6 +228,11 @@ createdb(const CreatedbStmt *stmt) elog(ERROR, "unrecognized node type: %d", nodeTag(dencoding->arg)); } + if (dcollate && dcollate->arg) + dbcollate = strVal(dcollate->arg); + if (dctype && dctype->arg) + dbctype = strVal(dctype->arg); + if (dconnlimit && dconnlimit->arg) dbconnlimit = intVal(dconnlimit->arg); @@ -243,7 +271,8 @@ createdb(const CreatedbStmt *stmt) if (!get_db_info(dbtemplate, ShareLock, &src_dboid, &src_owner, &src_encoding, &src_istemplate, &src_allowconn, &src_lastsysoid, - &src_frozenxid, &src_deftablespace)) + &src_frozenxid, &src_deftablespace, + &src_collate, &src_ctype)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("template database \"%s\" does not exist", @@ -262,9 +291,13 @@ createdb(const CreatedbStmt *stmt) dbtemplate))); } - /* If encoding is defaulted, use source's encoding */ + /* If encoding or locales are defaulted, use source's setting */ if (encoding < 0) encoding = src_encoding; + if (dbcollate == NULL) + dbcollate = src_collate; + if (dbctype == NULL) + dbctype = src_ctype; /* Some encodings are client only */ if (!PG_VALID_BE_ENCODING(encoding)) @@ -272,6 +305,16 @@ createdb(const CreatedbStmt *stmt) (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("invalid server encoding %d", encoding))); + /* Check that the chosen locales are valid */ + if (!check_locale(LC_COLLATE, dbcollate)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("invalid locale name %s", dbcollate))); + if (!check_locale(LC_CTYPE, dbctype)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("invalid locale name %s", dbctype))); + /* * Check whether encoding matches server locale settings. We allow * mismatch in three cases: @@ -290,7 +333,7 @@ createdb(const CreatedbStmt *stmt) * * Note: if you change this policy, fix initdb to match. */ - ctype_encoding = pg_get_encoding_from_locale(NULL); + ctype_encoding = pg_get_encoding_from_locale(dbctype); if (!(ctype_encoding == encoding || ctype_encoding == PG_SQL_ASCII || @@ -299,12 +342,32 @@ createdb(const CreatedbStmt *stmt) #endif (encoding == PG_SQL_ASCII && superuser()))) ereport(ERROR, - (errmsg("encoding %s does not match server's locale %s", + (errmsg("encoding %s does not match locale %s", pg_encoding_to_char(encoding), - setlocale(LC_CTYPE, NULL)), - errdetail("The server's LC_CTYPE setting requires encoding %s.", + dbctype), + errdetail("The chosen LC_CTYPE setting requires encoding %s.", pg_encoding_to_char(ctype_encoding)))); + /* + * Check that the new locale is compatible with the source database. + * + * We know that template0 doesn't contain any indexes that depend on + * collation or ctype, so template0 can be used as template for + * any locale. + */ + if (strcmp(dbtemplate, "template0") != 0) + { + if (strcmp(dbcollate, src_collate)) + ereport(ERROR, + (errmsg("new collation is incompatible with the collation of the template database (%s)", src_collate), + errhint("Use the same collation as in the template database, or use template0 as template"))); + + if (strcmp(dbctype, src_ctype)) + ereport(ERROR, + (errmsg("new ctype is incompatible with the ctype of the template database (%s)", src_ctype), + errhint("Use the same ctype as in the template database, or use template0 as template"))); + } + /* Resolve default tablespace for new database */ if (dtablespacename && dtablespacename->arg) { @@ -421,6 +484,10 @@ createdb(const CreatedbStmt *stmt) DirectFunctionCall1(namein, CStringGetDatum(dbname)); new_record[Anum_pg_database_datdba - 1] = ObjectIdGetDatum(datdba); new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding); + new_record[Anum_pg_database_datcollate - 1] = + DirectFunctionCall1(namein, CStringGetDatum(dbcollate)); + new_record[Anum_pg_database_datctype - 1] = + DirectFunctionCall1(namein, CStringGetDatum(dbctype)); new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false); new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true); new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit); @@ -629,7 +696,7 @@ dropdb(const char *dbname, bool missing_ok) pgdbrel = heap_open(DatabaseRelationId, RowExclusiveLock); if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL, - &db_istemplate, NULL, NULL, NULL, NULL)) + &db_istemplate, NULL, NULL, NULL, NULL, NULL, NULL)) { if (!missing_ok) { @@ -781,7 +848,7 @@ RenameDatabase(const char *oldname, const char *newname) rel = heap_open(DatabaseRelationId, RowExclusiveLock); if (!get_db_info(oldname, AccessExclusiveLock, &db_id, NULL, NULL, - NULL, NULL, NULL, NULL, NULL)) + NULL, NULL, NULL, NULL, NULL, NULL, NULL)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", oldname))); @@ -1168,7 +1235,7 @@ get_db_info(const char *name, LOCKMODE lockmode, Oid *dbIdP, Oid *ownerIdP, int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP, Oid *dbLastSysOidP, TransactionId *dbFrozenXidP, - Oid *dbTablespace) + Oid *dbTablespace, char **dbCollate, char **dbCtype) { bool result = false; Relation relation; @@ -1259,6 +1326,11 @@ get_db_info(const char *name, LOCKMODE lockmode, /* default tablespace for this database */ if (dbTablespace) *dbTablespace = dbform->dattablespace; + /* default locale settings for this database */ + if (dbCollate) + *dbCollate = pstrdup(NameStr(dbform->datcollate)); + if (dbCtype) + *dbCtype = pstrdup(NameStr(dbform->datctype)); ReleaseSysCache(tuple); result = true; break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 5ff353df0b..0831a94725 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -398,7 +398,7 @@ static TypeName *TableFuncTypeName(List *columns); CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB - CREATEROLE CREATEUSER CROSS CSV CURRENT_P CURRENT_DATE CURRENT_ROLE + CREATEROLE CREATEUSER CROSS CSV CTYPE CURRENT_P CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS @@ -5458,6 +5458,22 @@ createdb_opt_item: { $$ = makeDefElem("encoding", NULL); } + | COLLATE opt_equal Sconst + { + $$ = makeDefElem("collate", (Node *)makeString($3)); + } + | COLLATE opt_equal DEFAULT + { + $$ = makeDefElem("collate", NULL); + } + | CTYPE opt_equal Sconst + { + $$ = makeDefElem("ctype", (Node *)makeString($3)); + } + | CTYPE opt_equal DEFAULT + { + $$ = makeDefElem("ctype", NULL); + } | CONNECTION LIMIT opt_equal SignedIconst { $$ = makeDefElem("connectionlimit", (Node *)makeInteger($4)); @@ -9216,6 +9232,7 @@ unreserved_keyword: | CREATEROLE | CREATEUSER | CSV + | CTYPE | CURRENT_P | CURSOR | CYCLE diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index b30a478046..e2fe4bf000 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -114,6 +114,7 @@ const ScanKeyword ScanKeywords[] = { {"createuser", CREATEUSER, UNRESERVED_KEYWORD}, {"cross", CROSS, TYPE_FUNC_NAME_KEYWORD}, {"csv", CSV, UNRESERVED_KEYWORD}, + {"ctype", CTYPE, UNRESERVED_KEYWORD}, {"current", CURRENT_P, UNRESERVED_KEYWORD}, {"current_date", CURRENT_DATE, RESERVED_KEYWORD}, {"current_role", CURRENT_ROLE, RESERVED_KEYWORD}, diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index b93cb673d2..8a67ec550b 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -403,8 +403,8 @@ typedef struct char my_exec_path[MAXPGPATH]; char pkglib_path[MAXPGPATH]; char ExtraOptions[MAXPGPATH]; - char lc_collate[LOCALE_NAME_BUFLEN]; - char lc_ctype[LOCALE_NAME_BUFLEN]; + char lc_collate[NAMEDATALEN]; + char lc_ctype[NAMEDATALEN]; } BackendParameters; static void read_backend_variables(char *id, Port *port); @@ -4294,8 +4294,8 @@ save_backend_variables(BackendParameters * param, Port *port, strlcpy(param->ExtraOptions, ExtraOptions, MAXPGPATH); - strlcpy(param->lc_collate, setlocale(LC_COLLATE, NULL), LOCALE_NAME_BUFLEN); - strlcpy(param->lc_ctype, setlocale(LC_CTYPE, NULL), LOCALE_NAME_BUFLEN); + strlcpy(param->lc_collate, setlocale(LC_COLLATE, NULL), NAMEDATALEN); + strlcpy(param->lc_ctype, setlocale(LC_CTYPE, NULL), NAMEDATALEN); return true; } diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index c0a01ae155..6de06a7663 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -76,7 +76,7 @@ static bool CurrentLCTimeValid = false; /* Environment variable storage area */ -#define LC_ENV_BUFSIZE (LOCALE_NAME_BUFLEN + 20) +#define LC_ENV_BUFSIZE (NAMEDATALEN + 20) static char lc_collate_envbuf[LC_ENV_BUFSIZE]; static char lc_ctype_envbuf[LC_ENV_BUFSIZE]; @@ -189,6 +189,31 @@ pg_perm_setlocale(int category, const char *locale) } +/* + * Is the locale name valid for the locale category? + */ +bool +check_locale(int category, const char *value) +{ + char *save; + bool ret; + + save = setlocale(category, NULL); + if (!save) + return false; /* won't happen, we hope */ + + /* save may be pointing at a modifiable scratch variable, see above */ + save = pstrdup(save); + + /* set the locale with setlocale, to see if it accepts it. */ + ret = (setlocale(category, value) != NULL); + + setlocale(category, save); /* assume this won't fail */ + pfree(save); + + return ret; +} + /* GUC assign hooks */ /* @@ -203,21 +228,9 @@ pg_perm_setlocale(int category, const char *locale) static const char * locale_xxx_assign(int category, const char *value, bool doit, GucSource source) { - char *save; - - save = setlocale(category, NULL); - if (!save) - return NULL; /* won't happen, we hope */ - - /* save may be pointing at a modifiable scratch variable, see above */ - save = pstrdup(save); - - if (!setlocale(category, value)) + if (!check_locale(category, value)) value = NULL; /* set failure return marker */ - setlocale(category, save); /* assume this won't fail */ - pfree(save); - /* need to reload cache next time? */ if (doit && value != NULL) { diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 461edd96cf..2738c91a1e 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -159,6 +159,8 @@ CheckMyDatabase(const char *name, bool am_superuser) { HeapTuple tup; Form_pg_database dbform; + char *collate; + char *ctype; /* Fetch our real pg_database row */ tup = SearchSysCache(DATABASEOID, @@ -240,6 +242,28 @@ CheckMyDatabase(const char *name, bool am_superuser) /* If we have no other source of client_encoding, use server encoding */ SetConfigOption("client_encoding", GetDatabaseEncodingName(), PGC_BACKEND, PGC_S_DEFAULT); + + /* assign locale variables */ + collate = NameStr(dbform->datcollate); + ctype = NameStr(dbform->datctype); + + if (setlocale(LC_COLLATE, collate) == NULL) + ereport(FATAL, + (errmsg("database locale is incompatible with operating system"), + errdetail("The database was initialized with LC_COLLATE \"%s\", " + " which is not recognized by setlocale().", collate), + errhint("Recreate the database with another locale or install the missing locale."))); + + if (setlocale(LC_CTYPE, ctype) == NULL) + ereport(FATAL, + (errmsg("database locale is incompatible with operating system"), + errdetail("The database was initialized with LC_CTYPE \"%s\", " + " which is not recognized by setlocale().", ctype), + errhint("Recreate the database with another locale or install the missing locale."))); + + /* Make the locale settings visible as GUC variables, too */ + SetConfigOption("lc_collate", collate, PGC_INTERNAL, PGC_S_OVERRIDE); + SetConfigOption("lc_ctype", ctype, PGC_INTERNAL, PGC_S_OVERRIDE); /* * Lastly, set up any database-specific configuration variables. diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 3effafbc83..0b25d9ab69 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -1353,6 +1353,10 @@ bootstrap_template1(char *short_version) bki_lines = replace_token(bki_lines, "ENCODING", encodingid); + bki_lines = replace_token(bki_lines, "LC_COLLATE", lc_collate); + + bki_lines = replace_token(bki_lines, "LC_CTYPE", lc_ctype); + /* * Pass correct LC_xxx environment to bootstrap. * @@ -2179,6 +2183,8 @@ locale_date_order(const char *locale) /* * check if given string is a valid locale specifier + * + * this should match the backend check_locale() function */ static bool chklocale(const char *locale) @@ -2378,12 +2384,12 @@ usage(const char *progname) printf(_("\nOptions:\n")); printf(_(" [-D, --pgdata=]DATADIR location for this database cluster\n")); printf(_(" -E, --encoding=ENCODING set default encoding for new databases\n")); - printf(_(" --locale=LOCALE initialize database cluster with given locale\n")); + printf(_(" --locale=LOCALE set default locale for new databases\n")); printf(_(" --lc-collate, --lc-ctype, --lc-messages=LOCALE\n" " --lc-monetary, --lc-numeric, --lc-time=LOCALE\n" - " initialize database cluster with given locale\n" - " in the respective category (default taken from\n" - " environment)\n")); + " set default locale in the respective\n" + " category for new databases (default\n" + " taken from environment)\n")); printf(_(" --no-locale equivalent to --locale=C\n")); printf(_(" -T, --text-search-config=CFG\n" " default text search configuration\n")); diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 7970c32ec4..08e102f391 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -220,12 +220,5 @@ main(int argc, char *argv[]) (ControlFile.float4ByVal ? _("by value") : _("by reference"))); printf(_("Float8 argument passing: %s\n"), (ControlFile.float8ByVal ? _("by value") : _("by reference"))); - printf(_("Maximum length of locale name: %u\n"), - ControlFile.localeBuflen); - printf(_("LC_COLLATE: %s\n"), - ControlFile.lc_collate); - printf(_("LC_CTYPE: %s\n"), - ControlFile.lc_ctype); - return 0; } diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 52e56117b9..6a31dccc35 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1542,12 +1542,16 @@ dumpDatabase(Archive *AH) i_oid, i_dba, i_encoding, + i_collate, + i_ctype, i_tablespace; CatalogId dbCatId; DumpId dbDumpId; const char *datname, *dba, *encoding, + *collate, + *ctype, *tablespace; datname = PQdb(g_conn); @@ -1559,11 +1563,26 @@ dumpDatabase(Archive *AH) selectSourceSchema("pg_catalog"); /* Get the database owner and parameters from pg_database */ - if (g_fout->remoteVersion >= 80200) + if (g_fout->remoteVersion >= 80400) + { + appendPQExpBuffer(dbQry, "SELECT tableoid, oid, " + "(%s datdba) as dba, " + "pg_encoding_to_char(encoding) as encoding, " + "datcollate, datctype, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) as tablespace, " + "shobj_description(oid, 'pg_database') as description " + + "FROM pg_database " + "WHERE datname = ", + username_subquery); + appendStringLiteralAH(dbQry, datname, AH); + } + else if (g_fout->remoteVersion >= 80200) { appendPQExpBuffer(dbQry, "SELECT tableoid, oid, " "(%s datdba) as dba, " "pg_encoding_to_char(encoding) as encoding, " + "NULL as datcollate, NULL as datctype, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) as tablespace, " "shobj_description(oid, 'pg_database') as description " @@ -1577,6 +1596,7 @@ dumpDatabase(Archive *AH) appendPQExpBuffer(dbQry, "SELECT tableoid, oid, " "(%s datdba) as dba, " "pg_encoding_to_char(encoding) as encoding, " + "NULL as datcollate, NULL as datctype, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) as tablespace " "FROM pg_database " "WHERE datname = ", @@ -1588,6 +1608,7 @@ dumpDatabase(Archive *AH) appendPQExpBuffer(dbQry, "SELECT tableoid, oid, " "(%s datdba) as dba, " "pg_encoding_to_char(encoding) as encoding, " + "NULL as datcollate, NULL as datctype, " "NULL as tablespace " "FROM pg_database " "WHERE datname = ", @@ -1601,6 +1622,7 @@ dumpDatabase(Archive *AH) "oid, " "(%s datdba) as dba, " "pg_encoding_to_char(encoding) as encoding, " + "NULL as datcollate, NULL as datctype, " "NULL as tablespace " "FROM pg_database " "WHERE datname = ", @@ -1631,12 +1653,16 @@ dumpDatabase(Archive *AH) i_oid = PQfnumber(res, "oid"); i_dba = PQfnumber(res, "dba"); i_encoding = PQfnumber(res, "encoding"); + i_collate = PQfnumber(res, "collate"); + i_ctype = PQfnumber(res, "ctype"); i_tablespace = PQfnumber(res, "tablespace"); dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid)); dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid)); dba = PQgetvalue(res, 0, i_dba); encoding = PQgetvalue(res, 0, i_encoding); + collate = PQgetvalue(res, 0, i_collate); + ctype = PQgetvalue(res, 0, i_ctype); tablespace = PQgetvalue(res, 0, i_tablespace); appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0", @@ -1646,6 +1672,16 @@ dumpDatabase(Archive *AH) appendPQExpBuffer(creaQry, " ENCODING = "); appendStringLiteralAH(creaQry, encoding, AH); } + if (strlen(collate) > 0) + { + appendPQExpBuffer(creaQry, " COLLATE = "); + appendStringLiteralAH(creaQry, collate, AH); + } + if (strlen(ctype) > 0) + { + appendPQExpBuffer(creaQry, " CTYPE = "); + appendStringLiteralAH(creaQry, ctype, AH); + } if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0) appendPQExpBuffer(creaQry, " TABLESPACE = %s", fmtId(tablespace)); diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index fa51af0f3c..b00fb5ae95 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -925,11 +925,22 @@ dumpCreateDB(PGconn *conn) fprintf(OPF, "--\n-- Database creation\n--\n\n"); - if (server_version >= 80100) + if (server_version >= 80400) res = executeQuery(conn, "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " + "datcollate, datctype, " + "datistemplate, datacl, datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 80100) + res = executeQuery(conn, + "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, " "datistemplate, datacl, datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " @@ -939,6 +950,7 @@ dumpCreateDB(PGconn *conn) "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, " "datistemplate, datacl, -1 as datconnlimit, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " @@ -948,6 +960,7 @@ dumpCreateDB(PGconn *conn) "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, " "datistemplate, datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " @@ -959,6 +972,7 @@ dumpCreateDB(PGconn *conn) "(select usename from pg_shadow where usesysid=datdba), " "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, " "datistemplate, '' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " "FROM pg_database d " @@ -973,6 +987,7 @@ dumpCreateDB(PGconn *conn) "SELECT datname, " "(select usename from pg_shadow where usesysid=datdba), " "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, " "'f' as datistemplate, " "'' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " @@ -985,10 +1000,12 @@ dumpCreateDB(PGconn *conn) char *dbname = PQgetvalue(res, i, 0); char *dbowner = PQgetvalue(res, i, 1); char *dbencoding = PQgetvalue(res, i, 2); - char *dbistemplate = PQgetvalue(res, i, 3); - char *dbacl = PQgetvalue(res, i, 4); - char *dbconnlimit = PQgetvalue(res, i, 5); - char *dbtablespace = PQgetvalue(res, i, 6); + char *dbcollate = PQgetvalue(res, i, 3); + char *dbctype = PQgetvalue(res, i, 4); + char *dbistemplate = PQgetvalue(res, i, 5); + char *dbacl = PQgetvalue(res, i, 6); + char *dbconnlimit = PQgetvalue(res, i, 7); + char *dbtablespace = PQgetvalue(res, i, 8); char *fdbname; fdbname = strdup(fmtId(dbname)); @@ -1016,6 +1033,18 @@ dumpCreateDB(PGconn *conn) appendPQExpBuffer(buf, " ENCODING = "); appendStringLiteralConn(buf, dbencoding, conn); + if (strlen(dbcollate) != 0) + { + appendPQExpBuffer(buf, " COLLATE = "); + appendStringLiteralConn(buf, dbcollate, conn); + } + + if (strlen(dbctype) != 0) + { + appendPQExpBuffer(buf, " CTYPE = "); + appendStringLiteralConn(buf, dbctype, conn); + } + /* * Output tablespace if it isn't the default. For default, it * uses the default from the template database. If tablespace is diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c index 345b89cb80..0df796b680 100644 --- a/src/bin/pg_resetxlog/pg_resetxlog.c +++ b/src/bin/pg_resetxlog/pg_resetxlog.c @@ -493,22 +493,6 @@ GuessControlValues(void) #endif ControlFile.float4ByVal = FLOAT4PASSBYVAL; ControlFile.float8ByVal = FLOAT8PASSBYVAL; - ControlFile.localeBuflen = LOCALE_NAME_BUFLEN; - - localeptr = setlocale(LC_COLLATE, ""); - if (!localeptr) - { - fprintf(stderr, _("%s: invalid LC_COLLATE setting\n"), progname); - exit(1); - } - strlcpy(ControlFile.lc_collate, localeptr, sizeof(ControlFile.lc_collate)); - localeptr = setlocale(LC_CTYPE, ""); - if (!localeptr) - { - fprintf(stderr, _("%s: invalid LC_CTYPE setting\n"), progname); - exit(1); - } - strlcpy(ControlFile.lc_ctype, localeptr, sizeof(ControlFile.lc_ctype)); /* * XXX eventually, should try to grovel through old XLOG to develop more @@ -584,12 +568,6 @@ PrintControlValues(bool guessed) (ControlFile.float4ByVal ? _("by value") : _("by reference"))); printf(_("Float8 argument passing: %s\n"), (ControlFile.float8ByVal ? _("by value") : _("by reference"))); - printf(_("Maximum length of locale name: %u\n"), - ControlFile.localeBuflen); - printf(_("LC_COLLATE: %s\n"), - ControlFile.lc_collate); - printf(_("LC_CTYPE: %s\n"), - ControlFile.lc_ctype); } diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 900130d6eb..dbbd1ed656 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -454,11 +454,18 @@ listAllDbs(bool verbose) printfPQExpBuffer(&buf, "SELECT d.datname as \"%s\",\n" " pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n" - " pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n" - " d.datacl as \"%s\"", + " pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n", gettext_noop("Name"), gettext_noop("Owner"), - gettext_noop("Encoding"), + gettext_noop("Encoding")); + if (pset.sversion >= 80400) + appendPQExpBuffer(&buf, + " d.datcollate as \"%s\",\n" + " d.datctype as \"%s\",\n", + gettext_noop("Collation"), + gettext_noop("Ctype")); + appendPQExpBuffer(&buf, + " d.datacl as \"%s\"", gettext_noop("Access Privileges")); if (verbose && pset.sversion >= 80200) appendPQExpBuffer(&buf, diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c index 286667e83c..543a83fdeb 100644 --- a/src/bin/scripts/createdb.c +++ b/src/bin/scripts/createdb.c @@ -32,6 +32,8 @@ main(int argc, char *argv[]) {"tablespace", required_argument, NULL, 'D'}, {"template", required_argument, NULL, 'T'}, {"encoding", required_argument, NULL, 'E'}, + {"lc-collate", required_argument, NULL, 1}, + {"lc-ctype", required_argument, NULL, 2}, {NULL, 0, NULL, 0} }; @@ -50,6 +52,8 @@ main(int argc, char *argv[]) char *tablespace = NULL; char *template = NULL; char *encoding = NULL; + char *lc_collate = NULL; + char *lc_ctype = NULL; PQExpBufferData sql; @@ -95,6 +99,12 @@ main(int argc, char *argv[]) case 'E': encoding = optarg; break; + case 1: + lc_collate = optarg; + break; + case 2: + lc_ctype = optarg; + break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); @@ -152,6 +162,11 @@ main(int argc, char *argv[]) appendPQExpBuffer(&sql, " ENCODING '%s'", encoding); if (template) appendPQExpBuffer(&sql, " TEMPLATE %s", fmtId(template)); + if (lc_collate) + appendPQExpBuffer(&sql, " COLLATE '%s'", lc_collate); + if (lc_ctype) + appendPQExpBuffer(&sql, " CTYPE '%s'", lc_ctype); + appendPQExpBuffer(&sql, ";\n"); conn = connectDatabase(strcmp(dbname, "postgres") == 0 ? "template1" : "postgres", @@ -209,6 +224,8 @@ help(const char *progname) printf(_("\nOptions:\n")); printf(_(" -D, --tablespace=TABLESPACE default tablespace for the database\n")); printf(_(" -E, --encoding=ENCODING encoding for the database\n")); + printf(_(" --lc-collate=LOCALE LC_COLLATE setting for the database\n")); + printf(_(" --lc-ctype=LOCALE LC_CTYPE setting for the database\n")); printf(_(" -O, --owner=OWNER database user to own the new database\n")); printf(_(" -T, --template=TEMPLATE template database to copy\n")); printf(_(" -e, --echo show the commands being sent to the server\n")); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 54f6366927..85981f8b2c 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200809191 +#define CATALOG_VERSION_NO 200809231 #endif diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index 38b5a84fa6..38a8208797 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -21,7 +21,7 @@ /* Version identifier for this pg_control format */ -#define PG_CONTROL_VERSION 842 +#define PG_CONTROL_VERSION 843 /* * Body of CheckPoint XLOG records. This is declared here because we keep @@ -59,15 +59,12 @@ typedef enum DBState DB_IN_PRODUCTION } DBState; -#define LOCALE_NAME_BUFLEN 128 - /* * Contents of pg_control. * * NOTE: try to keep this under 512 bytes so that it will fit on one physical * sector of typical disk drives. This reduces the odds of corruption due to - * power failure midway through a write. Currently it fits comfortably, - * but we could probably reduce LOCALE_NAME_BUFLEN if things get tight. + * power failure midway through a write. */ typedef struct ControlFileData @@ -144,11 +141,6 @@ typedef struct ControlFileData bool float4ByVal; /* float4 pass-by-value? */ bool float8ByVal; /* float8, int8, etc pass-by-value? */ - /* active locales */ - uint32 localeBuflen; - char lc_collate[LOCALE_NAME_BUFLEN]; - char lc_ctype[LOCALE_NAME_BUFLEN]; - /* CRC of all above ... MUST BE LAST! */ pg_crc32 crc; } ControlFileData; diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h index 6e9e5d23bf..8eaa04f4cf 100644 --- a/src/include/catalog/pg_database.h +++ b/src/include/catalog/pg_database.h @@ -33,6 +33,8 @@ CATALOG(pg_database,1262) BKI_SHARED_RELATION NameData datname; /* database name */ Oid datdba; /* owner of database */ int4 encoding; /* character encoding */ + NameData datcollate; /* LC_COLLATE setting */ + NameData datctype; /* LC_CTYPE setting */ bool datistemplate; /* allowed as CREATE DATABASE template? */ bool datallowconn; /* new connections allowed? */ int4 datconnlimit; /* max connections allowed (-1=no limit) */ @@ -54,20 +56,22 @@ typedef FormData_pg_database *Form_pg_database; * compiler constants for pg_database * ---------------- */ -#define Natts_pg_database 11 +#define Natts_pg_database 13 #define Anum_pg_database_datname 1 #define Anum_pg_database_datdba 2 #define Anum_pg_database_encoding 3 -#define Anum_pg_database_datistemplate 4 -#define Anum_pg_database_datallowconn 5 -#define Anum_pg_database_datconnlimit 6 -#define Anum_pg_database_datlastsysoid 7 -#define Anum_pg_database_datfrozenxid 8 -#define Anum_pg_database_dattablespace 9 -#define Anum_pg_database_datconfig 10 -#define Anum_pg_database_datacl 11 +#define Anum_pg_database_datcollate 4 +#define Anum_pg_database_datctype 5 +#define Anum_pg_database_datistemplate 6 +#define Anum_pg_database_datallowconn 7 +#define Anum_pg_database_datconnlimit 8 +#define Anum_pg_database_datlastsysoid 9 +#define Anum_pg_database_datfrozenxid 10 +#define Anum_pg_database_dattablespace 11 +#define Anum_pg_database_datconfig 12 +#define Anum_pg_database_datacl 13 -DATA(insert OID = 1 ( template1 PGUID ENCODING t t -1 0 0 1663 _null_ _null_ )); +DATA(insert OID = 1 ( template1 PGUID ENCODING "LC_COLLATE" "LC_CTYPE" t t -1 0 0 1663 _null_ _null_)); SHDESCR("default template database"); #define TemplateDbOid 1 diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index 5a498239a2..2b60027a98 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -39,6 +39,7 @@ extern const char *locale_numeric_assign(const char *value, extern const char *locale_time_assign(const char *value, bool doit, GucSource source); +extern bool check_locale(int category, const char *locale); extern char *pg_perm_setlocale(int category, const char *locale); extern bool lc_collate_is_c(void); diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 0a8b62b7cc..949e76bf33 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -428,7 +428,7 @@ add_typedef(char *name, char * dimension, char * length, enum ECPGttype type_enu CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB - CREATEROLE CREATEUSER CROSS CSV CURRENT_P CURRENT_DATE CURRENT_ROLE + CREATEROLE CREATEUSER CROSS CSV CTYPE CURRENT_P CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS -- 2.11.4.GIT