Обсуждение: Improving collation-dependent indexes in system catalogs
Awhile back we noticed that a couple of system catalogs had
acquired indexes on "text" columns, which were unsafe because
their sort order was collation-dependent, so that cloning
template0 with a different database collation could yield
broken indexes. We fixed this in commit 0b28ea79 with a
bit of a hack: we said it's okay to have such indexes as long
as they use the text_pattern_ops opclass, making them
collation insensitive.
While fooling with the idea of making type "name" collation
aware, it occurred to me that there's a better, more general
answer, which is to insist that collation-aware system catalog
columns must be marked with C collation. This rule would apply
without modification to both "text" and "name" columns. In the
wake of commit 5e0928005, it also means that pg_statistic data
for such a column would port safely across a database collation
change, which up to now it does not. And I think we could have
the bootstrap code apply the rule automatically, making for one
less way to screw up when changing catalog definitions.
Thoughts, objections?
regards, tom lane
I wrote:
> While fooling with the idea of making type "name" collation
> aware, it occurred to me that there's a better, more general
> answer, which is to insist that collation-aware system catalog
> columns must be marked with C collation. This rule would apply
> without modification to both "text" and "name" columns. In the
> wake of commit 5e0928005, it also means that pg_statistic data
> for such a column would port safely across a database collation
> change, which up to now it does not. And I think we could have
> the bootstrap code apply the rule automatically, making for one
> less way to screw up when changing catalog definitions.
Concretely, this ...
regards, tom lane
diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c
index 781516c..5be4fe8 100644
*** a/src/backend/access/common/scankey.c
--- b/src/backend/access/common/scankey.c
*************** ScanKeyEntryInitialize(ScanKey entry,
*** 64,72 ****
* It cannot handle NULL arguments, unary operators, or nondefault operators,
* but we need none of those features for most hardwired lookups.
*
! * We set collation to DEFAULT_COLLATION_OID always. This is appropriate
! * for textual columns in system catalogs, and it will be ignored for
! * non-textual columns, so it's not worth trying to be more finicky.
*
* Note: CurrentMemoryContext at call should be as long-lived as the ScanKey
* itself, because that's what will be used for any subsidiary info attached
--- 64,72 ----
* It cannot handle NULL arguments, unary operators, or nondefault operators,
* but we need none of those features for most hardwired lookups.
*
! * We set collation to C_COLLATION_OID always. This is the correct value
! * for all collation-aware columns in system catalogs, and it will be ignored
! * for other column types, so it's not worth trying to be more finicky.
*
* Note: CurrentMemoryContext at call should be as long-lived as the ScanKey
* itself, because that's what will be used for any subsidiary info attached
*************** ScanKeyInit(ScanKey entry,
*** 83,89 ****
entry->sk_attno = attributeNumber;
entry->sk_strategy = strategy;
entry->sk_subtype = InvalidOid;
! entry->sk_collation = DEFAULT_COLLATION_OID;
entry->sk_argument = argument;
fmgr_info(procedure, &entry->sk_func);
}
--- 83,89 ----
entry->sk_attno = attributeNumber;
entry->sk_strategy = strategy;
entry->sk_subtype = InvalidOid;
! entry->sk_collation = C_COLLATION_OID;
entry->sk_argument = argument;
fmgr_info(procedure, &entry->sk_func);
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 7caab64..8e42255 100644
*** a/src/backend/bootstrap/bootstrap.c
--- b/src/backend/bootstrap/bootstrap.c
*************** DefineAttr(char *name, char *type, int a
*** 744,749 ****
--- 744,758 ----
attrtypes[attnum]->attndims = 0;
}
+ /*
+ * If a system catalog column is collation-aware, force it to use C
+ * collation, so that its behavior is independent of the database's
+ * collation. This is essential to allow template0 to be cloned with a
+ * different database collation.
+ */
+ if (OidIsValid(attrtypes[attnum]->attcollation))
+ attrtypes[attnum]->attcollation = C_COLLATION_OID;
+
attrtypes[attnum]->attstattarget = -1;
attrtypes[attnum]->attcacheoff = -1;
attrtypes[attnum]->atttypmod = -1;
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 8e2a248..115e4c6 100644
*** a/src/backend/catalog/genbki.pl
--- b/src/backend/catalog/genbki.pl
*************** my $GenbkiNextOid = $FirstGenbkiObjectId
*** 167,172 ****
--- 167,175 ----
my $BOOTSTRAP_SUPERUSERID =
Catalog::FindDefinedSymbolFromData($catalog_data{pg_authid},
'BOOTSTRAP_SUPERUSERID');
+ my $C_COLLATION_OID =
+ Catalog::FindDefinedSymbolFromData($catalog_data{pg_collation},
+ 'C_COLLATION_OID');
my $PG_CATALOG_NAMESPACE =
Catalog::FindDefinedSymbolFromData($catalog_data{pg_namespace},
'PG_CATALOG_NAMESPACE');
*************** sub morph_row_for_pgattr
*** 693,699 ****
# set attndims if it's an array type
$row->{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
! $row->{attcollation} = $type->{typcollation};
if (defined $attr->{forcenotnull})
{
--- 696,705 ----
# set attndims if it's an array type
$row->{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
!
! # collation-aware catalog columns must use C collation
! $row->{attcollation} = $type->{typcollation} != 0 ?
! $C_COLLATION_OID : 0;
if (defined $attr->{forcenotnull})
{
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index b31fd5a..fdbd6a0 100644
*** a/src/backend/utils/cache/catcache.c
--- b/src/backend/utils/cache/catcache.c
***************
*** 22,27 ****
--- 22,28 ----
#include "access/tuptoaster.h"
#include "access/valid.h"
#include "access/xact.h"
+ #include "catalog/pg_collation.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
*************** CatalogCacheInitializeCache(CatCache *ca
*** 1014,1021 ****
/* Fill in sk_strategy as well --- always standard equality */
cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber;
cache->cc_skey[i].sk_subtype = InvalidOid;
! /* Currently, there are no catcaches on collation-aware data types */
! cache->cc_skey[i].sk_collation = InvalidOid;
CACHE4_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p",
cache->cc_relname,
--- 1015,1022 ----
/* Fill in sk_strategy as well --- always standard equality */
cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber;
cache->cc_skey[i].sk_subtype = InvalidOid;
! /* If a catcache key requires a collation, it must be C collation */
! cache->cc_skey[i].sk_collation = C_COLLATION_OID;
CACHE4_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p",
cache->cc_relname,
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 2359b4c..f0c52d9 100644
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
*************** DECLARE_UNIQUE_INDEX(pg_default_acl_oid_
*** 310,319 ****
DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_role_setting using btree(setdatabase
oid_ops,setrole oid_ops));
#define DbRoleSettingDatidRolidIndexId 2965
! DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops,
objsubidint4_ops, provider text_pattern_ops));
#define SecLabelObjectIndexId 3597
! DECLARE_UNIQUE_INDEX(pg_shseclabel_object_index, 3593, on pg_shseclabel using btree(objoid oid_ops, classoid oid_ops,
providertext_pattern_ops));
#define SharedSecLabelObjectIndexId 3593
DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(oid oid_ops));
--- 310,319 ----
DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_role_setting using btree(setdatabase
oid_ops,setrole oid_ops));
#define DbRoleSettingDatidRolidIndexId 2965
! DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops,
objsubidint4_ops, provider text_ops));
#define SecLabelObjectIndexId 3597
! DECLARE_UNIQUE_INDEX(pg_shseclabel_object_index, 3593, on pg_shseclabel using btree(objoid oid_ops, classoid oid_ops,
providertext_ops));
#define SharedSecLabelObjectIndexId 3593
DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(oid oid_ops));
*************** DECLARE_UNIQUE_INDEX(pg_policy_polrelid_
*** 333,339 ****
DECLARE_UNIQUE_INDEX(pg_replication_origin_roiident_index, 6001, on pg_replication_origin using btree(roident
oid_ops));
#define ReplicationOriginIdentIndex 6001
! DECLARE_UNIQUE_INDEX(pg_replication_origin_roname_index, 6002, on pg_replication_origin using btree(roname
text_pattern_ops));
#define ReplicationOriginNameIndex 6002
DECLARE_UNIQUE_INDEX(pg_partitioned_table_partrelid_index, 3351, on pg_partitioned_table using btree(partrelid
oid_ops));
--- 333,339 ----
DECLARE_UNIQUE_INDEX(pg_replication_origin_roiident_index, 6001, on pg_replication_origin using btree(roident
oid_ops));
#define ReplicationOriginIdentIndex 6001
! DECLARE_UNIQUE_INDEX(pg_replication_origin_roname_index, 6002, on pg_replication_origin using btree(roname
text_ops));
#define ReplicationOriginNameIndex 6002
DECLARE_UNIQUE_INDEX(pg_partitioned_table_partrelid_index, 3351, on pg_partitioned_table using btree(partrelid
oid_ops));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 6072f6b..52cd7b9 100644
*** a/src/test/regress/expected/opr_sanity.out
--- b/src/test/regress/expected/opr_sanity.out
*************** ORDER BY 1;
*** 2060,2077 ****
-- a representational error in pg_index, but simply wrong catalog design.
-- It's bad because we expect to be able to clone template0 and assign the
-- copy a different database collation. It would especially not work for
! -- shared catalogs. Note that although text columns will show a collation
! -- in indcollation, they're still okay to index with text_pattern_ops,
! -- so allow that case.
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
FROM (SELECT indexrelid, indrelid,
unnest(indclass) as iclass, unnest(indcollation) as icoll
FROM pg_index
WHERE indrelid < 16384) ss
! WHERE icoll != 0 AND iclass !=
! (SELECT oid FROM pg_opclass
! WHERE opcname = 'text_pattern_ops' AND opcmethod =
! (SELECT oid FROM pg_am WHERE amname = 'btree'));
indexrelid | indrelid | iclass | icoll
------------+----------+--------+-------
(0 rows)
--- 2060,2073 ----
-- a representational error in pg_index, but simply wrong catalog design.
-- It's bad because we expect to be able to clone template0 and assign the
-- copy a different database collation. It would especially not work for
! -- shared catalogs. Collation-sensitive indexes should have "C" collation.
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
FROM (SELECT indexrelid, indrelid,
unnest(indclass) as iclass, unnest(indcollation) as icoll
FROM pg_index
WHERE indrelid < 16384) ss
! WHERE icoll != 0 AND
! icoll != (SELECT oid FROM pg_collation WHERE collname = 'C');
indexrelid | indrelid | iclass | icoll
------------+----------+--------+-------
(0 rows)
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 91c68f4..5cf4df1 100644
*** a/src/test/regress/sql/opr_sanity.sql
--- b/src/test/regress/sql/opr_sanity.sql
*************** ORDER BY 1;
*** 1333,1348 ****
-- a representational error in pg_index, but simply wrong catalog design.
-- It's bad because we expect to be able to clone template0 and assign the
-- copy a different database collation. It would especially not work for
! -- shared catalogs. Note that although text columns will show a collation
! -- in indcollation, they're still okay to index with text_pattern_ops,
! -- so allow that case.
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
FROM (SELECT indexrelid, indrelid,
unnest(indclass) as iclass, unnest(indcollation) as icoll
FROM pg_index
WHERE indrelid < 16384) ss
! WHERE icoll != 0 AND iclass !=
! (SELECT oid FROM pg_opclass
! WHERE opcname = 'text_pattern_ops' AND opcmethod =
! (SELECT oid FROM pg_am WHERE amname = 'btree'));
--- 1333,1344 ----
-- a representational error in pg_index, but simply wrong catalog design.
-- It's bad because we expect to be able to clone template0 and assign the
-- copy a different database collation. It would especially not work for
! -- shared catalogs. Collation-sensitive indexes should have "C" collation.
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
FROM (SELECT indexrelid, indrelid,
unnest(indclass) as iclass, unnest(indcollation) as icoll
FROM pg_index
WHERE indrelid < 16384) ss
! WHERE icoll != 0 AND
! icoll != (SELECT oid FROM pg_collation WHERE collname = 'C');
On 2018-Dec-15, Tom Lane wrote: > I wrote: > > While fooling with the idea of making type "name" collation > > aware, it occurred to me that there's a better, more general > > answer, which is to insist that collation-aware system catalog > > columns must be marked with C collation. > Concretely, this ... Looks sane in a quick once-over. I notice that some information_schema view columns end up with C collation after this patch, and others remain with default collation. Is that sensible? (I think the only two cases where this might matter at all are information_schema.parameters.parameter_name, information_schema.routines.external_name and information_schema.foreign_servers.foreign_server_type.) -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> I notice that some information_schema view columns end up with C
> collation after this patch, and others remain with default collation.
> Is that sensible? (I think the only two cases where this might matter
> at all are information_schema.parameters.parameter_name,
> information_schema.routines.external_name and
> information_schema.foreign_servers.foreign_server_type.)
Yeah. Looking closer at that, there are no collation-sensitive indexes
in information_schema (if there were, the existing opr_sanity test would
have caught 'em). But there are collation-sensitive table columns, which
do have pg_statistic entries, and those entries are at least nominally
broken by copying them into a database with a different default collation.
We could think of two ways to deal with that. One is to plaster
COLLATE "C" on each textual table column in the information_schema.
A more aggressive approach is to attach COLLATE "C" to each of the
domain types that information_schema defines, which fixes the table
columns a fortiori, and also causes all of the exposed information_schema
view columns to acquire database-independent collations.
I tried both ways, as in the attached patches below (each meant to be
applied on top of my patch upthread), and they both pass check-world.
A possible advantage of the second approach is that it could end up
allowing comparisons on information_schema view columns to be translated
to indexable comparisons on the underlying "name" columns, which would
be a pleasant outcome. On the other hand, people might be annoyed by
the semantics change, if they'd previously been doing that with the
expectation of getting database-collation-based comparisons.
I'm not sure whether the SQL standard says anything that either patch
would be violating. I see that it does say that these domains have
CHARACTER SET SQL_TEXT, and that the collation of that character set
is implementation-defined, so I think we could get away with changing
so far as spec compliance is concerned.
regards, tom lane
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 6227a8f3..742e2b6 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -1576,13 +1576,13 @@ GRANT SELECT ON sequences TO PUBLIC;
*/
CREATE TABLE sql_features (
- feature_id character_data,
- feature_name character_data,
- sub_feature_id character_data,
- sub_feature_name character_data,
- is_supported yes_or_no,
- is_verified_by character_data,
- comments character_data
+ feature_id character_data COLLATE "C",
+ feature_name character_data COLLATE "C",
+ sub_feature_id character_data COLLATE "C",
+ sub_feature_name character_data COLLATE "C",
+ is_supported yes_or_no COLLATE "C",
+ is_verified_by character_data COLLATE "C",
+ comments character_data COLLATE "C"
);
-- Will be filled with external data by initdb.
@@ -1599,11 +1599,11 @@ GRANT SELECT ON sql_features TO PUBLIC;
-- clause 9.1.
CREATE TABLE sql_implementation_info (
- implementation_info_id character_data,
- implementation_info_name character_data,
+ implementation_info_id character_data COLLATE "C",
+ implementation_info_name character_data COLLATE "C",
integer_value cardinal_number,
- character_value character_data,
- comments character_data
+ character_value character_data COLLATE "C",
+ comments character_data COLLATE "C"
);
INSERT INTO sql_implementation_info VALUES ('10003', 'CATALOG NAME', NULL, 'Y', NULL);
@@ -1628,13 +1628,13 @@ GRANT SELECT ON sql_implementation_info TO PUBLIC;
*/
CREATE TABLE sql_languages (
- sql_language_source character_data,
- sql_language_year character_data,
- sql_language_conformance character_data,
- sql_language_integrity character_data,
- sql_language_implementation character_data,
- sql_language_binding_style character_data,
- sql_language_programming_language character_data
+ sql_language_source character_data COLLATE "C",
+ sql_language_year character_data COLLATE "C",
+ sql_language_conformance character_data COLLATE "C",
+ sql_language_integrity character_data COLLATE "C",
+ sql_language_implementation character_data COLLATE "C",
+ sql_language_binding_style character_data COLLATE "C",
+ sql_language_programming_language character_data COLLATE "C"
);
INSERT INTO sql_languages VALUES ('ISO 9075', '1999', 'CORE', NULL, NULL, 'DIRECT', NULL);
@@ -1651,11 +1651,11 @@ GRANT SELECT ON sql_languages TO PUBLIC;
*/
CREATE TABLE sql_packages (
- feature_id character_data,
- feature_name character_data,
- is_supported yes_or_no,
- is_verified_by character_data,
- comments character_data
+ feature_id character_data COLLATE "C",
+ feature_name character_data COLLATE "C",
+ is_supported yes_or_no COLLATE "C",
+ is_verified_by character_data COLLATE "C",
+ comments character_data COLLATE "C"
);
INSERT INTO sql_packages VALUES ('PKG000', 'Core', 'NO', NULL, '');
@@ -1678,11 +1678,11 @@ GRANT SELECT ON sql_packages TO PUBLIC;
*/
CREATE TABLE sql_parts (
- feature_id character_data,
- feature_name character_data,
- is_supported yes_or_no,
- is_verified_by character_data,
- comments character_data
+ feature_id character_data COLLATE "C",
+ feature_name character_data COLLATE "C",
+ is_supported yes_or_no COLLATE "C",
+ is_verified_by character_data COLLATE "C",
+ comments character_data COLLATE "C"
);
INSERT INTO sql_parts VALUES ('1', 'Framework (SQL/Framework)', 'NO', NULL, '');
@@ -1705,9 +1705,9 @@ INSERT INTO sql_parts VALUES ('14', 'XML-Related Specifications (SQL/XML)', 'YES
CREATE TABLE sql_sizing (
sizing_id cardinal_number,
- sizing_name character_data,
+ sizing_name character_data COLLATE "C",
supported_value cardinal_number,
- comments character_data
+ comments character_data COLLATE "C"
);
INSERT INTO sql_sizing VALUES (34, 'MAXIMUM CATALOG NAME LENGTH', 63, NULL);
@@ -1753,10 +1753,10 @@ GRANT SELECT ON sql_sizing TO PUBLIC;
CREATE TABLE sql_sizing_profiles (
sizing_id cardinal_number,
- sizing_name character_data,
- profile_id character_data,
+ sizing_name character_data COLLATE "C",
+ profile_id character_data COLLATE "C",
required_value cardinal_number,
- comments character_data
+ comments character_data COLLATE "C"
);
GRANT SELECT ON sql_sizing_profiles TO PUBLIC;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 52cd7b9..6dca1b7 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2060,7 +2060,18 @@ ORDER BY 1;
-- a representational error in pg_index, but simply wrong catalog design.
-- It's bad because we expect to be able to clone template0 and assign the
-- copy a different database collation. It would especially not work for
--- shared catalogs. Collation-sensitive indexes should have "C" collation.
+-- shared catalogs.
+SELECT relname, attname, attcollation
+FROM pg_class c, pg_attribute a
+WHERE c.oid = attrelid AND c.oid < 16384 AND
+ c.relkind != 'v' AND -- we don't care about columns in views
+ attcollation != 0 AND
+ attcollation != (SELECT oid FROM pg_collation WHERE collname = 'C');
+ relname | attname | attcollation
+---------+---------+--------------
+(0 rows)
+
+-- Double-check that collation-sensitive indexes have "C" collation, too.
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
FROM (SELECT indexrelid, indrelid,
unnest(indclass) as iclass, unnest(indcollation) as icoll
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 5cf4df1..64eca7e 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1333,7 +1333,16 @@ ORDER BY 1;
-- a representational error in pg_index, but simply wrong catalog design.
-- It's bad because we expect to be able to clone template0 and assign the
-- copy a different database collation. It would especially not work for
--- shared catalogs. Collation-sensitive indexes should have "C" collation.
+-- shared catalogs.
+
+SELECT relname, attname, attcollation
+FROM pg_class c, pg_attribute a
+WHERE c.oid = attrelid AND c.oid < 16384 AND
+ c.relkind != 'v' AND -- we don't care about columns in views
+ attcollation != 0 AND
+ attcollation != (SELECT oid FROM pg_collation WHERE collname = 'C');
+
+-- Double-check that collation-sensitive indexes have "C" collation, too.
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
FROM (SELECT indexrelid, indrelid,
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 6227a8f3..0fbcfa8 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -208,7 +208,7 @@ CREATE DOMAIN cardinal_number AS integer
* CHARACTER_DATA domain
*/
-CREATE DOMAIN character_data AS character varying;
+CREATE DOMAIN character_data AS character varying COLLATE "C";
/*
@@ -216,7 +216,7 @@ CREATE DOMAIN character_data AS character varying;
* SQL_IDENTIFIER domain
*/
-CREATE DOMAIN sql_identifier AS character varying;
+CREATE DOMAIN sql_identifier AS character varying COLLATE "C";
/*
@@ -243,7 +243,7 @@ CREATE DOMAIN time_stamp AS timestamp(2) with time zone
* YES_OR_NO domain
*/
-CREATE DOMAIN yes_or_no AS character varying(3)
+CREATE DOMAIN yes_or_no AS character varying(3) COLLATE "C"
CONSTRAINT yes_or_no_check CHECK (value IN ('YES', 'NO'));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 52cd7b9..6dca1b7 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2060,7 +2060,18 @@ ORDER BY 1;
-- a representational error in pg_index, but simply wrong catalog design.
-- It's bad because we expect to be able to clone template0 and assign the
-- copy a different database collation. It would especially not work for
--- shared catalogs. Collation-sensitive indexes should have "C" collation.
+-- shared catalogs.
+SELECT relname, attname, attcollation
+FROM pg_class c, pg_attribute a
+WHERE c.oid = attrelid AND c.oid < 16384 AND
+ c.relkind != 'v' AND -- we don't care about columns in views
+ attcollation != 0 AND
+ attcollation != (SELECT oid FROM pg_collation WHERE collname = 'C');
+ relname | attname | attcollation
+---------+---------+--------------
+(0 rows)
+
+-- Double-check that collation-sensitive indexes have "C" collation, too.
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
FROM (SELECT indexrelid, indrelid,
unnest(indclass) as iclass, unnest(indcollation) as icoll
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 5cf4df1..64eca7e 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1333,7 +1333,16 @@ ORDER BY 1;
-- a representational error in pg_index, but simply wrong catalog design.
-- It's bad because we expect to be able to clone template0 and assign the
-- copy a different database collation. It would especially not work for
--- shared catalogs. Collation-sensitive indexes should have "C" collation.
+-- shared catalogs.
+
+SELECT relname, attname, attcollation
+FROM pg_class c, pg_attribute a
+WHERE c.oid = attrelid AND c.oid < 16384 AND
+ c.relkind != 'v' AND -- we don't care about columns in views
+ attcollation != 0 AND
+ attcollation != (SELECT oid FROM pg_collation WHERE collname = 'C');
+
+-- Double-check that collation-sensitive indexes have "C" collation, too.
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
FROM (SELECT indexrelid, indrelid,