Coverage Report

Created: 2026-11-09 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/access/table/tableam.c
Line
Count
Source
1
/*----------------------------------------------------------------------
2
 *
3
 * tableam.c
4
 *    Table access method routines too big to be inline functions.
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/access/table/tableam.c
12
 *
13
 * NOTES
14
 *    Note that most functions in here are documented in tableam.h, rather than
15
 *    here. That's because there's a lot of inline functions in tableam.h and
16
 *    it'd be harder to understand if one constantly had to switch between files.
17
 *
18
 *----------------------------------------------------------------------
19
 */
20
#include "postgres.h"
21
22
#include <math.h>
23
24
#include "access/syncscan.h"
25
#include "access/tableam.h"
26
#include "access/xact.h"
27
#include "optimizer/optimizer.h"
28
#include "optimizer/plancat.h"
29
#include "port/pg_bitutils.h"
30
#include "storage/bufmgr.h"
31
#include "storage/shmem.h"
32
#include "storage/smgr.h"
33
34
/*
35
 * Constants to control the behavior of block allocation to parallel workers
36
 * during a parallel seqscan.  Technically these values do not need to be
37
 * powers of 2, but having them as powers of 2 makes the math more optimal
38
 * and makes the ramp-down stepping more even.
39
 */
40
41
/* The number of I/O chunks we try to break a parallel seqscan down into */
42
#define PARALLEL_SEQSCAN_NCHUNKS      2048
43
/* Ramp down size of allocations when we've only this number of chunks left */
44
0
#define PARALLEL_SEQSCAN_RAMPDOWN_CHUNKS  64
45
/* Cap the size of parallel I/O chunks to this number of blocks */
46
#define PARALLEL_SEQSCAN_MAX_CHUNK_SIZE   8192
47
48
/* GUC variables */
49
char     *default_table_access_method = DEFAULT_TABLE_ACCESS_METHOD;
50
bool    synchronize_seqscans = true;
51
52
53
/* ----------------------------------------------------------------------------
54
 * Slot functions.
55
 * ----------------------------------------------------------------------------
56
 */
57
58
const TupleTableSlotOps *
59
table_slot_callbacks(Relation relation)
60
0
{
61
0
  const TupleTableSlotOps *tts_cb;
62
63
0
  if (relation->rd_tableam)
64
0
    tts_cb = relation->rd_tableam->slot_callbacks(relation);
65
0
  else if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
66
0
  {
67
    /*
68
     * Historically FDWs expect to store heap tuples in slots. Continue
69
     * handing them one, to make it less painful to adapt FDWs to new
70
     * versions. The cost of a heap slot over a virtual slot is pretty
71
     * small.
72
     */
73
0
    tts_cb = &TTSOpsHeapTuple;
74
0
  }
75
0
  else
76
0
  {
77
    /*
78
     * These need to be supported, as some parts of the code (like COPY)
79
     * need to create slots for such relations too. It seems better to
80
     * centralize the knowledge that a heap slot is the right thing in
81
     * that case here.
82
     */
83
0
    Assert(relation->rd_rel->relkind == RELKIND_VIEW ||
84
0
         relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
85
0
    tts_cb = &TTSOpsVirtual;
86
0
  }
87
88
0
  return tts_cb;
89
0
}
90
91
TupleTableSlot *
92
table_slot_create(Relation relation, List **reglist)
93
0
{
94
0
  const TupleTableSlotOps *tts_cb;
95
0
  TupleTableSlot *slot;
96
97
0
  tts_cb = table_slot_callbacks(relation);
98
0
  slot = MakeSingleTupleTableSlot(RelationGetDescr(relation), tts_cb);
99
100
0
  if (reglist)
101
0
    *reglist = lappend(*reglist, slot);
102
103
0
  return slot;
104
0
}
105
106
107
/* ----------------------------------------------------------------------------
108
 * Table scan functions.
109
 * ----------------------------------------------------------------------------
110
 */
111
112
TableScanDesc
113
table_beginscan_catalog(Relation relation, int nkeys, ScanKeyData *key)
114
0
{
115
0
  uint32    flags = SO_TYPE_SEQSCAN |
116
0
    SO_ALLOW_STRAT | SO_ALLOW_SYNC | SO_ALLOW_PAGEMODE | SO_TEMP_SNAPSHOT;
117
0
  Oid     relid = RelationGetRelid(relation);
118
0
  Snapshot  snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
119
120
0
  return table_beginscan_common(relation, snapshot, nkeys, key,
121
0
                  NULL, flags, SO_NONE);
122
0
}
123
124
125
/* ----------------------------------------------------------------------------
126
 * Parallel table scan related functions.
127
 * ----------------------------------------------------------------------------
128
 */
129
130
Size
131
table_parallelscan_estimate(Relation rel, Snapshot snapshot)
132
0
{
133
0
  Size    sz = 0;
134
135
0
  if (IsMVCCSnapshot(snapshot))
136
0
    sz = add_size(sz, EstimateSnapshotSpace(snapshot));
137
0
  else
138
0
    Assert(snapshot == SnapshotAny);
139
140
0
  sz = add_size(sz, rel->rd_tableam->parallelscan_estimate(rel));
141
142
0
  return sz;
143
0
}
144
145
void
146
table_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan,
147
                Snapshot snapshot)
