mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-30 00:04:49 -04:00 
			
		
		
		
	Allow vacuum to perform analyze with shared lock. Update cvs manual.
This commit is contained in:
		
							parent
							
								
									091126fa28
								
							
						
					
					
						commit
						659f79be7a
					
				| @ -1,5 +1,5 @@ | ||||
| <!-- | ||||
| $Header: /cvsroot/pgsql/doc/src/sgml/cvs.sgml,v 1.8 2000/05/02 20:01:51 thomas Exp $ | ||||
| $Header: /cvsroot/pgsql/doc/src/sgml/cvs.sgml,v 1.9 2000/05/29 15:44:54 momjian Exp $ | ||||
| CVS code repository | ||||
| Thomas Lockhart | ||||
| --> | ||||
| @ -184,7 +184,7 @@ cvs commit | ||||
|      Do an initial login to the <productname>CVS</productname> server: | ||||
| 
 | ||||
|      <programlisting> | ||||
| $ cvs -d :pserver:anoncvs@postgresql.org:/usr/local/cvsroot login | ||||
| $ cvs -d :pserver:anoncvs@postgresql.org:/home/projects/pgsql/cvsroot login | ||||
|      </programlisting> | ||||
| 
 | ||||
|      You will be prompted for a password; enter '<literal>postgresql</literal>'. | ||||
| @ -197,7 +197,7 @@ $ cvs -d :pserver:anoncvs@postgresql.org:/usr/local/cvsroot login | ||||
|     <para> | ||||
|      Fetch the <productname>Postgres</productname> sources: | ||||
|      <programlisting> | ||||
| cvs -z3 -d :pserver:anoncvs@postgresql.org:/usr/local/cvsroot co -P pgsql | ||||
| cvs -z3 -d :pserver:anoncvs@postgresql.org:/home/projects/pgsql/cvsroot co -P pgsql | ||||
|      </programlisting> | ||||
| 
 | ||||
|      which installs the <productname>Postgres</productname> sources into a  | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|  * | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.151 2000/05/29 01:55:07 momjian Exp $ | ||||
|  *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.152 2000/05/29 15:44:55 momjian Exp $ | ||||
|  * | ||||
| 
 | ||||
|  *------------------------------------------------------------------------- | ||||
| @ -77,15 +77,17 @@ static void vacuum_shutdown(void); | ||||
| static void vac_vacuum(NameData *VacRelP, bool analyze, List *va_cols); | ||||
| static VRelList getrels(NameData *VacRelP); | ||||
| static void vacuum_rel(Oid relid, bool analyze, List *va_cols); | ||||
| static void analyze_rel(Oid relid, List *va_cols); | ||||
| static void scan_heap(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages, VPageList fraged_pages); | ||||
| static void repair_frag(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages, VPageList fraged_pages, int nindices, Relation *Irel); | ||||
| static void vacuum_heap(VRelStats *vacrelstats, Relation onerel, VPageList vpl); | ||||
| static void vacuum_page(Page page, VPageDescr vpd); | ||||
| static void vacuum_index(VPageList vpl, Relation indrel, int num_tuples, int keep_tuples); | ||||
| static void scan_index(Relation indrel, int num_tuples); | ||||
| static void attr_stats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple); | ||||
| static void attr_stats(Relation onerel, int attr_cnt, VacAttrStats *vacattrstats, HeapTuple tuple); | ||||
| static void bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int *bucket_len); | ||||
| static void update_stats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats); | ||||
| static void update_relstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats); | ||||
| static void update_attstats(Oid relid, int natts, VacAttrStats *vacattrstats); | ||||
| static void del_stats(Oid relid, int attcnt, int *attnums); | ||||
| static VPageDescr tid_reaped(ItemPointer itemptr, VPageList vpl); | ||||
| static void reap_page(VPageList vpl, VPageDescr vpc); | ||||
| @ -100,63 +102,7 @@ static int	vac_cmp_offno(const void *left, const void *right); | ||||
| static int	vac_cmp_vtlinks(const void *left, const void *right); | ||||
| static bool enough_space(VPageDescr vpd, Size len); | ||||
| static char *show_rusage(struct rusage * ru0); | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * This routines handle a special cross-transaction portal. | ||||
|  * However it is automatically closed in case of abort. | ||||
|  */ | ||||
| void | ||||
| CommonSpecialPortalOpen(void) | ||||
| { | ||||
| 	char	   *pname; | ||||
| 
 | ||||
| 
 | ||||
| 	if (CommonSpecialPortalInUse) | ||||
| 		elog(ERROR, "CommonSpecialPortal is in use"); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Create a portal for safe memory across transactions. We need to | ||||
| 	 * palloc the name space for it because our hash function expects the | ||||
| 	 * name to be on a longword boundary.  CreatePortal copies the name to | ||||
| 	 * safe storage for us. | ||||
| 	 */ | ||||
| 	pname = pstrdup(VACPNAME); | ||||
| 	vac_portal = CreatePortal(pname); | ||||
| 	pfree(pname); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set flag to indicate that vac_portal must be removed after an error. | ||||
| 	 * This global variable is checked in the transaction manager on xact | ||||
| 	 * abort, and the routine CommonSpecialPortalClose() is called if | ||||
| 	 * necessary. | ||||
| 	 */ | ||||
| 	CommonSpecialPortalInUse = true; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| CommonSpecialPortalClose(void) | ||||
| { | ||||
| 	/* Clear flag first, to avoid recursion if PortalDrop elog's */ | ||||
| 	CommonSpecialPortalInUse = false; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Release our portal for cross-transaction memory. | ||||
| 	 */ | ||||
| 	PortalDrop(&vac_portal); | ||||
| } | ||||
| 
 | ||||
| PortalVariableMemory | ||||
| CommonSpecialPortalGetMemory(void) | ||||
| { | ||||
| 	return PortalGetVariableMemory(vac_portal); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| CommonSpecialPortalIsOpen(void) | ||||
| { | ||||
| 	return CommonSpecialPortalInUse; | ||||
| } | ||||
| /* CommonSpecialPortal function at the bottom */ | ||||
| 
 | ||||
| void | ||||
| vacuum(char *vacrel, bool verbose, bool analyze, List *va_spec) | ||||
| @ -299,6 +245,11 @@ vac_vacuum(NameData *VacRelP, bool analyze, List *va_cols) | ||||
| 	/* vacuum each heap relation */ | ||||
| 	for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next) | ||||
| 		vacuum_rel(cur->vrl_relid, analyze, va_cols); | ||||
| 
 | ||||
| 	/* analyze separately so locking is minimized */ | ||||
| 	if (analyze) | ||||
| 		for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next) | ||||
| 			analyze_rel(cur->vrl_relid, va_cols); | ||||
| } | ||||
| 
 | ||||
