From 2300ac0dc4fc5702a6a693fdd769be8784314882 Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Fri, 7 Feb 1997 16:24:12 +0000
Subject: [PATCH] Add attribute optimization statistics.

---
 src/backend/commands/vacuum.c      | 721 +++++++++++++++++++++--------
 src/backend/parser/analyze.c       |   6 +-
 src/backend/parser/catalog_utils.c |  18 +-
 src/backend/parser/parse_query.c   |   6 +-
 src/backend/utils/adt/selfuncs.c   |   9 +-
 src/include/commands/vacuum.h      |  40 +-
 src/include/parser/catalog_utils.h |   4 +-
 7 files changed, 580 insertions(+), 224 deletions(-)

diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 48e45674330..619506ff0ad 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.18 1997/01/29 02:59:03 vadim Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.19 1997/02/07 16:22:34 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,7 @@
 
 #include <postgres.h>
 
+#include <fmgr.h>
 #include <utils/portal.h>
 #include <access/genam.h>
 #include <access/heapam.h>
@@ -32,12 +33,17 @@
 #include <catalog/catalog.h>
 #include <catalog/pg_class.h>
 #include <catalog/pg_proc.h>
+#include <catalog/pg_statistic.h>
+#include <catalog/pg_type.h>
+#include <catalog/pg_operator.h>
 #include <storage/smgr.h>
 #include <storage/lmgr.h>
 #include <utils/inval.h>
 #include <utils/mcxt.h>
+#include <utils/inval.h>
 #include <utils/syscache.h>
 #include <commands/vacuum.h>
+#include <parser/catalog_utils.h>
 #include <storage/bufpage.h>
 #include "storage/shmem.h"
 #ifndef HAVE_GETRUSAGE
@@ -48,44 +54,48 @@
 #endif 
 
 bool VacuumRunning =	false;
-static int MESSLEV;	/* message level */
+static int MESSAGE_LEVEL;	/* message level */
 
-typedef struct {
-    FuncIndexInfo	finfo;
-    FuncIndexInfo	*finfoP;
-    IndexTupleForm	tform;
-    int			natts;
-} IndDesc;
+#define swapLong(a,b)	{long tmp; tmp=a; a=b; b=tmp;}
+#define swapInt(a,b)	{int tmp; tmp=a; a=b; b=tmp;}
+#define swapDatum(a,b)	{Datum tmp; tmp=a; a=b; b=tmp;}
+#define VacAttrStatsEqValid(stats) ( RegProcedureIsValid(stats->cmpeq))
+#define VacAttrStatsLtGtValid(stats) ( RegProcedureIsValid(stats->cmplt) && \
+				   RegProcedureIsValid(stats->cmpgt) && \
+				   RegProcedureIsValid(stats->outfunc) )
+  
 
 /* non-export function prototypes */
-static void _vc_init(void);
-static void _vc_shutdown(void);
-static void _vc_vacuum(NameData *VacRelP);
-static VRelList _vc_getrels(Portal p, NameData *VacRelP);
-static void _vc_vacone (VRelList curvrl);
-static void _vc_scanheap (VRelList curvrl, Relation onerel, VPageList Vvpl, VPageList Fvpl);
-static void _vc_rpfheap (VRelList curvrl, Relation onerel, VPageList Vvpl, VPageList Fvpl, int nindices, Relation *Irel);
-static void _vc_vacheap (VRelList curvrl, Relation onerel, VPageList vpl);
-static void _vc_vacpage (Page page, VPageDescr vpd, Relation archrel);
-static void _vc_vaconeind (VPageList vpl, Relation indrel, int nhtups);
-static void _vc_scanoneind (Relation indrel, int nhtups);
-static void _vc_updstats(Oid relid, int npages, int ntuples, bool hasindex);
-static void _vc_setpagelock(Relation rel, BlockNumber blkno);
-static VPageDescr _vc_tidreapped (ItemPointer itemptr, VPageList curvrl);
-static void _vc_reappage (VPageList vpl, VPageDescr vpc);
-static void _vc_vpinsert (VPageList vpl, VPageDescr vpnew);
-static void _vc_free(Portal p, VRelList vrl);
-static void _vc_getindices (Oid relid, int *nindices, Relation **Irel);
-static void _vc_clsindices (int nindices, Relation *Irel);
-static Relation _vc_getarchrel(Relation heaprel);
-static void _vc_archive(Relation archrel, HeapTuple htup);
-static bool _vc_isarchrel(char *rname);
-static void _vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc);
-static char * _vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *));
-static int _vc_cmp_blk (char *left, char *right);
-static int _vc_cmp_offno (char *left, char *right);
-static bool _vc_enough_space (VPageDescr vpd, Size len);
-
+static void vc_init(void);
+static void vc_shutdown(void);
+static void vc_vacuum(NameData *VacRelP);
+static VRelList vc_getrels(Portal p, NameData *VacRelP);
+static void vc_vacone (Oid relid);
+static void vc_scanheap (VRelStats *vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl);
+static void vc_rpfheap (VRelStats *vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl, int nindices, Relation *Irel);
+static void vc_vacheap (VRelStats *vacrelstats, Relation onerel, VPageList vpl);
+static void vc_vacpage (Page page, VPageDescr vpd, Relation archrel);
+static void vc_vaconeind (VPageList vpl, Relation indrel, int nhtups);
+static void vc_scanoneind (Relation indrel, int nhtups);
+static void vc_attrstats(Relation onerel, VacAttrStats *vacattrstats, HeapTuple htup);
+static void vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum *bucket, int16 *bucket_len);
+static void vc_updstats(Oid relid, int npages, int ntups, bool hasindex, VacAttrStats *vacattrstats);
+static void vc_delhilowstats(Oid relid);
+static void vc_setpagelock(Relation rel, BlockNumber blkno);
+static VPageDescr vc_tidreapped (ItemPointer itemptr, VPageList vpl);
+static void vc_reappage (VPageList vpl, VPageDescr vpc);
+static void vc_vpinsert (VPageList vpl, VPageDescr vpnew);
+static void vc_free(Portal p, VRelList vrl);
+static void vc_getindices (Oid relid, int *nindices, Relation **Irel);
+static void vc_clsindices (int nindices, Relation *Irel);
+static Relation vc_getarchrel(Relation heaprel);
+static void vc_archive(Relation archrel, HeapTuple htup);
+static bool vc_isarchrel(char *rname);
+static void vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc);
+static char * vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *));
+static int vc_cmp_blk (char *left, char *right);
+static int vc_cmp_offno (char *left, char *right);
+static bool vc_enough_space (VPageDescr vpd, Size len);
 
 void
 vacuum(char *vacrel, bool verbose)
@@ -93,30 +103,30 @@ vacuum(char *vacrel, bool verbose)
     NameData VacRel;
 
     if (verbose)
-	MESSLEV = NOTICE;
+	MESSAGE_LEVEL = NOTICE;
     else
-	MESSLEV = DEBUG;
+	MESSAGE_LEVEL = DEBUG;
 
     /* vacrel gets de-allocated on transaction commit */
     	
     /* initialize vacuum cleaner */
-    _vc_init();
+    vc_init();
 
     /* vacuum the database */
     if (vacrel)
     {
 	strcpy(VacRel.data,vacrel);
-	_vc_vacuum(&VacRel);
+	vc_vacuum(&VacRel);
     }
     else
-	_vc_vacuum(NULL);
+	vc_vacuum(NULL);
 
     /* clean up */
-    _vc_shutdown();
+    vc_shutdown();
 }
 
 /*
- *  _vc_init(), _vc_shutdown() -- start up and shut down the vacuum cleaner.
+ *  vc_init(), vc_shutdown() -- start up and shut down the vacuum cleaner.
  *
  *	We run exactly one vacuum cleaner at a time.  We use the file system
  *	to guarantee an exclusive lock on vacuuming, since a single vacuum
@@ -128,12 +138,12 @@ vacuum(char *vacrel, bool verbose)
  *	is invoked via a sql command, and so is already executing inside
  *	a transaction.  We need to leave ourselves in a predictable state
  *	on entry and exit to the vacuum cleaner.  We commit the transaction
- *	started in PostgresMain() inside _vc_init(), and start one in
- *	_vc_shutdown() to match the commit waiting for us back in
+ *	started in PostgresMain() inside vc_init(), and start one in
+ *	vc_shutdown() to match the commit waiting for us back in
  *	PostgresMain().
  */
 static void
-_vc_init()
+vc_init()
 {
     int fd;
 
@@ -156,7 +166,7 @@ _vc_init()
 }
 
 static void
-_vc_shutdown()
+vc_shutdown()
 {
     /* on entry, not in a transaction */
     if (unlink("pg_vlock") < 0)
@@ -167,6 +177,7 @@ _vc_shutdown()
 
     /* matches the CommitTransaction in PostgresMain() */
     StartTransactionCommand();
+
 }
 
 void