148
0
{
149
0
  Size    snapshot_off = rel->rd_tableam->parallelscan_initialize(rel, pscan);
150
151
0
  pscan->phs_snapshot_off = snapshot_off;
152
153
0
  if (IsMVCCSnapshot(snapshot))
154
0
  {
155
0
    SerializeSnapshot(snapshot, (char *) pscan + pscan->phs_snapshot_off);
156
0
    pscan->phs_snapshot_any = false;
157
0
  }
158
0
  else
159
0
  {
160
0
    Assert(snapshot == SnapshotAny);
161
0
    pscan->phs_snapshot_any = true;
162
0
  }
163
0
}
164
165
TableScanDesc
166
table_beginscan_parallel(Relation relation, ParallelTableScanDesc pscan,
167
             uint32 flags)
168
0
{
169
0
  Snapshot  snapshot;
170
0
  uint32    internal_flags = SO_TYPE_SEQSCAN |
171
0
    SO_ALLOW_STRAT | SO_ALLOW_SYNC | SO_ALLOW_PAGEMODE;
172
173
0
  Assert(RelFileLocatorEquals(relation->rd_locator, pscan->phs_locator));
174
175
0
  if (!pscan->phs_snapshot_any)
176
0
  {
177
    /* Snapshot was serialized -- restore it */
178
0
    snapshot = RestoreSnapshot((char *) pscan + pscan->phs_snapshot_off);
179
0
    RegisterSnapshot(snapshot);
180
0
    internal_flags |= SO_TEMP_SNAPSHOT;
181
0
  }
182
0
  else
183
0
  {
184
    /* SnapshotAny passed by caller (not serialized) */
185
0
    snapshot = SnapshotAny;
186
0
  }
187
188
0
  return table_beginscan_common(relation, snapshot, 0, NULL,
189
0
                  pscan, internal_flags, flags);
190
0
}
191
192
TableScanDesc
193
table_beginscan_parallel_tidrange(Relation relation,
194
                  ParallelTableScanDesc pscan,
195
                  uint32 flags)
196
0
{
197
0
  Snapshot  snapshot;
198
0
  TableScanDesc sscan;
199
0
  uint32    internal_flags = SO_TYPE_TIDRANGESCAN | SO_ALLOW_PAGEMODE;
200
201
0
  Assert(RelFileLocatorEquals(relation->rd_locator, pscan->phs_locator));
202
203
  /* disable syncscan in parallel tid range scan. */
204
0
  pscan->phs_syncscan = false;
205
206
0
  if (!pscan->phs_snapshot_any)
207
0
  {
208
    /* Snapshot was serialized -- restore it */
209
0
    snapshot = RestoreSnapshot((char *) pscan + pscan->phs_snapshot_off);
210
0
    RegisterSnapshot(snapshot);
211
0
    internal_flags |= SO_TEMP_SNAPSHOT;
212
0
  }
213
0
  else
214
0
  {
215
    /* SnapshotAny passed by caller (not serialized) */
216
0
    snapshot = SnapshotAny;
217
0
  }
218
219
0
  sscan = table_beginscan_common(relation, snapshot, 0, NULL,
220
0
                   pscan, internal_flags, flags);
221
0
  return sscan;
222
0
}
223
224
225
/* ------------------------------------------------------------------------
226
 * Functions for non-modifying operations on individual tuples
227
 * ------------------------------------------------------------------------
228
 */
