Skip to content

Commit 0b28ea7

Browse files
committed
Avoid collation dependence in indexes of system catalogs.
No index in template0 should have collation-dependent ordering, especially not indexes on shared catalogs. For most textual columns we avoid this issue by using type "name" (which sorts per strcmp()). However there are a few indexed columns that we'd prefer to use "text" for, and for that, the default opclass text_ops is unsafe. Fortunately, text_pattern_ops is safe (it sorts per memcmp()), and it has no real functional disadvantage for our purposes. So change the indexes on pg_seclabel.provider and pg_shseclabel.provider to use text_pattern_ops. In passing, also mark pg_replication_origin.roname as using text_pattern_ops --- for some reason it was labeled varchar_pattern_ops which is just wrong, even though it accidentally worked. Add regression test queries to catch future errors of these kinds. We still can't do anything about the misdeclared pg_seclabel and pg_shseclabel indexes in back branches :-(
1 parent afee043 commit 0b28ea7

File tree

4 files changed

+154
-10
lines changed

4 files changed

+154
-10
lines changed

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 201505153
56+
#define CATALOG_VERSION_NO 201505191
5757

5858
#endif

src/include/catalog/indexing.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,10 @@ DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btre
290290
DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_role_setting using btree(setdatabase oid_ops, setrole oid_ops));
291291
#define DbRoleSettingDatidRolidIndexId 2965
292292

293-
DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
293+
DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_pattern_ops));
294294
#define SecLabelObjectIndexId 3597
295295

296-
DECLARE_UNIQUE_INDEX(pg_shseclabel_object_index, 3593, on pg_shseclabel using btree(objoid oid_ops, classoid oid_ops, provider text_ops));
296+
DECLARE_UNIQUE_INDEX(pg_shseclabel_object_index, 3593, on pg_shseclabel using btree(objoid oid_ops, classoid oid_ops, provider text_pattern_ops));
297297
#define SharedSecLabelObjectIndexId 3593
298298

299299
DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(oid oid_ops));
@@ -313,7 +313,7 @@ DECLARE_UNIQUE_INDEX(pg_policy_polrelid_polname_index, 3258, on pg_policy using
313313
DECLARE_UNIQUE_INDEX(pg_replication_origin_roiident_index, 6001, on pg_replication_origin using btree(roident oid_ops));
314314
#define ReplicationOriginIdentIndex 6001
315315

316-
DECLARE_UNIQUE_INDEX(pg_replication_origin_roname_index, 6002, on pg_replication_origin using btree(roname varchar_pattern_ops));
316+
DECLARE_UNIQUE_INDEX(pg_replication_origin_roname_index, 6002, on pg_replication_origin using btree(roname text_pattern_ops));
317317
#define ReplicationOriginNameIndex 6002
318318

319319
DECLARE_UNIQUE_INDEX(pg_tablesample_method_name_index, 3331, on pg_tablesample_method using btree(tsmname name_ops));

src/test/regress/expected/opr_sanity.out

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
-- OPR_SANITY
33
-- Sanity checks for common errors in making operator/procedure system tables:
44
-- pg_operator, pg_proc, pg_cast, pg_aggregate, pg_am,
5-
-- pg_amop, pg_amproc, pg_opclass, pg_opfamily.
5+
-- pg_amop, pg_amproc, pg_opclass, pg_opfamily, pg_index.
66
--
77
-- Every test failure in this file should be closely inspected.
88
-- The description of the failing test should be read carefully before
@@ -27,7 +27,9 @@ SELECT ($1 = $2) OR
2727
($2 = 'pg_catalog.any'::pg_catalog.regtype) OR
2828
($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
2929
EXISTS(select 1 from pg_catalog.pg_type where
30-
oid = $1 and typelem != 0 and typlen = -1))
30+
oid = $1 and typelem != 0 and typlen = -1)) OR
31+
($2 = 'pg_catalog.anyrange'::pg_catalog.regtype AND
32+
(select typtype from pg_catalog.pg_type where oid = $1) = 'r')
3133
$$ language sql strict stable;
3234
-- This one ignores castcontext, so it considers only physical equivalence
3335
-- and not whether the coercion can be invoked implicitly.
@@ -39,7 +41,9 @@ SELECT ($1 = $2) OR
3941
($2 = 'pg_catalog.any'::pg_catalog.regtype) OR
4042
($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
4143
EXISTS(select 1 from pg_catalog.pg_type where
42-
oid = $1 and typelem != 0 and typlen = -1))
44+
oid = $1 and typelem != 0 and typlen = -1)) OR
45+
($2 = 'pg_catalog.anyrange'::pg_catalog.regtype AND
46+
(select typtype from pg_catalog.pg_type where oid = $1) = 'r')
4347
$$ language sql strict stable;
4448
-- **************** pg_proc ****************
4549
-- Look for illegal values in pg_proc fields.
@@ -2014,3 +2018,76 @@ WHERE p1.amproc = p2.oid AND
20142018
--------------+--------+--------
20152019
(0 rows)
20162020

