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 + -
显示快捷键?