| static VRelList | ||||
| @ -410,8 +361,7 @@ getrels(NameData *VacRelP) | ||||
| static void | ||||
| vacuum_rel(Oid relid, bool analyze, List *va_cols) | ||||
| { | ||||
| 	HeapTuple	tuple, | ||||
| 				typetuple; | ||||
| 	HeapTuple	tuple; | ||||
| 	Relation	onerel; | ||||
| 	VPageListData vacuum_pages; /* List of pages to vacuum and/or clean
 | ||||
| 								 * indices */ | ||||
| @ -474,125 +424,6 @@ vacuum_rel(Oid relid, bool analyze, List *va_cols) | ||||
| 	vacrelstats->num_pages = vacrelstats->num_tuples = 0; | ||||
| 	vacrelstats->hasindex = false; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * we can VACUUM ANALYZE any table except pg_statistic; see | ||||
| 	 * update_stats | ||||
| 	 */ | ||||
| 	if (analyze && | ||||
| 	 strcmp(RelationGetRelationName(onerel), StatisticRelationName) != 0) | ||||
| 	{ | ||||
| 		int			attr_cnt, | ||||
| 				   *attnums = NULL; | ||||
| 		Form_pg_attribute *attr; | ||||
| 
 | ||||
| 		attr_cnt = onerel->rd_att->natts; | ||||
| 		attr = onerel->rd_att->attrs; | ||||
| 
 | ||||
| 		if (va_cols != NIL) | ||||
| 		{ | ||||
| 			int			tcnt = 0; | ||||
| 			List	   *le; | ||||
| 
 | ||||
| 			if (length(va_cols) > attr_cnt) | ||||
| 				elog(ERROR, "vacuum: too many attributes specified for relation %s", | ||||
| 					 RelationGetRelationName(onerel)); | ||||
| 			attnums = (int *) palloc(attr_cnt * sizeof(int)); | ||||
| 			foreach(le, va_cols) | ||||
| 			{ | ||||
| 				char	   *col = (char *) lfirst(le); | ||||
| 
 | ||||
| 				for (i = 0; i < attr_cnt; i++) | ||||
| 				{ | ||||
| 					if (namestrcmp(&(attr[i]->attname), col) == 0) | ||||
| 						break; | ||||
| 				} | ||||
| 				if (i < attr_cnt)		/* found */ | ||||
| 					attnums[tcnt++] = i; | ||||
| 				else | ||||
| 				{ | ||||
| 					elog(ERROR, "vacuum: there is no attribute %s in %s", | ||||
| 						 col, RelationGetRelationName(onerel)); | ||||
| 				} | ||||
| 			} | ||||
| 			attr_cnt = tcnt; | ||||
| 		} | ||||
| 
 | ||||
| 		vacrelstats->vacattrstats = (VacAttrStats *) palloc(attr_cnt * sizeof(VacAttrStats)); | ||||
| 
 | ||||
| 		for (i = 0; i < attr_cnt; i++) | ||||
| 		{ | ||||
| 			Operator	func_operator; | ||||
| 			Form_pg_operator pgopform; | ||||
| 			VacAttrStats *stats; | ||||
| 
 | ||||
| 			stats = &vacrelstats->vacattrstats[i]; | ||||
| 			stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE); | ||||
| 			memmove(stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE); | ||||
| 			stats->best = stats->guess1 = stats->guess2 = 0; | ||||
| 			stats->max = stats->min = 0; | ||||
| 			stats->best_len = stats->guess1_len = stats->guess2_len = 0; | ||||
| 			stats->max_len = stats->min_len = 0; | ||||
| 			stats->initialized = false; | ||||
| 			stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0; | ||||
| 			stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0; | ||||
| 
 | ||||
| 			func_operator = oper("=", stats->attr->atttypid, stats->attr->atttypid, true); | ||||
| 			if (func_operator != NULL) | ||||
| 			{ | ||||
| 				pgopform = (Form_pg_operator) GETSTRUCT(func_operator); | ||||
| 				fmgr_info(pgopform->oprcode, &(stats->f_cmpeq)); | ||||
| 			} | ||||
| 			else | ||||
| 				stats->f_cmpeq.fn_addr = NULL; | ||||
| 
 | ||||
| 			func_operator = oper("<", stats->attr->atttypid, stats->attr->atttypid, true); | ||||
| 			if (func_operator != NULL) | ||||
| 			{ | ||||
| 				pgopform = (Form_pg_operator) GETSTRUCT(func_operator); | ||||
| 				fmgr_info(pgopform->oprcode, &(stats->f_cmplt)); | ||||
| 				stats->op_cmplt = oprid(func_operator); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				stats->f_cmplt.fn_addr = NULL; | ||||
| 				stats->op_cmplt = InvalidOid; | ||||
| 			} | ||||
| 
 | ||||
| 			func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true); | ||||
| 			if (func_operator != NULL) | ||||
| 			{ | ||||
| 				pgopform = (Form_pg_operator) GETSTRUCT(func_operator); | ||||
| 				fmgr_info(pgopform->oprcode, &(stats->f_cmpgt)); | ||||
| 			} | ||||
| 			else | ||||
| 				stats->f_cmpgt.fn_addr = NULL; | ||||
| 
 | ||||
| 			typetuple = SearchSysCacheTuple(TYPEOID, | ||||
| 								 ObjectIdGetDatum(stats->attr->atttypid), | ||||
| 											0, 0, 0); | ||||
| 			if (HeapTupleIsValid(typetuple)) | ||||
| 			{ | ||||
| 				stats->outfunc = ((Form_pg_type) GETSTRUCT(typetuple))->typoutput; | ||||
| 				stats->typelem = ((Form_pg_type) GETSTRUCT(typetuple))->typelem; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				stats->outfunc = InvalidOid; | ||||
| 				stats->typelem = InvalidOid; | ||||
| 			} | ||||
| 		} | ||||
| 		vacrelstats->va_natts = attr_cnt; | ||||
| 		/* delete existing pg_statistic rows for relation */ | ||||
| 		del_stats(relid, ((attnums) ? attr_cnt : 0), attnums); | ||||
| 		if (attnums) | ||||
| 			pfree(attnums); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		vacrelstats->va_natts = 0; | ||||
| 		vacrelstats->vacattrstats = (VacAttrStats *) NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	GetXmaxRecent(&XmaxRecent); | ||||
| 
 | ||||
