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