Skip to content

Commit 27bc177

Browse files
committed
Generalize relation analyze in table AM interface
Currently, there is just one algorithm for sampling tuples from a table written in acquire_sample_rows(). Custom table AM can just redefine the way to get the next block/tuple by implementing scan_analyze_next_block() and scan_analyze_next_tuple() API functions. This approach doesn't seem general enough. For instance, it's unclear how to sample this way index-organized tables. This commit allows table AM to encapsulate the whole sampling algorithm (currently implemented in acquire_sample_rows()) into the relation_analyze() API function. Discussion: https://postgr.es/m/CAPpHfdurb9ycV8udYqM%3Do0sPS66PJ4RCBM1g-bBpvzUfogY0EA%40mail.gmail.com Reviewed-by: Pavel Borisov, Matthias van de Meent
1 parent b154d8a commit 27bc177

File tree

7 files changed

+100
-125
lines changed

7 files changed

+100
-125
lines changed

src/backend/access/heap/heapam_handler.c

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ static TM_Result heapam_tuple_lock(Relation relation, ItemPointer tid,
5050
CommandId cid, LockTupleMode mode,
5151
LockWaitPolicy wait_policy, uint8 flags,
5252
TM_FailureData *tmfd);
53-
5453
static void reform_and_rewrite_tuple(HeapTuple tuple,
5554
Relation OldHeap, Relation NewHeap,
5655
Datum *values, bool *isnull, RewriteState rwstate);
@@ -1052,7 +1051,15 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
10521051
pfree(isnull);
10531052
}
10541053