| 	/* scan it */ | ||||
| @ -631,7 +462,7 @@ vacuum_rel(Oid relid, bool analyze, List *va_cols) | ||||
| 				vacuum_index(&vacuum_pages, Irel[i], vacrelstats->num_tuples, 0); | ||||
| 		} | ||||
| 		else | ||||
| /* just scan indices to update statistic */ | ||||
| 		/* just scan indices to update statistic */ | ||||
| 		{ | ||||
| 			for (i = 0; i < nindices; i++) | ||||
| 				scan_index(Irel[i], vacrelstats->num_tuples); | ||||
| @ -662,17 +493,197 @@ vacuum_rel(Oid relid, bool analyze, List *va_cols) | ||||
| 			pfree(fraged_pages.vpl_pagedesc); | ||||
| 	} | ||||
| 
 | ||||
| 	/* update statistics in pg_class */ | ||||
| 	update_stats(vacrelstats->relid, vacrelstats->num_pages, | ||||
| 			vacrelstats->num_tuples, vacrelstats->hasindex, vacrelstats); | ||||
| 
 | ||||
| 	/* all done with this class, but hold lock until commit */ | ||||
| 	heap_close(onerel, NoLock); | ||||
| 
 | ||||
| 	/* update statistics in pg_class */ | ||||
| 	update_relstats(vacrelstats->relid, vacrelstats->num_pages, | ||||
| 			vacrelstats->num_tuples, vacrelstats->hasindex, vacrelstats); | ||||
| 
 | ||||
| 	/* next command frees attribute stats */ | ||||
| 	CommitTransactionCommand(); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  *	analyze_rel() -- analyze relation | ||||
|  */ | ||||
| static void | ||||
| analyze_rel(Oid relid, List *va_cols) | ||||
| { | ||||
| 	HeapTuple	tuple, | ||||
| 				typetuple; | ||||
| 	Relation	onerel; | ||||
| 	int32		i; | ||||
| 	int			attr_cnt, | ||||
| 			   *attnums = NULL; | ||||
| 	Form_pg_attribute *attr; | ||||
| 	VacAttrStats *vacattrstats; | ||||
| 	HeapScanDesc scan; | ||||
| 
 | ||||
| 	StartTransactionCommand(); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Check for user-requested abort.	Note we want this to be inside a | ||||
| 	 * transaction, so xact.c doesn't issue useless NOTICE. | ||||
| 	 */ | ||||
| 	if (QueryCancel) | ||||
| 		CancelQuery(); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Race condition -- if the pg_class tuple has gone away since the | ||||
| 	 * last time we saw it, we don't need to vacuum it. | ||||
| 	 */ | ||||
| 	tuple = SearchSysCacheTuple(RELOID, | ||||
| 								ObjectIdGetDatum(relid), | ||||
| 								0, 0, 0); | ||||
| 	/*
 | ||||
| 	 * We can VACUUM ANALYZE any table except pg_statistic. | ||||
| 	 * see update_relstats | ||||
| 	 */ | ||||
| 	if (!HeapTupleIsValid(tuple) || | ||||
| 		strcmp(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname), | ||||
| 				StatisticRelationName) == 0) | ||||
| 	{ | ||||
| 		CommitTransactionCommand(); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Open the class, get an exclusive lock on it, and check permissions. | ||||
| 	 * | ||||
| 	 * Note we choose to treat permissions failure as a NOTICE and keep | ||||
| 	 * trying to vacuum the rest of the DB --- is this appropriate? | ||||
| 	 */ | ||||
| 	onerel = heap_open(relid, AccessShareLock); | ||||
| 
 | ||||
| #ifndef NO_SECURITY | ||||
| 	if (!pg_ownercheck(GetPgUserName(), RelationGetRelationName(onerel), | ||||
| 					   RELNAME)) | ||||
| 	{ | ||||
| 		/* we already did an elog during vacuum
 | ||||
| 		elog(NOTICE, "Skipping \"%s\" --- only table owner can VACUUM it", | ||||
| 			 RelationGetRelationName(onerel)); | ||||
| 		*/ | ||||
| 		heap_close(onerel, AccessExclusiveLock); | ||||
| 		CommitTransactionCommand(); | ||||
| 		return; | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	attr_cnt = onerel->rd_att->natts; | ||||
| 	attr = onerel->rd_att->attrs; | ||||
| 
 | ||||
| 	if (va_cols != NIL) | ||||
| 	{ | ||||
| 		int			tcnt = 0; | ||||
| 		List	   *le; | ||||
| 
 | ||||
| 		if (length(va_cols) > attr_cnt) | ||||
| 			elog(ERROR, "vacuum: too many attributes specified for relation %s", | ||||
| 				 RelationGetRelationName(onerel)); | ||||
| 		attnums = (int *) palloc(attr_cnt * sizeof(int)); | ||||
| 		foreach(le, va_cols) | ||||
| 		{ | ||||
| 			char	   *col = (char *) lfirst(le); | ||||
| 
 | ||||
| 			for (i = 0; i < attr_cnt; i++) | ||||
| 			{ | ||||
| 				if (namestrcmp(&(attr[i]->attname), col) == 0) | ||||
| 					break; | ||||
| 			} | ||||
| 			if (i < attr_cnt)		/* found */ | ||||
| 				attnums[tcnt++] = i; | ||||
| 			else | ||||
| 			{ | ||||
| 				elog(ERROR, "vacuum: there is no attribute %s in %s", | ||||
| 					 col, RelationGetRelationName(onerel)); | ||||
| 			} | ||||
| 		} | ||||
| 		attr_cnt = tcnt; | ||||
| 	} | ||||
| 
 | ||||
| 	vacattrstats = (VacAttrStats *) palloc(attr_cnt * sizeof(VacAttrStats)); | ||||
| 
 | ||||
| 	for (i = 0; i < attr_cnt; i++) | ||||
| 	{ | ||||
| 		Operator	func_operator; | ||||
| 		Form_pg_operator pgopform; | ||||
| 		VacAttrStats *stats; | ||||
| 
 | ||||
| 		stats = &vacattrstats[i]; | ||||
| 		stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE); | ||||
| 		memmove(stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE); | ||||
| 		stats->best = stats->guess1 = stats->guess2 = 0; | ||||
| 		stats->max = stats->min = 0; | ||||
| 		stats->best_len = stats->guess1_len = stats->guess2_len = 0; | ||||
| 		stats->max_len = stats->min_len = 0; | ||||
| 		stats->initialized = false; | ||||
| 		stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0; | ||||
| 		stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0; | ||||
| 
 | ||||
| 		func_operator = oper("=", stats->attr->atttypid, stats->attr->atttypid, true); | ||||
| 		if (func_operator != NULL) | ||||
| 		{ | ||||
| 			pgopform = (Form_pg_operator) GETSTRUCT(func_operator); | ||||
| 			fmgr_info(pgopform->oprcode, &(stats->f_cmpeq)); | ||||
| 		} | ||||
| 		else | ||||
| 			stats->f_cmpeq.fn_addr = NULL; | ||||
| 
 | ||||
| 		func_operator = oper("<", stats->attr->atttypid, stats->attr->atttypid, true); | ||||
| 		if (func_operator != NULL) | ||||
| 		{ | ||||
| 			pgopform = (Form_pg_operator) GETSTRUCT(func_operator); | ||||
| 			fmgr_info(pgopform->oprcode, &(stats->f_cmplt)); | ||||
| 			stats->op_cmplt = oprid(func_operator); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			stats->f_cmplt.fn_addr = NULL; | ||||
| 			stats->op_cmplt = InvalidOid; | ||||
| 		} | ||||
| 
 | ||||
| 		func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true); | ||||
| 		if (func_operator != NULL) | ||||
| 		{ | ||||
| 			pgopform = (Form_pg_operator) GETSTRUCT(func_operator); | ||||
| 			fmgr_info(pgopform->oprcode, &(stats->f_cmpgt)); | ||||
| 		} | ||||
| 		else | ||||
| 			stats->f_cmpgt.fn_addr = NULL; | ||||
| 
 | ||||
| 		typetuple = SearchSysCacheTuple(TYPEOID, | ||||
| 							 ObjectIdGetDatum(stats->attr->atttypid), | ||||
| 										0, 0, 0); | ||||
| 		if (HeapTupleIsValid(typetuple)) | ||||
| 		{ | ||||
| 			stats->outfunc = ((Form_pg_type) GETSTRUCT(typetuple))->typoutput; | ||||
| 			stats->typelem = ((Form_pg_type) GETSTRUCT(typetuple))->typelem; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			stats->outfunc = InvalidOid; | ||||
| 			stats->typelem = InvalidOid; | ||||
| 		} | ||||
| 	} | ||||
| 	/* delete existing pg_statistic rows for relation */ | ||||
| 	del_stats(relid, ((attnums) ? attr_cnt : 0), attnums); | ||||
| 
 | ||||