229
230
void
231
table_tuple_get_latest_tid(TableScanDesc scan, ItemPointer tid)
232
0
{
233
0
  Relation  rel = scan->rs_rd;
234
0
  const TableAmRoutine *tableam = rel->rd_tableam;
235
236
  /*
237
   * Since this can be called with user-supplied TID, don't trust the input
238
   * too much.
239
   */
240
0
  if (!tableam->tuple_tid_valid(scan, tid))
241
0
    ereport(ERROR,
242
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
243
0
         errmsg("tid (%u, %u) is not valid for relation \"%s\"",
244
0
            ItemPointerGetBlockNumberNoCheck(tid),
245
0
            ItemPointerGetOffsetNumberNoCheck(tid),
246
0
            RelationGetRelationName(rel))));
247
248
0
  tableam->tuple_get_latest_tid(scan, tid);
249
0
}
250
251
252
/* ----------------------------------------------------------------------------
253
 * Functions to make modifications a bit simpler.
254
 * ----------------------------------------------------------------------------
255
 */
256
257
/*
258
 * simple_table_tuple_insert - insert a tuple
259
 *
260
 * Currently, this routine differs from table_tuple_insert only in supplying a
261
 * default command ID and not allowing access to the speedup options.
262
 */
263
void
264
simple_table_tuple_insert(Relation rel, TupleTableSlot *slot)
265
0
{
266
0
  table_tuple_insert(rel, slot, GetCurrentCommandId(true), 0, NULL);
267
0
}
268
269
/*
270
 * simple_table_tuple_delete - delete a tuple
271
 *
272
 * This routine may be used to delete a tuple when concurrent updates of
273
 * the target tuple are not expected (for example, because we have a lock
274
 * on the relation associated with the tuple).  Any failure is reported
275
 * via ereport().
276
 */
277
void
278
simple_table_tuple_delete(Relation rel, ItemPointer tid, Snapshot snapshot)
279
0
{
280
0
  TM_Result result;
281
0
  TM_FailureData tmfd;
282
283
0
  result = table_tuple_delete(rel, tid,
284
0
                GetCurrentCommandId(true),
285
0
                0, snapshot, InvalidSnapshot,
286
0
                true /* wait for commit */ ,
287
0
                &tmfd);
288
289
0
  switch (result)
290
0
  {
291
0
    case TM_SelfModified:
292
      /* Tuple was already updated in current command? */
293
0
      elog(ERROR, "tuple already updated by self");
294
0
      break;
295
296
0
    case TM_Ok:
297
      /* done successfully */
298
0
      break;
299
300
0
    case TM_Updated:
301
0
      elog(ERROR, "tuple concurrently updated");
302
0
      break;
303
304
0
    case TM_Deleted:
305
0
      elog(ERROR, "tuple concurrently deleted");
306
0
      break;
307
308
0
    default:
309
0
      elog(ERROR, "unrecognized table_tuple_delete status: %u", result);
310
0
      break;
311
0
  }
312
0
}
313
314
/*
315
 * simple_table_tuple_update - replace a tuple
316
 *
317
 * This routine may be used to update a tuple when concurrent updates of
318
 * the target tuple are not expected (for example, because we have a lock
319
 * on the relation associated with the tuple).  Any failure is reported
320
 * via ereport().
321
 */
322
void
323
simple_table_tuple_update(Relation rel, ItemPointer otid,
324
              TupleTableSlot *slot,
325
              Snapshot snapshot,
326
              TU_UpdateIndexes *update_indexes)
