diff options
Diffstat (limited to 'src/backend')
-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 |
4 files changed, 138 insertions, 66 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, |