@@ -179,7 +190,7 @@ vc_abort()
 }
 
 /*
- *  _vc_vacuum() -- vacuum the database.
+ *  vc_vacuum() -- vacuum the database.
  *
  *	This routine builds a list of relations to vacuum, and then calls
  *	code that vacuums them one at a time.  We are careful to vacuum each
@@ -187,7 +198,7 @@ vc_abort()
  *	locks at one time.
  */
 static void
-_vc_vacuum(NameData *VacRelP)
+vc_vacuum(NameData *VacRelP)
 {
     VRelList vrl, cur;
     char *pname;
@@ -206,19 +217,24 @@ _vc_vacuum(NameData *VacRelP)
     pfree(pname);
 
     /* get list of relations */
-    vrl = _vc_getrels(p, VacRelP);
+    vrl = vc_getrels(p, VacRelP);
 
+    if (VacRelP != NULL)
+    	vc_delhilowstats(vrl->vrl_relid);
+    else
+    	vc_delhilowstats(InvalidOid);
+    	
     /* vacuum each heap relation */
     for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)
-	_vc_vacone (cur);
+	vc_vacone (cur->vrl_relid);
 
-    _vc_free(p, vrl);
+    vc_free(p, vrl);
 
     PortalDestroy(&p);
 }
 
 static VRelList
-_vc_getrels(Portal p, NameData *VacRelP)
+vc_getrels(Portal p, NameData *VacRelP)
 {
     Relation pgclass;
     TupleDesc pgcdesc;
@@ -246,7 +262,7 @@ _vc_getrels(Portal p, NameData *VacRelP)
 	ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relkind,
 			       CharacterEqualRegProcedure, CharGetDatum('r'));
     }
-
+ 
     portalmem = PortalGetVariableMemory(p);
     vrl = cur = (VRelList) NULL;
 
@@ -271,7 +287,7 @@ _vc_getrels(Portal p, NameData *VacRelP)
 	rname = (char*)d;
 
 	/* skip archive relations */
-	if (_vc_isarchrel(rname)) {
+	if (vc_isarchrel(rname)) {
 	    ReleaseBuffer(buf);
 	    continue;
 	}
@@ -320,9 +336,6 @@ _vc_getrels(Portal p, NameData *VacRelP)
 	(void) MemoryContextSwitchTo(old);
 
 	cur->vrl_relid = pgctup->t_oid;
-	cur->vrl_attlist = (VAttList) NULL;
-	cur->vrl_npages = cur->vrl_ntups = 0;
-	cur->vrl_hasindex = false;
 	cur->vrl_next = (VRelList) NULL;
 
 	/* wei hates it if you forget to do this */
@@ -332,8 +345,8 @@ _vc_getrels(Portal p, NameData *VacRelP)
     	elog(NOTICE, "Vacuum: table not found" );
 
     
-    heap_close(pgclass);
     heap_endscan(pgcscan);
+    heap_close(pgclass);
 
     CommitTransactionCommand();
 
@@ -341,10 +354,10 @@ _vc_getrels(Portal p, NameData *VacRelP)
 }
 
 /*
- *  _vc_vacone() -- vacuum one heap relation
+ *  vc_vacone() -- vacuum one heap relation
  *
  *	This routine vacuums a single heap, cleans out its indices, and
- *	updates its statistics npages and ntuples statistics.
+ *	updates its statistics npages and ntups statistics.
  *
  *	Doing one heap at a time incurs extra overhead, since we need to
  *	check that the heap exists again just before we vacuum it.  The
@@ -353,11 +366,11 @@ _vc_getrels(Portal p, NameData *VacRelP)
  *	us to lock the entire database during one pass of the vacuum cleaner.
  */
 static void
-_vc_vacone (VRelList curvrl)
+vc_vacone (Oid relid)
 {
     Relation pgclass;
     TupleDesc pgcdesc;
-    HeapTuple pgctup;
+    HeapTuple pgctup, pgttup;
     Buffer pgcbuf;
     HeapScanDesc pgcscan;
     Relation onerel;
@@ -366,14 +379,15 @@ _vc_vacone (VRelList curvrl)
     VPageListData Fvpl;	/* List of pages with space enough for re-using */
     VPageDescr *vpp;
     Relation *Irel;
-    int nindices;
-    int i;
-
+    int32 nindices, i, attr_cnt;
+    AttributeTupleForm *attr;
+    VRelStats *vacrelstats;
+    
     StartTransactionCommand();
 
     ScanKeyEntryInitialize(&pgckey, 0x0, ObjectIdAttributeNumber,
 			   ObjectIdEqualRegProcedure,
-			   ObjectIdGetDatum(curvrl->vrl_relid));
+			   ObjectIdGetDatum(relid));
 
     pgclass = heap_openr(RelationRelationName);
     pgcdesc = RelationGetTupleDescriptor(pgclass);
@@ -392,23 +406,80 @@ _vc_vacone (VRelList curvrl)
     }
 
     /* now open the class and vacuum it */
-    onerel = heap_open(curvrl->vrl_relid);
+    onerel = heap_open(relid);
+
+    attr_cnt = onerel->rd_att->natts;
+    attr = onerel->rd_att->attrs;
+
+    vacrelstats = (VRelStats *) palloc(sizeof(VRelStats));
+    vacrelstats->relid = relid;
+    vacrelstats->npages = vacrelstats->ntups = 0;
+    vacrelstats->hasindex = false;
+    vacrelstats->vacattrstats =
+		(VacAttrStats *) palloc(attr_cnt * sizeof(VacAttrStats));
+    
+    for (i = 0; i < attr_cnt; i++) {
+	Operator func_operator;
+	OperatorTupleForm pgopform;
+	VacAttrStats *stats = &vacrelstats->vacattrstats[i];
+
+	stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE);
+	memmove(stats->attr,attr[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 = (OperatorTupleForm) GETSTRUCT(func_operator);
+	    stats->cmpeq = pgopform->oprcode;
+	}
+	else	stats->cmpeq = InvalidOid;
+        func_operator = oper("<",stats->attr->atttypid,stats->attr->atttypid,true);
+	if (func_operator != NULL) {
+	    pgopform = (OperatorTupleForm) GETSTRUCT(func_operator);
+	    stats->cmplt = pgopform->oprcode;
+	}
+	else	stats->cmplt = InvalidOid;
+        func_operator = oper(">",stats->attr->atttypid,stats->attr->atttypid,true);
+	if (func_operator != NULL) {
+	    pgopform = (OperatorTupleForm) GETSTRUCT(func_operator);
+	    stats->cmpgt = pgopform->oprcode;
+	}
+	else	stats->cmpgt = InvalidOid;
+        func_operator = oper(">",stats->attr->atttypid,stats->attr->atttypid,true);
+	if (func_operator != NULL) {
+	    pgopform = (OperatorTupleForm) GETSTRUCT(func_operator);
+	    stats->cmpgt = pgopform->oprcode;
+	}
+	else	stats->cmpgt = InvalidOid;
+	pgttup = SearchSysCacheTuple(TYPOID,
+                                    ObjectIdGetDatum(stats->attr->atttypid),
+                                    0,0,0);
+        if (HeapTupleIsValid(pgttup))
+            stats->outfunc = ((TypeTupleForm) GETSTRUCT(pgttup))->typoutput;
+	else	stats->outfunc = InvalidOid;
+    }
 
     /* we require the relation to be locked until the indices are cleaned */
     RelationSetLockForWrite(onerel);
 
     /* scan it */
     Vvpl.vpl_npages = Fvpl.vpl_npages = 0;
-    _vc_scanheap(curvrl, onerel, &Vvpl, &Fvpl);
+    vc_scanheap(vacrelstats, onerel, &Vvpl, &Fvpl);
 
     /* Now open indices */
     Irel = (Relation *) NULL;
-    _vc_getindices(curvrl->vrl_relid, &nindices, &Irel);
+    vc_getindices(vacrelstats->relid, &nindices, &Irel);
     
     if ( nindices > 0 )
-	curvrl->vrl_hasindex = true;
+	vacrelstats->hasindex = true;
     else
-	curvrl->vrl_hasindex = false;
+	vacrelstats->hasindex = false;
 
     /* Clean/scan index relation(s) */
     if ( Irel != (Relation*) NULL )
@@ -416,23 +487,23 @@ _vc_vacone (VRelList curvrl)
     	if ( Vvpl.vpl_npages > 0 )
     	{
 	    for (i = 0; i < nindices; i++)
-	    	_vc_vaconeind (&Vvpl, Irel[i], curvrl->vrl_ntups);
+	    	vc_vaconeind (&Vvpl, Irel[i], vacrelstats->ntups);
     	}
     	else	/* just scan indices to update statistic */
     	{
 	    for (i = 0; i < nindices; i++)
-	    	_vc_scanoneind (Irel[i], curvrl->vrl_ntups);
+	    	vc_scanoneind (Irel[i], vacrelstats->ntups);
     	}
     }
 
     if ( Fvpl.vpl_npages > 0 )		/* Try to shrink heap */