327
0
{
328
0
  TM_Result result;
329
0
  TM_FailureData tmfd;
330
0
  LockTupleMode lockmode;
331
332
0
  result = table_tuple_update(rel, otid, slot,
333
0
                GetCurrentCommandId(true),
334
0
                0, snapshot, InvalidSnapshot,
335
0
                true /* wait for commit */ ,
336
0
                &tmfd, &lockmode, update_indexes);
337
338
0
  switch (result)
339
0
  {
340
0
    case TM_SelfModified:
341
      /* Tuple was already updated in current command? */
342
0
      elog(ERROR, "tuple already updated by self");
343
0
      break;
344
345
0
    case TM_Ok:
346
      /* done successfully */
347
0
      break;
348
349
0
    case TM_Updated:
350
0
      elog(ERROR, "tuple concurrently updated");
351
0
      break;
352
353
0
    case TM_Deleted:
354
0
      elog(ERROR, "tuple concurrently deleted");
355
0
      break;
356
357
0
    default:
358
0
      elog(ERROR, "unrecognized table_tuple_update status: %u", result);
359
0
      break;
360
0
  }
361
0
}
362
363
364
/* ----------------------------------------------------------------------------
365
 * Helper functions to implement parallel scans for block oriented AMs.
366
 * ----------------------------------------------------------------------------
367
 */
368
369
Size
370
table_block_parallelscan_estimate(Relation rel)
371
0
{
372
0
  return sizeof(ParallelBlockTableScanDescData);
373
0
}
374
375
Size
376
table_block_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan)
377
0
{
378
0
  ParallelBlockTableScanDesc bpscan = (ParallelBlockTableScanDesc) pscan;
379
380
0
  bpscan->base.phs_locator = rel->rd_locator;
381
0
  bpscan->phs_nblocks = RelationGetNumberOfBlocks(rel);
382
  /* compare phs_syncscan initialization to similar logic in initscan */
383
0
  bpscan->base.phs_syncscan = synchronize_seqscans &&
384
0
    !RelationUsesLocalBuffers(rel) &&
385
0
    bpscan->phs_nblocks > NBuffers / 4;
386
0
  pg_atomic_init_u32(&bpscan->phs_startblock, InvalidBlockNumber);
387
0
  pg_atomic_init_u32(&bpscan->phs_numblock, InvalidBlockNumber);
388
0
  pg_atomic_init_u64(&bpscan->phs_nallocated, 0);
389
390
0
  return sizeof(ParallelBlockTableScanDescData);
391
0
}
392
393
void
394
table_block_parallelscan_reinitialize(Relation rel, ParallelTableScanDesc pscan)
395
0
{
396
0
  ParallelBlockTableScanDesc bpscan = (ParallelBlockTableScanDesc) pscan;
397
398
  /*
399
   * Reset the start block and block limit too.  These are set by the first
400
   * participant to call table_block_parallelscan_startblock_init(), and a
401
   * rescan may want to scan a different range of blocks, e.g. a TID Range
402
   * Scan with different TID bounds.
403
   */
404
0
  pg_atomic_write_u32(&bpscan->phs_startblock, InvalidBlockNumber);
405
0
  pg_atomic_write_u32(&bpscan->phs_numblock, InvalidBlockNumber);
406
0
  pg_atomic_write_u64(&bpscan->phs_nallocated, 0);
407
0
}
408
409
/*
410
 * find and set the scan's startblock
411
 *
412
 * Determine where the parallel seq scan should start.  This function may be
413
 * called many times, once by each parallel worker.  We must be careful only
414
 * to set the phs_startblock and phs_numblock fields once.
415
 *
416
 * Callers may optionally specify a non-InvalidBlockNumber value for
417
 * 'startblock' to force the scan to start at the given page.  Likewise,
418
 * 'numblocks' can be specified as a non-InvalidBlockNumber to limit the
419
 * number of blocks to scan to that many blocks.
420
 */
421
void
422
table_block_parallelscan_startblock_init(Relation rel,
423
                     ParallelBlockTableScanWorker pbscanwork,
424
                     ParallelBlockTableScanDesc pbscan,
425
                     BlockNumber startblock,
426
                     BlockNumber numblocks)
