Martijn van Oosterhout wrote:
On Wed, Sep 10, 2008 at 12:51:02PM +0300, Heikki Linnakangas wrote:
Since the set of collations isn't exactly denumerable, we need some way
to allow the user to specify the collation they want. The only
collation PostgreSQL knows about is the C collation. Anything else is
user-defined.
Let's just use the name of the OS locale, like we do now. Having a pg_collation catalog just moves the problem elsewhere: we'd still need something in pg_collation to tie the collation to the OS locale.

There's not a one-to-one mapping between collation and locale name. A
locale name includes information about the charset and a collation may
have paramters like case-sensetivity and pad-attribute which are not
present in the locale name. You need a mapping anyway, which is what
this table is for.

Ideally, we would delegate the case-sensitivity and padding to the collation implementation (ie. OS setlocale() or ICU). That said, I don't think operating systems normally ship case-insensitive variants of locales by default, so I agree it would be nice if we could implement that ourselves. Still, we could identify case-sensitive locale names for example by a suffix, like "en_GB.UTF8.case-insensitive".

I agree we will eventually need a way to give shorthand names for collations, and a pg_collation catalog will then come handy. But that can wait until we have the basic infrastructure ready to support column and query-level collation.

But that put us back where we started: every database having the same
collation. We're trying to move away from that. Just reindex everything
and be done with it.
That's easier said than done, unfortunately.

I don't see an alternative.

Well, I proposed disallowing using a different collation than the source database, except for using template0 as the source. That's pretty limited, but is trivial to implement and still let's you have databases with different collations in the same cluster.

I worked a bit on Radek's patch, stripping out all the pg_collate and pg_charset catalog changes and commands, leaving just the core functionality of database-level collations. It needs some cleanup and documentation, but something like this I'd like to commit in this commit fest. The new catalogs can wait until we have a real need for them.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com
*** src/backend/access/transam/xlog.c
--- src/backend/access/transam/xlog.c
***************
*** 3847,3853 **** WriteControlFile(void)
  {
  	int			fd;
  	char		buffer[PG_CONTROL_SIZE];		/* need not be aligned */
- 	char	   *localeptr;
  
  	/*
  	 * Initialize version and compatibility-check fields
--- 3847,3852 ----
***************
*** 3875,3893 **** WriteControlFile(void)
  #endif
  	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,
--- 3874,3880 ----
  #endif
  	ControlFile->float4ByVal = FLOAT4PASSBYVAL;
  	ControlFile->float8ByVal = FLOAT8PASSBYVAL;
! 	
  	/* Contents are protected with a CRC */
  	INIT_CRC32(ControlFile->crc);
  	COMP_CRC32(ControlFile->crc,
***************
*** 4126,4159 **** 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
--- 4113,4118 ----
*** src/backend/commands/dbcommands.c
--- src/backend/commands/dbcommands.c
***************
*** 69,75 **** 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);
  static bool have_createdb_privilege(void);
  static void remove_dbtablespaces(Oid db_id);
  static bool check_db_file_conflict(Oid db_id);
--- 69,75 ----
  			Oid *dbIdP, Oid *ownerIdP,
  			int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
  			Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
! 			Oid *dbTablespace, char **dbCollation, 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,92 **** createdb(const CreatedbStmt *stmt)
--- 87,94 ----
  	Oid			src_dboid;
  	Oid			src_owner;
  	int			src_encoding;
+ 	char	   *src_collation;
+ 	char	   *src_ctype;
  	bool		src_istemplate;
  	bool		src_allowconn;
  	Oid			src_lastsysoid;
***************
*** 104,113 **** createdb(const CreatedbStmt *stmt)
--- 106,119 ----
  	DefElem    *downer = NULL;
  	DefElem    *dtemplate = NULL;
  	DefElem    *dencoding = NULL;
+ 	DefElem    *dcollation = NULL;
+ 	DefElem    *dctype = NULL;
  	DefElem    *dconnlimit = NULL;
  	char	   *dbname = stmt->dbname;
  	char	   *dbowner = NULL;
  	const char *dbtemplate = NULL;
+ 	char	   *lc_collate = NULL;
+ 	char	   *lc_ctype = NULL;
  	int			encoding = -1;
  	int			dbconnlimit = -1;
  	int			ctype_encoding;
***************
*** 152,157 **** createdb(const CreatedbStmt *stmt)
--- 158,179 ----
  						 errmsg("conflicting or redundant options")));
  			dencoding = defel;
  		}
