0001-pgstattuple-Use-streaming-read-API-in-pgstatindex-fu.patch
application/x-patch
Filename: 0001-pgstattuple-Use-streaming-read-API-in-pgstatindex-fu.patch
Type: application/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0001
Subject: pgstattuple: Use streaming read API in pgstatindex functions
| File | + | − |
|---|---|---|
| contrib/pgstattuple/pgstatindex.c | 121 | 84 |
From 153ab2799803dc402789d0aa825456ea12f2d3d9 Mon Sep 17 00:00:00 2001
From: alterego655 <824662526@qq.com>
Date: Sun, 12 Oct 2025 21:27:22 +0800
Subject: [PATCH] pgstattuple: Use streaming read API in pgstatindex functions
Replace synchronous ReadBufferExtended() loops with the streaming read
API in pgstatindex_impl() and pgstathashindex().
---
contrib/pgstattuple/pgstatindex.c | 205 ++++++++++++++++++------------
1 file changed, 121 insertions(+), 84 deletions(-)
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index 40823d54fca..a708cc417b0 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -37,6 +37,7 @@
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
+#include "storage/read_stream.h"
#include "utils/rel.h"
#include "utils/varlena.h"
@@ -273,60 +274,77 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
indexStat.fragments = 0;
/*
- * Scan all blocks except the metapage
+ * Scan all blocks except the metapage using streaming reads
*/
nblocks = RelationGetNumberOfBlocks(rel);
- for (blkno = 1; blkno < nblocks; blkno++)
{
- Buffer buffer;
- Page page;
- BTPageOpaque opaque;
-
- CHECK_FOR_INTERRUPTS();
-
- /* Read and lock buffer */
- buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);
- LockBuffer(buffer, BUFFER_LOCK_SHARE);
-
- page = BufferGetPage(buffer);
- opaque = BTPageGetOpaque(page);
-
- /*
- * Determine page type, and update totals.
- *
- * Note that we arbitrarily bucket deleted pages together without
- * considering if they're leaf pages or internal pages.
- */
- if (P_ISDELETED(opaque))
- indexStat.deleted_pages++;
- else if (P_IGNORE(opaque))
- indexStat.empty_pages++; /* this is the "half dead" state */
- else if (P_ISLEAF(opaque))
+ BlockRangeReadStreamPrivate p;
+ ReadStream *stream;
+
+ p.current_blocknum = 1;
+ p.last_exclusive = nblocks;
+
+ stream = read_stream_begin_relation(READ_STREAM_FULL |
+ READ_STREAM_USE_BATCHING,
+ bstrategy,
+ rel,
+ MAIN_FORKNUM,
+ block_range_read_stream_cb,
+ &p,
+ 0);
+
+ for (blkno = 1; blkno < nblocks; blkno++)
{
- int max_avail;
+ Buffer buffer;
+ Page page;
+ BTPageOpaque opaque;
- max_avail = BLCKSZ - (BLCKSZ - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
- indexStat.max_avail += max_avail;
- indexStat.free_space += PageGetExactFreeSpace(page);
+ CHECK_FOR_INTERRUPTS();
- indexStat.leaf_pages++;
+ buffer = read_stream_next_buffer(stream, NULL);
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
+
+ page = BufferGetPage(buffer);
+ opaque = BTPageGetOpaque(page);
/*
- * If the next leaf is on an earlier block, it means a
- * fragmentation.
+ * Determine page type, and update totals.
+ *
+ * Note that we arbitrarily bucket deleted pages together without
+ * considering if they're leaf pages or internal pages.
*/
- if (opaque->btpo_next != P_NONE && opaque->btpo_next < blkno)
- indexStat.fragments++;
- }
- else
- indexStat.internal_pages++;
+ if (P_ISDELETED(opaque))
+ indexStat.deleted_pages++;
+ else if (P_IGNORE(opaque))
+ indexStat.empty_pages++; /* this is the "half dead" state */
+ else if (P_ISLEAF(opaque))
+ {
+ int max_avail;
- /* Unlock and release buffer */
- LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
- ReleaseBuffer(buffer);
+ max_avail = BLCKSZ - (BLCKSZ - ((PageHeader) page)->pd_special + SizeOfPageHeaderData);
+ indexStat.max_avail += max_avail;
+ indexStat.free_space += PageGetExactFreeSpace(page);
+
+ indexStat.leaf_pages++;
+
+ /*
+ * If the next leaf is on an earlier block, it means a
+ * fragmentation.
+ */
+ if (opaque->btpo_next != P_NONE && opaque->btpo_next < blkno)
+ indexStat.fragments++;
+ }
+ else
+ indexStat.internal_pages++;
+
+ UnlockReleaseBuffer(buffer);
}
+ Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer);
+ read_stream_end(stream);
+}
+
relation_close(rel, AccessShareLock);
/*----------------------------
@@ -636,60 +654,79 @@ pgstathashindex(PG_FUNCTION_ARGS)
/* prepare access strategy for this index */
bstrategy = GetAccessStrategy(BAS_BULKREAD);
- /* Start from blkno 1 as 0th block is metapage */
- for (blkno = 1; blkno < nblocks; blkno++)
+ /* Scan all blocks except the metapage using streaming reads */
{
- Buffer buf;
- Page page;
-
- CHECK_FOR_INTERRUPTS();
-
- buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
- bstrategy);
- LockBuffer(buf, BUFFER_LOCK_SHARE);
- page = BufferGetPage(buf);
-
- if (PageIsNew(page))
- stats.unused_pages++;
- else if (PageGetSpecialSize(page) !=
- MAXALIGN(sizeof(HashPageOpaqueData)))
- ereport(ERROR,
- (errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("index \"%s\" contains corrupted page at block %u",
- RelationGetRelationName(rel),
- BufferGetBlockNumber(buf))));
- else
+ BlockRangeReadStreamPrivate p;
+ ReadStream *stream;
+
+ p.current_blocknum = 1;
+ p.last_exclusive = nblocks;
+
+ stream = read_stream_begin_relation(READ_STREAM_FULL |
+ READ_STREAM_USE_BATCHING,
+ bstrategy,
+ rel,
+ MAIN_FORKNUM,
+ block_range_read_stream_cb,
+ &p,
+ 0);
+
+ for (blkno = 1; blkno < nblocks; blkno++)
{
- HashPageOpaque opaque;
- int pagetype;
+ Buffer buf;
+ Page page;
- opaque = HashPageGetOpaque(page);
- pagetype = opaque->hasho_flag & LH_PAGE_TYPE;
+ CHECK_FOR_INTERRUPTS();
- if (pagetype == LH_BUCKET_PAGE)
- {
- stats.bucket_pages++;
- GetHashPageStats(page, &stats);
- }
- else if (pagetype == LH_OVERFLOW_PAGE)
- {
- stats.overflow_pages++;
- GetHashPageStats(page, &stats);
- }
- else if (pagetype == LH_BITMAP_PAGE)
- stats.bitmap_pages++;
- else if (pagetype == LH_UNUSED_PAGE)
+ buf = read_stream_next_buffer(stream, NULL);
+ LockBuffer(buf, BUFFER_LOCK_SHARE);
+ page = BufferGetPage(buf);
+
+ if (PageIsNew(page))
stats.unused_pages++;
- else
+ else if (PageGetSpecialSize(page) !=
+ MAXALIGN(sizeof(HashPageOpaqueData)))
ereport(ERROR,
(errcode(ERRCODE_INDEX_CORRUPTED),
- errmsg("unexpected page type 0x%04X in HASH index \"%s\" block %u",
- opaque->hasho_flag, RelationGetRelationName(rel),
+ errmsg("index \"%s\" contains corrupted page at block %u",
+ RelationGetRelationName(rel),
BufferGetBlockNumber(buf))));
- }
+ else
+ {
+ HashPageOpaque opaque;
+ int pagetype;
+
+ opaque = HashPageGetOpaque(page);
+ pagetype = opaque->hasho_flag & LH_PAGE_TYPE;
+
+ if (pagetype == LH_BUCKET_PAGE)
+ {
+ stats.bucket_pages++;
+ GetHashPageStats(page, &stats);
+ }
+ else if (pagetype == LH_OVERFLOW_PAGE)
+ {
+ stats.overflow_pages++;
+ GetHashPageStats(page, &stats);
+ }
+ else if (pagetype == LH_BITMAP_PAGE)
+ stats.bitmap_pages++;
+ else if (pagetype == LH_UNUSED_PAGE)
+ stats.unused_pages++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INDEX_CORRUPTED),
+ errmsg("unexpected page type 0x%04X in HASH index \"%s\" block %u",
+ opaque->hasho_flag, RelationGetRelationName(rel),
+ BufferGetBlockNumber(buf))));
+ }
UnlockReleaseBuffer(buf);
}
+ Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer);
+ read_stream_end(stream);
+}
+
/* Done accessing the index */
index_close(rel, AccessShareLock);
--
2.51.0