mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Provide a way block-level table AMs could re-use acquire_sample_rows()
While keeping API the same, this commit provides a way for block-level table AMs to re-use existing acquire_sample_rows() by providing custom callbacks for getting the next block and the next tuple. Reported-by: Andres Freund Discussion: https://postgr.es/m/20240407214001.jgpg5q3yv33ve6y3%40awork3.anarazel.de Reviewed-by: Pavel Borisov
This commit is contained in:
parent
df64c81ca9
commit
dd1f6b0c17
@ -2666,6 +2666,18 @@ SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* heapap_analyze -- implementation of relation_analyze() for heap
|
||||||
|
* table access method
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
heapam_analyze(Relation relation, AcquireSampleRowsFunc *func,
|
||||||
|
BlockNumber *totalpages, BufferAccessStrategy bstrategy)
|
||||||
|
{
|
||||||
|
block_level_table_analyze(relation, func, totalpages, bstrategy,
|
||||||
|
heapam_scan_analyze_next_block,
|
||||||
|
heapam_scan_analyze_next_tuple);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------
|
||||||
* Definition of the heap table access method.
|
* Definition of the heap table access method.
|
||||||
|
@ -76,6 +76,8 @@ int default_statistics_target = 100;
|
|||||||
/* A few variables that don't seem worth passing around as parameters */
|
/* A few variables that don't seem worth passing around as parameters */
|
||||||
static MemoryContext anl_context = NULL;
|
static MemoryContext anl_context = NULL;
|
||||||
static BufferAccessStrategy vac_strategy;
|
static BufferAccessStrategy vac_strategy;
|
||||||
|
static ScanAnalyzeNextBlockFunc scan_analyze_next_block;
|
||||||
|
static ScanAnalyzeNextTupleFunc scan_analyze_next_tuple;
|
||||||
|
|
||||||
|
|
||||||
static void do_analyze_rel(Relation onerel,
|
static void do_analyze_rel(Relation onerel,
|
||||||
@ -88,9 +90,6 @@ static void compute_index_stats(Relation onerel, double totalrows,
|
|||||||
MemoryContext col_context);
|
MemoryContext col_context);
|
||||||
static VacAttrStats *examine_attribute(Relation onerel, int attnum,
|
static VacAttrStats *examine_attribute(Relation onerel, int attnum,
|
||||||
Node *index_expr);
|
Node *index_expr);
|
||||||
static int acquire_sample_rows(Relation onerel, int elevel,
|
|
||||||
HeapTuple *rows, int targrows,
|
|
||||||
double *totalrows, double *totaldeadrows);
|
|
||||||
static int compare_rows(const void *a, const void *b, void *arg);
|
static int compare_rows(const void *a, const void *b, void *arg);
|
||||||
static int acquire_inherited_sample_rows(Relation onerel, int elevel,
|
static int acquire_inherited_sample_rows(Relation onerel, int elevel,
|
||||||
HeapTuple *rows, int targrows,
|
HeapTuple *rows, int targrows,
|
||||||
@ -191,7 +190,10 @@ analyze_rel(Oid relid, RangeVar *relation,
|
|||||||
if (onerel->rd_rel->relkind == RELKIND_RELATION ||
|
if (onerel->rd_rel->relkind == RELKIND_RELATION ||
|
||||||
onerel->rd_rel->relkind == RELKIND_MATVIEW)
|
onerel->rd_rel->relkind == RELKIND_MATVIEW)
|
||||||
{
|
{
|
||||||
/* Use row acquisition function provided by table AM */
|
/*
|
||||||
|
* Get row acquisition function, blocks and tuples iteration callbacks
|
||||||
|
* provided by table AM
|
||||||
|
*/
|
||||||
table_relation_analyze(onerel, &acquirefunc,
|
table_relation_analyze(onerel, &acquirefunc,
|
||||||
&relpages, vac_strategy);
|
&relpages, vac_strategy);
|
||||||
}
|
}
|
||||||
@ -1117,15 +1119,17 @@ block_sampling_read_stream_next(ReadStream *stream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* acquire_sample_rows -- acquire a random sample of rows from the heap
|
* acquire_sample_rows -- acquire a random sample of rows from the
|
||||||
|
* block-based relation
|
||||||
*
|
*
|
||||||
* Selected rows are returned in the caller-allocated array rows[], which
|
* Selected rows are returned in the caller-allocated array rows[], which
|
||||||
* must have at least targrows entries.
|
* must have at least targrows entries.
|
||||||
* The actual number of rows selected is returned as the function result.
|
* The actual number of rows selected is returned as the function result.
|
||||||
* We also estimate the total numbers of live and dead rows in the heap,
|
* We also estimate the total numbers of live and dead rows in the relation,
|
||||||
* and return them into *totalrows and *totaldeadrows, respectively.
|
* and return them into *totalrows and *totaldeadrows, respectively.
|
||||||
*
|
*
|
||||||
* The returned list of tuples is in order by physical position in the heap.
|
* The returned list of tuples is in order by physical position in the
|
||||||
|
* relation.
|
||||||
* (We will rely on this later to derive correlation estimates.)
|
* (We will rely on this later to derive correlation estimates.)
|
||||||
*
|
*
|
||||||
* As of May 2004 we use a new two-stage method: Stage one selects up
|
* As of May 2004 we use a new two-stage method: Stage one selects up
|
||||||
@ -1147,7 +1151,7 @@ block_sampling_read_stream_next(ReadStream *stream,
|
|||||||
* look at a statistically unbiased set of blocks, we should get
|
* look at a statistically unbiased set of blocks, we should get
|
||||||
* unbiased estimates of the average numbers of live and dead rows per
|
* unbiased estimates of the average numbers of live and dead rows per
|
||||||
* block. The previous sampling method put too much credence in the row
|
* block. The previous sampling method put too much credence in the row
|
||||||
* density near the start of the heap.
|
* density near the start of the relation.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
acquire_sample_rows(Relation onerel, int elevel,
|
acquire_sample_rows(Relation onerel, int elevel,
|
||||||
@ -1188,7 +1192,7 @@ acquire_sample_rows(Relation onerel, int elevel,
|
|||||||
/* Prepare for sampling rows */
|
/* Prepare for sampling rows */
|
||||||
reservoir_init_selection_state(&rstate, targrows);
|
reservoir_init_selection_state(&rstate, targrows);
|
||||||
|
|
||||||
scan = heap_beginscan(onerel, NULL, 0, NULL, NULL, SO_TYPE_ANALYZE);
|
scan = table_beginscan_analyze(onerel);
|
||||||
slot = table_slot_create(onerel, NULL);
|
slot = table_slot_create(onerel, NULL);
|
||||||
|
|
||||||
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE,
|
stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE,
|
||||||
@ -1200,11 +1204,11 @@ acquire_sample_rows(Relation onerel, int elevel,
|
|||||||
0);
|
0);
|
||||||
|
|
||||||
/* Outer loop over blocks to sample */
|
/* Outer loop over blocks to sample */
|
||||||
while (heapam_scan_analyze_next_block(scan, stream))
|
while (scan_analyze_next_block(scan, stream))
|
||||||
{
|
{
|
||||||
vacuum_delay_point();
|
vacuum_delay_point();
|
||||||
|
|
||||||
while (heapam_scan_analyze_next_tuple(scan, OldestXmin, &liverows, &deadrows, slot))
|
while (scan_analyze_next_tuple(scan, OldestXmin, &liverows, &deadrows, slot))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The first targrows sample rows are simply copied into the
|
* The first targrows sample rows are simply copied into the
|
||||||
@ -1256,7 +1260,7 @@ acquire_sample_rows(Relation onerel, int elevel,
|
|||||||
read_stream_end(stream);
|
read_stream_end(stream);
|
||||||
|
|
||||||
ExecDropSingleTupleTableSlot(slot);
|
ExecDropSingleTupleTableSlot(slot);
|
||||||
heap_endscan(scan);
|
table_endscan(scan);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we didn't find as many tuples as we wanted then we're done. No sort
|
* If we didn't find as many tuples as we wanted then we're done. No sort
|
||||||
@ -1328,16 +1332,22 @@ compare_rows(const void *a, const void *b, void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* heapam_analyze -- implementation of relation_analyze() table access method
|
* block_level_table_analyze -- implementation of relation_analyze() for
|
||||||
* callback for heap
|
* block-level table access methods
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
heapam_analyze(Relation relation, AcquireSampleRowsFunc *func,
|
block_level_table_analyze(Relation relation,
|
||||||
BlockNumber *totalpages, BufferAccessStrategy bstrategy)
|
AcquireSampleRowsFunc *func,
|
||||||
|
BlockNumber *totalpages,
|
||||||
|
BufferAccessStrategy bstrategy,
|
||||||
|
ScanAnalyzeNextBlockFunc scan_analyze_next_block_cb,
|
||||||
|
ScanAnalyzeNextTupleFunc scan_analyze_next_tuple_cb)
|
||||||
{
|
{
|
||||||
*func = acquire_sample_rows;
|
*func = acquire_sample_rows;
|
||||||
*totalpages = RelationGetNumberOfBlocks(relation);
|
*totalpages = RelationGetNumberOfBlocks(relation);
|
||||||
vac_strategy = bstrategy;
|
vac_strategy = bstrategy;
|
||||||
|
scan_analyze_next_block = scan_analyze_next_block_cb;
|
||||||
|
scan_analyze_next_tuple = scan_analyze_next_tuple_cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1020,6 +1020,19 @@ table_beginscan_tid(Relation rel, Snapshot snapshot)
|
|||||||
return rel->rd_tableam->scan_begin(rel, snapshot, 0, NULL, NULL, flags);
|
return rel->rd_tableam->scan_begin(rel, snapshot, 0, NULL, NULL, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* table_beginscan_analyze is an alternative entry point for setting up a
|
||||||
|
* TableScanDesc for an ANALYZE scan. As with bitmap scans, it's worth using
|
||||||
|
* the same data structure although the behavior is rather different.
|
||||||
|
*/
|
||||||
|
static inline TableScanDesc
|
||||||
|
table_beginscan_analyze(Relation rel)
|
||||||
|
{
|
||||||
|
uint32 flags = SO_TYPE_ANALYZE;
|
||||||
|
|
||||||
|
return rel->rd_tableam->scan_begin(rel, NULL, 0, NULL, NULL, flags);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* End relation scan.
|
* End relation scan.
|
||||||
*/
|
*/
|
||||||
|
@ -21,9 +21,11 @@
|
|||||||
#include "catalog/pg_class.h"
|
#include "catalog/pg_class.h"
|
||||||
#include "catalog/pg_statistic.h"
|
#include "catalog/pg_statistic.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
|
#include "executor/tuptable.h"
|
||||||
#include "parser/parse_node.h"
|
#include "parser/parse_node.h"
|
||||||
#include "storage/buf.h"
|
#include "storage/buf.h"
|
||||||
#include "storage/lock.h"
|
#include "storage/lock.h"
|
||||||
|
#include "storage/read_stream.h"
|
||||||
#include "utils/relcache.h"
|
#include "utils/relcache.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -390,12 +392,60 @@ extern void parallel_vacuum_cleanup_all_indexes(ParallelVacuumState *pvs,
|
|||||||
extern void parallel_vacuum_main(dsm_segment *seg, shm_toc *toc);
|
extern void parallel_vacuum_main(dsm_segment *seg, shm_toc *toc);
|
||||||
|
|
||||||
/* in commands/analyze.c */
|
/* in commands/analyze.c */
|
||||||
|
|
||||||
|
struct TableScanDescData;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A callback to prepare to analyze block from `stream` of `scan`. The scan
|
||||||
|
* has been started with table_beginscan_analyze().
|
||||||
|
*
|
||||||
|
* The callback may acquire resources like locks that are held until
|
||||||
|
* ScanAnalyzeNextTupleFunc returns false. In some cases it could be
|
||||||
|
* useful to hold a lock until all tuples in a block have been analyzed by
|
||||||
|
* ScanAnalyzeNextTupleFunc.
|
||||||
|
*
|
||||||
|
* The callback can return false if the block is not suitable for
|
||||||
|
* sampling, e.g. because it's a metapage that could never contain tuples.
|
||||||
|
*
|
||||||
|
* This is primarily suited for block-based AMs. It's not clear what a
|
||||||
|
* good interface for non block-based AMs would be, so there isn't one
|
||||||
|
* yet and sampling using a custom implementation of acquire_sample_rows
|
||||||
|
* may be preferred.
|
||||||
|
*/
|
||||||
|
typedef bool (*ScanAnalyzeNextBlockFunc) (struct TableScanDescData *scan,
|
||||||
|
ReadStream *stream);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A callback to iterate over tuples in the block selected with
|
||||||
|
* ScanAnalyzeNextBlockFunc (which needs to have returned true, and
|
||||||
|
* this routine may not have returned false for the same block before). If
|
||||||
|
* a tuple that's suitable for sampling is found, true is returned and a
|
||||||
|
* tuple is stored in `slot`.
|
||||||
|
*
|
||||||
|
* *liverows and *deadrows are incremented according to the encountered
|
||||||
|
* tuples.
|
||||||
|
*
|
||||||
|
* Not every AM might have a meaningful concept of dead rows, in which
|
||||||
|
* case it's OK to not increment *deadrows - but note that that may
|
||||||
|
* influence autovacuum scheduling (see comment for relation_vacuum
|
||||||
|
* callback).
|
||||||
|
*/
|
||||||
|
typedef bool (*ScanAnalyzeNextTupleFunc) (struct TableScanDescData *scan,
|
||||||
|
TransactionId OldestXmin,
|
||||||
|
double *liverows,
|
||||||
|
double *deadrows,
|
||||||
|
TupleTableSlot *slot);
|
||||||
|
|
||||||
extern void analyze_rel(Oid relid, RangeVar *relation,
|
extern void analyze_rel(Oid relid, RangeVar *relation,
|
||||||
VacuumParams *params, List *va_cols, bool in_outer_xact,
|
VacuumParams *params, List *va_cols, bool in_outer_xact,
|
||||||
BufferAccessStrategy bstrategy);
|
BufferAccessStrategy bstrategy);
|
||||||
extern void heapam_analyze(Relation relation, AcquireSampleRowsFunc *func,
|
extern void block_level_table_analyze(Relation relation,
|
||||||
BlockNumber *totalpages,
|
AcquireSampleRowsFunc *func,
|
||||||
BufferAccessStrategy bstrategy);
|
BlockNumber *totalpages,
|
||||||
|
BufferAccessStrategy bstrategy,
|
||||||
|
ScanAnalyzeNextBlockFunc scan_analyze_next_block_cb,
|
||||||
|
ScanAnalyzeNextTupleFunc scan_analyze_next_tuple_cb);
|
||||||
|
|
||||||
extern bool std_typanalyze(VacAttrStats *stats);
|
extern bool std_typanalyze(VacAttrStats *stats);
|
||||||
|
|
||||||
|
@ -2535,6 +2535,8 @@ ScalarIOData
|
|||||||
ScalarItem
|
ScalarItem
|
||||||
ScalarMCVItem
|
ScalarMCVItem
|
||||||
Scan
|
Scan
|
||||||
|
ScanAnalyzeNextBlockFunc
|
||||||
|
ScanAnalyzeNextTupleFunc
|
||||||
ScanDirection
|
ScanDirection
|
||||||
ScanKey
|
ScanKey
|
||||||
ScanKeyData
|
ScanKeyData
|
||||||
|
Loading…
x
Reference in New Issue
Block a user