+ 		else if (strcmp(defel->defname, "collate") == 0)
+ 		{
+ 			if (dcollation)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			dcollation = 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,210 **** createdb(const CreatedbStmt *stmt)
--- 227,247 ----
  			elog(ERROR, "unrecognized node type: %d",
  				 nodeTag(dencoding->arg));
  	}
+ 	/*
+ 	if (dlc_collate && dlc_collate->arg) {
+ 		lc_collate = strVal(dlc_collate->arg);
+ 		if ((locale_collate_assign(lc_collate, false, (GucSource)NULL)) == NULL)
+ 			ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				errmsg("%s is not a valid LC_COLLATE name",
+ 						lc_collate)));	
+ 	}
+ 	*/
+ 	if (dcollation && dcollation->arg)
+ 		lc_collate = strVal(dcollation->arg);
+ 	if (dctype && dctype->arg)
+ 		lc_ctype = strVal(dctype->arg);
+ 	
  	if (dconnlimit && dconnlimit->arg)
  		dbconnlimit = intVal(dconnlimit->arg);
  
***************
*** 243,249 **** 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))
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_DATABASE),
  				 errmsg("template database \"%s\" does not exist",
--- 280,286 ----
  	if (!get_db_info(dbtemplate, ShareLock,
  					 &src_dboid, &src_owner, &src_encoding,
  					 &src_istemplate, &src_allowconn, &src_lastsysoid,
! 					 &src_frozenxid, &src_deftablespace, &src_collation, &src_ctype))
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_DATABASE),
  				 errmsg("template database \"%s\" does not exist",
***************
*** 305,310 **** createdb(const CreatedbStmt *stmt)
--- 342,359 ----
  			 errdetail("The server's LC_CTYPE setting requires encoding %s.",
  					   pg_encoding_to_char(ctype_encoding))));
  
+ 	/*
+ 	 * Message about reindexing new database
+ 	 *
+ 	 * We know that template0 doesn't contain any indexes that depend on
+ 	 * collation or ctype.
+ 	 */
+ 	if (strcmp(dbtemplate, "template0") != 0 &&
+ 		(strcmp(lc_collate, src_collation) || strcmp(lc_ctype, src_ctype)))
+ 		ereport(NOTICE,
+ 				(errmsg("database \"%s\" needs to be reindexed manually (REINDEX DATABASE)",
+ 						dbname)));		
+ 
  	/* Resolve default tablespace for new database */
  	if (dtablespacename && dtablespacename->arg)
  	{
***************
*** 369,374 **** createdb(const CreatedbStmt *stmt)
--- 418,432 ----
  		/* Note there is no additional permission check in this path */
  	}
  
+ 	/*	
+ 	 * If collation is specified for database, use it, otherwise inherit
+ 	 * database cluster's collation.
+ 	 */
+ 	if (lc_collate == NULL)
+ 		lc_collate = src_collation;
+ 	if (lc_ctype == NULL)
+ 		lc_ctype = src_ctype;
+ 
  	/*
  	 * Check for db name conflict.	This is just to give a more friendly error
  	 * message than "unique index violation".  There's a race condition but
***************
*** 421,426 **** createdb(const CreatedbStmt *stmt)
--- 479,486 ----
  		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_collation - 1] = DirectFunctionCall1(namein, CStringGetDatum(lc_collate));
+ 	new_record[Anum_pg_database_ctype - 1] = DirectFunctionCall1(namein, CStringGetDatum(lc_ctype));
  	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,635 **** 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))
  	{
  		if (!missing_ok)
  		{
--- 689,695 ----
  	pgdbrel = heap_open(DatabaseRelationId, RowExclusiveLock);
  
  	if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL,
! 					 &db_istemplate, NULL, NULL, NULL, NULL, NULL, NULL))
  	{
  		if (!missing_ok)
  		{
***************
*** 781,787 **** 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))
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_DATABASE),
  				 errmsg("database \"%s\" does not exist", oldname)));
--- 841,847 ----
  	rel = heap_open(DatabaseRelationId, RowExclusiveLock);
  
  	if (!get_db_info(oldname, AccessExclusiveLock, &db_id, NULL, NULL,
! 					 NULL, NULL, NULL, NULL, NULL, NULL, NULL))
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_DATABASE),
  				 errmsg("database \"%s\" does not exist", oldname)));
***************
*** 1168,1174 **** get_db_info(const char *name, LOCKMODE lockmode,
  			Oid *dbIdP, Oid *ownerIdP,
  			int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
  			Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
! 			Oid *dbTablespace)
  {
  	bool		result = false;
  	Relation	relation;
--- 1228,1234 ----
  			Oid *dbIdP, Oid *ownerIdP,
  			int *encodingP, bool *dbIsTemplateP, bool *dbAllowConnP,
  			Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
! 			Oid *dbTablespace, char **dbCollation, char **dbCtype)
  {
  	bool		result = false;
  	Relation	relation;
***************
*** 1259,1264 **** get_db_info(const char *name, LOCKMODE lockmode,
--- 1319,1329 ----
  				/* default tablespace for this database */
  				if (dbTablespace)
  					*dbTablespace = dbform->dattablespace;
