diff --git a/doc/src/sgml/cvs.sgml b/doc/src/sgml/cvs.sgml
index 50f9a0ccbc3031d5cfead4b962b00062be1d1f63..bf2ee60ebc7a2caf54fd77b49d01a4c995e8c830 100644
--- a/doc/src/sgml/cvs.sgml
+++ b/doc/src/sgml/cvs.sgml
@@ -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 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 3160e2011080bff04c1f529f1a61837ff82257af..bd8386be3726e8578ff67ef18ec0ceb50f5e4009 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -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)
-	{
-		VacAttrStats *vacattrstats = vacrelstats->vacattrstats;
-		int			natts = vacrelstats->va_natts;
+/*
+ *	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);
+	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);
+	/* 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);
+	scan = heap_beginscan(ad, false, SnapshotNow, 1, &askey);
 
-		while (HeapTupleIsValid(atup = heap_getnext(scan, 0)))
+	while (HeapTupleIsValid(atup = heap_getnext(scan, 0)))
+	{
+		int			i;
+		VacAttrStats *stats;
+
+		attp = (Form_pg_attribute) GETSTRUCT(atup);
+		if (attp->attnum <= 0)		/* skip system attributes for now */
+			continue;
+
+		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;
-			}
-			if (i >= natts)
-				continue;		/* skip attr if no stats collected */
-			stats = &(vacattrstats[i]);
 
-			if (VacAttrStatsEqValid(stats))
+				/*
+				 * empty relation, so put a dummy value in
+				 * attdisbursion
+				 */
+				selratio = 0;
+			}
+			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)
-					{
-
-						/*
-						 * 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);
+					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.
+			/*
+			 * 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[]
+				 * ----------------
+				 */
+				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.)
 				 *
-				 * Note vacuum_rel() has seen to it that we won't come here
-				 * when vacuuming pg_statistic itself.
+				 *	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 (VacAttrStatsLtGtValid(stats) && stats->initialized)
+				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];
-
-					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];
+					/* 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)
 {
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 136721c4e17d9657e82b69a34093ed0e2883683b..afdd0a9e6b90fd36bc10ba9da5a1c0bae85d5955 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -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;