1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

Restructure index access method API to hide most of it at the C level.

This patch reduces pg_am to just two columns, a name and a handler
function.  All the data formerly obtained from pg_am is now provided
in a C struct returned by the handler function.  This is similar to
the designs we've adopted for FDWs and tablesample methods.  There
are multiple advantages.  For one, the index AM's support functions
are now simple C functions, making them faster to call and much less
error-prone, since the C compiler can now check function signatures.
For another, this will make it far more practical to define index access
methods in installable extensions.

A disadvantage is that SQL-level code can no longer see attributes
of index AMs; in particular, some of the crosschecks in the opr_sanity
regression test are no longer possible from SQL.  We've addressed that
by adding a facility for the index AM to perform such checks instead.
(Much more could be done in that line, but for now we're content if the
amvalidate functions more or less replace what opr_sanity used to do.)
We might also want to expose some sort of reporting functionality, but
this patch doesn't do that.

Alexander Korotkov, reviewed by Petr Jelínek, and rather heavily
editorialized on by me.
This commit is contained in:
Tom Lane
2016-01-17 19:36:59 -05:00
parent 8d290c8ec6
commit 65c5fcd353
93 changed files with 2493 additions and 1924 deletions

View File

@ -73,131 +73,11 @@ WHERE aggmtranstype != 0 AND
------+---------------
(0 rows)
SELECT ctid, amkeytype
SELECT ctid, amhandler
FROM pg_catalog.pg_am fk
WHERE amkeytype != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amkeytype);
ctid | amkeytype
------+-----------
(0 rows)
SELECT ctid, aminsert
FROM pg_catalog.pg_am fk
WHERE aminsert != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aminsert);
ctid | aminsert
------+----------
(0 rows)
SELECT ctid, ambeginscan
FROM pg_catalog.pg_am fk
WHERE ambeginscan != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambeginscan);
ctid | ambeginscan
------+-------------
(0 rows)
SELECT ctid, amgettuple
FROM pg_catalog.pg_am fk
WHERE amgettuple != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
ctid | amgettuple
------+------------
(0 rows)
SELECT ctid, amgetbitmap
FROM pg_catalog.pg_am fk
WHERE amgetbitmap != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetbitmap);
ctid | amgetbitmap
------+-------------
(0 rows)
SELECT ctid, amrescan
FROM pg_catalog.pg_am fk
WHERE amrescan != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrescan);
ctid | amrescan
------+----------
(0 rows)
SELECT ctid, amendscan
FROM pg_catalog.pg_am fk
WHERE amendscan != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amendscan);
ctid | amendscan
------+-----------
(0 rows)
SELECT ctid, ammarkpos
FROM pg_catalog.pg_am fk
WHERE ammarkpos != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ammarkpos);
ctid | ammarkpos
------+-----------
(0 rows)
SELECT ctid, amrestrpos
FROM pg_catalog.pg_am fk
WHERE amrestrpos != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrestrpos);
ctid | amrestrpos
------+------------
(0 rows)
SELECT ctid, ambuild
FROM pg_catalog.pg_am fk
WHERE ambuild != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuild);
ctid | ambuild
------+---------
(0 rows)
SELECT ctid, ambuildempty
FROM pg_catalog.pg_am fk
WHERE ambuildempty != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuildempty);
ctid | ambuildempty
------+--------------
(0 rows)
SELECT ctid, ambulkdelete
FROM pg_catalog.pg_am fk
WHERE ambulkdelete != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambulkdelete);
ctid | ambulkdelete
------+--------------
(0 rows)
SELECT ctid, amvacuumcleanup
FROM pg_catalog.pg_am fk
WHERE amvacuumcleanup != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amvacuumcleanup);
ctid | amvacuumcleanup
------+-----------------
(0 rows)
SELECT ctid, amcanreturn
FROM pg_catalog.pg_am fk
WHERE amcanreturn != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcanreturn);
ctid | amcanreturn
------+-------------
(0 rows)
SELECT ctid, amcostestimate
FROM pg_catalog.pg_am fk
WHERE amcostestimate != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcostestimate);
ctid | amcostestimate
------+----------------
(0 rows)
SELECT ctid, amoptions
FROM pg_catalog.pg_am fk
WHERE amoptions != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amoptions);
ctid | amoptions
WHERE amhandler != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amhandler);
ctid | amhandler
------+-----------
(0 rows)
@ -809,6 +689,14 @@ WHERE opfowner != 0 AND
------+----------
(0 rows)
SELECT ctid, polrelid
FROM pg_catalog.pg_policy fk
WHERE polrelid != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.polrelid);
ctid | polrelid
------+----------
(0 rows)
SELECT ctid, pronamespace
FROM pg_catalog.pg_proc fk
WHERE pronamespace != 0 AND