427
0
{
428
0
  StaticAssertDecl(MaxBlockNumber <= 0xFFFFFFFE,
429
0
           "pg_nextpower2_32 may be too small for non-standard BlockNumber width");
430
431
0
  BlockNumber scan_nblocks;
432
433
  /* Reset the state we use for controlling allocation size. */
434
0
  memset(pbscanwork, 0, sizeof(*pbscanwork));
435
436
  /*
437
   * When the caller specified a limit on the number of blocks to scan, set
438
   * that in the ParallelBlockTableScanDesc, if it's not been done by
439
   * another worker already.
440
   */
441
0
  if (numblocks != InvalidBlockNumber)
442
0
  {
443
0
    uint32    expected = InvalidBlockNumber;
444
445
0
    pg_atomic_compare_exchange_u32(&pbscan->phs_numblock, &expected,
446
0
                     numblocks);
447
0
  }
448
449
  /*
450
   * If the scan's phs_startblock has not yet been initialized, we must do
451
   * so now.  If a startblock was specified, start there, otherwise if this
452
   * is not a synchronized scan, we just start at block 0, but if it is a
453
   * synchronized scan, we must get the starting position from the
454
   * synchronized scan machinery.
455
   *
456
   * If another worker initializes phs_startblock concurrently, just use
457
   * their value.
458
   */
459
0
  if (pg_atomic_read_u32(&pbscan->phs_startblock) == InvalidBlockNumber)
460
0
  {
461
0
    BlockNumber newstartblock;
462
0
    uint32    expected = InvalidBlockNumber;
463
464
0
    if (startblock != InvalidBlockNumber)
465
0
      newstartblock = startblock;
466
0
    else if (!pbscan->base.phs_syncscan)
467
0
      newstartblock = 0;
468
0
    else
469
0
      newstartblock = ss_get_location(rel, pbscan->phs_nblocks);
470
471
0
    pg_atomic_compare_exchange_u32(&pbscan->phs_startblock, &expected,
472
0
                     newstartblock);
473
0
  }
474
475
  /*
476
   * Figure out how many blocks we're going to scan; either all of them, or
477
   * just phs_numblock's worth, if a limit has been imposed.
478
   */
479
0
  if (pg_atomic_read_u32(&pbscan->phs_numblock) == InvalidBlockNumber)
480
0
    scan_nblocks = pbscan->phs_nblocks;
481
0
  else
482
0
    scan_nblocks = pg_atomic_read_u32(&pbscan->phs_numblock);
483
484
  /*
485
   * We determine the chunk size based on scan_nblocks.  First we split
486
   * scan_nblocks into PARALLEL_SEQSCAN_NCHUNKS chunks then we calculate the
487
   * next highest power of 2 number of the result.  This means we split the
488
   * blocks we're scanning into somewhere between PARALLEL_SEQSCAN_NCHUNKS
489
   * and PARALLEL_SEQSCAN_NCHUNKS / 2 chunks.
490
   */
491
0
  pbscanwork->phsw_chunk_size = pg_nextpower2_32(Max(scan_nblocks /
492
0
                             PARALLEL_SEQSCAN_NCHUNKS, 1));
493
494
  /*
495
   * Ensure we don't go over the maximum chunk size with larger tables. This
496
   * means we may get much more than PARALLEL_SEQSCAN_NCHUNKS for larger
497
   * tables.  Too large a chunk size has been shown to be detrimental to
498
   * sequential scan performance.
499
   */
500
0
  pbscanwork->phsw_chunk_size = Min(pbscanwork->phsw_chunk_size,
501
0
                    PARALLEL_SEQSCAN_MAX_CHUNK_SIZE);
502
0
}
503
504
/*
505
 * get the next page to scan
506
 *
507
 * Get the next page to scan.  Even if there are no pages left to scan,
508
 * another backend could have grabbed a page to scan and not yet finished
509
 * looking at it, so it doesn't follow that the scan is done when the first
510
 * backend gets an InvalidBlockNumber return.
511
 */
512
BlockNumber
513
table_block_parallelscan_nextpage(Relation rel,
514
                  ParallelBlockTableScanWorker pbscanwork,
515
                  ParallelBlockTableScanDesc pbscan)
