*/
#include "postgres.h"
+#include <math.h>
+
#include "miscadmin.h"
#include "access/genam.h"
#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"
}
+/* ------------------------------------------------------------------------
+ * 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.
* ------------------------------------------------------------------------
.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,
};
#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"
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);
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
- case RELKIND_INDEX:
case RELKIND_MATVIEW:
case RELKIND_TOASTVALUE:
- /* it has storage, ok to call the smgr */
- curpages = RelationGetNumberOfBlocks(rel);
+ table_relation_estimate_size(rel, attr_widths, pages, tuples,
+ allvisfrac);
+ break;
+
+ case RELKIND_INDEX:
/*
- * 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.
+ * XXX: It'd probably be good to move this into a callback,
+ * individual index types e.g. know if they have a metapage.
*/
- if (curpages < 10 &&
- rel->rd_rel->relpages == 0 &&
- !rel->rd_rel->relhassubclass &&
- rel->rd_rel->relkind != RELKIND_INDEX)
- curpages = 10;
+
+ /* 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;
/* report estimated # pages */
*pages = curpages;
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--;
* 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;
else
*allvisfrac = (double) relallvisible / curpages;
break;
+
case RELKIND_SEQUENCE:
/* Sequences always have a known size */
*pages = 1;
* 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;
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;
}
+/* ----------------------------------------------------------------------------
+ * 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.
* ----------------------------------------------------------------------------
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,