aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-03-11 13:20:11 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2011-03-11 13:20:11 -0500
commite3c732a85c0f247617b2d44ea567f35731b03ea6 (patch)
tree9693d47782b9f7431a56474e8c5b972d351bd4ec /src
parentac435a79c88f51be6bf3eb5df618c2bac6123ae4 (diff)
downloadpostgresql-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.sql6
-rw-r--r--src/backend/catalog/namespace.c48
-rw-r--r--src/backend/catalog/pg_collation.c67
-rw-r--r--src/backend/commands/collationcmds.c83
-rw-r--r--src/bin/psql/tab-complete.c2
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_collation.h10
-rw-r--r--src/test/regress/expected/collate.linux.utf8.out12
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%';