516
0
{
517
0
  BlockNumber scan_nblocks;
518
0
  BlockNumber page;
519
0
  uint64    nallocated;
520
521
  /*
522
   * The logic below allocates block numbers out to parallel workers in a
523
   * way that each worker will receive a set of consecutive block numbers to
524
   * scan.  Earlier versions of this would allocate the next highest block
525
   * number to the next worker to call this function.  This would generally
526
   * result in workers never receiving consecutive block numbers.  Some
527
   * operating systems would not detect the sequential I/O pattern due to
528
   * each backend being a different process which could result in poor
529
   * performance due to inefficient or no readahead.  To work around this
530
   * issue, we now allocate a range of block numbers for each worker and
531
   * when they come back for another block, we give them the next one in
532
   * that range until the range is complete.  When the worker completes the
533
   * range of blocks we then allocate another range for it and return the
534
   * first block number from that range.
535
   *
536
   * Here we name these ranges of blocks "chunks".  The initial size of
537
   * these chunks is determined in table_block_parallelscan_startblock_init
538
   * based on the number of blocks to scan.  Towards the end of the scan, we
539
   * start making reductions in the size of the chunks in order to attempt
540
   * to divide the remaining work over all the workers as evenly as
541
   * possible.
542
   *
543
   * Here pbscanwork is local worker memory.  phsw_chunk_remaining tracks
544
   * the number of blocks remaining in the chunk.  When that reaches 0 then
545
   * we must allocate a new chunk for the worker.
546
   *
547
   * phs_nallocated tracks how many blocks have been allocated to workers
548
   * already.  When phs_nallocated >= rs_nblocks, all blocks have been
549
   * allocated.
550
   *
551
   * Because we use an atomic fetch-and-add to fetch the current value, the
552
   * phs_nallocated counter will exceed rs_nblocks, because workers will
553
   * still increment the value, when they try to allocate the next block but
554
   * all blocks have been allocated already. The counter must be 64 bits
555
   * wide because of that, to avoid wrapping around when scan_nblocks is
556
   * close to 2^32.
557
   *
558
   * The actual block to return is calculated by adding the counter to the
559
   * starting block number, modulo phs_nblocks.
560
   */
561
562
  /* First, figure out how many blocks we're planning on scanning */
563
0
  if (pg_atomic_read_u32(&pbscan->phs_numblock) == InvalidBlockNumber)
564
0
    scan_nblocks = pbscan->phs_nblocks;
565
0
  else
566
0
    scan_nblocks = pg_atomic_read_u32(&pbscan->phs_numblock);
567
568
  /*
569
   * Now check if we have any remaining blocks in a previous chunk for this
570
   * worker.  We must consume all of the blocks from that before we allocate
571
   * a new chunk to the worker.
572
   */
573
0
  if (pbscanwork->phsw_chunk_remaining > 0)
574
0
  {
575
    /*
576
     * Give them the next block in the range and update the remaining
577
     * number of blocks.
578
     */
579
0
    nallocated = ++pbscanwork->phsw_nallocated;
580
0
    pbscanwork->phsw_chunk_remaining--;
581
0
  }
582
0
  else
583
0
  {
584
    /*
585
     * When we've only got PARALLEL_SEQSCAN_RAMPDOWN_CHUNKS chunks
586
     * remaining in the scan, we half the chunk size.  Since we reduce the
587
     * chunk size here, we'll hit this again after doing
588
     * PARALLEL_SEQSCAN_RAMPDOWN_CHUNKS at the new size.  After a few
589
     * iterations of this, we'll end up doing the last few blocks with the
590
     * chunk size set to 1.
591
     */
592
0
    if (pbscanwork->phsw_chunk_size > 1 &&
593
0
      pbscanwork->phsw_nallocated > scan_nblocks -
594
0
      (pbscanwork->phsw_chunk_size * PARALLEL_SEQSCAN_RAMPDOWN_CHUNKS))
595
0
      pbscanwork->phsw_chunk_size >>= 1;
596
597
0
    nallocated = pbscanwork->phsw_nallocated =
598
0
      pg_atomic_fetch_add_u64(&pbscan->phs_nallocated,
599
0
                  pbscanwork->phsw_chunk_size);
600
601
    /*
602
     * Set the remaining number of blocks in this chunk so that subsequent
603
     * calls from this worker continue on with this chunk until it's done.
604
     */
605
0
    pbscanwork->phsw_chunk_remaining = pbscanwork->phsw_chunk_size - 1;
606
0
  }
607
608
  /* Check if we've run out of blocks to scan */
609
0
  if (nallocated >= scan_nblocks)
610
0
    page = InvalidBlockNumber; /* all blocks have been allocated */
611
0
  else
612
0
    page = (nallocated + pg_atomic_read_u32(&pbscan->phs_startblock)) % pbscan->phs_nblocks;
613
614
  /*
615
   * Report scan location.  Normally, we report the current page number.
616
   * When we reach the end of the scan, though, we report the starting page,
617
   * not the ending page, just so the starting positions for later scans
618
   * doesn't slew backwards.  We only report the position at the end of the
619
   * scan once, though: subsequent callers will report nothing.
620
   */
621
0
  if (pbscan->base.phs_syncscan)
622
0
  {
623
0
    if (page != InvalidBlockNumber)
624
0
      ss_report_location(rel, page);
625
0
    else if (nallocated == pbscan->phs_nblocks)
626
0
      ss_report_location(rel, pg_atomic_read_u32(&pbscan->phs_startblock));
627
0
  }
628
629
0
  return page;
630
0
}
631
632
/* ----------------------------------------------------------------------------
633
 * Helper functions to implement relation sizing for block oriented AMs.
634
 * ----------------------------------------------------------------------------
635
 */
