v7-0003-BRIN-Backfill-empty-ranges.patch
application/octet-stream
Filename: v7-0003-BRIN-Backfill-empty-ranges.patch
Type: application/octet-stream
Part: 1
Patch
Format: format-patch
Series: patch v7-0003
Subject: BRIN: Backfill empty ranges
| File | + | − |
|---|---|---|
| src/backend/access/brin/brin.c | 49 | 20 |
From 922460a1d56c0365770bfb08bdd8e2b291164362 Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
Date: Thu, 30 Nov 2023 18:12:16 +0100
Subject: [PATCH v7 3/3] BRIN: Backfill empty ranges
If we don't, then an index with WHERE (practically_nonnull IS NULL)
would be a full table scan due to missing entries in the ranges table.
The issue is fixed for both normal and parallel index creation.
---
src/backend/access/brin/brin.c | 69 ++++++++++++++++++++++++----------
1 file changed, 49 insertions(+), 20 deletions(-)
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 251350fde2..9d2179e6d7 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -165,6 +165,7 @@ typedef struct BrinBuildState
double bs_numtuples;
double bs_reltuples;
Buffer bs_currentInsertBuf;
+ BlockNumber bs_tablePages;
BlockNumber bs_pagesPerRange;
BlockNumber bs_currRangeStart;
BrinRevmap *bs_rmAccess;
@@ -1134,6 +1135,7 @@ brinbuild(Relation heap, Relation index, IndexInfo *indexInfo)
state->bs_spool = (BrinSpool *) palloc0(sizeof(BrinSpool));
state->bs_spool->heap = heap;
state->bs_spool->index = index;
+ state->bs_tablePages = RelationGetNumberOfBlocks(heap);
/*
* Attempt to launch parallel worker scan when required
@@ -1208,6 +1210,23 @@ brinbuild(Relation heap, Relation index, IndexInfo *indexInfo)
/* process the final batch */
form_and_insert_tuple(state);
+ state->bs_currRangeStart += state->bs_pagesPerRange;
+ /*
+ * Backfill the final ranges with empty data.
+ *
+ * This saves us from doing what amounts to full table scans when the
+ * index is built on stupid index quals like WHERE (nonnull_column IS
+ * NULL).
+ */
+ while (state->bs_currRangeStart + state->bs_pagesPerRange - 1 < state->bs_tablePages)
+ {
+ brin_memtuple_initialize(state->bs_dtuple, state->bs_bdesc);
+
+ form_and_insert_tuple(state);
+
+ state->bs_currRangeStart += state->bs_pagesPerRange;
+ }
+
/* track the number of relation tuples */
state->bs_reltuples = reltuples;
}
@@ -2625,13 +2644,34 @@ _brin_end_parallel(BrinLeader *brinleader, BrinBuildState *state)
tuplesort_end(spool->sortstate);
- /* Fill empty ranges at the end, for all ranges missing in the tuplesort. */
- prevblkno = (prevblkno == InvalidBlockNumber) ? 0 : prevblkno;
- while (prevblkno + state->bs_pagesPerRange < memtuple->bt_blkno)
+ /* Fill the BRIN tuple for the last page range with data. */
+ if (prevblkno != InvalidBlockNumber)
{
- /* the missing range */
+ BrinTuple *tmp;
+ Size len;
+
+ tmp = brin_form_tuple(state->bs_bdesc, memtuple->bt_blkno,
+ memtuple, &len);
+
+ brin_doinsert(state->bs_irel, state->bs_pagesPerRange, state->bs_rmAccess,
+ &state->bs_currentInsertBuf, tmp->bt_blkno, tmp, len);
+
+ pfree(tmp);
+ }
+
+ /*
+ * Fill empty ranges at the end, for all ranges missing in the tuplesort.
+ *
+ * Starting from here, prevblkno is the to-be-inserted range's start block
+ * number. Note that we don't fill in the relation's last page range.
+ */
+ if (prevblkno == InvalidBlockNumber)
+ prevblkno = 0;
+ else
prevblkno += state->bs_pagesPerRange;
+ while (prevblkno + state->bs_pagesPerRange < state->bs_tablePages)
+ {
/* Did we already build the empty range? If not, do it now. */
if (emptyTuple == NULL)
{
@@ -2641,32 +2681,21 @@ _brin_end_parallel(BrinLeader *brinleader, BrinBuildState *state)
}
else
{
- /* we already have am "empty range" tuple, just set the block */
+ /* we already have an "empty range" tuple, just set the block */
emptyTuple->bt_blkno = prevblkno;
}
+ /* Insert the missing range */
brin_doinsert(state->bs_irel, state->bs_pagesPerRange, state->bs_rmAccess,
&state->bs_currentInsertBuf,
emptyTuple->bt_blkno, emptyTuple, emptySize);
- }
- /* Fill the BRIN tuple for the last page range. */
- if (prevblkno != InvalidBlockNumber)
- {
- BrinTuple *tmp;
- Size len;
-
- tmp = brin_form_tuple(state->bs_bdesc, memtuple->bt_blkno,
- memtuple, &len);
-
- brin_doinsert(state->bs_irel, state->bs_pagesPerRange, state->bs_rmAccess,
- &state->bs_currentInsertBuf, tmp->bt_blkno, tmp, len);
-
- pfree(tmp);
+ /* ... and update to the next range's block number */
+ prevblkno += state->bs_pagesPerRange;
}
/*
- * Switch back to the originam memory context, and destroy the one we
+ * Switch back to the original memory context, and destroy the one we
* created to isolate the union_tuple calls.
*/
MemoryContextSwitchTo(oldCxt);
--
2.40.1