View File

@ -1565,6 +1565,32 @@ WHERE p1.oid != p2.oid AND
-----+-----
(0 rows)
-- Ask access methods to validate opclasses
SELECT oid, opcname FROM pg_opclass WHERE NOT amvalidate(oid);
oid | opcname
-----+---------
(0 rows)
-- **************** pg_am ****************
-- Look for illegal values in pg_am fields
SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
oid | amname
-----+--------
(0 rows)
-- Check for amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
WHERE p2.oid = p1.amhandler AND
(p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
-----+--------+-----+---------
(0 rows)
-- **************** pg_amop ****************
-- Look for illegal values in pg_amop fields
SELECT p1.amopfamily, p1.amopstrategy
@ -1610,49 +1636,6 @@ WHERE p1.amopsortfamily <> 0 AND NOT EXISTS
------------+--------------
(0 rows)
-- check for ordering operators not supported by parent AM
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
FROM pg_amop AS p1, pg_am AS p2
WHERE p1.amopmethod = p2.oid AND
p1.amoppurpose = 'o' AND NOT p2.amcanorderbyop;
amopfamily | amopopr | oid | amname
------------+---------+-----+--------
(0 rows)
-- Cross-check amopstrategy index against parent AM
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
FROM pg_amop AS p1, pg_am AS p2
WHERE p1.amopmethod = p2.oid AND
p1.amopstrategy > p2.amstrategies AND p2.amstrategies <> 0;
amopfamily | amopopr | oid | amname
------------+---------+-----+--------
(0 rows)
-- Detect missing pg_amop entries: should have as many strategy operators
-- as AM expects for each datatype combination supported by the opfamily.
-- We can't check this for AMs with variable strategy sets.
SELECT p1.amname, p2.amoplefttype, p2.amoprighttype
FROM pg_am AS p1, pg_amop AS p2
WHERE p2.amopmethod = p1.oid AND
p1.amstrategies <> 0 AND
p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
WHERE p3.amopfamily = p2.amopfamily AND
p3.amoplefttype = p2.amoplefttype AND
p3.amoprighttype = p2.amoprighttype AND
p3.amoppurpose = 's');
amname | amoplefttype | amoprighttype
--------+--------------+---------------
(0 rows)
-- Currently, none of the AMs with fixed strategy sets support ordering ops.
SELECT p1.amname, p2.amopfamily, p2.amopstrategy
FROM pg_am AS p1, pg_amop AS p2
WHERE p2.amopmethod = p1.oid AND
p1.amstrategies <> 0 AND p2.amoppurpose <> 's';
amname | amopfamily | amopstrategy
--------+------------+--------------
(0 rows)
-- Check that amopopr points at a reasonable-looking operator, ie a binary
-- operator. If it's a search operator it had better yield boolean,
-- otherwise an input type of its sort opfamily.
@ -1935,65 +1918,6 @@ WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
--------------+-----------
(0 rows)
-- Cross-check amprocnum index against parent AM
SELECT p1.amprocfamily, p1.amprocnum, p2.oid, p2.amname
FROM pg_amproc AS p1, pg_am AS p2, pg_opfamily AS p3
WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
p1.amprocnum > p2.amsupport;
amprocfamily | amprocnum | oid | amname
--------------+-----------+-----+--------
(0 rows)
-- Detect missing pg_amproc entries: should have as many support functions
-- as AM expects for each datatype combination supported by the opfamily.
SELECT * FROM (
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype,
array_agg(p3.amprocnum ORDER BY amprocnum) AS procnums
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid
GROUP BY p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
) AS t
WHERE NOT (
-- btree has one mandatory and one optional support function.
-- hash has one support function, which is mandatory.
-- GiST has eight support functions, one of which is optional.
-- GIN has six support functions. 1-3 are mandatory, 5 is optional, and
-- at least one of 4 and 6 must be given.
-- SP-GiST has five support functions, all mandatory
-- BRIN has four mandatory support functions, and a bunch of optionals
amname = 'btree' AND procnums @> '{1}' OR
amname = 'hash' AND procnums = '{1}' OR
amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
amname = 'brin' AND procnums @> '{1, 2, 3, 4}'
);
amname | opfname | amproclefttype | amprocrighttype | procnums
--------+---------+----------------+-----------------+----------
(0 rows)
-- Also, check if there are any pg_opclass entries that don't seem to have
-- pg_amproc support.
SELECT * FROM (
SELECT amname, opcname, array_agg(amprocnum ORDER BY amprocnum) as procnums
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
amproclefttype = amprocrighttype AND amproclefttype = opcintype
GROUP BY amname, opcname, amprocfamily
) AS t
WHERE NOT (
-- same per-AM rules as above
amname = 'btree' AND procnums @> '{1}' OR
amname = 'hash' AND procnums = '{1}' OR
amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
amname = 'brin' AND procnums @> '{1, 2, 3, 4}'
);
amname | opcname | procnums
--------+---------+----------
(0 rows)
-- Unfortunately, we can't check the amproc link very well because the
-- signature of the function may be different for different support routines
-- or different base data types.

