From ede000ac82133434ebe293506d5d434f6f360904 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Sun, 23 Oct 2022 14:44:43 -0700 Subject: [PATCH v2 09/14] heapam: Add num_pages to RelationGetBufferForTuple() This will be useful to compute the number of pages to extend a relation by. Author: Reviewed-by: Discussion: https://postgr.es/m/ Backpatch: --- src/include/access/hio.h | 14 +++++++- src/backend/access/heap/heapam.c | 60 +++++++++++++++++++++++++++++--- src/backend/access/heap/hio.c | 8 ++++- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/src/include/access/hio.h b/src/include/access/hio.h index 3f20b585326..dd61462d988 100644 --- a/src/include/access/hio.h +++ b/src/include/access/hio.h @@ -30,6 +30,17 @@ typedef struct BulkInsertStateData { BufferAccessStrategy strategy; /* our BULKWRITE strategy object */ Buffer current_buf; /* current insertion target page */ + + /* + * State for bulk extensions. Further pages that were unused at the time + * of the extension. They might be in use by the time we use them though, + * so rechecks are needed. + * + * FIXME: Perhaps these should live in RelationData instead, alongside the + * targetblock? + */ + BlockNumber next_free; + BlockNumber last_free; } BulkInsertStateData; @@ -38,6 +49,7 @@ extern void RelationPutHeapTuple(Relation relation, Buffer buffer, extern Buffer RelationGetBufferForTuple(Relation relation, Size len, Buffer otherBuffer, int options, BulkInsertStateData *bistate, - Buffer *vmbuffer, Buffer *vmbuffer_other); + Buffer *vmbuffer, Buffer *vmbuffer_other, + int num_pages); #endif /* HIO_H */ diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 63c4f01f0fd..e73a750892c 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -1979,6 +1979,8 @@ GetBulkInsertState(void) bistate = (BulkInsertState) palloc(sizeof(BulkInsertStateData)); bistate->strategy = GetAccessStrategy(BAS_BULKWRITE); bistate->current_buf = InvalidBuffer; + bistate->next_free = InvalidBlockNumber; + bistate->last_free = InvalidBlockNumber; return bistate; } @@ -2052,7 +2054,8 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid, */ buffer = RelationGetBufferForTuple(relation, heaptup->t_len, InvalidBuffer, options, bistate, - &vmbuffer, NULL); + &vmbuffer, NULL, + 0); /* * We're about to do the actual insert -- but check for conflict first, to @@ -2255,6 +2258,33 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid, return tup; } +/* + * Helper for heap_multi_insert() that computes the number of full pages s + */ +static int +heap_multi_insert_pages(HeapTuple *heaptuples, int done, int ntuples, Size saveFreeSpace) +{ + size_t page_avail; + int npages = 0; + + page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace; + npages++; + + for (int i = done; i < ntuples; i++) + { + size_t tup_sz = sizeof(ItemIdData) + MAXALIGN(heaptuples[i]->t_len); + + if (page_avail < tup_sz) + { + npages++; + page_avail = BLCKSZ - SizeOfPageHeaderData - saveFreeSpace; + } + page_avail -= tup_sz; + } + + return npages; +} + /* * heap_multi_insert - insert multiple tuples into a heap * @@ -2281,6 +2311,9 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, Size saveFreeSpace; bool need_tuple_data = RelationIsLogicallyLogged(relation); bool need_cids = RelationIsAccessibleInLogicalDecoding(relation); + bool starting_with_empty_page = false; + int npages = 0; + int npages_used = 0; /* currently not needed (thus unsupported) for heap_multi_insert() */ Assert(!(options & HEAP_INSERT_NO_LOGICAL)); @@ -2331,13 +2364,30 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, while (ndone < ntuples) { Buffer buffer; - bool starting_with_empty_page; bool all_visible_cleared = false; bool all_frozen_set = false; int nthispage; CHECK_FOR_INTERRUPTS(); + /* + * Compute number of pages needed to insert tuples in the worst + * case. This will be used to determine how much to extend the + * relation by in RelationGetBufferForTuple(), if needed. If we + * filled a prior page from scratch, we can just update our last + * computation, but if we started with a partially filled page + * recompute from scratch, the number of potentially required pages + * can vary due to tuples needing to fit onto the page, page headers + * etc. + */ + if (ndone == 0 || !starting_with_empty_page) + { + npages = heap_multi_insert_pages(heaptuples, ndone, ntuples, saveFreeSpace); + npages_used = 0; + } + else + npages_used++; + /* * Find buffer where at least the next tuple will fit. If the page is * all-visible, this will also pin the requisite visibility map page. @@ -2347,7 +2397,8 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, */ buffer = RelationGetBufferForTuple(relation, heaptuples[ndone]->t_len, InvalidBuffer, options, bistate, - &vmbuffer, NULL); + &vmbuffer, NULL, + npages - npages_used); page = BufferGetPage(buffer); starting_with_empty_page = PageGetMaxOffsetNumber(page) == 0; @@ -3770,7 +3821,8 @@ l2: /* It doesn't fit, must use RelationGetBufferForTuple. */ newbuf = RelationGetBufferForTuple(relation, heaptup->t_len, buffer, 0, NULL, - &vmbuffer_new, &vmbuffer); + &vmbuffer_new, &vmbuffer, + 0); /* We're all done. */ break; } diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c index 7479212d4e0..65886839e70 100644 --- a/src/backend/access/heap/hio.c +++ b/src/backend/access/heap/hio.c @@ -275,6 +275,11 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate) * Returns pinned and exclusive-locked buffer of a page in given relation * with free space >= given len. * + * If num_pages is > 1, the relation will be extended by at least that many + * pages when we decide to extend the relation. This is more efficient for + * callers that know they will need multiple pages + * (e.g. heap_multi_insert()). + * * If otherBuffer is not InvalidBuffer, then it references a previously * pinned buffer of another page in the same relation; on return, this * buffer will also be exclusive-locked. (This case is used by heap_update; @@ -333,7 +338,8 @@ Buffer RelationGetBufferForTuple(Relation relation, Size len, Buffer otherBuffer, int options, BulkInsertState bistate, - Buffer *vmbuffer, Buffer *vmbuffer_other) + Buffer *vmbuffer, Buffer *vmbuffer_other, + int num_pages) { bool use_fsm = !(options & HEAP_INSERT_SKIP_FSM); Buffer buffer = InvalidBuffer; -- 2.38.0