-    	_vc_rpfheap (curvrl, onerel, &Vvpl, &Fvpl, nindices, Irel);
+    	vc_rpfheap (vacrelstats, onerel, &Vvpl, &Fvpl, nindices, Irel);
     else 
     {
     	if ( Irel != (Relation*) NULL )
-	    _vc_clsindices (nindices, Irel);
+	    vc_clsindices (nindices, Irel);
     	if ( Vvpl.vpl_npages > 0 )	/* Clean pages from Vvpl list */
-	    _vc_vacheap (curvrl, onerel, &Vvpl);
+	    vc_vacheap (vacrelstats, onerel, &Vvpl);
     }
 
     /* ok - free Vvpl list of reapped pages */
@@ -452,14 +523,18 @@ _vc_vacone (VRelList curvrl)
     heap_close(pgclass);
 
     /* update statistics in pg_class */
-    _vc_updstats(curvrl->vrl_relid, curvrl->vrl_npages, curvrl->vrl_ntups,
-		 curvrl->vrl_hasindex);
+    vc_updstats(vacrelstats->relid, vacrelstats->npages, vacrelstats->ntups,
+			 vacrelstats->hasindex, vacrelstats->vacattrstats);
+
+    /* next command frees everything anyway */
+    if (vacrelstats->vacattrstats != NULL)
+	pfree(vacrelstats);
 
     CommitTransactionCommand();
 }
 
 /*
- *  _vc_scanheap() -- scan an open heap relation
+ *  vc_scanheap() -- scan an open heap relation
  *
  *	This routine sets commit times, constructs Vvpl list of 
  *	empty/uninitialized pages and pages with dead tuples and
@@ -468,7 +543,7 @@ _vc_vacone (VRelList curvrl)
  *	on the number of live tuples in a heap.
  */
 static void
-_vc_scanheap (VRelList curvrl, Relation onerel, 
+vc_scanheap (VRelStats *vacrelstats, Relation onerel, 
 			VPageList Vvpl, VPageList Fvpl)
 {
     int nblocks, blkno;
@@ -487,14 +562,14 @@ _vc_scanheap (VRelList curvrl, Relation onerel,
     Size frsize, frsusf;
     Size min_tlen = MAXTUPLEN;
     Size max_tlen = 0;
-    int i;
+    int32 i/*, attr_cnt*/;
     struct rusage ru0, ru1;
 
     getrusage(RUSAGE_SELF, &ru0);
 
     nvac = ntups = nunused = ncrash = nempg = nnepg = nchpg = nemend = 0;
     frsize = frsusf = 0;
-    
+
     relname = (RelationGetRelationName(onerel))->data;
 
     nblocks = RelationGetNumberOfBlocks(onerel);
@@ -532,7 +607,7 @@ _vc_scanheap (VRelList curvrl, Relation onerel,
 	    frsize += (vpc->vpd_free - sizeof (ItemIdData));
 	    nnepg++;
 	    nemend++;
-	    _vc_reappage (Vvpl, vpc);
+	    vc_reappage (Vvpl, vpc);
 	    WriteBuffer(buf);
 	    continue;
 	}
@@ -542,7 +617,7 @@ _vc_scanheap (VRelList curvrl, Relation onerel,
 	    frsize += (vpc->vpd_free - sizeof (ItemIdData));
 	    nempg++;
 	    nemend++;
-	    _vc_reappage (Vvpl, vpc);
+	    vc_reappage (Vvpl, vpc);
 	    ReleaseBuffer(buf);
 	    continue;
 	}
@@ -585,7 +660,7 @@ _vc_scanheap (VRelList curvrl, Relation onerel,
 		    tupgone = true;
 		}
 		else {
-		    elog (MESSLEV, "Rel %.*s: InsertTransactionInProgress %u for TID %u/%u",
+		    elog (MESSAGE_LEVEL, "Rel %.*s: InsertTransactionInProgress %u for TID %u/%u",
 			NAMEDATALEN, relname, htup->t_xmin, blkno, offnum);
 		}
 	    }
@@ -677,6 +752,7 @@ DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.",
 		    min_tlen = htup->t_len;
 		if ( htup->t_len > max_tlen )
 		    max_tlen = htup->t_len;
+		vc_attrstats(onerel, vacrelstats->vacattrstats, htup);
 	    }
 	}
 
@@ -692,7 +768,7 @@ DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.",
 	    PageRepairFragmentation(tempPage);
 	    vpc->vpd_free = ((PageHeader)tempPage)->pd_upper - ((PageHeader)tempPage)->pd_lower;
 	    frsize += vpc->vpd_free;
-	    _vc_reappage (Vvpl, vpc);
+	    vc_reappage (Vvpl, vpc);
 	    pfree (tempPage);
 	    tempPage = (Page) NULL;
 	}
@@ -700,7 +776,7 @@ DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.",
 	{ /* there are only ~LP_USED line pointers */
 	    vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower;
 	    frsize += vpc->vpd_free;
-	    _vc_reappage (Vvpl, vpc);
+	    vc_reappage (Vvpl, vpc);
 	}
 	if ( dobufrel )
 	    ReleaseBuffer(buf);
@@ -713,12 +789,13 @@ DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.",
     pfree (vpc);
 
     /* save stats in the rel list for use later */
-    curvrl->vrl_ntups = ntups;
-    curvrl->vrl_npages = nblocks;
+    vacrelstats->ntups = ntups;
+    vacrelstats->npages = nblocks;
+/*    vacrelstats->natts = attr_cnt;*/
     if ( ntups == 0 )
     	min_tlen = max_tlen = 0;
-    curvrl->vrl_min_tlen = min_tlen;
-    curvrl->vrl_max_tlen = max_tlen;
+    vacrelstats->min_tlen = min_tlen;
+    vacrelstats->max_tlen = max_tlen;
     
     Vvpl->vpl_nemend = nemend;
     Fvpl->vpl_nemend = nemend;
@@ -738,9 +815,9 @@ DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.",
 	for (i = 0; i < nusf; i++)
     	{
 	    vp = Vvpl->vpl_pgdesc[i];
-	    if ( _vc_enough_space (vp, min_tlen) )
+	    if ( vc_enough_space (vp, min_tlen) )
 	    {
-		_vc_vpinsert (Fvpl, vp);
+		vc_vpinsert (Fvpl, vp);
 		frsusf += vp->vpd_free;
 	    }
 	}
@@ -748,7 +825,7 @@ DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.",
 
     getrusage(RUSAGE_SELF, &ru1);
     
-    elog (MESSLEV, "Rel %.*s: Pages %u: Changed %u, Reapped %u, Empty %u, New %u; \
+    elog (MESSAGE_LEVEL, "Rel %.*s: Pages %u: Changed %u, Reapped %u, Empty %u, New %u; \
 Tup %u: Vac %u, Crash %u, UnUsed %u, MinLen %u, MaxLen %u; Re-using: Free/Avail. Space %u/%u; EndEmpty/Avail. Pages %u/%u. Elapsed %u/%u sec.",
 	NAMEDATALEN, relname, 
 	nblocks, nchpg, Vvpl->vpl_npages, nempg, nnepg,
@@ -757,11 +834,11 @@ Tup %u: Vac %u, Crash %u, UnUsed %u, MinLen %u, MaxLen %u; Re-using: Free/Avail.
 	ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, 
 	ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
 
-} /* _vc_scanheap */
+} /* vc_scanheap */
 
 
 /*
- *  _vc_rpfheap() -- try to repaire relation' fragmentation
+ *  vc_rpfheap() -- try to repaire relation' fragmentation
  *
  *	This routine marks dead tuples as unused and tries re-use dead space
  *	by moving tuples (and inserting indices if needed). It constructs 
@@ -771,7 +848,7 @@ Tup %u: Vac %u, Crash %u, UnUsed %u, MinLen %u, MaxLen %u; Re-using: Free/Avail.
  *	if some end-blocks are gone away.
  */
 static void