| 	scan = heap_beginscan(onerel, false, SnapshotNow, 0, NULL); | ||||
| 
 | ||||
| 	while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) | ||||
| 		attr_stats(onerel, attr_cnt, vacattrstats, tuple); | ||||
| 
 | ||||
| 	heap_endscan(scan); | ||||
| 
 | ||||
| 	heap_close(onerel, AccessShareLock); | ||||
| 
 | ||||
| 	/* update statistics in pg_class */ | ||||
| 	update_attstats(relid, attr_cnt, vacattrstats); | ||||
| 
 | ||||
| 	CommitTransactionCommand(); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  *	scan_heap() -- scan an open heap relation | ||||
|  * | ||||
| @ -979,7 +990,6 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, | ||||
| 					min_tlen = tuple.t_len; | ||||
| 				if (tuple.t_len > max_tlen) | ||||
| 					max_tlen = tuple.t_len; | ||||
| 				attr_stats(onerel, vacrelstats, &tuple); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| @ -2106,7 +2116,7 @@ scan_index(Relation indrel, int num_tuples) | ||||
| 
 | ||||
| 	/* now update statistics in pg_class */ | ||||
| 	nipages = RelationGetNumberOfBlocks(indrel); | ||||
| 	update_stats(RelationGetRelid(indrel), nipages, nitups, false, NULL); | ||||
| 	update_relstats(RelationGetRelid(indrel), nipages, nitups, false, NULL); | ||||
| 
 | ||||
| 	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %u. %s", | ||||
| 		 RelationGetRelationName(indrel), nipages, nitups, | ||||
| @ -2183,7 +2193,7 @@ vacuum_index(VPageList vpl, Relation indrel, int num_tuples, int keep_tuples) | ||||
| 
 | ||||
| 	/* now update statistics in pg_class */ | ||||
| 	num_pages = RelationGetNumberOfBlocks(indrel); | ||||
| 	update_stats(RelationGetRelid(indrel), num_pages, num_index_tuples, false, NULL); | ||||
| 	update_relstats(RelationGetRelid(indrel), num_pages, num_index_tuples, false, NULL); | ||||
| 
 | ||||