636
637
/*
638
 * table_block_relation_size
639
 *
640
 * If a table AM uses the various relation forks as the sole place where data
641
 * is stored, and if it uses them in the expected manner (e.g. the actual data
642
 * is in the main fork rather than some other), it can use this implementation
643
 * of the relation_size callback rather than implementing its own.
644
 */
645
uint64
646
table_block_relation_size(Relation rel, ForkNumber forkNumber)
647
0
{
648
0
  uint64    nblocks = 0;
649
650
  /* InvalidForkNumber indicates returning the size for all forks */
651
0
  if (forkNumber == InvalidForkNumber)
652
0
  {
653
0
    for (int i = 0; i < MAX_FORKNUM; i++)
654
0
      nblocks += smgrnblocks(RelationGetSmgr(rel), i);
655
0
  }
656
0
  else
657
0
    nblocks = smgrnblocks(RelationGetSmgr(rel), forkNumber);
658
659
0
  return nblocks * BLCKSZ;
660
0
}
661
662
/*
663
 * table_block_relation_estimate_size
664
 *
665
 * This function can't be directly used as the implementation of the
666
 * relation_estimate_size callback, because it has a few additional parameters.
667
 * Instead, it is intended to be used as a helper function; the caller can
668
 * pass through the arguments to its relation_estimate_size function plus the
669
 * additional values required here.
670
 *
671
 * overhead_bytes_per_tuple should contain the approximate number of bytes
672
 * of storage required to store a tuple above and beyond what is required for
673
 * the tuple data proper. Typically, this would include things like the
674
 * size of the tuple header and item pointer. This is only used for query
675
 * planning, so a table AM where the value is not constant could choose to
676
 * pass a "best guess".
677
 *
678
 * usable_bytes_per_page should contain the approximate number of bytes per
679
 * page usable for tuple data, excluding the page header and any anticipated
680
 * special space.
681
 */
682
void
683
table_block_relation_estimate_size(Relation rel, int32 *attr_widths,
684
                   BlockNumber *pages, double *tuples,
685
                   double *allvisfrac,
686
                   Size overhead_bytes_per_tuple,
687
                   Size usable_bytes_per_page)