View File

@ -37,70 +37,10 @@ SELECT ctid, aggmtranstype
FROM pg_catalog.pg_aggregate fk
WHERE aggmtranstype != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.aggmtranstype);
SELECT ctid, amkeytype
SELECT ctid, amhandler
FROM pg_catalog.pg_am fk
WHERE amkeytype != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amkeytype);
SELECT ctid, aminsert
FROM pg_catalog.pg_am fk
WHERE aminsert != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aminsert);
SELECT ctid, ambeginscan
FROM pg_catalog.pg_am fk
WHERE ambeginscan != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambeginscan);
SELECT ctid, amgettuple
FROM pg_catalog.pg_am fk
WHERE amgettuple != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
SELECT ctid, amgetbitmap
FROM pg_catalog.pg_am fk
WHERE amgetbitmap != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetbitmap);
SELECT ctid, amrescan
FROM pg_catalog.pg_am fk
WHERE amrescan != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrescan);
SELECT ctid, amendscan
FROM pg_catalog.pg_am fk
WHERE amendscan != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amendscan);
SELECT ctid, ammarkpos
FROM pg_catalog.pg_am fk
WHERE ammarkpos != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ammarkpos);
SELECT ctid, amrestrpos
FROM pg_catalog.pg_am fk
WHERE amrestrpos != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrestrpos);
SELECT ctid, ambuild
FROM pg_catalog.pg_am fk
WHERE ambuild != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuild);
SELECT ctid, ambuildempty
FROM pg_catalog.pg_am fk
WHERE ambuildempty != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuildempty);
SELECT ctid, ambulkdelete
FROM pg_catalog.pg_am fk
WHERE ambulkdelete != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambulkdelete);
SELECT ctid, amvacuumcleanup
FROM pg_catalog.pg_am fk
WHERE amvacuumcleanup != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amvacuumcleanup);
SELECT ctid, amcanreturn
FROM pg_catalog.pg_am fk
WHERE amcanreturn != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcanreturn);
SELECT ctid, amcostestimate
FROM pg_catalog.pg_am fk
WHERE amcostestimate != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcostestimate);
SELECT ctid, amoptions
FROM pg_catalog.pg_am fk
WHERE amoptions != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amoptions);
WHERE amhandler != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amhandler);
SELECT ctid, amopfamily
FROM pg_catalog.pg_amop fk
WHERE amopfamily != 0 AND
@ -405,6 +345,10 @@ SELECT ctid, opfowner
FROM pg_catalog.pg_opfamily fk
WHERE opfowner != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.opfowner);
SELECT ctid, polrelid
FROM pg_catalog.pg_policy fk
WHERE polrelid != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.polrelid);
SELECT ctid, pronamespace
FROM pg_catalog.pg_proc fk
WHERE pronamespace != 0 AND

View File