2021+
-- **************** pg_index ****************
2022+
-- Look for illegal values in pg_index fields.
2023+
SELECT p1.indexrelid, p1.indrelid
2024+
FROM pg_index as p1
2025+
WHERE p1.indexrelid = 0 OR p1.indrelid = 0 OR
2026+
p1.indnatts <= 0 OR p1.indnatts > 32;
2027+
indexrelid | indrelid
2028+
------------+----------
2029+
(0 rows)
2030+
2031+
-- oidvector and int2vector fields should be of length indnatts.
2032+
SELECT p1.indexrelid, p1.indrelid
2033+
FROM pg_index as p1
2034+
WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR
2035+
array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR
2036+
array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR
2037+
array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1;
2038+
indexrelid | indrelid
2039+
------------+----------
2040+
(0 rows)
2041+
2042+
-- Check that opclasses and collations match the underlying columns.
2043+
-- (As written, this test ignores expression indexes.)
2044+
SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname
2045+
FROM (SELECT indexrelid, indrelid, unnest(indkey) as ikey,
2046+
unnest(indclass) as iclass, unnest(indcollation) as icoll
2047+
FROM pg_index) ss,
2048+
pg_attribute a,
2049+
pg_opclass opc
2050+
WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND
2051+
(NOT binary_coercible(atttypid, opcintype) OR icoll != attcollation);
2052+
indexrelid | indrelid | attname | atttypid | opcname
2053+
------------+----------+---------+----------+---------
2054+
(0 rows)
2055+
2056+
-- For system catalogs, be even tighter: nearly all indexes should be
2057+
-- exact type matches not binary-coercible matches. At this writing
2058+
-- the only exception is an OID index on a regproc column.
2059+
SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname
2060+
FROM (SELECT indexrelid, indrelid, unnest(indkey) as ikey,
2061+
unnest(indclass) as iclass, unnest(indcollation) as icoll
2062+
FROM pg_index
2063+
WHERE indrelid < 16384) ss,
2064+
pg_attribute a,
2065+
pg_opclass opc
2066+
WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND
2067+
(opcintype != atttypid OR icoll != attcollation)
2068+
ORDER BY 1;
2069+
indexrelid | indrelid | attname | atttypid | opcname
2070+
--------------------------+--------------+----------+----------+---------
2071+
pg_aggregate_fnoid_index | pg_aggregate | aggfnoid | regproc | oid_ops
2072+
(1 row)
2073+
2074+
-- Check for system catalogs with collation-sensitive ordering. This is not
2075+
-- a representational error in pg_index, but simply wrong catalog design.
2076+
-- It's bad because we expect to be able to clone template0 and assign the
2077+
-- copy a different database collation. It would especially not work for
2078+
-- shared catalogs. Note that although text columns will show a collation
2079+
-- in indcollation, they're still okay to index with text_pattern_ops,
2080+
-- so allow that case.
2081+
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
2082+
FROM (SELECT indexrelid, indrelid,
2083+
unnest(indclass) as iclass, unnest(indcollation) as icoll
2084+
FROM pg_index
2085+
WHERE indrelid < 16384) ss
2086+
WHERE icoll != 0 AND iclass !=
2087+
(SELECT oid FROM pg_opclass
2088+
WHERE opcname = 'text_pattern_ops' AND opcmethod =
2089+
(SELECT oid FROM pg_am WHERE amname = 'btree'));
2090+
indexrelid | indrelid | iclass | icoll
2091+
------------+----------+--------+-------
2092+
(0 rows)
2093+