+  				/* default locale settings for this database */
+  				if (dbCollation)
+  					*dbCollation = pstrdup(NameStr(dbform->collation));
+  				if (dbCtype)
+  					*dbCtype = pstrdup(NameStr(dbform->ctype));
  				ReleaseSysCache(tuple);
  				result = true;
  				break;
*** src/backend/parser/gram.y
--- src/backend/parser/gram.y
***************
*** 398,404 **** 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
  	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
  
  	DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
--- 398,404 ----
  	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 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,5463 **** createdb_opt_item:
--- 5458,5479 ----
  				{
  					$$ = 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,9221 **** unreserved_keyword:
--- 9232,9238 ----
  			| CREATEROLE
  			| CREATEUSER
  			| CSV
+ 			| CTYPE
  			| CURRENT_P
  			| CURSOR
  			| CYCLE
*** src/backend/parser/keywords.c
--- src/backend/parser/keywords.c
***************
*** 114,119 **** const ScanKeyword ScanKeywords[] = {
--- 114,120 ----
  	{"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},
*** src/backend/utils/adt/pg_locale.c
--- src/backend/utils/adt/pg_locale.c
***************
*** 228,233 **** locale_xxx_assign(int category, const char *value, bool doit, GucSource source)
--- 228,245 ----
  	return value;
  }
  
+ const char *
+ locale_collate_assign(const char *value, bool doit, GucSource source)
+ {
+ 	return locale_xxx_assign(LC_COLLATE, value, doit, source);
+ }
+ 
+ const char *
+ locale_ctype_assign(const char *value, bool doit, GucSource source)
+ {
+ 	return locale_xxx_assign(LC_CTYPE, value, doit, source);
+ }
+ 
  
  const char *
  locale_monetary_assign(const char *value, bool doit, GucSource source)
*** src/backend/utils/init/postinit.c
--- src/backend/utils/init/postinit.c
***************
*** 159,164 **** CheckMyDatabase(const char *name, bool am_superuser)
--- 159,166 ----
  {
  	HeapTuple	tup;
  	Form_pg_database dbform;
+ 	char	   *collate;
+ 	char	   *ctype;
  
  	/* Fetch our real pg_database row */
  	tup = SearchSysCache(DATABASEOID,
***************
*** 240,245 **** CheckMyDatabase(const char *name, bool am_superuser)
--- 242,270 ----
  	/* 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->collation);
+ 	ctype = NameStr(dbform->ctype);
+ 					
+ 	if (setlocale(LC_COLLATE, collate) == NULL)
+ 		ereport(FATAL, 
+ 			(errmsg("database local is incompatible with OS"),
+ 			errdetail("The database was initialized with LC_COLLATE \"%s\", "
+ 						" which is not recognized by setlocale().", collate),
+ 			errhint("Try to recreate the database or install locale support.")));
+ 			
+ 	if (setlocale(LC_CTYPE, ctype) == NULL)
+ 		ereport(FATAL, 
+ 			(errmsg("database local is incompatible with OS"),
+ 			errdetail("The database was initialized with LC_CTYPE \"%s\", "
+ 						" which is not recognized by setlocale().", ctype),
+ 			errhint("Try to recreate the database or install locale support.")));
+ 			
+ 	/* Record it as a GUC internal option, too */
+ 	
+ 	SetConfigOption("lc_collate", collate, PGC_INTERNAL, PGC_S_DATABASE);
+ 	SetConfigOption("lc_ctype", ctype, PGC_INTERNAL, PGC_S_DATABASE);
  
  	/*
  	 * Lastly, set up any database-specific configuration variables.
*** src/bin/initdb/initdb.c
--- src/bin/initdb/initdb.c
***************
*** 1171,1176 **** setup_config(void)
--- 1171,1184 ----
  	conflines = replace_token(conflines, "#port = 5432", repltok);
  #endif
  
+ 	snprintf(repltok, sizeof(repltok), "lc_collate = '%s'",
+ 			 escape_quotes(lc_collate));
+ 	conflines = replace_token(conflines, "#lc_collate = 'C'", repltok);
+ 	
+ 	snprintf(repltok, sizeof(repltok), "lc_ctype = '%s'",
+ 			 escape_quotes(lc_collate));
+ 	conflines = replace_token(conflines, "#lc_ctype = 'C'", repltok);
+ 
  	snprintf(repltok, sizeof(repltok), "lc_messages = '%s'",
  			 escape_quotes(lc_messages));
  	conflines = replace_token(conflines, "#lc_messages = 'C'", repltok);
***************
*** 1353,1358 **** bootstrap_template1(char *short_version)
--- 1361,1370 ----
  
  	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.
  	 *
***************
*** 2806,2815 **** main(int argc, char *argv[])
  		strcmp(lc_ctype, lc_numeric) == 0 &&
  		strcmp(lc_ctype, lc_monetary) == 0 &&
  		strcmp(lc_ctype, lc_messages) == 0)
! 		printf(_("The database cluster will be initialized with locale %s.\n"), lc_ctype);
  	else
  	{
! 		printf(_("The database cluster will be initialized with locales\n"
  				 "  COLLATE:  %s\n"
  				 "  CTYPE:    %s\n"
  				 "  MESSAGES: %s\n"
--- 2818,2827 ----
  		strcmp(lc_ctype, lc_numeric) == 0 &&
  		strcmp(lc_ctype, lc_monetary) == 0 &&
  		strcmp(lc_ctype, lc_messages) == 0)
! 		printf(_("The database template1 will be initialized with locale %s.\n"), lc_ctype);
  	else
  	{
! 		printf(_("The database template1 will be initialized with locales\n"
  				 "  COLLATE:  %s\n"
  				 "  CTYPE:    %s\n"
  				 "  MESSAGES: %s\n"
*** src/bin/pg_controldata/pg_controldata.c
--- src/bin/pg_controldata/pg_controldata.c
***************
*** 220,231 **** 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;
  }
--- 220,224 ----
*** src/bin/pg_resetxlog/pg_resetxlog.c
--- src/bin/pg_resetxlog/pg_resetxlog.c
***************
*** 493,514 **** 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
--- 493,498 ----
***************
*** 584,595 **** 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);
  }
  
  
--- 568,573 ----
*** src/bin/scripts/createdb.c
--- src/bin/scripts/createdb.c
***************
*** 32,37 **** main(int argc, char *argv[])
--- 32,39 ----
  		{"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,55 **** main(int argc, char *argv[])
--- 52,59 ----
  	char	   *tablespace = NULL;
  	char	   *template = NULL;
  	char	   *encoding = NULL;
+ 	char	   *lc_collate = NULL;
+ 	char	   *lc_ctype = NULL;
  
  	PQExpBufferData sql;
  
***************
*** 95,100 **** main(int argc, char *argv[])
--- 99,110 ----
  			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,159 **** main(int argc, char *argv[])
  		appendPQExpBuffer(&sql, " ENCODING '%s'", encoding);
  	if (template)
  		appendPQExpBuffer(&sql, " TEMPLATE %s", fmtId(template));
  	appendPQExpBuffer(&sql, ";\n");
! 
  	conn = connectDatabase(strcmp(dbname, "postgres") == 0 ? "template1" : "postgres",
  						   host, port, username, password, progname);
  
--- 162,174 ----
  		appendPQExpBuffer(&sql, " ENCODING '%s'", encoding);
  	if (template)
  		appendPQExpBuffer(&sql, " TEMPLATE %s", fmtId(template));
+ 	if (lc_collate)
+ 		appendPQExpBuffer(&sql, " LCCOLLATE %s", fmtId(lc_collate));
+ 	if (lc_ctype)
+ 		appendPQExpBuffer(&sql, " LCCTYPE %s", fmtId(lc_ctype));
+ 	
  	appendPQExpBuffer(&sql, ";\n");
! 	
  	conn = connectDatabase(strcmp(dbname, "postgres") == 0 ? "template1" : "postgres",
  						   host, port, username, password, progname);
  
***************
*** 207,224 **** help(const char *progname)
  	printf(_("Usage:\n"));
  	printf(_("  %s [OPTION]... [DBNAME] [DESCRIPTION]\n"), progname);
  	printf(_("\nOptions:\n"));
! 	printf(_("  -D, --tablespace=TABLESPACE  default tablespace for the database\n"));
! 	printf(_("  -E, --encoding=ENCODING      encoding 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"));
! 	printf(_("  --help                       show this help, then exit\n"));
! 	printf(_("  --version                    output version information, then exit\n"));
  	printf(_("\nConnection options:\n"));
! 	printf(_("  -h, --host=HOSTNAME          database server host or socket directory\n"));
! 	printf(_("  -p, --port=PORT              database server port\n"));
! 	printf(_("  -U, --username=USERNAME      user name to connect as\n"));
! 	printf(_("  -W, --password               force password prompt\n"));
  	printf(_("\nBy default, a database with the same name as the current user is created.\n"));
  	printf(_("\nReport bugs to <[EMAIL PROTECTED]>.\n"));
  }
--- 222,240 ----
  	printf(_("Usage:\n"));
  	printf(_("  %s [OPTION]... [DBNAME] [DESCRIPTION]\n"), 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, --lc-ctype=LOCALE	locale 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"));
! 	printf(_("  --help                       	show this help, then exit\n"));
! 	printf(_("  --version                    	output version information, then exit\n"));
  	printf(_("\nConnection options:\n"));
! 	printf(_("  -h, --host=HOSTNAME          	database server host or socket directory\n"));
! 	printf(_("  -p, --port=PORT              	database server port\n"));
! 	printf(_("  -U, --username=USERNAME      	user name to connect as\n"));
! 	printf(_("  -W, --password               	force password prompt\n"));
  	printf(_("\nBy default, a database with the same name as the current user is created.\n"));
  	printf(_("\nReport bugs to <[EMAIL PROTECTED]>.\n"));
  }
*** src/include/catalog/pg_control.h
--- src/include/catalog/pg_control.h
***************
*** 144,154 **** 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;
--- 144,149 ----
*** src/include/catalog/pg_database.h
--- src/include/catalog/pg_database.h
***************
*** 33,38 **** CATALOG(pg_database,1262) BKI_SHARED_RELATION
--- 33,40 ----
  	NameData	datname;		/* database name */
  	Oid			datdba;			/* owner of database */
  	int4		encoding;		/* character encoding */
+ 	NameData	collation;		/* LC_COLLATE of database */
+ 	NameData	ctype;			/* LC_CTYPE of database */
  	bool		datistemplate;	/* allowed as CREATE DATABASE template? */
  	bool		datallowconn;	/* new connections allowed? */
  	int4		datconnlimit;	/* max connections allowed (-1=no limit) */
***************
*** 54,73 **** typedef FormData_pg_database *Form_pg_database;
   *		compiler constants for pg_database
   * ----------------
   */
! #define Natts_pg_database				11
  #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
  
! DATA(insert OID = 1 (  template1 PGUID ENCODING t t -1 0 0 1663 _null_ _null_ ));
  SHDESCR("default template database");
  #define TemplateDbOid			1
  
--- 56,77 ----
   *		compiler constants for pg_database
   * ----------------
   */
! #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_collation		4
! #define Anum_pg_database_ctype			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 "LC_COLLATE" "LC_CTYPE" t t -1 0 0 1663 _null_ _null_));
  SHDESCR("default template database");
  #define TemplateDbOid			1
  
*** src/interfaces/ecpg/preproc/preproc.y
--- src/interfaces/ecpg/preproc/preproc.y
***************
*** 428,434 **** 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
  	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
  
  	DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
--- 428,434 ----
  	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 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
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to