-_vc_rpfheap (VRelList curvrl, Relation onerel, 
+vc_rpfheap (VRelStats *vacrelstats, Relation onerel, 
 		VPageList Vvpl, VPageList Fvpl, int nindices, Relation *Irel)
 {
     TransactionId myXID;
@@ -806,7 +883,7 @@ _vc_rpfheap (VRelList curvrl, Relation onerel,
 	
     if ( Irel != (Relation*) NULL )	/* preparation for index' inserts */
     {
-	_vc_mkindesc (onerel, nindices, Irel, &Idesc);
+	vc_mkindesc (onerel, nindices, Irel, &Idesc);
 	tupdesc = RelationGetTupleDescriptor(onerel);
 	idatum = (Datum *) palloc(INDEX_MAX_KEYS * sizeof (*idatum));
 	inulls = (char *) palloc(INDEX_MAX_KEYS * sizeof (*inulls));
@@ -815,7 +892,7 @@ _vc_rpfheap (VRelList curvrl, Relation onerel,
     /* if the relation has an archive, open it */
     if (onerel->rd_rel->relarch != 'n')
     {
-	archrel = _vc_getarchrel(onerel);
+	archrel = vc_getarchrel(onerel);
 	/* Archive tuples from "empty" end-pages */
 	for ( vpp = Vvpl->vpl_pgdesc + Vvpl->vpl_npages - 1, 
 				i = Vvpl->vpl_nemend; i > 0; i--, vpp-- )
@@ -825,7 +902,7 @@ _vc_rpfheap (VRelList curvrl, Relation onerel,
 	    	buf = ReadBuffer(onerel, (*vpp)->vpd_blkno);
 	    	page = BufferGetPage(buf);
 	    	Assert ( !PageIsEmpty(page) );
-		_vc_vacpage (page, *vpp, archrel);
+		vc_vacpage (page, *vpp, archrel);
 		WriteBuffer (buf);
 	    }
 	}
@@ -848,7 +925,7 @@ _vc_rpfheap (VRelList curvrl, Relation onerel,
     vpc = (VPageDescr) palloc (sizeof(VPageDescrData) + MaxOffsetNumber*sizeof(OffsetNumber));
     vpc->vpd_nusd = vpc->vpd_noff = 0;
 	
-    nblocks = curvrl->vrl_npages;
+    nblocks = vacrelstats->npages;
     for (blkno = nblocks - Vvpl->vpl_nemend - 1; ; blkno--)
     {
 	/* if it's reapped page and it was used by me - quit */
@@ -868,7 +945,7 @@ _vc_rpfheap (VRelList curvrl, Relation onerel,
 	    if ( Vvplast->vpd_noff > 0 )	/* there are dead tuples */
 	    {					/* on this page - clean */
 		Assert ( ! isempty );
-		_vc_vacpage (page, Vvplast, archrel);
+		vc_vacpage (page, Vvplast, archrel);
 		dowrite = true;
 	    }
 	    else
@@ -913,7 +990,7 @@ _vc_rpfheap (VRelList curvrl, Relation onerel,
 		
 	    /* try to find new page for this tuple */
 	    if ( ToBuf == InvalidBuffer ||
-		! _vc_enough_space (ToVpd, tlen) )
+		! vc_enough_space (ToVpd, tlen) )
 	    {
 		if ( ToBuf != InvalidBuffer )
 		{
@@ -923,7 +1000,7 @@ _vc_rpfheap (VRelList curvrl, Relation onerel,
  		     * If no one tuple can't be added to this page -
  		     * remove page from Fvpl. - vadim 11/27/96
  		     */ 
- 		    if ( !_vc_enough_space (ToVpd, curvrl->vrl_min_tlen) )
+ 		    if ( !vc_enough_space (ToVpd, vacrelstats->min_tlen) )
  		    {
  		    	if ( ToVpd != Fvplast )
  		    	{
@@ -943,7 +1020,7 @@ _vc_rpfheap (VRelList curvrl, Relation onerel,
  		}
 		for (i=0; i < Fnpages; i++)
 		{
-		    if ( _vc_enough_space (Fvpl->vpl_pgdesc[i], tlen) )
+		    if ( vc_enough_space (Fvpl->vpl_pgdesc[i], tlen) )
 			break;
 		}
 		if ( i == Fnpages )
@@ -954,7 +1031,7 @@ _vc_rpfheap (VRelList curvrl, Relation onerel,
 		ToPage = BufferGetPage(ToBuf);
 		/* if this page was not used before - clean it */
 		if ( ! PageIsEmpty(ToPage) && ToVpd->vpd_nusd == 0 )
-		    _vc_vacpage (ToPage, ToVpd, archrel);
+		    vc_vacpage (ToPage, ToVpd, archrel);
 	    }
 		
 	    /* copy tuple */
@@ -1022,7 +1099,7 @@ failed to add item with len = %u to page %u (free space %u, nusd %u, noff %u)",
 
 	if ( vpc->vpd_noff > 0 )		/* some tuples were moved */
 	{
-	    _vc_reappage (&Nvpl, vpc);
+	    vc_reappage (&Nvpl, vpc);
 	    WriteBuffer(buf);
 	}
 	else if ( dowrite )
@@ -1071,7 +1148,7 @@ failed to add item with len = %u to page %u (free space %u, nusd %u, noff %u)",
 	{
 	    /* noff == 0 in empty pages only - such pages should be re-used */
 	    Assert ( (*vpp)->vpd_noff > 0 );
-	    _vc_vacpage (page, *vpp, archrel);
+	    vc_vacpage (page, *vpp, archrel);
 	}
 	else				/* this page was used */
 	{
@@ -1100,7 +1177,7 @@ failed to add item with len = %u to page %u (free space %u, nusd %u, noff %u)",
 
     getrusage(RUSAGE_SELF, &ru1);
     
-    elog (MESSLEV, "Rel %.*s: Pages: %u --> %u; Tuple(s) moved: %u. \
+    elog (MESSAGE_LEVEL, "Rel %.*s: Pages: %u --> %u; Tuple(s) moved: %u. \
 Elapsed %u/%u sec.",
 		NAMEDATALEN, (RelationGetRelationName(onerel))->data, 
 		nblocks, blkno, nmoved,
@@ -1122,7 +1199,7 @@ Elapsed %u/%u sec.",
 		vpsave = *vpleft; *vpleft = *vpright; *vpright = vpsave;
 	    }
 	    for (i = 0; i < nindices; i++)
-		_vc_vaconeind (&Nvpl, Irel[i], curvrl->vrl_ntups);
+		vc_vaconeind (&Nvpl, Irel[i], vacrelstats->ntups);
 	}
 
 	/* 
@@ -1165,7 +1242,7 @@ Elapsed %u/%u sec.",
     {
     	blkno = smgrtruncate (onerel->rd_rel->relsmgr, onerel, blkno);
 	Assert ( blkno >= 0 );
-	curvrl->vrl_npages = blkno;	/* set new number of blocks */
+	vacrelstats->npages = blkno;	/* set new number of blocks */
     }
 
     if ( archrel != (Relation) NULL )
@@ -1176,21 +1253,21 @@ Elapsed %u/%u sec.",
 	pfree (Idesc);
 	pfree (idatum);
 	pfree (inulls);
-	_vc_clsindices (nindices, Irel);
+	vc_clsindices (nindices, Irel);
     }
 
     pfree (vpc);
 
-} /* _vc_rpfheap */
+} /* vc_rpfheap */
 
 /*
- *  _vc_vacheap() -- free dead tuples
+ *  vc_vacheap() -- free dead tuples
  *
  *	This routine marks dead tuples as unused and truncates relation
  *	if there are "empty" end-blocks.
  */
 static void
-_vc_vacheap (VRelList curvrl, Relation onerel, VPageList Vvpl)
+vc_vacheap (VRelStats *vacrelstats, Relation onerel, VPageList Vvpl)
 {
     Buffer buf;
     Page page;
@@ -1202,7 +1279,7 @@ _vc_vacheap (VRelList curvrl, Relation onerel, VPageList Vvpl)
     nblocks = Vvpl->vpl_npages;
     /* if the relation has an archive, open it */
     if (onerel->rd_rel->relarch != 'n')
-	archrel = _vc_getarchrel(onerel);
+	archrel = vc_getarchrel(onerel);
     else
     {
 	archrel = (Relation) NULL;
@@ -1215,7 +1292,7 @@ _vc_vacheap (VRelList curvrl, Relation onerel, VPageList Vvpl)
 	{
 	    buf = ReadBuffer(onerel, (*vpp)->vpd_blkno);
 	    page = BufferGetPage (buf);
-	    _vc_vacpage (page, *vpp, archrel);
+	    vc_vacpage (page, *vpp, archrel);
 	    WriteBuffer (buf);
 	}
     }
@@ -1223,11 +1300,11 @@ _vc_vacheap (VRelList curvrl, Relation onerel, VPageList Vvpl)
     /* truncate relation if there are some empty end-pages */
     if ( Vvpl->vpl_nemend > 0 )
     {
-	Assert ( curvrl->vrl_npages >= Vvpl->vpl_nemend );
-	nblocks = curvrl->vrl_npages - Vvpl->vpl_nemend;
-	elog (MESSLEV, "Rel %.*s: Pages: %u --> %u.",
+	Assert ( vacrelstats->npages >= Vvpl->vpl_nemend );
+	nblocks = vacrelstats->npages - Vvpl->vpl_nemend;
+	elog (MESSAGE_LEVEL, "Rel %.*s: Pages: %u --> %u.",
 		NAMEDATALEN, (RelationGetRelationName(onerel))->data, 
-		curvrl->vrl_npages, nblocks);
+		vacrelstats->npages, nblocks);
 
 	/* 
 	 * we have to flush "empty" end-pages (if changed, but who knows it)
@@ -1237,20 +1314,20 @@ _vc_vacheap (VRelList curvrl, Relation onerel, VPageList Vvpl)
 
     	nblocks = smgrtruncate (onerel->rd_rel->relsmgr, onerel, nblocks);
 	Assert ( nblocks >= 0 );
-	curvrl->vrl_npages = nblocks;	/* set new number of blocks */
+	vacrelstats->npages = nblocks;	/* set new number of blocks */
     }
 
     if ( archrel != (Relation) NULL )
 	heap_close(archrel);
 
-} /* _vc_vacheap */
+} /* vc_vacheap */
 
 /*
- *  _vc_vacpage() -- free (and archive if needed) dead tuples on a page
+ *  vc_vacpage() -- free (and archive if needed) dead tuples on a page
  *		     and repaire its fragmentation.
  */
 static void
-_vc_vacpage (Page page, VPageDescr vpd, Relation archrel)
+vc_vacpage (Page page, VPageDescr vpd, Relation archrel)
 {
     ItemId itemid;
     HeapTuple htup;
@@ -1263,20 +1340,20 @@ _vc_vacpage (Page page, VPageDescr vpd, Relation archrel)
 	if ( archrel != (Relation) NULL && ItemIdIsUsed(itemid) )
 	{
 	    htup = (HeapTuple) PageGetItem (page, itemid);
-	    _vc_archive (archrel, htup);
+	    vc_archive (archrel, htup);
 	}
 	itemid->lp_flags &= ~LP_USED;
     }
     PageRepairFragmentation(page);
 
-} /* _vc_vacpage */
+} /* vc_vacpage */
 
 /*
  *  _vc_scanoneind() -- scan one index relation to update statistic.
  *
  */
 static void
-_vc_scanoneind (Relation indrel, int nhtups)
+vc_scanoneind (Relation indrel, int nhtups)
 {
     RetrieveIndexResult res;
     IndexScanDesc iscan;
@@ -1301,11 +1378,11 @@ _vc_scanoneind (Relation indrel, int nhtups)
 
     /* now update statistics in pg_class */
     nipages = RelationGetNumberOfBlocks(indrel);
-    _vc_updstats(indrel->rd_id, nipages, nitups, false);
+    vc_updstats(indrel->rd_id, nipages, nitups, false, NULL);
 
     getrusage(RUSAGE_SELF, &ru1);
 
-    elog (MESSLEV, "Ind %.*s: Pages %u; Tuples %u. Elapsed %u/%u sec.",
+    elog (MESSAGE_LEVEL, "Ind %.*s: Pages %u; Tuples %u. Elapsed %u/%u sec.",
 	NAMEDATALEN, indrel->rd_rel->relname.data, nipages, nitups, 
 	ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, 
 	ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
@@ -1314,10 +1391,10 @@ _vc_scanoneind (Relation indrel, int nhtups)
     	elog (NOTICE, "NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)",
     		nitups, nhtups);
 
-} /* _vc_scanoneind */
+} /* vc_scanoneind */
 
 /*
- *  _vc_vaconeind() -- vacuum one index relation.
+ *  vc_vaconeind() -- vacuum one index relation.
  *
  *	Vpl is the VPageList of the heap we're currently vacuuming.
  *	It's locked. Indrel is an index relation on the vacuumed heap. 
@@ -1329,7 +1406,7 @@ _vc_scanoneind (Relation indrel, int nhtups)
  *	pg_class.
  */
 static void
-_vc_vaconeind(VPageList vpl, Relation indrel, int nhtups)
+vc_vaconeind(VPageList vpl, Relation indrel, int nhtups)
 {
     RetrieveIndexResult res;
     IndexScanDesc iscan;
@@ -1351,7 +1428,7 @@ _vc_vaconeind(VPageList vpl, Relation indrel, int nhtups)
 	   != (RetrieveIndexResult) NULL) {
 	heapptr = &res->heap_iptr;
 
-	if ( (vp = _vc_tidreapped (heapptr, vpl)) != (VPageDescr) NULL)
+	if ( (vp = vc_tidreapped (heapptr, vpl)) != (VPageDescr) NULL)
 	{
 #if 0
 	    elog(DEBUG, "<%x,%x> -> <%x,%x>",
@@ -1380,11 +1457,11 @@ _vc_vaconeind(VPageList vpl, Relation indrel, int nhtups)
 
     /* now update statistics in pg_class */
     nipages = RelationGetNumberOfBlocks(indrel);
-    _vc_updstats(indrel->rd_id, nipages, nitups, false);
+    vc_updstats(indrel->rd_id, nipages, nitups, false, NULL);
 
     getrusage(RUSAGE_SELF, &ru1);
 
-    elog (MESSLEV, "Ind %.*s: Pages %u; Tuples %u: Deleted %u. Elapsed %u/%u sec.",
+    elog (MESSAGE_LEVEL, "Ind %.*s: Pages %u; Tuples %u: Deleted %u. Elapsed %u/%u sec.",
 	NAMEDATALEN, indrel->rd_rel->relname.data, nipages, nitups, nvac,
 	ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, 
 	ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
@@ -1393,15 +1470,15 @@ _vc_vaconeind(VPageList vpl, Relation indrel, int nhtups)
     	elog (NOTICE, "NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)",
     		nitups, nhtups);
 
-} /* _vc_vaconeind */
+} /* vc_vaconeind */
 
 /*
- *  _vc_tidreapped() -- is a particular tid reapped?
+ *  vc_tidreapped() -- is a particular tid reapped?
  *
  *	vpl->VPageDescr_array is sorted in right order.
  */
 static VPageDescr
-_vc_tidreapped(ItemPointer itemptr, VPageList vpl)
+vc_tidreapped(ItemPointer itemptr, VPageList vpl)
 {
     OffsetNumber ioffno;
     OffsetNumber *voff;
@@ -1412,9 +1489,9 @@ _vc_tidreapped(ItemPointer itemptr, VPageList vpl)
     ioffno = ItemPointerGetOffsetNumber(itemptr);
 	
     vp = &vpd;
-    vpp = (VPageDescr*) _vc_find_eq ((char*)(vpl->vpl_pgdesc), 
+    vpp = (VPageDescr*) vc_find_eq ((char*)(vpl->vpl_pgdesc), 
 		vpl->vpl_npages, sizeof (VPageDescr), (char*)&vp, 
-		_vc_cmp_blk);
+		vc_cmp_blk);
 
     if ( vpp == (VPageDescr*) NULL )
 	return ((VPageDescr)NULL);
@@ -1426,74 +1503,333 @@ _vc_tidreapped(ItemPointer itemptr, VPageList vpl)
 	return (vp);
     }
     
-    voff = (OffsetNumber*) _vc_find_eq ((char*)(vp->vpd_voff), 
+    voff = (OffsetNumber*) vc_find_eq ((char*)(vp->vpd_voff), 
 		vp->vpd_noff, sizeof (OffsetNumber), (char*)&ioffno, 
-		_vc_cmp_offno);
+		vc_cmp_offno);
 
     if ( voff == (OffsetNumber*) NULL )
 	return ((VPageDescr)NULL);
 
     return (vp);
 
-} /* _vc_tidreapped */
+} /* vc_tidreapped */
 
 /*
- *  _vc_updstats() -- update pg_class statistics for one relation
+ *  vc_attrstats() -- compute column statistics used by the optimzer
+ *
+ *  We compute the column min, max, null and non-null counts.
+ *  Plus we attempt to find the count of the value that occurs most
+ *  frequently in each column
+ *  These figures are used to compute the selectivity of the column
+ *
+ *  We use a three-bucked cache to get the most frequent item
+ *  The 'guess' buckets count hits.  A cache miss causes guess1
+ *  to get the most hit 'guess' item in the most recent cycle, and
+ *  the new item goes into guess2.  Whenever the total count of hits
+ *  of a 'guess' entry is larger than 'best', 'guess' becomes 'best'.
+ *
+ *  This method works perfectly for columns with unique values, and columns
+ *  with only two unique values, plus nulls.
+ *
+ *  It becomes less perfect as the number of unique values increases and
+ *  their distribution in the table becomes more random.
+ *
+ */
+static void
+vc_attrstats(Relation onerel, VacAttrStats *vacattrstats, HeapTuple htup)
+{
+    int i, attr_cnt;
+    AttributeTupleForm *attr;
+    TupleDesc tupDesc;
+    Datum value;
+    bool isnull;
+
+    attr_cnt = onerel->rd_att->natts;
+    attr = onerel->rd_att->attrs;
+    tupDesc = onerel->rd_att;
+
+    for (i = 0; i < attr_cnt; i++) {
+	VacAttrStats *stats = &vacattrstats[i];
+	bool value_hit = true;
+
+	value = (Datum) heap_getattr(htup, InvalidBuffer, i+1, tupDesc, &isnull);
+
+	if (!VacAttrStatsEqValid(stats))
+		continue;
+
+	if (isnull)
+	    stats->null_cnt++;
+	else {
+	    stats->nonnull_cnt++;
+	    if (stats->initialized == false) {
+		vc_bucketcpy(stats->attr, value, &stats->best, &stats->best_len);
+		/* best_cnt gets incremented later */
+		vc_bucketcpy(stats->attr, value, &stats->guess1, &stats->guess1_len);
+		stats->guess1_cnt = stats->guess1_hits = 1;
+		vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len);
+		stats->guess2_hits = 1;
+		if (VacAttrStatsLtGtValid(stats)) {
+		    vc_bucketcpy(stats->attr, value, &stats->max , &stats->max_len);
+		    vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len);
+		}
+		stats->initialized = true;
+	    }
+	    if (VacAttrStatsLtGtValid(stats) && fmgr(stats->cmplt,value,stats->min)) {
+		vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len);
+		stats->min_cnt = 0;
+	    }
+	    if (VacAttrStatsLtGtValid(stats) && fmgr(stats->cmpgt,value,stats->max)) {
+		vc_bucketcpy(stats->attr, value, &stats->max, &stats->max_len);
+		stats->max_cnt = 0;
+	    }
+	    if (VacAttrStatsLtGtValid(stats) && fmgr(stats->cmpeq,value,stats->min))
+		stats->min_cnt++;
+	    else if (VacAttrStatsLtGtValid(stats) && fmgr(stats->cmpeq,value,stats->max))
+		stats->	max_cnt++;
+	    if (fmgr(stats->cmpeq,value,stats->best))
+		stats->	best_cnt++;
+	    else if (fmgr(stats->cmpeq,value,stats->guess1)) {
+		stats->guess1_cnt++;
+		stats->	guess1_hits++;
+	    }
+	    else if (fmgr(stats->cmpeq,value,stats->guess2))
+		stats->guess2_hits++;
+	    else value_hit = false;
+
+	    if (stats->guess2_hits > stats->guess1_hits) {
+		swapDatum(stats->guess1,stats->guess2);
+		swapInt(stats->guess1_len,stats->guess2_len);
+		stats->guess1_cnt = stats->guess2_hits;
+		swapLong(stats->guess1_hits, stats->guess2_hits);
+	    }
+	    if (stats->guess1_cnt > stats->best_cnt) {
+		swapDatum(stats->best,stats->guess1);
+		swapLong(stats->best_cnt,stats->guess1_cnt);
+		stats->guess1_hits = 1;
+		stats->	guess2_hits = 1;
+	    }
+	    if (!value_hit) {
+		vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len);
+		stats->guess1_hits = 1;
+		stats->	guess2_hits = 1;
+	    }
+	}
+    }
+    return;
+}
+
+/*
+ *  vc_bucketcpy() -- update pg_class statistics for one relation
+ *
+ */
+static void
+vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum *bucket, int16 *bucket_len)
+{
+    if (attr->attbyval && attr->attlen != -1)
+    	*bucket = value;
+    else {
+    	int len = (attr->attlen != -1 ? attr->attlen : VARSIZE(value));
+
+ 	if (len > *bucket_len)
+    	{
+	    if (*bucket_len != 0)
+	    	pfree(DatumGetPointer(*bucket));
+	    *bucket = PointerGetDatum(palloc(len));
+	    *bucket_len = len;
+	}
+    	memmove(DatumGetPointer(*bucket), DatumGetPointer(value), len);
+    }
+}
+
+/*
+ *  vc_updstats() -- update pg_class statistics for one relation
  *
  *	This routine works for both index and heap relation entries in
  *	pg_class.  We violate no-overwrite semantics here by storing new
- *	values for ntuples, npages, and hasindex directly in the pg_class
+ *	values for ntups, npages, and hasindex directly in the pg_class
  *	tuple that's already on the page.  The reason for this is that if
  *	we updated these tuples in the usual way, then every tuple in pg_class
  *	would be replaced every day.  This would make planning and executing
  *	historical queries very expensive.
  */
 static void
-_vc_updstats(Oid relid, int npages, int ntuples, bool hasindex)
+vc_updstats(Oid relid, int npages, int ntups, bool hasindex, VacAttrStats *vacattrstats)
 {
-    Relation rd;
-    HeapScanDesc sdesc;
-    HeapTuple tup;
-    Buffer buf;
+    Relation rd, ad, sd;
+    HeapScanDesc rsdesc, asdesc;
+    TupleDesc sdesc;
+    HeapTuple rtup, atup, stup;
+    Buffer rbuf, abuf;
     Form_pg_class pgcform;
-    ScanKeyData skey;
+    ScanKeyData rskey, askey;
+    AttributeTupleForm attp;
 
     /*
      * update number of tuples and number of pages in pg_class
      */
-    ScanKeyEntryInitialize(&skey, 0x0, ObjectIdAttributeNumber,
+    ScanKeyEntryInitialize(&rskey, 0x0, ObjectIdAttributeNumber,
 			   ObjectIdEqualRegProcedure,
 			   ObjectIdGetDatum(relid));
 
     rd = heap_openr(RelationRelationName);
-    sdesc = heap_beginscan(rd, false, NowTimeQual, 1, &skey);
+    rsdesc = heap_beginscan(rd, false, NowTimeQual, 1, &rskey);
 
-    if (!HeapTupleIsValid(tup = heap_getnext(sdesc, 0, &buf)))
+    if (!HeapTupleIsValid(rtup = heap_getnext(rsdesc, 0, &rbuf)))
 	elog(WARN, "pg_class entry for relid %d vanished during vacuuming",
 		   relid);
 
     /* overwrite the existing statistics in the tuple */
-    _vc_setpagelock(rd, BufferGetBlockNumber(buf));
-    pgcform = (Form_pg_class) GETSTRUCT(tup);
-    pgcform->reltuples = ntuples;
+    vc_setpagelock(rd, BufferGetBlockNumber(rbuf));
+    pgcform = (Form_pg_class) GETSTRUCT(rtup);
+    pgcform->reltuples = ntups;
     pgcform->relpages = npages;
     pgcform->relhasindex = hasindex;
+
+    if (vacattrstats != NULL)
+    {
+    	ad = heap_openr(AttributeRelationName);
+	sd = heap_openr(StatisticRelationName);
+        ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid,
+		   F_INT4EQ, relid);
+
+	asdesc = heap_beginscan(ad, false, NowTimeQual, 1, &askey);
+
+	while (HeapTupleIsValid(atup = heap_getnext(asdesc, 0, &abuf))) {
+	    int slot, i;
+	    double selratio;  /* average ratio of rows selected for a random constant */
+	    VacAttrStats *stats;
+	    Datum values[ Natts_pg_statistic ];
+    	    char nulls[ Natts_pg_statistic ];
+
+	    attp = (AttributeTupleForm) GETSTRUCT(atup);
+	    slot = attp->attnum - 1;
+	    if (slot < 0) /* skip system attributes for now,
+			   they are unique anyway */
+		continue;            
+	    stats = &vacattrstats[slot];
+
+	    /* overwrite the existing statistics in the tuple */
+	    if (VacAttrStatsEqValid(stats)) {
+
+		vc_setpagelock(ad, BufferGetBlockNumber(abuf));
+
+		if (stats->nonnull_cnt + stats->null_cnt == 0 ||
+		    (stats->null_cnt <= 1 && stats->best_cnt == 1))
+		    selratio = 0;
+		else if (VacAttrStatsLtGtValid(stats) && stats->min_cnt + stats->max_cnt == stats->nonnull_cnt)
+		{
+		    double min_cnt_d = stats->min_cnt, 
+			   max_cnt_d = stats->max_cnt, 
+			   null_cnt_d = stats->null_cnt,
+			   nonnullcnt_d = stats->nonnull_cnt; /* prevent overflow */
+		    selratio = (min_cnt_d*min_cnt_d+max_cnt_d*max_cnt_d+null_cnt_d*null_cnt_d)/
+				(nonnullcnt_d+null_cnt_d)/(nonnullcnt_d+null_cnt_d);
+		}
+		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;
+		}
+		if (selratio > 1.0)
+		    selratio = 1.0;
+	    	attp->attnvals = (selratio ? (selratio * ATTNVALS_SCALE) : 0);
+		WriteNoReleaseBuffer(abuf);
+
+		/* DO PG_STATISTIC INSERTS */
+
+		/* doing system relations, especially pg_statistic is a problem */
+		if (VacAttrStatsLtGtValid(stats) && stats->initialized /* &&
+			!IsSystemRelationName(pgcform->relname.data)*/) {
+		    func_ptr out_function;
+		    char *out_string;
+		    int dummy;
+		
+		    for (i = 0; i < Natts_pg_statistic; ++i) nulls[i] = ' ';
+
+		    /* ----------------
+	    	     *	initialize values[]
+		     * ----------------
+		     */
+		    i = 0;
+		    values[i++] = (Datum) relid;	/* 1 */
+		    values[i++] = (Datum) attp->attnum;	/* 2 */
+		    values[i++] = (Datum) InvalidOid; 	/* 3 */
+		    fmgr_info(stats->outfunc, &out_function, &dummy);
+		    out_string = (*out_function)(stats->min, stats->attr->atttypid);
+		    values[i++] = (Datum) fmgr(TextInRegProcedure,out_string);
+		    pfree(out_string);
+		    out_string = (char *)(*out_function)(stats->max, stats->attr->atttypid);
+		    values[i++] = (Datum) fmgr(TextInRegProcedure,out_string);
+		    pfree(out_string);
+
+		    sdesc = sd->rd_att;
+	
+		    stup = heap_formtuple(sdesc, values, nulls);
+
+		    /* ----------------
+		     *	insert the tuple in the relation and get the tuple's oid.
+		     * ----------------
+		     */
+		    heap_insert(sd, stup);
+		    pfree(DatumGetPointer(values[3]));
+		    pfree(DatumGetPointer(values[4]));
+		    pfree(stup);
+		}
+	    }
+	}
+	heap_endscan(asdesc);
+	heap_close(ad);
+	heap_close(sd);
+    }
  
     /* XXX -- after write, should invalidate relcache in other backends */
-    WriteNoReleaseBuffer(buf);	/* heap_endscan release scan' buffers ? */
+    WriteNoReleaseBuffer(rbuf);	/* heap_endscan release scan' buffers ? */
 
     /* invalidating system relations confuses the function cache
        of pg_operator and pg_opclass */
     if ( !IsSystemRelationName(pgcform->relname.data))
-    	RelationInvalidateHeapTuple(rd, tup);
+    	RelationInvalidateHeapTuple(rd, rtup);
 
     /* that's all, folks */
-    heap_endscan(sdesc);
+    heap_endscan(rsdesc);
     heap_close(rd);
 
 }
 
-static void _vc_setpagelock(Relation rel, BlockNumber blkno)
+/*
+ *  vc_delhilowstats() -- delete pg_statistics rows
+ *
+ */
+static void
+vc_delhilowstats(Oid relid)
+{
+    Relation pgstatistic;
+    HeapScanDesc pgsscan;
+    HeapTuple pgstup;
+    ScanKeyData  pgskey;
+
+    pgstatistic = heap_openr(StatisticRelationName);
+
+    if (relid != InvalidOid ) {
+	ScanKeyEntryInitialize(&pgskey, 0x0, Anum_pg_statistic_starelid,
+				   ObjectIdEqualRegProcedure,
+				   ObjectIdGetDatum(relid));
+    	pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 1, &pgskey);
+    }
+    else
+	pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 0, NULL);
+
+    while (HeapTupleIsValid(pgstup = heap_getnext(pgsscan, 0, NULL))) {
+	heap_delete(pgstatistic, &pgstup->t_ctid);
+    }
+
+    heap_endscan(pgsscan);
+    heap_close(pgstatistic);
+}
+
+static void vc_setpagelock(Relation rel, BlockNumber blkno)
 {
     ItemPointerData itm;
 
@@ -1504,14 +1840,14 @@ static void _vc_setpagelock(Relation rel, BlockNumber blkno)
 
 
 /*
- *  _vc_reappage() -- save a page on the array of reapped pages.
+ *  vc_reappage() -- save a page on the array of reapped pages.
  *
  *	As a side effect of the way that the vacuuming loop for a given
  *	relation works, higher pages come after lower pages in the array
  *	(and highest tid on a page is last).
  */
 static void 
-_vc_reappage(VPageList vpl, VPageDescr vpc)
+vc_reappage(VPageList vpl, VPageDescr vpc)
 {
     VPageDescr newvpd;
 
@@ -1527,12 +1863,12 @@ _vc_reappage(VPageList vpl, VPageDescr vpc)
     newvpd->vpd_noff = vpc->vpd_noff;
 
     /* insert this page into vpl list */
-    _vc_vpinsert (vpl, newvpd);
+    vc_vpinsert (vpl, newvpd);
     
-} /* _vc_reappage */
+} /* vc_reappage */
 
 static void
-_vc_vpinsert (VPageList vpl, VPageDescr vpnew)
+vc_vpinsert (VPageList vpl, VPageDescr vpnew)
 {
 
     /* allocate a VPageDescr entry if needed */
@@ -1546,10 +1882,9 @@ _vc_vpinsert (VPageList vpl, VPageDescr vpnew)
 }
 
 static void
-_vc_free(Portal p, VRelList vrl)
+vc_free(Portal p, VRelList vrl)
 {
     VRelList p_vrl;
-    VAttList p_val, val;
     MemoryContext old;
     PortalVariableMemory pmem;
 
@@ -1558,14 +1893,6 @@ _vc_free(Portal p, VRelList vrl)
 
     while (vrl != (VRelList) NULL) {
 
-	/* free attribute list */
-	val = vrl->vrl_attlist;
-	while (val != (VAttList) NULL) {
-	    p_val = val;
-	    val = val->val_next;
-	    pfree(p_val);
-	}
-
 	/* free rel list entry */
 	p_vrl = vrl;
 	vrl = vrl->vrl_next;
@@ -1576,7 +1903,7 @@ _vc_free(Portal p, VRelList vrl)
 }
 
 /*
- *  _vc_getarchrel() -- open the archive relation for a heap relation
+ *  vc_getarchrel() -- open the archive relation for a heap relation
  *
  *	The archive relation is named 'a,XXXXX' for the heap relation
  *	whose relid is XXXXX.
@@ -1585,7 +1912,7 @@ _vc_free(Portal p, VRelList vrl)
 #define ARCHIVE_PREFIX	"a,"
 
 static Relation
-_vc_getarchrel(Relation heaprel)
+vc_getarchrel(Relation heaprel)
 {
     Relation archrel;
     char *archrelname;
@@ -1600,19 +1927,19 @@ _vc_getarchrel(Relation heaprel)
 }
 
 /*
- *  _vc_archive() -- write a tuple to an archive relation
+ *  vc_archive() -- write a tuple to an archive relation
  *
  *	In the future, this will invoke the archived accessd method.  For
  *	now, archive relations are on mag disk.
  */
 static void
-_vc_archive(Relation archrel, HeapTuple htup)
+vc_archive(Relation archrel, HeapTuple htup)
 {
     doinsert(archrel, htup);
 }
 
 static bool
-_vc_isarchrel(char *rname)
+vc_isarchrel(char *rname)
 {
     if (strncmp(ARCHIVE_PREFIX, rname,strlen(ARCHIVE_PREFIX)) == 0)
 	return (true);
@@ -1621,7 +1948,7 @@ _vc_isarchrel(char *rname)
 }
 
 static char *
-_vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *))
+vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *))
 {
     int res;
     int last = nelem - 1;
@@ -1671,10 +1998,10 @@ _vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, ch
 	first_move = true;
     }
 
-} /* _vc_find_eq */
+} /* vc_find_eq */
 
 static int 
-_vc_cmp_blk (char *left, char *right)
+vc_cmp_blk (char *left, char *right)
 {
     BlockNumber lblk, rblk;
 
@@ -1687,10 +2014,10 @@ _vc_cmp_blk (char *left, char *right)
     	return (0);
     return (1);
 
-} /* _vc_cmp_blk */
+} /* vc_cmp_blk */
 
 static int 
-_vc_cmp_offno (char *left, char *right)
+vc_cmp_offno (char *left, char *right)
 {
 
     if ( *(OffsetNumber*)left < *(OffsetNumber*)right )
@@ -1699,11 +2026,11 @@ _vc_cmp_offno (char *left, char *right)
     	return (0);
     return (1);
 
-} /* _vc_cmp_offno */
+} /* vc_cmp_offno */
 
 
 static void
-_vc_getindices (Oid relid, int *nindices, Relation **Irel)
+vc_getindices (Oid relid, int *nindices, Relation **Irel)
 {
     Relation pgindex;
     Relation irel;
@@ -1773,11 +2100,11 @@ _vc_getindices (Oid relid, int *nindices, Relation **Irel)
 	*Irel = (Relation *) NULL;
     }
 
-} /* _vc_getindices */
+} /* vc_getindices */
 
 
 static void
-_vc_clsindices (int nindices, Relation *Irel)
+vc_clsindices (int nindices, Relation *Irel)
 {
 
     if ( Irel == (Relation*) NULL )
@@ -1788,11 +2115,11 @@ _vc_clsindices (int nindices, Relation *Irel)
     }
     pfree (Irel);
 
-} /* _vc_clsindices */
+} /* vc_clsindices */
 
 
 static void
-_vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc)
+vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc)
 {
     IndDesc *idcur;
     HeapTuple pgIndexTup;
@@ -1824,11 +2151,11 @@ _vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc)
 	idcur->natts = natts;
     }
     
-} /* _vc_mkindesc */
+} /* vc_mkindesc */
 
 
 static bool
-_vc_enough_space (VPageDescr vpd, Size len)
+vc_enough_space (VPageDescr vpd, Size len)
 {
 
     len = DOUBLEALIGN(len);
@@ -1845,4 +2172,4 @@ _vc_enough_space (VPageDescr vpd, Size len)
     
     return (false);
     
-} /* _vc_enough_space */
+} /* vc_enough_space */
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 463cc06c822..21667455a85 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.20 1997/01/22 01:42:54 momjian Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.21 1997/02/07 16:22:50 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1486,7 +1486,7 @@ any_ordering_op(int restype)
     Operator order_op;
     Oid order_opid;
     
-    order_op = oper("<",restype,restype);
+    order_op = oper("<",restype,restype,false);
     order_opid = oprid(order_op);
     
     return order_opid;
@@ -1554,7 +1554,7 @@ transformSortClause(ParseState *pstate,
 	sortcl->resdom = resdom = restarget->resdom;
 	sortcl->opoid = oprid(oper(sortby->useOp,
 				   resdom->restype,
-				   resdom->restype));
+				   resdom->restype,false));
 	if (sortlist == NIL) {
 	    s = sortlist = lcons(sortcl, NIL);
 	}else {
diff --git a/src/backend/parser/catalog_utils.c b/src/backend/parser/catalog_utils.c
index e444742ef3e..661cea098bb 100644
--- a/src/backend/parser/catalog_utils.c
+++ b/src/backend/parser/catalog_utils.c
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.15 1997/01/22 01:43:08 momjian Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.16 1997/02/07 16:23:08 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -473,7 +473,7 @@ binary_oper_select_candidate(Oid arg1,
 /* Given operator, types of arg1, and arg2, return oper struct */
 /* arg1, arg2 --typeids */
 Operator
-oper(char *op, Oid arg1, Oid arg2)
+oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
 {
     HeapTuple tup;
     CandidateList candidates;
@@ -492,7 +492,8 @@ oper(char *op, Oid arg1, Oid arg2)
 	    /*
 	     * no operators of the desired types found
 	     */
-	    op_error(op, arg1, arg2);
+	    if (!noWarnings)
+		op_error(op, arg1, arg2);
 	    return(NULL);
 	} else if (ncandidates == 1) {
 	    /*
@@ -523,11 +524,12 @@ oper(char *op, Oid arg1, Oid arg2)
 		/* we chose none of them */
 		tp1 = get_id_type(arg1);
 		tp2 = get_id_type(arg2);
-		elog(NOTICE, "there is more than one operator %s for types", op);
-		elog(NOTICE, "%s and %s. You will have to retype this query",
-		     tname(tp1), tname(tp2));
-		elog(WARN, "using an explicit cast");
-		
+		if (!noWarnings) {
+		    elog(NOTICE, "there is more than one operator %s for types", op);
+		    elog(NOTICE, "%s and %s. You will have to retype this query",
+		        tname(tp1), tname(tp2));
+		    elog(WARN, "using an explicit cast");
+		}
 		return(NULL);
 	    }
 	}
diff --git a/src/backend/parser/parse_query.c b/src/backend/parser/parse_query.c
index 372f758a140..a6dd24d00e8 100644
--- a/src/backend/parser/parse_query.c
+++ b/src/backend/parser/parse_query.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.12 1997/01/22 01:43:19 momjian Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.13 1997/02/07 16:23:21 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -352,7 +352,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
 
 	/* right operator */
 	ltypeId = (ltree==NULL) ? UNKNOWNOID : exprType(ltree);
-	temp = right_oper(opname, ltypeId);
+ 	temp = oper(opname, ltypeId, rtypeId, false);
 	opform = (OperatorTupleForm) GETSTRUCT(temp);
 	left = make_operand(opname, ltree, ltypeId, opform->oprleft);
 	right = NULL;
@@ -411,7 +411,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
 	   ((Const *)ltree)->constbyval = tbyval(newtype);
 	}
 
-	temp = oper(opname, ltypeId, rtypeId);
+	temp = oper(opname, ltypeId, rtypeId, false);
 	opform = (OperatorTupleForm) GETSTRUCT(temp);
 	left = make_operand(opname, ltree, ltypeId, opform->oprleft);
 	right = make_operand(opname, rtree, rtypeId, opform->oprright);
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c8d7bf49ec5..769024bd427 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.4 1996/11/03 06:53:08 scrappy Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.5 1997/02/07 16:23:39 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,6 +33,7 @@
 #include "utils/lsyscache.h"	/* for get_oprrest() */
 #include "catalog/pg_statistic.h"
 
+#include "commands/vacuum.h" /* for ATTNVALS_SCALE */
 
 /* N is not a valid var/constant or relation id */
 #define	NONVALUE(N)	((N) == -1)
@@ -68,7 +69,7 @@ eqsel(Oid opid,
 	if (nvals == 0)
 	    *result = 0.0;
 	else
-	    *result = 1.0 / nvals;
+	    *result = ((float64data)nvals) / ((float64data)ATTNVALS_SCALE);
     }
     return(result);
 }
@@ -125,7 +126,7 @@ intltsel(Oid opid,
 	    if (nvals == 0)
 		*result = 1.0 / 3.0;
 	    else
-		*result = 3.0 / nvals;
+		*result = 3.0 * ((float64data)nvals) / ((float64data)ATTNVALS_SCALE);
 	}else {
 	    bottom = high - low;
 	    if (bottom == 0)
@@ -192,7 +193,7 @@ eqjoinsel(Oid opid,
 	if (max == 0)
 	    *result = 1.0;
 	else
-	    *result = 1.0 / max;
+	    *result = ((float64data)max) / ((float64data)ATTNVALS_SCALE);
     }
     return(result);
 }
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 8cf220d9cdd..224bea45d7c 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -6,13 +6,16 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: vacuum.h,v 1.5 1997/01/13 03:44:54 momjian Exp $
+ * $Id: vacuum.h,v 1.6 1997/02/07 16:23:57 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef	VACUUM_H
 #define	VACUUM_H
 
+#include <access/funcindex.h>
+#include <catalog/pg_index.h>
+
 typedef struct VAttListData {
     int			val_dummy;
     struct VAttListData	*val_next;
@@ -38,23 +41,46 @@ typedef struct VPageListData {
 
 typedef VPageListData	*VPageList;
 
+typedef struct {
+    FuncIndexInfo	finfo;
+    FuncIndexInfo	*finfoP;
+    IndexTupleForm	tform;
+    int			natts;
+} IndDesc;
+
+typedef struct {
+    AttributeTupleForm attr;
+    Datum best, guess1, guess2, max, min;
+    int16 best_len, guess1_len, guess2_len, max_len, min_len;
+    int32 best_cnt, guess1_cnt, guess1_hits, guess2_hits, null_cnt,nonnull_cnt;
+    int32 max_cnt, min_cnt;
+    regproc cmpeq, cmplt, cmpgt, outfunc;
+    bool initialized;
+} VacAttrStats;
+
 typedef struct VRelListData {
     Oid			vrl_relid;
-    VAttList		vrl_attlist;
-    int			vrl_ntups;
-    int			vrl_npages;
-    Size		vrl_min_tlen;
-    Size		vrl_max_tlen;
-    bool		vrl_hasindex;
     struct VRelListData	*vrl_next;
 } VRelListData;
 
 typedef VRelListData	*VRelList;
 
+typedef struct VRelStats {
+    Oid			relid;
+    int			ntups;
+    int			npages;
+    Size		min_tlen;
+    Size		max_tlen;
+    bool		hasindex;
+    int			natts;
+    VacAttrStats	*vacattrstats;
+} VRelStats;
+
 extern bool VacuumRunning;
 
 extern void vc_abort(void);
 extern void vacuum(char *vacrel, bool verbose);
 
+#define ATTNVALS_SCALE 	1000000000 /* XXX so it can act as a float4 */
 
 #endif	/* VACUUM_H */
diff --git a/src/include/parser/catalog_utils.h b/src/include/parser/catalog_utils.h
index df0d58ad18a..98b03fe41aa 100644
--- a/src/include/parser/catalog_utils.h
+++ b/src/include/parser/catalog_utils.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catalog_utils.h,v 1.7 1997/01/22 01:44:02 momjian Exp $
+ * $Id: catalog_utils.h,v 1.8 1997/02/07 16:24:12 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,7 +31,7 @@ extern bool tbyval(Type t);
 extern char *tname(Type t);
 extern int tbyvalue(Type t);
 extern Oid oprid(Operator op);
-extern Operator oper(char *op, Oid arg1, Oid arg2);
+extern Operator oper(char *op, Oid arg1, Oid arg2, bool noWarnings);
 extern Operator right_oper(char *op, Oid arg);
 extern Operator left_oper(char *op, Oid arg);
 extern int varattno(Relation rd, char *a);