@ -50,6 +50,7 @@ SELECT ($1 = $2) OR
(select typtype from pg_catalog.pg_type where oid = $1) = 'r')
$$ language sql strict stable;
-- **************** pg_proc ****************
-- Look for illegal values in pg_proc fields.
@ -1001,6 +1002,7 @@ SELECT p.oid, proname
FROM pg_proc AS p JOIN pg_aggregate AS a ON a.aggfnoid = p.oid
WHERE proisagg AND provariadic != 0 AND a.aggkind = 'n';
-- **************** pg_opfamily ****************
-- Look for illegal values in pg_opfamily fields
@ -1009,6 +1011,7 @@ SELECT p1.oid
FROM pg_opfamily as p1
WHERE p1.opfmethod = 0 OR p1.opfnamespace = 0;
-- **************** pg_opclass ****************
-- Look for illegal values in pg_opclass fields
@ -1033,6 +1036,29 @@ WHERE p1.oid != p2.oid AND
p1.opcmethod = p2.opcmethod AND p1.opcintype = p2.opcintype AND
p1.opcdefault AND p2.opcdefault;
-- Ask access methods to validate opclasses
SELECT oid, opcname FROM pg_opclass WHERE NOT amvalidate(oid);
-- **************** pg_am ****************
-- Look for illegal values in pg_am fields
SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
-- Check for amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
WHERE p2.oid = p1.amhandler AND
(p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
-- Look for illegal values in pg_amop fields
@ -1068,41 +1094,6 @@ WHERE p1.amopsortfamily <> 0 AND NOT EXISTS
(SELECT 1 from pg_opfamily op WHERE op.oid = p1.amopsortfamily
AND op.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree'));
-- check for ordering operators not supported by parent AM
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
FROM pg_amop AS p1, pg_am AS p2
WHERE p1.amopmethod = p2.oid AND
p1.amoppurpose = 'o' AND NOT p2.amcanorderbyop;
-- Cross-check amopstrategy index against parent AM
SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
FROM pg_amop AS p1, pg_am AS p2
WHERE p1.amopmethod = p2.oid AND
p1.amopstrategy > p2.amstrategies AND p2.amstrategies <> 0;
-- Detect missing pg_amop entries: should have as many strategy operators
-- as AM expects for each datatype combination supported by the opfamily.
-- We can't check this for AMs with variable strategy sets.
SELECT p1.amname, p2.amoplefttype, p2.amoprighttype
FROM pg_am AS p1, pg_amop AS p2
WHERE p2.amopmethod = p1.oid AND
p1.amstrategies <> 0 AND
p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
WHERE p3.amopfamily = p2.amopfamily AND
p3.amoplefttype = p2.amoplefttype AND
p3.amoprighttype = p2.amoprighttype AND
p3.amoppurpose = 's');
-- Currently, none of the AMs with fixed strategy sets support ordering ops.
SELECT p1.amname, p2.amopfamily, p2.amopstrategy
FROM pg_am AS p1, pg_amop AS p2
WHERE p2.amopmethod = p1.oid AND
p1.amstrategies <> 0 AND p2.amoppurpose <> 's';
-- Check that amopopr points at a reasonable-looking operator, ie a binary
-- operator. If it's a search operator it had better yield boolean,
-- otherwise an input type of its sort opfamily.
@ -1249,59 +1240,6 @@ FROM pg_amproc as p1
WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
OR p1.amprocnum < 1 OR p1.amproc = 0;
-- Cross-check amprocnum index against parent AM
SELECT p1.amprocfamily, p1.amprocnum, p2.oid, p2.amname
FROM pg_amproc AS p1, pg_am AS p2, pg_opfamily AS p3
WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
p1.amprocnum > p2.amsupport;
-- Detect missing pg_amproc entries: should have as many support functions
-- as AM expects for each datatype combination supported by the opfamily.
SELECT * FROM (
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype,
array_agg(p3.amprocnum ORDER BY amprocnum) AS procnums
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid
GROUP BY p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
) AS t
WHERE NOT (
-- btree has one mandatory and one optional support function.
-- hash has one support function, which is mandatory.
-- GiST has eight support functions, one of which is optional.
-- GIN has six support functions. 1-3 are mandatory, 5 is optional, and
-- at least one of 4 and 6 must be given.
-- SP-GiST has five support functions, all mandatory
-- BRIN has four mandatory support functions, and a bunch of optionals
amname = 'btree' AND procnums @> '{1}' OR
amname = 'hash' AND procnums = '{1}' OR
amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
amname = 'brin' AND procnums @> '{1, 2, 3, 4}'
);
-- Also, check if there are any pg_opclass entries that don't seem to have
-- pg_amproc support.
SELECT * FROM (
SELECT amname, opcname, array_agg(amprocnum ORDER BY amprocnum) as procnums
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
amproclefttype = amprocrighttype AND amproclefttype = opcintype
GROUP BY amname, opcname, amprocfamily
) AS t
WHERE NOT (
-- same per-AM rules as above
amname = 'btree' AND procnums @> '{1}' OR
amname = 'hash' AND procnums = '{1}' OR
amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
amname = 'brin' AND procnums @> '{1, 2, 3, 4}'
);
-- Unfortunately, we can't check the amproc link very well because the
-- signature of the function may be different for different support routines
-- or different base data types.
@ -1395,6 +1333,7 @@ WHERE p1.amproc = p2.oid AND
p1.amproclefttype != p1.amprocrighttype AND
p2.provolatile = 'v';
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.