@@ -317,10 +317,10 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate)
317
317
* BULKWRITE buffer selection strategy object to the buffer manager.
318
318
* Passing NULL for bistate selects the default behavior.
319
319
*
320
- * We always try to avoid filling existing pages further than the fillfactor.
321
- * This is OK since this routine is not consulted when updating a tuple and
322
- * keeping it on the same page, which is the scenario fillfactor is meant
323
- * to reserve space for.
320
+ * We don't fill existing pages further than the fillfactor, except for large
321
+ * tuples in nearly-empty pages. This is OK since this routine is not
322
+ * consulted when updating a tuple and keeping it on the same page, which is
323
+ * the scenario fillfactor is meant to reserve space for.
324
324
*
325
325
* ereport(ERROR) is allowed here, so this routine *must* be called
326
326
* before any (unlogged) changes are made in buffer pool.
@@ -334,8 +334,10 @@ RelationGetBufferForTuple(Relation relation, Size len,
334
334
bool use_fsm = !(options & HEAP_INSERT_SKIP_FSM );
335
335
Buffer buffer = InvalidBuffer ;
336
336
Page page ;
337
- Size pageFreeSpace = 0 ,
338
- saveFreeSpace = 0 ;
337
+ Size nearlyEmptyFreeSpace ,
338
+ pageFreeSpace = 0 ,
339
+ saveFreeSpace = 0 ,
340
+ targetFreeSpace = 0 ;
339
341
BlockNumber targetBlock ,
340
342
otherBlock ;
341
343
bool needLock ;
@@ -358,6 +360,19 @@ RelationGetBufferForTuple(Relation relation, Size len,
358
360
saveFreeSpace = RelationGetTargetPageFreeSpace (relation ,
359
361
HEAP_DEFAULT_FILLFACTOR );
360
362
363
+ /*
364
+ * Since pages without tuples can still have line pointers, we consider
365
+ * pages "empty" when the unavailable space is slight. This threshold is
366
+ * somewhat arbitrary, but it should prevent most unnecessary relation
367
+ * extensions while inserting large tuples into low-fillfactor tables.
368
+ */
369
+ nearlyEmptyFreeSpace = MaxHeapTupleSize -
370
+ (MaxHeapTuplesPerPage / 8 * sizeof (ItemIdData ));
371
+ if (len + saveFreeSpace > nearlyEmptyFreeSpace )
372
+ targetFreeSpace = Max (len , nearlyEmptyFreeSpace );
373
+ else
374
+ targetFreeSpace = len + saveFreeSpace ;
375
+
361
376
if (otherBuffer != InvalidBuffer )
362
377
otherBlock = BufferGetBlockNumber (otherBuffer );
363
378
else
@@ -376,13 +391,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
376
391
* When use_fsm is false, we either put the tuple onto the existing target
377
392
* page or extend the relation.
378
393
*/
379
- if (len + saveFreeSpace > MaxHeapTupleSize )
380
- {
381
- /* can't fit, don't bother asking FSM */
382
- targetBlock = InvalidBlockNumber ;
383
- use_fsm = false;
384
- }
385
- else if (bistate && bistate -> current_buf != InvalidBuffer )
394
+ if (bistate && bistate -> current_buf != InvalidBuffer )
386
395
targetBlock = BufferGetBlockNumber (bistate -> current_buf );
387
396
else
388
397
targetBlock = RelationGetTargetBlock (relation );
@@ -393,7 +402,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
393
402
* We have no cached target page, so ask the FSM for an initial
394
403
* target.
395
404
*/
396
- targetBlock = GetPageWithFreeSpace (relation , len + saveFreeSpace );
405
+ targetBlock = GetPageWithFreeSpace (relation , targetFreeSpace );
397
406
}
398
407
399
408
/*
@@ -517,7 +526,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
517
526
}
518
527
519
528
pageFreeSpace = PageGetHeapFreeSpace (page );
520
- if (len + saveFreeSpace <= pageFreeSpace )
529
+ if (targetFreeSpace <= pageFreeSpace )
521
530
{
522
531
/* use this page as future insert target, too */
523
532
RelationSetTargetBlock (relation , targetBlock );
@@ -550,7 +559,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
550
559
targetBlock = RecordAndGetPageWithFreeSpace (relation ,
551
560
targetBlock ,
552
561
pageFreeSpace ,
553
- len + saveFreeSpace );
562
+ targetFreeSpace );
554
563
}
555
564
556
565
/*
@@ -582,7 +591,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
582
591
* Check if some other backend has extended a block for us while
583
592
* we were waiting on the lock.
584
593
*/
585
- targetBlock = GetPageWithFreeSpace (relation , len + saveFreeSpace );
594
+ targetBlock = GetPageWithFreeSpace (relation , targetFreeSpace );
586
595
587
596
/*
588
597
* If some other waiter has already extended the relation, we
0 commit comments