diff options
Diffstat (limited to 'src/backend/commands/dbcommands.c')
-rw-r--r-- | src/backend/commands/dbcommands.c | 94 |
1 files changed, 83 insertions, 11 deletions
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index f4bfa5cf6db..ce3754f5928 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.210 2008/08/04 18:03:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.211 2008/09/23 09:20:35 heikki Exp $ * *------------------------------------------------------------------------- */ @@ -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; |