pageinspect: Fix failure with hash_bitmap_info() for partitioned indexes

This function reads directly a page from a relation, relying on
index_open() to open the index to read from.  Unfortunately, this would
crash when using partitioned indexes, as these can be opened with
index_open() but they have no physical pages.

Alexander has fixed the module, while I have written the test.

Author: Alexander Lakhin, Michael Paquier
Discussion: https://postgr.es/m/18246-f4d9ff7cb3af77e6@postgresql.org
Backpatch-through: 12
This commit is contained in:
Michael Paquier 2023-12-19 18:19:18 +09:00
parent f729fdab45
commit bfbe4a146e
3 changed files with 14 additions and 2 deletions

View File

@ -1,6 +1,8 @@
CREATE TABLE test_hash (a int, b text); CREATE TABLE test_hash (a int, b text);
INSERT INTO test_hash VALUES (1, 'one'); INSERT INTO test_hash VALUES (1, 'one');
CREATE INDEX test_hash_a_idx ON test_hash USING hash (a); CREATE INDEX test_hash_a_idx ON test_hash USING hash (a);
CREATE TABLE test_hash_part (a int, b int) PARTITION BY RANGE (a);
CREATE INDEX test_hash_part_idx ON test_hash_part USING hash(b);
\x \x
SELECT hash_page_type(get_raw_page('test_hash_a_idx', 0)); SELECT hash_page_type(get_raw_page('test_hash_a_idx', 0));
-[ RECORD 1 ]--+--------- -[ RECORD 1 ]--+---------
@ -44,6 +46,8 @@ SELECT * FROM hash_bitmap_info('test_hash_a_idx', 5);
ERROR: invalid overflow block number 5 ERROR: invalid overflow block number 5
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 6); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 6);
ERROR: block number 6 is out of range for relation "test_hash_a_idx" ERROR: block number 6 is out of range for relation "test_hash_a_idx"
SELECT * FROM hash_bitmap_info('test_hash_part_idx', 1); -- error
ERROR: "test_hash_part_idx" is not a hash index
SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM
hash_metapage_info(get_raw_page('test_hash_a_idx', 0)); hash_metapage_info(get_raw_page('test_hash_a_idx', 0));
@ -203,3 +207,4 @@ SELECT hash_page_type(decode(repeat('00', :block_size), 'hex'));
hash_page_type | unused hash_page_type | unused
DROP TABLE test_hash; DROP TABLE test_hash;
DROP TABLE test_hash_part;

View File

@ -12,6 +12,7 @@
#include "access/hash.h" #include "access/hash.h"
#include "access/htup_details.h" #include "access/htup_details.h"
#include "access/relation.h"
#include "catalog/pg_am.h" #include "catalog/pg_am.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "funcapi.h" #include "funcapi.h"
@ -27,6 +28,7 @@ PG_FUNCTION_INFO_V1(hash_page_items);
PG_FUNCTION_INFO_V1(hash_bitmap_info); PG_FUNCTION_INFO_V1(hash_bitmap_info);
PG_FUNCTION_INFO_V1(hash_metapage_info); PG_FUNCTION_INFO_V1(hash_metapage_info);
#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
#define IS_HASH(r) ((r)->rd_rel->relam == HASH_AM_OID) #define IS_HASH(r) ((r)->rd_rel->relam == HASH_AM_OID)
/* ------------------------------------------------ /* ------------------------------------------------
@ -417,9 +419,9 @@ hash_bitmap_info(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions"))); errmsg("must be superuser to use raw page functions")));
indexRel = index_open(indexRelid, AccessShareLock); indexRel = relation_open(indexRelid, AccessShareLock);
if (!IS_HASH(indexRel)) if (!IS_INDEX(indexRel) || !IS_HASH(indexRel))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a %s index", errmsg("\"%s\" is not a %s index",

View File

@ -2,6 +2,9 @@ CREATE TABLE test_hash (a int, b text);
INSERT INTO test_hash VALUES (1, 'one'); INSERT INTO test_hash VALUES (1, 'one');
CREATE INDEX test_hash_a_idx ON test_hash USING hash (a); CREATE INDEX test_hash_a_idx ON test_hash USING hash (a);
CREATE TABLE test_hash_part (a int, b int) PARTITION BY RANGE (a);
CREATE INDEX test_hash_part_idx ON test_hash_part USING hash(b);
\x \x
SELECT hash_page_type(get_raw_page('test_hash_a_idx', 0)); SELECT hash_page_type(get_raw_page('test_hash_a_idx', 0));
@ -21,6 +24,7 @@ SELECT * FROM hash_bitmap_info('test_hash_a_idx', 3);
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 4); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 4);
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 5); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 5);
SELECT * FROM hash_bitmap_info('test_hash_a_idx', 6); SELECT * FROM hash_bitmap_info('test_hash_a_idx', 6);
SELECT * FROM hash_bitmap_info('test_hash_part_idx', 1); -- error
SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
@ -106,3 +110,4 @@ SELECT hash_page_stats(decode(repeat('00', :block_size), 'hex'));
SELECT hash_page_type(decode(repeat('00', :block_size), 'hex')); SELECT hash_page_type(decode(repeat('00', :block_size), 'hex'));
DROP TABLE test_hash; DROP TABLE test_hash;
DROP TABLE test_hash_part;