diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2011-03-11 13:20:11 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2011-03-11 13:20:11 -0500 |
commit | e3c732a85c0f247617b2d44ea567f35731b03ea6 (patch) | |
tree | 9693d47782b9f7431a56474e8c5b972d351bd4ec /src | |
parent | ac435a79c88f51be6bf3eb5df618c2bac6123ae4 (diff) | |
download | postgresql-e3c732a85c0f247617b2d44ea567f35731b03ea6.tar.gz postgresql-e3c732a85c0f247617b2d44ea567f35731b03ea6.zip |
Create an explicit concept of collations that work for any encoding.
Use collencoding = -1 to represent such a collation in pg_collation.
We need this to make the "default" entry work sanely, and a later
patch will fix the C/POSIX entries to be represented this way instead
of duplicating them across all encodings. All lookup operations now
search first for an entry that's database-encoding-specific, and then
for the same name with collencoding = -1.
Also some incidental code cleanup in collationcmds.c and pg_collation.c.
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/catalog/information_schema.sql | 6 | ||||
-rw-r--r-- | src/backend/catalog/namespace.c | 48 | ||||
-rw-r--r-- | src/backend/catalog/pg_collation.c | 67 | ||||
-rw-r--r-- | src/backend/commands/collationcmds.c | 83 | ||||
-rw-r--r-- | src/bin/psql/tab-complete.c | 2 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
-rw-r--r-- | src/include/catalog/pg_collation.h | 10 | ||||
-rw-r--r-- | src/test/regress/expected/collate.linux.utf8.out | 12 |
8 files changed, 151 insertions, 79 deletions
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 1c47d81ba8c..c623fb7e75c 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -448,7 +448,7 @@ CREATE VIEW collations AS CAST('NO PAD' AS character_data) AS pad_attribute FROM pg_collation c, pg_namespace nc WHERE c.collnamespace = nc.oid - AND collencoding = (SELECT encoding FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database()); + AND collencoding IN (-1, (SELECT encoding FROM pg_database WHERE datname = current_database())); GRANT SELECT ON collations TO PUBLIC; @@ -467,7 +467,7 @@ CREATE VIEW collation_character_set_applicability AS CAST(getdatabaseencoding() AS sql_identifier) AS character_set_name FROM pg_collation c, pg_namespace nc WHERE c.collnamespace = nc.oid - AND collencoding = (SELECT encoding FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database()); + AND collencoding IN (-1, (SELECT encoding FROM pg_database WHERE datname = current_database())); GRANT SELECT ON collation_character_set_applicability TO PUBLIC; @@ -2036,7 +2036,7 @@ CREATE VIEW usage_privileges AS WHERE u.oid = c.collowner AND c.collnamespace = n.oid - AND c.collencoding = (SELECT encoding FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database()) + AND collencoding IN (-1, (SELECT encoding FROM pg_database WHERE datname = current_database())) UNION ALL diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 8b04b9fd9b3..77c9805aed1 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -1617,13 +1617,11 @@ OpfamilyIsVisible(Oid opfid) * CollationGetCollid * Try to resolve an unqualified collation name. * Returns OID if collation found in search path, else InvalidOid. - * - * This is essentially the same as RelnameGetRelid. */ Oid CollationGetCollid(const char *collname) { - Oid collid; + int32 dbencoding = GetDatabaseEncoding(); ListCell *l; recomputeNamespacePath(); @@ -1631,13 +1629,23 @@ CollationGetCollid(const char *collname) foreach(l, activeSearchPath) { Oid namespaceId = lfirst_oid(l); + Oid collid; if (namespaceId == myTempNamespace) continue; /* do not look in temp namespace */ + /* Check for database-encoding-specific entry */ + collid = GetSysCacheOid3(COLLNAMEENCNSP, + PointerGetDatum(collname), + Int32GetDatum(dbencoding), + ObjectIdGetDatum(namespaceId)); + if (OidIsValid(collid)) + return collid; + + /* Check for any-encoding entry */ collid = GetSysCacheOid3(COLLNAMEENCNSP, PointerGetDatum(collname), - Int32GetDatum(GetDatabaseEncoding()), + Int32GetDatum(-1), ObjectIdGetDatum(namespaceId)); if (OidIsValid(collid)) return collid; @@ -2901,12 +2909,10 @@ get_collation_oid(List *name, bool missing_ok) { char *schemaname; char *collation_name; + int32 dbencoding = GetDatabaseEncoding(); Oid namespaceId; - Oid colloid = InvalidOid; + Oid colloid; ListCell *l; - int encoding; - - encoding = GetDatabaseEncoding(); /* deconstruct the name list */ DeconstructQualifiedName(name, &schemaname, &collation_name); @@ -2915,10 +2921,20 @@ get_collation_oid(List *name, bool missing_ok) { /* use exact schema given */ namespaceId = LookupExplicitNamespace(schemaname); + + /* first try for encoding-specific entry, then any-encoding */ colloid = GetSysCacheOid3(COLLNAMEENCNSP, PointerGetDatum(collation_name), - Int32GetDatum(encoding), + Int32GetDatum(dbencoding), ObjectIdGetDatum(namespaceId)); + if (OidIsValid(colloid)) + return colloid; + colloid = GetSysCacheOid3(COLLNAMEENCNSP, + PointerGetDatum(collation_name), + Int32GetDatum(-1), + ObjectIdGetDatum(namespaceId)); + if (OidIsValid(colloid)) + return colloid; } else { @@ -2934,7 +2950,13 @@ get_collation_oid(List *name, bool missing_ok) colloid = GetSysCacheOid3(COLLNAMEENCNSP, PointerGetDatum(collation_name), - Int32GetDatum(encoding), + Int32GetDatum(dbencoding), + ObjectIdGetDatum(namespaceId)); + if (OidIsValid(colloid)) + return colloid; + colloid = GetSysCacheOid3(COLLNAMEENCNSP, + PointerGetDatum(collation_name), + Int32GetDatum(-1), ObjectIdGetDatum(namespaceId)); if (OidIsValid(colloid)) return colloid; @@ -2942,12 +2964,12 @@ get_collation_oid(List *name, bool missing_ok) } /* Not found in path */ - if (!OidIsValid(colloid) && !missing_ok) + if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("collation \"%s\" for current database encoding \"%s\" does not exist", + errmsg("collation \"%s\" for encoding \"%s\" does not exist", NameListToString(name), GetDatabaseEncodingName()))); - return colloid; + return InvalidOid; } /* diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c index 54a75a6f623..708078463ba 100644 --- a/src/backend/catalog/pg_collation.c +++ b/src/backend/catalog/pg_collation.c @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "access/genam.h" #include "access/heapam.h" #include "access/sysattr.h" #include "catalog/dependency.h" @@ -22,16 +23,13 @@ #include "catalog/pg_collation.h" #include "catalog/pg_collation_fn.h" #include "catalog/pg_namespace.h" -#include "catalog/pg_proc.h" #include "mb/pg_wchar.h" -#include "miscadmin.h" -#include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" -#include "utils/rel.h" #include "utils/syscache.h" #include "utils/tqual.h" + /* * CollationCreate * @@ -43,12 +41,11 @@ CollationCreate(const char *collname, Oid collnamespace, int32 collencoding, const char *collcollate, const char *collctype) { - int i; Relation rel; TupleDesc tupDesc; HeapTuple tup; - bool nulls[Natts_pg_collation]; Datum values[Natts_pg_collation]; + bool nulls[Natts_pg_collation]; NameData name_name, name_collate, name_ctype; Oid oid; ObjectAddress myself, @@ -60,7 +57,13 @@ CollationCreate(const char *collname, Oid collnamespace, AssertArg(collcollate); AssertArg(collctype); - /* make sure there is no existing collation of same name */ + /* + * Make sure there is no existing collation of same name & encoding. + * + * This would be caught by the unique index anyway; we're just giving + * a friendlier error message. The unique index provides a backstop + * against race conditions. + */ if (SearchSysCacheExists3(COLLNAMEENCNSP, PointerGetDatum(collname), Int32GetDatum(collencoding), @@ -70,18 +73,27 @@ CollationCreate(const char *collname, Oid collnamespace, errmsg("collation \"%s\" for encoding \"%s\" already exists", collname, pg_encoding_to_char(collencoding)))); + /* + * Also forbid matching an any-encoding entry. This test of course is + * not backed up by the unique index, but it's not a problem since we + * don't support adding any-encoding entries after initdb. + */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + PointerGetDatum(collname), + Int32GetDatum(-1), + ObjectIdGetDatum(collnamespace))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" already exists", + collname))); + /* open pg_collation */ rel = heap_open(CollationRelationId, RowExclusiveLock); - tupDesc = rel->rd_att; - - /* initialize nulls and values */ - for (i = 0; i < Natts_pg_collation; i++) - { - nulls[i] = false; - values[i] = (Datum) NULL; - } + tupDesc = RelationGetDescr(rel); /* form a tuple */ + memset(nulls, 0, sizeof(nulls)); + namestrcpy(&name_name, collname); values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name); values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace); @@ -101,8 +113,9 @@ CollationCreate(const char *collname, Oid collnamespace, /* update the index if any */ CatalogUpdateIndexes(rel, tup); + /* set up dependencies for the new collation */ myself.classId = CollationRelationId; - myself.objectId = HeapTupleGetOid(tup); + myself.objectId = oid; myself.objectSubId = 0; /* create dependency on namespace */ @@ -120,7 +133,7 @@ CollationCreate(const char *collname, Oid collnamespace, /* Post creation hook for new collation */ InvokeObjectAccessHook(OAT_POST_CREATE, - CollationRelationId, HeapTupleGetOid(tup), 0); + CollationRelationId, oid, 0); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); @@ -138,26 +151,28 @@ void RemoveCollationById(Oid collationOid) { Relation rel; - HeapTuple tuple; - HeapScanDesc scan; ScanKeyData scanKeyData; + SysScanDesc scandesc; + HeapTuple tuple; + + rel = heap_open(CollationRelationId, RowExclusiveLock); ScanKeyInit(&scanKeyData, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(collationOid)); - /* open pg_collation */ - rel = heap_open(CollationRelationId, RowExclusiveLock); + scandesc = systable_beginscan(rel, CollationOidIndexId, true, + SnapshotNow, 1, &scanKeyData); - scan = heap_beginscan(rel, SnapshotNow, - 1, &scanKeyData); + tuple = systable_getnext(scandesc); - /* search for the target tuple */ - if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) + if (HeapTupleIsValid(tuple)) simple_heap_delete(rel, &tuple->t_self); else elog(ERROR, "could not find tuple for collation %u", collationOid); - heap_endscan(scan); + + systable_endscan(scandesc); + heap_close(rel, RowExclusiveLock); } diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index a52cb351ac8..6dafb7223c4 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * collationcmds.c - * collation creation command support code + * collation-related commands support code * * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -27,7 +27,6 @@ #include "commands/defrem.h" #include "mb/pg_wchar.h" #include "miscadmin.h" -#include "parser/parse_type.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -134,11 +133,11 @@ DefineCollation(List *names, List *parameters) check_encoding_locale_matches(GetDatabaseEncoding(), collcollate, collctype); newoid = CollationCreate(collName, - collNamespace, - GetUserId(), - GetDatabaseEncoding(), - collcollate, - collctype); + collNamespace, + GetUserId(), + GetDatabaseEncoding(), + collcollate, + collctype); /* check that the locales can be loaded */ CommandCounterIncrement(); @@ -235,11 +234,22 @@ RenameCollation(List *name, const char *newname) ObjectIdGetDatum(namespaceOid))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("collation \"%s\" for current database encoding \"%s\" already exists in schema \"%s\"", + errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"", newname, GetDatabaseEncodingName(), get_namespace_name(namespaceOid)))); + /* mustn't match an any-encoding entry, either */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + CStringGetDatum(newname), + Int32GetDatum(-1), + ObjectIdGetDatum(namespaceOid))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" already exists in schema \"%s\"", + newname, + get_namespace_name(namespaceOid)))); + /* must be owner */ if (!pg_collation_ownercheck(collationOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, @@ -256,8 +266,9 @@ RenameCollation(List *name, const char *newname) simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); - heap_close(rel, NoLock); heap_freetuple(tup); + + heap_close(rel, RowExclusiveLock); } /* @@ -275,7 +286,7 @@ AlterCollationOwner(List *name, Oid newOwnerId) AlterCollationOwner_internal(rel, collationOid, newOwnerId); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); } /* @@ -290,7 +301,7 @@ AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId) AlterCollationOwner_internal(rel, collationOid, newOwnerId); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); } /* @@ -364,24 +375,14 @@ AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId) void AlterCollationNamespace(List *name, const char *newschema) { - Oid collOid, nspOid; - Relation rel; - - rel = heap_open(CollationRelationId, RowExclusiveLock); + Oid collOid, + nspOid; collOid = get_collation_oid(name, false); - /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, COLLOID, -1, - collOid, nspOid, - Anum_pg_collation_collname, - Anum_pg_collation_collnamespace, - Anum_pg_collation_collowner, - ACL_KIND_COLLATION); - - heap_close(rel, NoLock); + AlterCollationNamespace_oid(collOid, nspOid); } /* @@ -392,9 +393,43 @@ AlterCollationNamespace_oid(Oid collOid, Oid newNspOid) { Oid oldNspOid; Relation rel; + char *collation_name; rel = heap_open(CollationRelationId, RowExclusiveLock); + /* + * We have to check for name collision ourselves, because + * AlterObjectNamespace doesn't know how to deal with the encoding + * considerations. + */ + collation_name = get_collation_name(collOid); + if (!collation_name) + elog(ERROR, "cache lookup failed for collation %u", collOid); + + /* make sure the name doesn't already exist in new schema */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + CStringGetDatum(collation_name), + Int32GetDatum(GetDatabaseEncoding()), + ObjectIdGetDatum(newNspOid))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"", + collation_name, + GetDatabaseEncodingName(), + get_namespace_name(newNspOid)))); + + /* mustn't match an any-encoding entry, either */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + CStringGetDatum(collation_name), + Int32GetDatum(-1), + ObjectIdGetDatum(newNspOid))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" already exists in schema \"%s\"", + collation_name, + get_namespace_name(newNspOid)))); + + /* OK, do the work */ oldNspOid = AlterObjectNamespace(rel, COLLOID, -1, collOid, newNspOid, Anum_pg_collation_collname, diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 2214471ceab..7019123725d 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -609,7 +609,7 @@ static const pgsql_thing_t words_after_create[] = { {"AGGREGATE", NULL, &Query_for_list_of_aggregates}, {"CAST", NULL, NULL}, /* Casts have complex structures for names, so * skip it */ - {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding = pg_char_to_encoding(getdatabaseencoding()) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"}, + {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding())) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"}, /* * CREATE CONSTRAINT TRIGGER is not supported here because it is designed diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 22a0b89b442..27ce7955772 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201103101 +#define CATALOG_VERSION_NO 201103111 #endif diff --git a/src/include/catalog/pg_collation.h b/src/include/catalog/pg_collation.h index 6decfb13b39..e90aa050f54 100644 --- a/src/include/catalog/pg_collation.h +++ b/src/include/catalog/pg_collation.h @@ -32,9 +32,9 @@ CATALOG(pg_collation,3456) { NameData collname; /* collation name */ - Oid collnamespace; /* OID of namespace containing this collation */ - Oid collowner; - int4 collencoding; /* encoding that this collation applies to */ + Oid collnamespace; /* OID of namespace containing collation */ + Oid collowner; /* owner of collation */ + int4 collencoding; /* encoding for this collation; -1 = "all" */ NameData collcollate; /* LC_COLLATE setting */ NameData collctype; /* LC_CTYPE setting */ } FormData_pg_collation; @@ -58,8 +58,8 @@ typedef FormData_pg_collation *Form_pg_collation; #define Anum_pg_collation_collcollate 5 #define Anum_pg_collation_collctype 6 -DATA(insert OID = 100 ( default PGNSP PGUID 0 "" "" )); -DESCR("placeholder for default collation"); +DATA(insert OID = 100 ( default PGNSP PGUID -1 "" "" )); +DESCR("database's default collation"); #define DEFAULT_COLLATION_OID 100 #endif /* PG_COLLATION_H */ diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index 5ad5de2f00b..2102298abaa 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -18,14 +18,14 @@ CREATE TABLE collate_test_fail ( a int, b text COLLATE "ja_JP.eucjp" ); -ERROR: collation "ja_JP.eucjp" for current database encoding "UTF8" does not exist +ERROR: collation "ja_JP.eucjp" for encoding "UTF8" does not exist LINE 3: b text COLLATE "ja_JP.eucjp" ^ CREATE TABLE collate_test_fail ( a int, b text COLLATE "foo" ); -ERROR: collation "foo" for current database encoding "UTF8" does not exist +ERROR: collation "foo" for encoding "UTF8" does not exist LINE 3: b text COLLATE "foo" ^ CREATE TABLE collate_test_fail ( @@ -752,7 +752,7 @@ ERROR: parameter "lc_ctype" must be specified CREATE COLLATION testx (locale = 'nonsense'); -- fail ERROR: could not create locale "nonsense": No such file or directory CREATE COLLATION test4 FROM nonsense; -ERROR: collation "nonsense" for current database encoding "UTF8" does not exist +ERROR: collation "nonsense" for encoding "UTF8" does not exist CREATE COLLATION test5 FROM test0; SELECT collname, collencoding, collcollate, collctype FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; collname | collencoding | collcollate | collctype @@ -764,9 +764,9 @@ SELECT collname, collencoding, collcollate, collctype FROM pg_collation WHERE co ALTER COLLATION test1 RENAME TO test11; ALTER COLLATION test0 RENAME TO test11; -- fail -ERROR: collation "test11" for current database encoding "UTF8" already exists in schema "public" +ERROR: collation "test11" for encoding "UTF8" already exists in schema "public" ALTER COLLATION test1 RENAME TO test22; -- fail -ERROR: collation "test1" for current database encoding "UTF8" does not exist +ERROR: collation "test1" for encoding "UTF8" does not exist ALTER COLLATION test11 OWNER TO regress_test_role; ALTER COLLATION test11 OWNER TO nonsense; ERROR: role "nonsense" does not exist @@ -785,7 +785,7 @@ SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') DROP COLLATION test0, test_schema.test11, test5; DROP COLLATION test0; -- fail -ERROR: collation "test0" for current database encoding "UTF8" does not exist +ERROR: collation "test0" for encoding "UTF8" does not exist DROP COLLATION IF EXISTS test0; NOTICE: collation "test0" does not exist, skipping SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; |