| 	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %u: Deleted %u. %s", | ||||
| 		 RelationGetRelationName(indrel), num_pages, | ||||
| @ -2263,11 +2273,9 @@ tid_reaped(ItemPointer itemptr, VPageList vpl) | ||||
|  * | ||||
|  */ | ||||
| static void | ||||
| attr_stats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple) | ||||
| attr_stats(Relation onerel, int attr_cnt, VacAttrStats *vacattrstats, HeapTuple tuple) | ||||
| { | ||||
| 	int			i, | ||||
| 				attr_cnt = vacrelstats->va_natts; | ||||
| 	VacAttrStats *vacattrstats = vacrelstats->vacattrstats; | ||||
| 	int			i; | ||||
| 	TupleDesc	tupDesc = onerel->rd_att; | ||||
| 	Datum		value; | ||||
| 	bool		isnull; | ||||
| @ -2387,7 +2395,7 @@ bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int *bucket_len) | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  *	update_stats() -- update statistics for one relation | ||||
|  *	update_relstats() -- update statistics for one relation | ||||
|  * | ||||
|  *		Statistics are stored in several places: the pg_class row for the | ||||
|  *		relation has stats about the whole relation, the pg_attribute rows | ||||
| @ -2408,29 +2416,15 @@ bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int *bucket_len) | ||||
|  *		Updating pg_class's own statistics would be especially tricky. | ||||
|  *		Of course, this only works for fixed-size never-null columns, but | ||||
|  *		these are. | ||||
|  * | ||||
|  *		Updates of pg_attribute statistics are handled in the same way | ||||
|  *		for the same reasons. | ||||
|  * | ||||
|  *		To keep things simple, we punt for pg_statistic, and don't try | ||||
|  *		to compute or store rows for pg_statistic itself in pg_statistic. | ||||
|  *		This could possibly be made to work, but it's not worth the trouble. | ||||
|  */ | ||||
| static void | ||||
| update_stats(Oid relid, int num_pages, int num_tuples, bool hasindex, | ||||
| update_relstats(Oid relid, int num_pages, int num_tuples, bool hasindex, | ||||
| 			VRelStats *vacrelstats) | ||||
| { | ||||
| 	Relation	rd, | ||||
| 				ad, | ||||
| 				sd; | ||||
| 	HeapScanDesc scan; | ||||
| 	Relation	rd; | ||||
| 	HeapTupleData rtup; | ||||
| 	HeapTuple	ctup, | ||||
| 				atup, | ||||
| 				stup; | ||||
| 	HeapTuple	ctup; | ||||
| 	Form_pg_class pgcform; | ||||
| 	ScanKeyData askey; | ||||
| 	Form_pg_attribute attp; | ||||
| 	Buffer		buffer; | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -2461,202 +2455,217 @@ update_stats(Oid relid, int num_pages, int num_tuples, bool hasindex, | ||||
| 	WriteBuffer(buffer); | ||||
| 
 | ||||
| 	heap_close(rd, RowExclusiveLock); | ||||
| } | ||||
| 
 | ||||
| 	if (vacrelstats != NULL && vacrelstats->va_natts > 0) | ||||
| /*
 | ||||
|  *	update_attstats() -- update attribute statistics for one relation | ||||
|  * | ||||
|  *		Updates of pg_attribute statistics are handled by over-write. | ||||
|  *		for reasons described above. | ||||
|  * | ||||
|  *		To keep things simple, we punt for pg_statistic, and don't try | ||||
|  *		to compute or store rows for pg_statistic itself in pg_statistic. | ||||
|  *		This could possibly be made to work, but it's not worth the trouble. | ||||
|  */ | ||||
| static void | ||||
| update_attstats(Oid relid, int natts, VacAttrStats *vacattrstats) | ||||
| { | ||||
| 	Relation	ad, | ||||
| 				sd; | ||||
| 	HeapScanDesc scan; | ||||
| 	HeapTuple	atup, | ||||
| 				stup; | ||||
| 	ScanKeyData askey; | ||||
| 	Form_pg_attribute attp; | ||||
| 
 | ||||
| 	ad = heap_openr(AttributeRelationName, RowExclusiveLock); | ||||
| 	sd = heap_openr(StatisticRelationName, RowExclusiveLock); | ||||
| 
 | ||||
| 	/* Find pg_attribute rows for this relation */ | ||||
| 	ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid, | ||||
| 						   F_INT4EQ, relid); | ||||
| 
 | ||||
| 	scan = heap_beginscan(ad, false, SnapshotNow, 1, &askey); | ||||
| 
 | ||||
| 	while (HeapTupleIsValid(atup = heap_getnext(scan, 0))) | ||||
| 	{ | ||||
| 		VacAttrStats *vacattrstats = vacrelstats->vacattrstats; | ||||
| 		int			natts = vacrelstats->va_natts; | ||||
| 		int			i; | ||||
| 		VacAttrStats *stats; | ||||
| 
 | ||||
| 		ad = heap_openr(AttributeRelationName, RowExclusiveLock); | ||||
| 		sd = heap_openr(StatisticRelationName, RowExclusiveLock); | ||||
| 		attp = (Form_pg_attribute) GETSTRUCT(atup); | ||||
| 		if (attp->attnum <= 0)		/* skip system attributes for now */ | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* Find pg_attribute rows for this relation */ | ||||
| 		ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid, | ||||
| 							   F_INT4EQ, relid); | ||||
| 
 | ||||
| 		scan = heap_beginscan(ad, false, SnapshotNow, 1, &askey); | ||||
| 
 | ||||
| 		while (HeapTupleIsValid(atup = heap_getnext(scan, 0))) | ||||
| 		for (i = 0; i < natts; i++) | ||||
| 		{ | ||||
| 			int			i; | ||||
| 			VacAttrStats *stats; | ||||
| 			if (attp->attnum == vacattrstats[i].attr->attnum) | ||||
| 				break; | ||||
| 		} | ||||
| 		if (i >= natts) | ||||
| 			continue;		/* skip attr if no stats collected */ | ||||
| 		stats = &(vacattrstats[i]); | ||||
| 
 | ||||
| 			attp = (Form_pg_attribute) GETSTRUCT(atup); | ||||
| 			if (attp->attnum <= 0)		/* skip system attributes for now */ | ||||
| 				continue; | ||||
| 		if (VacAttrStatsEqValid(stats)) | ||||
| 		{ | ||||
| 			float32data selratio;	/* average ratio of rows selected
 | ||||
| 									 * for a random constant */ | ||||
| 
 | ||||
| 			for (i = 0; i < natts; i++) | ||||
| 			/* Compute disbursion */ | ||||
| 			if (stats->nonnull_cnt == 0 && stats->null_cnt == 0) | ||||
| 			{ | ||||
| 				if (attp->attnum == vacattrstats[i].attr->attnum) | ||||
| 					break; | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * empty relation, so put a dummy value in | ||||
| 				 * attdisbursion | ||||
| 				 */ | ||||
| 				selratio = 0; | ||||
| 			} | ||||
| 			if (i >= natts) | ||||
| 				continue;		/* skip attr if no stats collected */ | ||||
| 			stats = &(vacattrstats[i]); | ||||
| 
 | ||||
| 			if (VacAttrStatsEqValid(stats)) | ||||
| 			else if (stats->null_cnt <= 1 && stats->best_cnt == 1) | ||||
| 			{ | ||||
| 				float32data selratio;	/* average ratio of rows selected
 | ||||
| 										 * for a random constant */ | ||||
| 
 | ||||
| 				/* Compute disbursion */ | ||||
| 				if (stats->nonnull_cnt == 0 && stats->null_cnt == 0) | ||||
| 				/*
 | ||||
| 				 * looks like we have a unique-key attribute --- flag | ||||
| 				 * this with special -1.0 flag value. | ||||
| 				 * | ||||
| 				 * The correct disbursion is 1.0/numberOfRows, but since | ||||
| 				 * the relation row count can get updated without | ||||
| 				 * recomputing disbursion, we want to store a | ||||
| 				 * "symbolic" value and figure 1.0/numberOfRows on the | ||||
| 				 * fly. | ||||
| 				 */ | ||||
| 				selratio = -1; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				if (VacAttrStatsLtGtValid(stats) && | ||||
| 				stats->min_cnt + stats->max_cnt == stats->nonnull_cnt) | ||||
| 				{ | ||||
| 
 | ||||
| 					/*
 | ||||
| 					 * empty relation, so put a dummy value in | ||||
| 					 * attdisbursion | ||||
| 					 * exact result when there are just 1 or 2 | ||||
| 					 * values... | ||||
| 					 */ | ||||
| 					selratio = 0; | ||||
| 				} | ||||
| 				else if (stats->null_cnt <= 1 && stats->best_cnt == 1) | ||||
| 				{ | ||||
| 					double		min_cnt_d = stats->min_cnt, | ||||
| 								max_cnt_d = stats->max_cnt, | ||||
| 								null_cnt_d = stats->null_cnt; | ||||
| 					double		total = ((double) stats->nonnull_cnt) + null_cnt_d; | ||||
| 
 | ||||
| 					/*
 | ||||
| 					 * looks like we have a unique-key attribute --- flag | ||||
| 					 * this with special -1.0 flag value. | ||||
| 					 * | ||||
| 					 * The correct disbursion is 1.0/numberOfRows, but since | ||||
| 					 * the relation row count can get updated without | ||||
| 					 * recomputing disbursion, we want to store a | ||||
| 					 * "symbolic" value and figure 1.0/numberOfRows on the | ||||
| 					 * fly. | ||||
| 					 */ | ||||
| 					selratio = -1; | ||||
| 					selratio = (min_cnt_d * min_cnt_d + max_cnt_d * max_cnt_d + null_cnt_d * null_cnt_d) / (total * total); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					if (VacAttrStatsLtGtValid(stats) && | ||||
| 					stats->min_cnt + stats->max_cnt == stats->nonnull_cnt) | ||||
| 					{ | ||||
| 					double		most = (double) (stats->best_cnt > stats->null_cnt ? stats->best_cnt : stats->null_cnt); | ||||
| 					double		total = ((double) stats->nonnull_cnt) + ((double) stats->null_cnt); | ||||
| 
 | ||||
| 						/*
 | ||||
| 						 * exact result when there are just 1 or 2 | ||||
| 						 * values... | ||||
| 						 */ | ||||
| 						double		min_cnt_d = stats->min_cnt, | ||||
| 									max_cnt_d = stats->max_cnt, | ||||
| 									null_cnt_d = stats->null_cnt; | ||||
| 						double		total = ((double) stats->nonnull_cnt) + null_cnt_d; | ||||
| 
 | ||||
| 						selratio = (min_cnt_d * min_cnt_d + max_cnt_d * max_cnt_d + null_cnt_d * null_cnt_d) / (total * total); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						double		most = (double) (stats->best_cnt > stats->null_cnt ? stats->best_cnt : stats->null_cnt); | ||||
| 						double		total = ((double) stats->nonnull_cnt) + ((double) stats->null_cnt); | ||||
| 
 | ||||
| 						/*
 | ||||
| 						 * we assume count of other values are 20% of best | ||||
| 						 * count in table | ||||
| 						 */ | ||||
| 						selratio = (most * most + 0.20 * most * (total - most)) / (total * total); | ||||
| 					} | ||||
| 					/* Make sure calculated values are in-range */ | ||||
| 					if (selratio < 0.0) | ||||
| 						selratio = 0.0; | ||||
| 					else if (selratio > 1.0) | ||||
| 						selratio = 1.0; | ||||
| 					/*
 | ||||
| 					 * we assume count of other values are 20% of best | ||||
| 					 * count in table | ||||
| 					 */ | ||||
| 					selratio = (most * most + 0.20 * most * (total - most)) / (total * total); | ||||
| 				} | ||||
| 				/* Make sure calculated values are in-range */ | ||||
| 				if (selratio < 0.0) | ||||
| 					selratio = 0.0; | ||||
| 				else if (selratio > 1.0) | ||||
| 					selratio = 1.0; | ||||
| 			} | ||||
| 
 | ||||
| 				/* overwrite the existing statistics in the tuple */ | ||||
| 				attp->attdisbursion = selratio; | ||||
| 			/* overwrite the existing statistics in the tuple */ | ||||
| 			attp->attdisbursion = selratio; | ||||
| 
 | ||||
| 				/* invalidate the tuple in the cache and write the buffer */ | ||||
| 				RelationInvalidateHeapTuple(ad, atup); | ||||
| 				WriteNoReleaseBuffer(scan->rs_cbuf); | ||||
| 			/* invalidate the tuple in the cache and write the buffer */ | ||||
| 			RelationInvalidateHeapTuple(ad, atup); | ||||
| 			WriteNoReleaseBuffer(scan->rs_cbuf); | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * Create pg_statistic tuples for the relation, if we have | ||||
| 				 * gathered the right data.  del_stats() previously | ||||
| 				 * deleted all the pg_statistic tuples for the rel, so we | ||||
| 				 * just have to insert new ones here. | ||||
| 				 * | ||||
| 				 * Note vacuum_rel() has seen to it that we won't come here | ||||
| 				 * when vacuuming pg_statistic itself. | ||||
| 			/*
 | ||||
| 			 * Create pg_statistic tuples for the relation, if we have | ||||
| 			 * gathered the right data.  del_stats() previously | ||||
| 			 * deleted all the pg_statistic tuples for the rel, so we | ||||
| 			 * just have to insert new ones here. | ||||
| 			 * | ||||
| 			 * Note vacuum_rel() has seen to it that we won't come here | ||||
| 			 * when vacuuming pg_statistic itself. | ||||
| 			 */ | ||||
| 			if (VacAttrStatsLtGtValid(stats) && stats->initialized) | ||||
| 			{ | ||||
| 				float32data nullratio; | ||||
| 				float32data bestratio; | ||||
| 				FmgrInfo	out_function; | ||||
| 				char	   *out_string; | ||||
| 				double		best_cnt_d = stats->best_cnt, | ||||
| 							null_cnt_d = stats->null_cnt, | ||||
| 							nonnull_cnt_d = stats->nonnull_cnt;		/* prevent overflow */ | ||||
| 				Datum		values[Natts_pg_statistic]; | ||||
| 				char		nulls[Natts_pg_statistic]; | ||||
| 
 | ||||
| 				nullratio = null_cnt_d / (nonnull_cnt_d + null_cnt_d); | ||||
| 				bestratio = best_cnt_d / (nonnull_cnt_d + null_cnt_d); | ||||
| 
 | ||||
| 				fmgr_info(stats->outfunc, &out_function); | ||||
| 
 | ||||
| 				for (i = 0; i < Natts_pg_statistic; ++i) | ||||
| 					nulls[i] = ' '; | ||||
| 
 | ||||
| 				/* ----------------
 | ||||
| 				 *	initialize values[] | ||||
| 				 * ---------------- | ||||
| 				 */ | ||||
| 				if (VacAttrStatsLtGtValid(stats) && stats->initialized) | ||||
| 				i = 0; | ||||
| 				values[i++] = (Datum) relid;		/* starelid */ | ||||
| 				values[i++] = (Datum) attp->attnum; /* staattnum */ | ||||
| 				values[i++] = (Datum) stats->op_cmplt;		/* staop */ | ||||
| 				/* hack: this code knows float4 is pass-by-ref */ | ||||
| 				values[i++] = PointerGetDatum(&nullratio);	/* stanullfrac */ | ||||
| 				values[i++] = PointerGetDatum(&bestratio);	/* stacommonfrac */ | ||||
| 				out_string = (*fmgr_faddr(&out_function)) (stats->best, stats->typelem, stats->attr->atttypmod); | ||||
| 				values[i++] = PointerGetDatum(textin(out_string));	/* stacommonval */ | ||||
| 				pfree(out_string); | ||||
| 				out_string = (*fmgr_faddr(&out_function)) (stats->min, stats->typelem, stats->attr->atttypmod); | ||||
| 				values[i++] = PointerGetDatum(textin(out_string));	/* staloval */ | ||||
| 				pfree(out_string); | ||||
| 				out_string = (char *) (*fmgr_faddr(&out_function)) (stats->max, stats->typelem, stats->attr->atttypmod); | ||||
| 				values[i++] = PointerGetDatum(textin(out_string));	/* stahival */ | ||||
| 				pfree(out_string); | ||||
| 
 | ||||
| 				stup = heap_formtuple(sd->rd_att, values, nulls); | ||||
| 
 | ||||
| 				/* ----------------
 | ||||
| 				 *	Watch out for oversize tuple, which can happen if | ||||
| 				 *	all three of the saved data values are long. | ||||
| 				 *	Our fallback strategy is just to not store the | ||||
| 				 *	pg_statistic tuple at all in that case.  (We could | ||||
| 				 *	replace the values by NULLs and still store the | ||||
| 				 *	numeric stats, but presently selfuncs.c couldn't | ||||
| 				 *	do anything useful with that case anyway.) | ||||
| 				 * | ||||
| 				 *	We could reduce the probability of overflow, but not | ||||
| 				 *	prevent it, by storing the data values as compressed | ||||
| 				 *	text; is that worth doing?	The problem should go | ||||
| 				 *	away whenever long tuples get implemented... | ||||
| 				 * ---------------- | ||||
| 				 */ | ||||
| 				if (MAXALIGN(stup->t_len) <= MaxTupleSize) | ||||
| 				{ | ||||
| 					float32data nullratio; | ||||
| 					float32data bestratio; | ||||
| 					FmgrInfo	out_function; | ||||
| 					char	   *out_string; | ||||
| 					double		best_cnt_d = stats->best_cnt, | ||||
| 								null_cnt_d = stats->null_cnt, | ||||
| 								nonnull_cnt_d = stats->nonnull_cnt;		/* prevent overflow */ | ||||
| 					Datum		values[Natts_pg_statistic]; | ||||
| 					char		nulls[Natts_pg_statistic]; | ||||
| 					/* OK, store tuple and update indexes too */ | ||||
| 					Relation	irelations[Num_pg_statistic_indices]; | ||||
| 
 | ||||
| 					nullratio = null_cnt_d / (nonnull_cnt_d + null_cnt_d); | ||||
| 					bestratio = best_cnt_d / (nonnull_cnt_d + null_cnt_d); | ||||
| 
 | ||||
| 					fmgr_info(stats->outfunc, &out_function); | ||||
| 
 | ||||
| 					for (i = 0; i < Natts_pg_statistic; ++i) | ||||
| 						nulls[i] = ' '; | ||||
| 
 | ||||
| 					/* ----------------
 | ||||
| 					 *	initialize values[] | ||||
| 					 * ---------------- | ||||
| 					 */ | ||||
| 					i = 0; | ||||
| 					values[i++] = (Datum) relid;		/* starelid */ | ||||
| 					values[i++] = (Datum) attp->attnum; /* staattnum */ | ||||
| 					values[i++] = (Datum) stats->op_cmplt;		/* staop */ | ||||
| 					/* hack: this code knows float4 is pass-by-ref */ | ||||
| 					values[i++] = PointerGetDatum(&nullratio);	/* stanullfrac */ | ||||
| 					values[i++] = PointerGetDatum(&bestratio);	/* stacommonfrac */ | ||||
| 					out_string = (*fmgr_faddr(&out_function)) (stats->best, stats->typelem, stats->attr->atttypmod); | ||||
| 					values[i++] = PointerGetDatum(textin(out_string));	/* stacommonval */ | ||||
| 					pfree(out_string); | ||||
| 					out_string = (*fmgr_faddr(&out_function)) (stats->min, stats->typelem, stats->attr->atttypmod); | ||||
| 					values[i++] = PointerGetDatum(textin(out_string));	/* staloval */ | ||||
| 					pfree(out_string); | ||||
| 					out_string = (char *) (*fmgr_faddr(&out_function)) (stats->max, stats->typelem, stats->attr->atttypmod); | ||||
| 					values[i++] = PointerGetDatum(textin(out_string));	/* stahival */ | ||||
| 					pfree(out_string); | ||||
| 
 | ||||
| 					stup = heap_formtuple(sd->rd_att, values, nulls); | ||||
| 
 | ||||
| 					/* ----------------
 | ||||
| 					 *	Watch out for oversize tuple, which can happen if | ||||
| 					 *	all three of the saved data values are long. | ||||
| 					 *	Our fallback strategy is just to not store the | ||||
| 					 *	pg_statistic tuple at all in that case.  (We could | ||||
| 					 *	replace the values by NULLs and still store the | ||||
| 					 *	numeric stats, but presently selfuncs.c couldn't | ||||
| 					 *	do anything useful with that case anyway.) | ||||
| 					 * | ||||
| 					 *	We could reduce the probability of overflow, but not | ||||
| 					 *	prevent it, by storing the data values as compressed | ||||
| 					 *	text; is that worth doing?	The problem should go | ||||
| 					 *	away whenever long tuples get implemented... | ||||
| 					 * ---------------- | ||||
| 					 */ | ||||
| 					if (MAXALIGN(stup->t_len) <= MaxTupleSize) | ||||
| 					{ | ||||
| 						/* OK, store tuple and update indexes too */ | ||||
| 						Relation	irelations[Num_pg_statistic_indices]; | ||||
| 
 | ||||
| 						heap_insert(sd, stup); | ||||
| 						CatalogOpenIndices(Num_pg_statistic_indices, Name_pg_statistic_indices, irelations); | ||||
| 						CatalogIndexInsert(irelations, Num_pg_statistic_indices, sd, stup); | ||||
| 						CatalogCloseIndices(Num_pg_statistic_indices, irelations); | ||||
| 					} | ||||
| 
 | ||||
| 					/* release allocated space */ | ||||
| 					pfree(DatumGetPointer(values[Anum_pg_statistic_stacommonval - 1])); | ||||
| 					pfree(DatumGetPointer(values[Anum_pg_statistic_staloval - 1])); | ||||
| 					pfree(DatumGetPointer(values[Anum_pg_statistic_stahival - 1])); | ||||
| 					heap_freetuple(stup); | ||||
| 					heap_insert(sd, stup); | ||||
| 					CatalogOpenIndices(Num_pg_statistic_indices, Name_pg_statistic_indices, irelations); | ||||
| 					CatalogIndexInsert(irelations, Num_pg_statistic_indices, sd, stup); | ||||
| 					CatalogCloseIndices(Num_pg_statistic_indices, irelations); | ||||
| 				} | ||||
| 
 | ||||
| 				/* release allocated space */ | ||||
| 				pfree(DatumGetPointer(values[Anum_pg_statistic_stacommonval - 1])); | ||||
| 				pfree(DatumGetPointer(values[Anum_pg_statistic_staloval - 1])); | ||||
| 				pfree(DatumGetPointer(values[Anum_pg_statistic_stahival - 1])); | ||||
| 				heap_freetuple(stup); | ||||
| 			} | ||||
| 		} | ||||
| 		heap_endscan(scan); | ||||
| 		/* close rels, but hold locks till upcoming commit */ | ||||
| 		heap_close(ad, NoLock); | ||||
| 		heap_close(sd, NoLock); | ||||
| 	} | ||||
| 	heap_endscan(scan); | ||||
| 	/* close rels, but hold locks till upcoming commit */ | ||||
| 	heap_close(ad, NoLock); | ||||
| 	heap_close(sd, NoLock); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -2867,6 +2876,62 @@ vac_cmp_vtlinks(const void *left, const void *right) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * This routines handle a special cross-transaction portal. | ||||
|  * However it is automatically closed in case of abort. | ||||
|  */ | ||||
| void | ||||
| CommonSpecialPortalOpen(void) | ||||
| { | ||||
| 	char	   *pname; | ||||
| 
 | ||||
| 
 | ||||
| 	if (CommonSpecialPortalInUse) | ||||
| 		elog(ERROR, "CommonSpecialPortal is in use"); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Create a portal for safe memory across transactions. We need to | ||||
| 	 * palloc the name space for it because our hash function expects the | ||||
| 	 * name to be on a longword boundary.  CreatePortal copies the name to | ||||
| 	 * safe storage for us. | ||||
| 	 */ | ||||
| 	pname = pstrdup(VACPNAME); | ||||
| 	vac_portal = CreatePortal(pname); | ||||
| 	pfree(pname); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set flag to indicate that vac_portal must be removed after an error. | ||||
| 	 * This global variable is checked in the transaction manager on xact | ||||
| 	 * abort, and the routine CommonSpecialPortalClose() is called if | ||||
| 	 * necessary. | ||||
| 	 */ | ||||
| 	CommonSpecialPortalInUse = true; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| CommonSpecialPortalClose(void) | ||||
| { | ||||
| 	/* Clear flag first, to avoid recursion if PortalDrop elog's */ | ||||
| 	CommonSpecialPortalInUse = false; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Release our portal for cross-transaction memory. | ||||
| 	 */ | ||||
| 	PortalDrop(&vac_portal); | ||||
| } | ||||
| 
 | ||||
| PortalVariableMemory | ||||
| CommonSpecialPortalGetMemory(void) | ||||
| { | ||||
| 	return PortalGetVariableMemory(vac_portal); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| CommonSpecialPortalIsOpen(void) | ||||
| { | ||||
| 	return CommonSpecialPortalInUse; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| get_indices(Oid relid, int *nindices, Relation **Irel) | ||||
| { | ||||
|  | ||||
| @ -7,7 +7,7 @@ | ||||
|  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc | ||||
|  * Portions Copyright (c) 1994, Regents of the University of California | ||||
|  * | ||||
|  * $Id: vacuum.h,v 1.27 2000/04/12 17:16:32 momjian Exp $ | ||||
|  * $Id: vacuum.h,v 1.28 2000/05/29 15:44:55 momjian Exp $ | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
| @ -125,8 +125,6 @@ typedef struct VRelStats | ||||
| 	Size		min_tlen; | ||||
| 	Size		max_tlen; | ||||
| 	bool		hasindex; | ||||
| 	int			va_natts;		/* number of attrs being analyzed */ | ||||
| 	VacAttrStats *vacattrstats; | ||||
| 	int			num_vtlinks; | ||||
| 	VTupleLink	vtlinks; | ||||
| } VRelStats; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user