688
0
{
689
0
  BlockNumber curpages;
690
0
  BlockNumber relpages;
691
0
  double    reltuples;
692
0
  BlockNumber relallvisible;
693
0
  double    density;
694
695
  /* it should have storage, so we can call the smgr */
696
0
  curpages = RelationGetNumberOfBlocks(rel);
697
698
  /* coerce values in pg_class to more desirable types */
699
0
  relpages = (BlockNumber) rel->rd_rel->relpages;
700
0
  reltuples = (double) rel->rd_rel->reltuples;
701
0
  relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
702
703
  /*
704
   * HACK: if the relation has never yet been vacuumed, use a minimum size
705
   * estimate of 10 pages.  The idea here is to avoid assuming a
706
   * newly-created table is really small, even if it currently is, because
707
   * that may not be true once some data gets loaded into it.  Once a vacuum
708
   * or analyze cycle has been done on it, it's more reasonable to believe
709
   * the size is somewhat stable.
710
   *
711
   * (Note that this is only an issue if the plan gets cached and used again
712
   * after the table has been filled.  What we're trying to avoid is using a
713
   * nestloop-type plan on a table that has grown substantially since the
714
   * plan was made.  Normally, autovacuum/autoanalyze will occur once enough
715
   * inserts have happened and cause cached-plan invalidation; but that
716
   * doesn't happen instantaneously, and it won't happen at all for cases
717
   * such as temporary tables.)
718
   *
719
   * We test "never vacuumed" by seeing whether reltuples < 0.
720
   *
721
   * If the table has inheritance children, we don't apply this heuristic.
722
   * Totally empty parent tables are quite common, so we should be willing
723
   * to believe that they are empty.
724
   */
725
0
  if (curpages < 10 &&
726
0
    reltuples < 0 &&
727
0
    !rel->rd_rel->relhassubclass)
728
0
    curpages = 10;
729
730
  /* report estimated # pages */
731
0
  *pages = curpages;
732
  /* quick exit if rel is clearly empty */
733
0
  if (curpages == 0)
734
0
  {
735
0
    *tuples = 0;
736
0
    *allvisfrac = 0;
737
0
    return;
738
0
  }
739
740
  /* estimate number of tuples from previous tuple density */
741
0
  if (reltuples >= 0 && relpages > 0)
742
0
    density = reltuples / (double) relpages;
743
0
  else
744
0
  {
745
    /*
746
     * When we have no data because the relation was never yet vacuumed,
747
     * estimate tuple width from attribute datatypes.  We assume here that
748
     * the pages are completely full, which is OK for tables but is
749
     * probably an overestimate for indexes.  Fortunately
750
     * get_relation_info() can clamp the overestimate to the parent
751
     * table's size.
752
     *
753
     * Note: this code intentionally disregards alignment considerations,
754
     * because (a) that would be gilding the lily considering how crude
755
     * the estimate is, (b) it creates platform dependencies in the
756
     * default plans which are kind of a headache for regression testing,
757
     * and (c) different table AMs might use different padding schemes.
758
     */
759
0
    int32   tuple_width;
760
0
    int     fillfactor;
761
762
    /*
763
     * Without reltuples/relpages, we also need to consider fillfactor.
764
     * The other branch considers it implicitly by calculating density
765
     * from actual relpages/reltuples statistics.
766
     */
767
0
    fillfactor = RelationGetFillFactor(rel, HEAP_DEFAULT_FILLFACTOR);
768
769
0
    tuple_width = get_rel_data_width(rel, attr_widths);
770
0
    tuple_width += overhead_bytes_per_tuple;
771
    /* note: integer division is intentional here */
772
0
    density = (usable_bytes_per_page * fillfactor / 100) / tuple_width;
773
    /* There's at least one row on the page, even with low fillfactor. */
774
0
    density = clamp_row_est(density);
775
0
  }
776
0
  *tuples = rint(density * (double) curpages);
777
778
  /*
779
   * We use relallvisible as-is, rather than scaling it up like we do for
780
   * the pages and tuples counts, on the theory that any pages added since
781
   * the last VACUUM are most likely not marked all-visible.  But costsize.c
782
   * wants it converted to a fraction.
783
   */
784
0
  if (relallvisible == 0 || curpages <= 0)
785
0
    *allvisfrac = 0;
786
0
  else if ((double) relallvisible >= curpages)
787
0
    *allvisfrac = 1;
788
0
  else
789
0
    *allvisfrac = (double) relallvisible / curpages;
790
0
}