src/test/regress/sql/opr_sanity.sql

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
-- OPR_SANITY
33
-- Sanity checks for common errors in making operator/procedure system tables:
44
-- pg_operator, pg_proc, pg_cast, pg_aggregate, pg_am,
5-
-- pg_amop, pg_amproc, pg_opclass, pg_opfamily.
5+
-- pg_amop, pg_amproc, pg_opclass, pg_opfamily, pg_index.
66
--
77
-- Every test failure in this file should be closely inspected.
88
-- The description of the failing test should be read carefully before
@@ -30,7 +30,9 @@ SELECT ($1 = $2) OR
3030
($2 = 'pg_catalog.any'::pg_catalog.regtype) OR
3131
($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
3232
EXISTS(select 1 from pg_catalog.pg_type where
33-
oid = $1 and typelem != 0 and typlen = -1))
33+
oid = $1 and typelem != 0 and typlen = -1)) OR
34+
($2 = 'pg_catalog.anyrange'::pg_catalog.regtype AND
35+
(select typtype from pg_catalog.pg_type where oid = $1) = 'r')
3436
$$ language sql strict stable;
3537

3638
-- This one ignores castcontext, so it considers only physical equivalence
@@ -43,7 +45,9 @@ SELECT ($1 = $2) OR
4345
($2 = 'pg_catalog.any'::pg_catalog.regtype) OR
4446
($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
4547
EXISTS(select 1 from pg_catalog.pg_type where
46-
oid = $1 and typelem != 0 and typlen = -1))
48+
oid = $1 and typelem != 0 and typlen = -1)) OR
49+
($2 = 'pg_catalog.anyrange'::pg_catalog.regtype AND
50+
(select typtype from pg_catalog.pg_type where oid = $1) = 'r')
4751
$$ language sql strict stable;
4852

4953
-- **************** pg_proc ****************
@@ -1316,3 +1320,66 @@ FROM pg_amproc AS p1, pg_proc AS p2
13161320
WHERE p1.amproc = p2.oid AND
13171321
p1.amproclefttype != p1.amprocrighttype AND
13181322
p2.provolatile = 'v';
1323+
1324+
-- **************** pg_index ****************
1325+
1326+
-- Look for illegal values in pg_index fields.
1327+
1328+
SELECT p1.indexrelid, p1.indrelid
1329+
FROM pg_index as p1
1330+
WHERE p1.indexrelid = 0 OR p1.indrelid = 0 OR
1331+
p1.indnatts <= 0 OR p1.indnatts > 32;
1332+
1333+
-- oidvector and int2vector fields should be of length indnatts.
1334+
1335+
SELECT p1.indexrelid, p1.indrelid
1336+
FROM pg_index as p1
1337+
WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR
1338+
array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR
1339+
array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR
1340+
array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1;
1341+
1342+
-- Check that opclasses and collations match the underlying columns.
1343+
-- (As written, this test ignores expression indexes.)
1344+
1345+
SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname
1346+
FROM (SELECT indexrelid, indrelid, unnest(indkey) as ikey,
1347+
unnest(indclass) as iclass, unnest(indcollation) as icoll
1348+
FROM pg_index) ss,
1349+
pg_attribute a,
1350+
pg_opclass opc
1351+
WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND
1352+
(NOT binary_coercible(atttypid, opcintype) OR icoll != attcollation);
1353+
1354+
-- For system catalogs, be even tighter: nearly all indexes should be
1355+
-- exact type matches not binary-coercible matches. At this writing
1356+
-- the only exception is an OID index on a regproc column.
1357+
1358+
SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname
1359+
FROM (SELECT indexrelid, indrelid, unnest(indkey) as ikey,
1360+
unnest(indclass) as iclass, unnest(indcollation) as icoll
1361+
FROM pg_index
1362+
WHERE indrelid < 16384) ss,
1363+
pg_attribute a,
1364+
pg_opclass opc
1365+
WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND
1366+
(opcintype != atttypid OR icoll != attcollation)
1367+
ORDER BY 1;
1368+
1369+
-- Check for system catalogs with collation-sensitive ordering. This is not
1370+
-- a representational error in pg_index, but simply wrong catalog design.
1371+
-- It's bad because we expect to be able to clone template0 and assign the
1372+
-- copy a different database collation. It would especially not work for
1373+
-- shared catalogs. Note that although text columns will show a collation
1374+
-- in indcollation, they're still okay to index with text_pattern_ops,
1375+
-- so allow that case.
1376+
1377+
SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll
1378+
FROM (SELECT indexrelid, indrelid,
1379+
unnest(indclass) as iclass, unnest(indcollation) as icoll
1380+
FROM pg_index
1381+
WHERE indrelid < 16384) ss
1382+
WHERE icoll != 0 AND iclass !=
1383+
(SELECT oid FROM pg_opclass
1384+
WHERE opcname = 'text_pattern_ops' AND opcmethod =
1385+
(SELECT oid FROM pg_am WHERE amname = 'btree'));

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy