analyze.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,819 行 · 第 1/4 页
C
1,819 行
/*------------------------------------------------------------------------- * * analyze.c * the postgres statistics generator * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.64 2003/10/18 15:38:06 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <math.h>#include "access/heapam.h"#include "access/tuptoaster.h"#include "catalog/catalog.h"#include "catalog/catname.h"#include "catalog/indexing.h"#include "catalog/namespace.h"#include "catalog/pg_operator.h"#include "catalog/pg_statistic.h"#include "catalog/pg_type.h"#include "commands/vacuum.h"#include "miscadmin.h"#include "parser/parse_oper.h"#include "parser/parse_relation.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/datum.h"#include "utils/fmgroids.h"#include "utils/lsyscache.h"#include "utils/syscache.h"#include "utils/tuplesort.h"/* * Analysis algorithms supported */typedef enum{ ALG_MINIMAL = 1, /* Compute only most-common-values */ ALG_SCALAR /* Compute MCV, histogram, sort * correlation */} AlgCode;/* * To avoid consuming too much memory during analysis and/or too much space * in the resulting pg_statistic rows, we ignore varlena datums that are wider * than WIDTH_THRESHOLD (after detoasting!). This is legitimate for MCV * and distinct-value calculations since a wide value is unlikely to be * duplicated at all, much less be a most-common value. For the same reason, * ignoring wide values will not affect our estimates of histogram bin * boundaries very much. */#define WIDTH_THRESHOLD 1024/* * We build one of these structs for each attribute (column) that is to be * analyzed. The struct and subsidiary data are in anl_context, * so they live until the end of the ANALYZE operation. */typedef struct{ /* These fields are set up by examine_attribute */ int attnum; /* attribute number */ AlgCode algcode; /* Which algorithm to use for this column */ int minrows; /* Minimum # of rows wanted for stats */ Form_pg_attribute attr; /* copy of pg_attribute row for column */ Form_pg_type attrtype; /* copy of pg_type row for column */ Oid eqopr; /* '=' operator for datatype, if any */ Oid eqfunc; /* and associated function */ Oid ltopr; /* '<' operator for datatype, if any */ /* * These fields are filled in by the actual statistics-gathering * routine */ bool stats_valid; float4 stanullfrac; /* fraction of entries that are NULL */ int4 stawidth; /* average width */ float4 stadistinct; /* # distinct values */ int2 stakind[STATISTIC_NUM_SLOTS]; Oid staop[STATISTIC_NUM_SLOTS]; int numnumbers[STATISTIC_NUM_SLOTS]; float4 *stanumbers[STATISTIC_NUM_SLOTS]; int numvalues[STATISTIC_NUM_SLOTS]; Datum *stavalues[STATISTIC_NUM_SLOTS];} VacAttrStats;typedef struct{ Datum value; /* a data value */ int tupno; /* position index for tuple it came from */} ScalarItem;typedef struct{ int count; /* # of duplicates */ int first; /* values[] index of first occurrence */} ScalarMCVItem;#define swapInt(a,b) do {int _tmp; _tmp=a; a=b; b=_tmp;} while(0)#define swapDatum(a,b) do {Datum _tmp; _tmp=a; a=b; b=_tmp;} while(0)/* Default statistics target (GUC parameter) */int default_statistics_target = 10;static int elevel = -1;static MemoryContext anl_context = NULL;/* context information for compare_scalars() */static FmgrInfo *datumCmpFn;static SortFunctionKind datumCmpFnKind;static int *datumCmpTupnoLink;static VacAttrStats *examine_attribute(Relation onerel, int attnum);static int acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows, double *totalrows);static double random_fract(void);static double init_selection_state(int n);static double select_next_random_record(double t, int n, double *stateptr);static int compare_rows(const void *a, const void *b);static int compare_scalars(const void *a, const void *b);static int compare_mcvs(const void *a, const void *b);static void compute_minimal_stats(VacAttrStats *stats, TupleDesc tupDesc, double totalrows, HeapTuple *rows, int numrows);static void compute_scalar_stats(VacAttrStats *stats, TupleDesc tupDesc, double totalrows, HeapTuple *rows, int numrows);static void update_attstats(Oid relid, int natts, VacAttrStats **vacattrstats);/* * analyze_rel() -- analyze one relation */voidanalyze_rel(Oid relid, VacuumStmt *vacstmt){ Relation onerel; int attr_cnt, tcnt, i; VacAttrStats **vacattrstats; int targrows, numrows; double totalrows; HeapTuple *rows; if (vacstmt->verbose) elevel = INFO; else elevel = DEBUG2; /* * Use the current context for storing analysis info. vacuum.c * ensures that this context will be cleared when I return, thus * releasing the memory allocated here. */ anl_context = CurrentMemoryContext; /* * Check for user-requested abort. Note we want this to be inside a * transaction, so xact.c doesn't issue useless WARNING. */ CHECK_FOR_INTERRUPTS(); /* * Race condition -- if the pg_class tuple has gone away since the * last time we saw it, we don't need to process it. */ if (!SearchSysCacheExists(RELOID, ObjectIdGetDatum(relid), 0, 0, 0)) return; /* * Open the class, getting only a read lock on it, and check * permissions. Permissions check should match vacuum's check! */ onerel = relation_open(relid, AccessShareLock); if (!(pg_class_ownercheck(RelationGetRelid(onerel), GetUserId()) || (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !onerel->rd_rel->relisshared))) { /* No need for a WARNING if we already complained during VACUUM */ if (!vacstmt->vacuum) ereport(WARNING, (errmsg("skipping \"%s\" --- only table or database owner can analyze it", RelationGetRelationName(onerel)))); relation_close(onerel, AccessShareLock); return; } /* * Check that it's a plain table; we used to do this in getrels() but * seems safer to check after we've locked the relation. */ if (onerel->rd_rel->relkind != RELKIND_RELATION) { /* No need for a WARNING if we already complained during VACUUM */ if (!vacstmt->vacuum) ereport(WARNING, (errmsg("skipping \"%s\" --- cannot analyze indexes, views, or special system tables", RelationGetRelationName(onerel)))); relation_close(onerel, AccessShareLock); return; } /* * Silently ignore tables that are temp tables of other backends --- * trying to analyze these is rather pointless, since their contents * are probably not up-to-date on disk. (We don't throw a warning * here; it would just lead to chatter during a database-wide * ANALYZE.) */ if (isOtherTempNamespace(RelationGetNamespace(onerel))) { relation_close(onerel, AccessShareLock); return; } /* * We can ANALYZE any table except pg_statistic. See update_attstats */ if (IsSystemNamespace(RelationGetNamespace(onerel)) && strcmp(RelationGetRelationName(onerel), StatisticRelationName) == 0) { relation_close(onerel, AccessShareLock); return; } ereport(elevel, (errmsg("analyzing \"%s.%s\"", get_namespace_name(RelationGetNamespace(onerel)), RelationGetRelationName(onerel)))); /* * Determine which columns to analyze * * Note that system attributes are never analyzed. */ if (vacstmt->va_cols != NIL) { List *le; vacattrstats = (VacAttrStats **) palloc(length(vacstmt->va_cols) * sizeof(VacAttrStats *)); tcnt = 0; foreach(le, vacstmt->va_cols) { char *col = strVal(lfirst(le)); i = attnameAttNum(onerel, col, false); vacattrstats[tcnt] = examine_attribute(onerel, i); if (vacattrstats[tcnt] != NULL) tcnt++; } attr_cnt = tcnt; } else { attr_cnt = onerel->rd_att->natts; /* +1 here is just to avoid palloc(0) with zero-column table */ vacattrstats = (VacAttrStats **) palloc((attr_cnt + 1) * sizeof(VacAttrStats *)); tcnt = 0; for (i = 1; i <= attr_cnt; i++) { vacattrstats[tcnt] = examine_attribute(onerel, i); if (vacattrstats[tcnt] != NULL) tcnt++; } attr_cnt = tcnt; } /* * Quit if no analyzable columns */ if (attr_cnt <= 0) { relation_close(onerel, AccessShareLock); return; } /* * Determine how many rows we need to sample, using the worst case * from all analyzable columns. We use a lower bound of 100 rows to * avoid possible overflow in Vitter's algorithm. */ targrows = 100; for (i = 0; i < attr_cnt; i++) { if (targrows < vacattrstats[i]->minrows) targrows = vacattrstats[i]->minrows; } /* * Acquire the sample rows */ rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple)); numrows = acquire_sample_rows(onerel, rows, targrows, &totalrows); /* * If we are running a standalone ANALYZE, update pages/tuples stats * in pg_class. We have the accurate page count from heap_beginscan, * but only an approximate number of tuples; therefore, if we are part * of VACUUM ANALYZE do *not* overwrite the accurate count already * inserted by VACUUM. */ if (!vacstmt->vacuum) vac_update_relstats(RelationGetRelid(onerel), onerel->rd_nblocks, totalrows, RelationGetForm(onerel)->relhasindex); /* * Compute the statistics. Temporary results during the calculations * for each column are stored in a child context. The calc routines * are responsible to make sure that whatever they store into the * VacAttrStats structure is allocated in anl_context. */ if (numrows > 0) { MemoryContext col_context, old_context; col_context = AllocSetContextCreate(anl_context, "Analyze Column", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); old_context = MemoryContextSwitchTo(col_context); for (i = 0; i < attr_cnt; i++) { switch (vacattrstats[i]->algcode) { case ALG_MINIMAL: compute_minimal_stats(vacattrstats[i], onerel->rd_att, totalrows, rows, numrows); break; case ALG_SCALAR: compute_scalar_stats(vacattrstats[i], onerel->rd_att, totalrows, rows, numrows); break; } MemoryContextResetAndDeleteChildren(col_context); } MemoryContextSwitchTo(old_context); MemoryContextDelete(col_context); /* * Emit the completed stats rows into pg_statistic, replacing any * previous statistics for the target columns. (If there are * stats in pg_statistic for columns we didn't process, we leave * them alone.) */ update_attstats(relid, attr_cnt, vacattrstats); } /* * Close source relation now, but keep lock so that no one deletes it * before we commit. (If someone did, they'd fail to clean up the * entries we made in pg_statistic.) */ relation_close(onerel, NoLock);}/* * examine_attribute -- pre-analysis of a single column * * Determine whether the column is analyzable; if so, create and initialize * a VacAttrStats struct for it. If not, return NULL. */static VacAttrStats *examine_attribute(Relation onerel, int attnum){ Form_pg_attribute attr = onerel->rd_att->attrs[attnum - 1]; Operator func_operator; HeapTuple typtuple; Oid eqopr = InvalidOid; Oid eqfunc = InvalidOid; Oid ltopr = InvalidOid; VacAttrStats *stats; /* Don't analyze dropped columns */ if (attr->attisdropped) return NULL; /* Don't analyze column if user has specified not to */ if (attr->attstattarget == 0) return NULL; /* If column has no "=" operator, we can't do much of anything */ func_operator = equality_oper(attr->atttypid, true); if (func_operator != NULL) { eqopr = oprid(func_operator); eqfunc = oprfuncid(func_operator); ReleaseSysCache(func_operator); } if (!OidIsValid(eqfunc)) return NULL; /* * If we have "=" then we're at least able to do the minimal * algorithm, so start filling in a VacAttrStats struct. */ stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats)); stats->attnum = attnum; stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE); memcpy(stats->attr, attr, ATTRIBUTE_TUPLE_SIZE); typtuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(attr->atttypid), 0, 0, 0); if (!HeapTupleIsValid(typtuple)) elog(ERROR, "cache lookup failed for type %u", attr->atttypid); stats->attrtype = (Form_pg_type) palloc(sizeof(FormData_pg_type)); memcpy(stats->attrtype, GETSTRUCT(typtuple), sizeof(FormData_pg_type)); ReleaseSysCache(typtuple); stats->eqopr = eqopr; stats->eqfunc = eqfunc; /* If the attstattarget column is negative, use the default value */ if (stats->attr->attstattarget < 0) stats->attr->attstattarget = default_statistics_target; /* Is there a "<" operator with suitable semantics? */ func_operator = ordering_oper(attr->atttypid, true); if (func_operator != NULL) { ltopr = oprid(func_operator); ReleaseSysCache(func_operator); } stats->ltopr = ltopr; /* * Determine the algorithm to use (this will get more complicated * later) */ if (OidIsValid(ltopr)) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?