1055-
static bool
1054+
/*
1055+
* Prepare to analyze block `blockno` of `scan`. The scan has been started
1056+
* with SO_TYPE_ANALYZE option.
1057+
*
1058+
* This routine holds a buffer pin and lock on the heap page. They are held
1059+
* until heapam_scan_analyze_next_tuple() returns false. That is until all the
1060+
* items of the heap page are analyzed.
1061+
*/
1062+
void
10561063
heapam_scan_analyze_next_block(TableScanDesc scan, BlockNumber blockno,
10571064
BufferAccessStrategy bstrategy)
10581065
{
@@ -1072,12 +1079,19 @@ heapam_scan_analyze_next_block(TableScanDesc scan, BlockNumber blockno,
10721079
hscan->rs_cbuf = ReadBufferExtended(scan->rs_rd, MAIN_FORKNUM,
10731080
blockno, RBM_NORMAL, bstrategy);
10741081
LockBuffer(hscan->rs_cbuf, BUFFER_LOCK_SHARE);
1075-
1076-
/* in heap all blocks can contain tuples, so always return true */
1077-
return true;
10781082
}
10791083

1080-
static bool
1084+
/*
1085+
* Iterate over tuples in the block selected with
1086+
* heapam_scan_analyze_next_block(). If a tuple that's suitable for sampling
1087+
* is found, true is returned and a tuple is stored in `slot`. When no more
1088+
* tuples for sampling, false is returned and the pin and lock acquired by
1089+
* heapam_scan_analyze_next_block() are released.
1090+
*
1091+
* *liverows and *deadrows are incremented according to the encountered
1092+
* tuples.
1093+
*/
1094+
bool
10811095
heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
10821096
double *liverows, double *deadrows,
10831097
TupleTableSlot *slot)
@@ -2637,10 +2651,9 @@ static const TableAmRoutine heapam_methods = {
26372651
.relation_copy_data = heapam_relation_copy_data,
26382652
.relation_copy_for_cluster = heapam_relation_copy_for_cluster,
26392653
.relation_vacuum = heap_vacuum_rel,
2640-
.scan_analyze_next_block = heapam_scan_analyze_next_block,
2641-
.scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,
26422654
.index_build_range_scan = heapam_index_build_range_scan,
26432655
.index_validate_scan = heapam_index_validate_scan,
2656+
.relation_analyze = heapam_analyze,
26442657

26452658
.free_rd_amcache = NULL,
26462659
.relation_size = table_block_relation_size,

src/backend/access/table/tableamapi.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@ GetTableAmRoutine(Oid amhandler)
8181
Assert(routine->relation_copy_data != NULL);
8282
Assert(routine->relation_copy_for_cluster != NULL);
8383
Assert(routine->relation_vacuum != NULL);
84-
Assert(routine->scan_analyze_next_block != NULL);
85-
Assert(routine->scan_analyze_next_tuple != NULL);
8684
Assert(routine->index_build_range_scan != NULL);
8785
Assert(routine->index_validate_scan != NULL);
8886

src/backend/commands/analyze.c

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <math.h>
1818

1919
#include "access/detoast.h"
20+
#include "access/heapam.h"
2021
#include "access/genam.h"
2122
#include "access/multixact.h"
2223
#include "access/relation.h"
@@ -190,10 +191,9 @@ analyze_rel(Oid relid, RangeVar *relation,
190191
if (onerel->rd_rel->relkind == RELKIND_RELATION ||
191192
onerel->rd_rel->relkind == RELKIND_MATVIEW)
192193
{
193-
/* Regular table, so we'll use the regular row acquisition function */
194-
acquirefunc = acquire_sample_rows;
195-
/* Also get regular table's size */
196-
relpages = RelationGetNumberOfBlocks(onerel);
194+
/* Use row acquisition function provided by table AM */
195+
table_relation_analyze(onerel, &acquirefunc,
196+
&relpages, vac_strategy);
197197
}
198198
else if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
199199
{
@@ -1103,15 +1103,15 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr)
11031103
}
11041104

11051105
/*
1106-
* acquire_sample_rows -- acquire a random sample of rows from the table
1106+
* acquire_sample_rows -- acquire a random sample of rows from the heap
11071107
*
11081108
* Selected rows are returned in the caller-allocated array rows[], which
11091109
* must have at least targrows entries.
11101110
* The actual number of rows selected is returned as the function result.
1111-
* We also estimate the total numbers of live and dead rows in the table,
1111+
* We also estimate the total numbers of live and dead rows in the heap,
11121112
* and return them into *totalrows and *totaldeadrows, respectively.
11131113
*
1114-
* The returned list of tuples is in order by physical position in the table.
1114+
* The returned list of tuples is in order by physical position in the heap.
11151115
* (We will rely on this later to derive correlation estimates.)
11161116
*
11171117
* As of May 2004 we use a new two-stage method: Stage one selects up
@@ -1133,7 +1133,7 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr)
11331133
* look at a statistically unbiased set of blocks, we should get
11341134
* unbiased estimates of the average numbers of live and dead rows per
11351135
* block. The previous sampling method put too much credence in the row
1136-
* density near the start of the table.
1136+
* density near the start of the heap.
11371137
*/
11381138
static int
11391139
acquire_sample_rows(Relation onerel, int elevel,
@@ -1184,7 +1184,7 @@ acquire_sample_rows(Relation onerel, int elevel,
11841184
/* Prepare for sampling rows */
11851185
reservoir_init_selection_state(&rstate, targrows);
11861186

1187-
scan = table_beginscan_analyze(onerel);
1187+
scan = heap_beginscan(onerel, NULL, 0, NULL, NULL, SO_TYPE_ANALYZE);
11881188
slot = table_slot_create(onerel, NULL);
11891189

11901190
#ifdef USE_PREFETCH
@@ -1214,7 +1214,6 @@ acquire_sample_rows(Relation onerel, int elevel,
12141214
/* Outer loop over blocks to sample */
12151215
while (BlockSampler_HasMore(&bs))
12161216
{
1217-
bool block_accepted;
12181217
BlockNumber targblock = BlockSampler_Next(&bs);
12191218
#ifdef USE_PREFETCH
12201219
BlockNumber prefetch_targblock = InvalidBlockNumber;
@@ -1230,29 +1229,19 @@ acquire_sample_rows(Relation onerel, int elevel,
12301229

12311230
vacuum_delay_point();
12321231

1233-
block_accepted = table_scan_analyze_next_block(scan, targblock, vac_strategy);
1232+
heapam_scan_analyze_next_block(scan, targblock, vac_strategy);
12341233

12351234
#ifdef USE_PREFETCH
12361235

12371236
/*
12381237
* When pre-fetching, after we get a block, tell the kernel about the
12391238
* next one we will want, if there's any left.
1240-
*
1241-
* We want to do this even if the table_scan_analyze_next_block() call
1242-
* above decides against analyzing the block it picked.
12431239
*/
12441240
if (prefetch_maximum && prefetch_targblock != InvalidBlockNumber)
12451241
PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, prefetch_targblock);
12461242
#endif
12471243

1248-
/*
1249-
* Don't analyze if table_scan_analyze_next_block() indicated this
1250-
* block is unsuitable for analyzing.
1251-
*/
1252-
if (!block_accepted)
1253-
continue;
1254-
1255-
while (table_scan_analyze_next_tuple(scan, OldestXmin, &liverows, &deadrows, slot))
1244+
while (heapam_scan_analyze_next_tuple(scan, OldestXmin, &liverows, &deadrows, slot))
12561245
{
12571246
/*
12581247
* The first targrows sample rows are simply copied into the
@@ -1302,7 +1291,7 @@ acquire_sample_rows(Relation onerel, int elevel,
13021291
}
13031292

13041293
ExecDropSingleTupleTableSlot(slot);
1305-
table_endscan(scan);
1294+
heap_endscan(scan);
13061295

13071296
/*
13081297
* If we didn't find as many tuples as we wanted then we're done. No sort
@@ -1373,6 +1362,19 @@ compare_rows(const void *a, const void *b, void *arg)
13731362
return 0;
13741363
}
13751364

1365+
/*
1366+
* heapam_analyze -- implementation of relation_analyze() table access method
1367+
* callback for heap
1368+
*/
1369+
void
1370+
heapam_analyze(Relation relation, AcquireSampleRowsFunc *func,
1371+
BlockNumber *totalpages, BufferAccessStrategy bstrategy)
1372+
{
1373+
*func = acquire_sample_rows;
1374+
*totalpages = RelationGetNumberOfBlocks(relation);
1375+
vac_strategy = bstrategy;
1376+
}
1377+
13761378

13771379
/*
13781380
* acquire_inherited_sample_rows -- acquire sample rows from inheritance tree
@@ -1462,9 +1464,9 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
14621464
if (childrel->rd_rel->relkind == RELKIND_RELATION ||
14631465
childrel->rd_rel->relkind == RELKIND_MATVIEW)
14641466
{
1465-
/* Regular table, so use the regular row acquisition function */
1466-
acquirefunc = acquire_sample_rows;
1467-
relpages = RelationGetNumberOfBlocks(childrel);
1467+
/* Use row acquisition function provided by table AM */
1468+
table_relation_analyze(childrel, &acquirefunc,
1469+
&relpages, vac_strategy);
14681470
}
14691471
else if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
14701472
{

src/include/access/heapam.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,15 @@ extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
369369
extern bool HeapTupleIsSurelyDead(HeapTuple htup,
370370
struct GlobalVisState *vistest);
371371

372+
/* in heap/heapam_handler.c*/
373+
extern void heapam_scan_analyze_next_block(TableScanDesc scan,
374+
BlockNumber blockno,
375+
BufferAccessStrategy bstrategy);
376+
extern bool heapam_scan_analyze_next_tuple(TableScanDesc scan,
377+
TransactionId OldestXmin,
378+
double *liverows, double *deadrows,
379+
TupleTableSlot *slot);
380+
372381
/*
373382
* To avoid leaking too much knowledge about reorderbuffer implementation
374383
* details this is implemented in reorderbuffer.c not heapam_visibility.c

src/include/access/tableam.h

Lines changed: 22 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "access/relscan.h"
2121
#include "access/sdir.h"
2222
#include "access/xact.h"
23+
#include "commands/vacuum.h"
2324
#include "executor/tuptable.h"
2425
#include "utils/rel.h"
2526
#include "utils/snapshot.h"
@@ -658,41 +659,6 @@ typedef struct TableAmRoutine
658659
struct VacuumParams *params,
659660
BufferAccessStrategy bstrategy);
660661

661-
/*
662-
* Prepare to analyze block `blockno` of `scan`. The scan has been started
663-
* with table_beginscan_analyze(). See also
664-
* table_scan_analyze_next_block().
665-
*
666-
* The callback may acquire resources like locks that are held until
667-
* table_scan_analyze_next_tuple() returns false. It e.g. can make sense
668-
* to hold a lock until all tuples on a block have been analyzed by
669-
* scan_analyze_next_tuple.
670-
*
671-
* The callback can return false if the block is not suitable for
672-
* sampling, e.g. because it's a metapage that could never contain tuples.
673-
*
674-
* XXX: This obviously is primarily suited for block-based AMs. It's not
675-
* clear what a good interface for non block based AMs would be, so there
676-
* isn't one yet.
677-
*/
678-
bool (*scan_analyze_next_block) (TableScanDesc scan,
679-
BlockNumber blockno,
680-
BufferAccessStrategy bstrategy);
681-
682-
/*
683-
* See table_scan_analyze_next_tuple().
684-
*
685-
* Not every AM might have a meaningful concept of dead rows, in which
686-
* case it's OK to not increment *deadrows - but note that that may
687-
* influence autovacuum scheduling (see comment for relation_vacuum
688-
* callback).
689-
*/
690-
bool (*scan_analyze_next_tuple) (TableScanDesc scan,
691-
TransactionId OldestXmin,
692-
double *liverows,
693-
double *deadrows,
694-
TupleTableSlot *slot);
695-
696662
/* see table_index_build_range_scan for reference about parameters */
697663
double (*index_build_range_scan) (Relation table_rel,
698664
Relation index_rel,
@@ -713,6 +679,12 @@ typedef struct TableAmRoutine
713679
Snapshot snapshot,
714680
struct ValidateIndexState *state);
715681

682+
/* See table_relation_analyze() */
683+
void (*relation_analyze) (Relation relation,
684+
AcquireSampleRowsFunc *func,
685+
BlockNumber *totalpages,
686+
BufferAccessStrategy bstrategy);
687+
716688

717689
/* ------------------------------------------------------------------------
718690
* Miscellaneous functions.
@@ -1008,19 +980,6 @@ table_beginscan_tid(Relation rel, Snapshot snapshot)
1008980
return rel->rd_tableam->scan_begin(rel, snapshot, 0, NULL, NULL, flags);
1009981
}
1010982

1011-
/*
1012-
* table_beginscan_analyze is an alternative entry point for setting up a
1013-
* TableScanDesc for an ANALYZE scan. As with bitmap scans, it's worth using
1014-
* the same data structure although the behavior is rather different.
1015-
*/
1016-
static inline TableScanDesc
1017-
table_beginscan_analyze(Relation rel)
1018-
{
1019-
uint32 flags = SO_TYPE_ANALYZE;
1020-
1021-
return rel->rd_tableam->scan_begin(rel, NULL, 0, NULL, NULL, flags);
1022-
}
1023-
1024983
/*
1025984
* End relation scan.
1026985
*/
@@ -1746,42 +1705,6 @@ table_relation_vacuum(Relation rel, struct VacuumParams *params,
17461705
rel->rd_tableam->relation_vacuum(rel, params, bstrategy);
17471706
}
17481707

1749-
/*
1750-
* Prepare to analyze block `blockno` of `scan`. The scan needs to have been
1751-
* started with table_beginscan_analyze(). Note that this routine might
1752-
* acquire resources like locks that are held until
1753-
* table_scan_analyze_next_tuple() returns false.
1754-
*
1755-
* Returns false if block is unsuitable for sampling, true otherwise.
1756-
*/
1757-
static inline bool
1758-
table_scan_analyze_next_block(TableScanDesc scan, BlockNumber blockno,
1759-
BufferAccessStrategy bstrategy)
1760-
{
1761-
return scan->rs_rd->rd_tableam->scan_analyze_next_block(scan, blockno,
1762-
bstrategy);
1763-
}
1764-
1765-
/*
1766-
* Iterate over tuples in the block selected with
1767-
* table_scan_analyze_next_block() (which needs to have returned true, and
1768-
* this routine may not have returned false for the same block before). If a
1769-
* tuple that's suitable for sampling is found, true is returned and a tuple
1770-
* is stored in `slot`.
1771-
*
1772-
* *liverows and *deadrows are incremented according to the encountered
1773-
* tuples.
1774-
*/
1775-
static inline bool
1776-
table_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
1777-
double *liverows, double *deadrows,
1778-
TupleTableSlot *slot)
1779-
{
1780-
return scan->rs_rd->rd_tableam->scan_analyze_next_tuple(scan, OldestXmin,
1781-
liverows, deadrows,
1782-
slot);
1783-
}
1784-
17851708
/*
17861709
* table_index_build_scan - scan the table to find tuples to be indexed
17871710
*
@@ -1887,6 +1810,21 @@ table_index_validate_scan(Relation table_rel,
18871810
state);
18881811
}
18891812

1813+
/*
1814+
* table_relation_analyze - fill the infromation for a sampling statistics
1815+
* acquisition
1816+
*
1817+
* The pointer to a function that will collect sample rows from the table
1818+
* should be stored to `*func`, plus the estimated size of the table in pages
1819+
* should br stored to `*totalpages`.
1820+
*/
1821+
static inline void
1822+
table_relation_analyze(Relation relation, AcquireSampleRowsFunc *func,
1823+
BlockNumber *totalpages, BufferAccessStrategy bstrategy)
1824+
{
1825+
relation->rd_tableam->relation_analyze(relation, func,
1826+
totalpages, bstrategy);
1827+
}
18901828

18911829
/* ----------------------------------------------------------------------------
18921830
* Miscellaneous functionality

0 commit comments

Comments
 (0)