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.
       <entry><structfield>collencoding</structfield></entry>
       <entry><type>int4</type></entry>
       <entry></entry>
-      <entry>Encoding to which the collation is applicable</entry>
+      <entry>Encoding in which the collation is applicable, or -1 if it
+       works for any encoding</entry>
      </row>
 
      <row>
    <structfield>collencoding</>, <structfield>collnamespace</>) not just
    (<structfield>collname</>, <structfield>collnamespace</>).
    <productname>PostgreSQL</productname> generally ignores all
-   collations not belonging to the current database's encoding; therefore
-   it is sufficient to use a qualified SQL name
+   collations that do not have <structfield>collencoding</> equal to
+   either the current database's encoding or -1, and creation of new
+   entries matching an entry with <structfield>collencoding</> = -1
+   is forbidden.  Therefore it is sufficient to use a qualified SQL name
    (<replaceable>schema</>.<replaceable>name</>) to identify a collation,
    even though this is not unique according to the catalog definition.
-   The current database's encoding is automatically used as an additional
-   lookup key.  The reason for defining the catalog this way is that
+   The reason for defining the catalog this way is that
    <application>initdb</> fills it in at cluster initialization time with
    entries for all locales available on the system, so it must be able to
    hold entries for all encodings that might ever be used in the cluster.
 
            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;
 
            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;
 
 
     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
 
 
  * 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();
        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;
 {
        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);
        {
                /* 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
        {
 
                        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;
        }
 
        /* 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;
 }
 
 /*
 
  */
 #include "postgres.h"
 
+#include "access/genam.h"
 #include "access/heapam.h"
 #include "access/sysattr.h"
 #include "catalog/dependency.h"
 #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
  *
                                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,
        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),
                                 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);
        /* 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 */
 
        /* Post creation hook for new collation */
        InvokeObjectAccessHook(OAT_POST_CREATE,
-                                                  CollationRelationId, HeapTupleGetOid(tup), 0);
+                                                  CollationRelationId, oid, 0);
 
        heap_freetuple(tup);
        heap_close(rel, RowExclusiveLock);
 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);
 }
 
 /*-------------------------------------------------------------------------
  *
  * 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
 #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"
        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();
                                                          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,
        simple_heap_update(rel, &tup->t_self, tup);
        CatalogUpdateIndexes(rel, tup);
 
-       heap_close(rel, NoLock);
        heap_freetuple(tup);
+
+       heap_close(rel, RowExclusiveLock);
 }
 
 /*
 
        AlterCollationOwner_internal(rel, collationOid, newOwnerId);
 
-       heap_close(rel, NoLock);
+       heap_close(rel, RowExclusiveLock);
 }
 
 /*
 
        AlterCollationOwner_internal(rel, collationOid, newOwnerId);
 
-       heap_close(rel, NoLock);
+       heap_close(rel, RowExclusiveLock);
 }
 
 /*
 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);
 }
 
 /*
 {
        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,
 
        {"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
 
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201103101
+#define CATALOG_VERSION_NO     201103111
 
 #endif
 
 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;
 #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 */
 
     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 (
 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  
 
 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
 
 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%';