mirror of https://github.com/postgres/postgres
tableam: Move heap specific logic from estimate_rel_size below tableam.
This just moves the table/matview[/toast] determination of relation size to a callback, and uses a copy of the existing logic to implement that callback for heap. It probably would make sense to also move the index specific logic into a callback, so the metapage handling (and probably more) can be index specific. But that's a separate task. Author: Andres Freund Discussion: https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
This commit is contained in:
parent
737a292b5d
commit
696d78469f
|
@ -19,6 +19,8 @@
|
|||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "miscadmin.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
|
@ -33,6 +35,7 @@
|
|||
#include "catalog/storage_xlog.h"
|
||||
#include "commands/progress.h"
|
||||
#include "executor/executor.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "pgstat.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "storage/bufpage.h"
|
||||
|
@ -1870,6 +1873,114 @@ reform_and_rewrite_tuple(HeapTuple tuple,
|
|||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Planner related callbacks for the heap AM
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void
|
||||
heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
|
||||
BlockNumber *pages, double *tuples,
|
||||
double *allvisfrac)
|
||||
{
|
||||
BlockNumber curpages;
|
||||
BlockNumber relpages;
|
||||
double reltuples;
|
||||
BlockNumber relallvisible;
|
||||
double density;
|
||||
|
||||
/* it has storage, ok to call the smgr */
|
||||
curpages = RelationGetNumberOfBlocks(rel);
|
||||
|
||||
/* coerce values in pg_class to more desirable types */
|
||||
relpages = (BlockNumber) rel->rd_rel->relpages;
|
||||
reltuples = (double) rel->rd_rel->reltuples;
|
||||
relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
|
||||
|
||||
/*
|
||||
* HACK: if the relation has never yet been vacuumed, use a minimum size
|
||||
* estimate of 10 pages. The idea here is to avoid assuming a
|
||||
* newly-created table is really small, even if it currently is, because
|
||||
* that may not be true once some data gets loaded into it. Once a vacuum
|
||||
* or analyze cycle has been done on it, it's more reasonable to believe
|
||||
* the size is somewhat stable.
|
||||
*
|
||||
* (Note that this is only an issue if the plan gets cached and used again
|
||||
* after the table has been filled. What we're trying to avoid is using a
|
||||
* nestloop-type plan on a table that has grown substantially since the
|
||||
* plan was made. Normally, autovacuum/autoanalyze will occur once enough
|
||||
* inserts have happened and cause cached-plan invalidation; but that
|
||||
* doesn't happen instantaneously, and it won't happen at all for cases
|
||||
* such as temporary tables.)
|
||||
*
|
||||
* We approximate "never vacuumed" by "has relpages = 0", which means this
|
||||
* will also fire on genuinely empty relations. Not great, but
|
||||
* fortunately that's a seldom-seen case in the real world, and it
|
||||
* shouldn't degrade the quality of the plan too much anyway to err in
|
||||
* this direction.
|
||||
*
|
||||
* If the table has inheritance children, we don't apply this heuristic.
|
||||
* Totally empty parent tables are quite common, so we should be willing
|
||||
* to believe that they are empty.
|
||||
*/
|
||||
if (curpages < 10 &&
|
||||
relpages == 0 &&
|
||||
!rel->rd_rel->relhassubclass)
|
||||
curpages = 10;
|
||||
|
||||
/* report estimated # pages */
|
||||
*pages = curpages;
|
||||
/* quick exit if rel is clearly empty */
|
||||
if (curpages == 0)
|
||||
{
|
||||
*tuples = 0;
|
||||
*allvisfrac = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* estimate number of tuples from previous tuple density */
|
||||
if (relpages > 0)
|
||||
density = reltuples / (double) relpages;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* When we have no data because the relation was truncated, estimate
|
||||
* tuple width from attribute datatypes. We assume here that the
|
||||
* pages are completely full, which is OK for tables (since they've
|
||||
* presumably not been VACUUMed yet) but is probably an overestimate
|
||||
* for indexes. Fortunately get_relation_info() can clamp the
|
||||
* overestimate to the parent table's size.
|
||||
*
|
||||
* Note: this code intentionally disregards alignment considerations,
|
||||
* because (a) that would be gilding the lily considering how crude
|
||||
* the estimate is, and (b) it creates platform dependencies in the
|
||||
* default plans which are kind of a headache for regression testing.
|
||||
*/
|
||||
int32 tuple_width;
|
||||
|
||||
tuple_width = get_rel_data_width(rel, attr_widths);
|
||||
tuple_width += MAXALIGN(SizeofHeapTupleHeader);
|
||||
tuple_width += sizeof(ItemIdData);
|
||||
/* note: integer division is intentional here */
|
||||
density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width;
|
||||
}
|
||||
*tuples = rint(density * (double) curpages);
|
||||
|
||||
/*
|
||||
* We use relallvisible as-is, rather than scaling it up like we do for
|
||||
* the pages and tuples counts, on the theory that any pages added since
|
||||
* the last VACUUM are most likely not marked all-visible. But costsize.c
|
||||
* wants it converted to a fraction.
|
||||
*/
|
||||
if (relallvisible == 0 || curpages <= 0)
|
||||
*allvisfrac = 0;
|
||||
else if ((double) relallvisible >= curpages)
|
||||
*allvisfrac = 1;
|
||||
else
|
||||
*allvisfrac = (double) relallvisible / curpages;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Definition of the heap table access method.
|
||||
* ------------------------------------------------------------------------
|
||||
|
@ -1915,6 +2026,8 @@ static const TableAmRoutine heapam_methods = {
|
|||
.scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,
|
||||
.index_build_range_scan = heapam_index_build_range_scan,
|
||||
.index_validate_scan = heapam_index_validate_scan,
|
||||
|
||||
.relation_estimate_size = heapam_estimate_rel_size,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "access/genam.h"
|
||||
#include "access/htup_details.h"
|
||||
#include "access/nbtree.h"
|
||||
#include "access/tableam.h"
|
||||
#include "access/sysattr.h"
|
||||
#include "access/table.h"
|
||||
#include "access/transam.h"
|
||||
|
@ -64,7 +65,6 @@ static void get_relation_foreign_keys(PlannerInfo *root, RelOptInfo *rel,
|
|||
Relation relation, bool inhparent);
|
||||
static bool infer_collation_opclass_match(InferenceElem *elem, Relation idxRel,
|
||||
List *idxExprs);
|
||||
static int32 get_rel_data_width(Relation rel, int32 *attr_widths);
|
||||
static List *get_relation_constraints(PlannerInfo *root,
|
||||
Oid relationObjectId, RelOptInfo *rel,
|
||||
bool include_notnull);
|
||||
|
@ -948,47 +948,26 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
|
|||
switch (rel->rd_rel->relkind)
|
||||
{
|
||||
case RELKIND_RELATION:
|
||||
case RELKIND_INDEX:
|
||||
case RELKIND_MATVIEW:
|
||||
case RELKIND_TOASTVALUE:
|
||||
table_relation_estimate_size(rel, attr_widths, pages, tuples,
|
||||
allvisfrac);
|
||||
break;
|
||||
|
||||
case RELKIND_INDEX:
|
||||
|
||||
/*
|
||||
* XXX: It'd probably be good to move this into a callback,
|
||||
* individual index types e.g. know if they have a metapage.
|
||||
*/
|
||||
|
||||
/* it has storage, ok to call the smgr */
|
||||
curpages = RelationGetNumberOfBlocks(rel);
|
||||
|
||||
/*
|
||||
* HACK: if the relation has never yet been vacuumed, use a
|
||||
* minimum size estimate of 10 pages. The idea here is to avoid
|
||||
* assuming a newly-created table is really small, even if it
|
||||
* currently is, because that may not be true once some data gets
|
||||
* loaded into it. Once a vacuum or analyze cycle has been done
|
||||
* on it, it's more reasonable to believe the size is somewhat
|
||||
* stable.
|
||||
*
|
||||
* (Note that this is only an issue if the plan gets cached and
|
||||
* used again after the table has been filled. What we're trying
|
||||
* to avoid is using a nestloop-type plan on a table that has
|
||||
* grown substantially since the plan was made. Normally,
|
||||
* autovacuum/autoanalyze will occur once enough inserts have
|
||||
* happened and cause cached-plan invalidation; but that doesn't
|
||||
* happen instantaneously, and it won't happen at all for cases
|
||||
* such as temporary tables.)
|
||||
*
|
||||
* We approximate "never vacuumed" by "has relpages = 0", which
|
||||
* means this will also fire on genuinely empty relations. Not
|
||||
* great, but fortunately that's a seldom-seen case in the real
|
||||
* world, and it shouldn't degrade the quality of the plan too
|
||||
* much anyway to err in this direction.
|
||||
*
|
||||
* There are two exceptions wherein we don't apply this heuristic.
|
||||
* One is if the table has inheritance children. Totally empty
|
||||
* parent tables are quite common, so we should be willing to
|
||||
* believe that they are empty. Also, we don't apply the 10-page
|
||||
* minimum to indexes.
|
||||
*/
|
||||
if (curpages < 10 &&
|
||||
rel->rd_rel->relpages == 0 &&
|
||||
!rel->rd_rel->relhassubclass &&
|
||||
rel->rd_rel->relkind != RELKIND_INDEX)
|
||||
curpages = 10;
|
||||
/* coerce values in pg_class to more desirable types */
|
||||
relpages = (BlockNumber) rel->rd_rel->relpages;
|
||||
reltuples = (double) rel->rd_rel->reltuples;
|
||||
relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
|
||||
|
||||
/* report estimated # pages */
|
||||
*pages = curpages;
|
||||
|
@ -1005,13 +984,12 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
|
|||
relallvisible = (BlockNumber) rel->rd_rel->relallvisible;
|
||||
|
||||
/*
|
||||
* If it's an index, discount the metapage while estimating the
|
||||
* number of tuples. This is a kluge because it assumes more than
|
||||
* it ought to about index structure. Currently it's OK for
|
||||
* btree, hash, and GIN indexes but suspect for GiST indexes.
|
||||
* Discount the metapage while estimating the number of tuples.
|
||||
* This is a kluge because it assumes more than it ought to about
|
||||
* index structure. Currently it's OK for btree, hash, and GIN
|
||||
* indexes but suspect for GiST indexes.
|
||||
*/
|
||||
if (rel->rd_rel->relkind == RELKIND_INDEX &&
|
||||
relpages > 0)
|
||||
if (relpages > 0)
|
||||
{
|
||||
curpages--;
|
||||
relpages--;
|
||||
|
@ -1036,6 +1014,8 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
|
|||
* considering how crude the estimate is, and (b) it creates
|
||||
* platform dependencies in the default plans which are kind
|
||||
* of a headache for regression testing.
|
||||
*
|
||||
* XXX: Should this logic be more index specific?
|
||||
*/
|
||||
int32 tuple_width;
|
||||
|
||||
|
@ -1060,6 +1040,7 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
|
|||
else
|
||||
*allvisfrac = (double) relallvisible / curpages;
|
||||
break;
|
||||
|
||||
case RELKIND_SEQUENCE:
|
||||
/* Sequences always have a known size */
|
||||
*pages = 1;
|
||||
|
@ -1095,7 +1076,7 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
|
|||
* since they might be mostly NULLs, treating them as zero-width is not
|
||||
* necessarily the wrong thing anyway.
|
||||
*/
|
||||
static int32
|
||||
int32
|
||||
get_rel_data_width(Relation rel, int32 *attr_widths)
|
||||
{
|
||||
int32 tuple_width = 0;
|
||||
|
|
|
@ -491,6 +491,22 @@ typedef struct TableAmRoutine
|
|||
Snapshot snapshot,
|
||||
struct ValidateIndexState *state);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* Planner related functions.
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* See table_relation_estimate_size().
|
||||
*
|
||||
* While block oriented, it shouldn't be too hard to for an AM that
|
||||
* doesn't internally use blocks to convert into a usable representation.
|
||||
*/
|
||||
void (*relation_estimate_size) (Relation rel, int32 *attr_widths,
|
||||
BlockNumber *pages, double *tuples,
|
||||
double *allvisfrac);
|
||||
|
||||
} TableAmRoutine;
|
||||
|
||||
|
||||
|
@ -1286,6 +1302,25 @@ table_index_validate_scan(Relation heap_rel,
|
|||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Planner related functionality
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Estimate the current size of the relation, as an AM specific workhorse for
|
||||
* estimate_rel_size(). Look there for an explanation of the parameters.
|
||||
*/
|
||||
static inline void
|
||||
table_relation_estimate_size(Relation rel, int32 *attr_widths,
|
||||
BlockNumber *pages, double *tuples,
|
||||
double *allvisfrac)
|
||||
{
|
||||
rel->rd_tableam->relation_estimate_size(rel, attr_widths, pages, tuples,
|
||||
allvisfrac);
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions to make modifications a bit simpler.
|
||||
* ----------------------------------------------------------------------------
|
||||
|
|
|
@ -33,6 +33,7 @@ extern List *infer_arbiter_indexes(PlannerInfo *root);
|
|||
extern void estimate_rel_size(Relation rel, int32 *attr_widths,
|
||||
BlockNumber *pages, double *tuples, double *allvisfrac);
|
||||
|
||||
extern int32 get_rel_data_width(Relation rel, int32 *attr_widths);
|
||||
extern int32 get_relation_data_width(Oid relid, int32 *attr_widths);
|
||||
|
||||
extern bool relation_excluded_by_constraints(PlannerInfo *root,
|
||||
|
|
Loading…
Reference in New Issue