diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index c2d62822e5ab3236be2bc1baf4aa2054417ef682..4baae556b317e960141cdea99aaf4f4d9f898dac 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.82 2004/01/06 23:55:18 tgl Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.83 2004/02/12 23:41:00 tgl Exp $
  -->
 
 <chapter id="catalogs">
@@ -3524,6 +3524,13 @@
       <entry>Output conversion function (binary format), or 0 if none</entry>
      </row>
 
+     <row>
+      <entry><structfield>typanalyze</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Custom ANALYZE function, or 0 to use the standard function</entry>
+     </row>
+
      <row>
       <entry><structfield>typalign</structfield></entry>
       <entry><type>char</type></entry>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index dfb7ab1510978ed257672436fa686995c78bac15..ba63f4378bbbae8f7e0ccae23297c9b4378a7c05 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.48 2003/11/29 19:51:38 pgsql Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.49 2004/02/12 23:41:02 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -28,6 +28,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
     OUTPUT = <replaceable class="parameter">output_function</replaceable>
     [ , RECEIVE = <replaceable class="parameter">receive_function</replaceable> ]
     [ , SEND = <replaceable class="parameter">send_function</replaceable> ]
+    [ , ANALYZE = <replaceable class="parameter">analyze_function</replaceable> ]
     [ , INTERNALLENGTH = { <replaceable class="parameter">internallength</replaceable> | VARIABLE } ]
     [ , PASSEDBYVALUE ]
     [ , ALIGNMENT = <replaceable class="parameter">alignment</replaceable> ]
@@ -83,8 +84,9 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
    <replaceable class="parameter">input_function</replaceable> and
    <replaceable class="parameter">output_function</replaceable>
    are required, while the functions
-   <replaceable class="parameter">receive_function</replaceable> and
-   <replaceable class="parameter">send_function</replaceable>
+   <replaceable class="parameter">receive_function</replaceable>,
+   <replaceable class="parameter">send_function</replaceable> and
+   <replaceable class="parameter">analyze_function</replaceable>
    are optional.  Generally these functions have to be coded in C
    or another low-level language.
   </para>
@@ -152,6 +154,19 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
    shell entry with a complete type definition, and the new type can be used.
   </para>
 
+  <para>
+   The optional <replaceable class="parameter">analyze_function</replaceable>
+   performs type-specific statistics collection for columns of the data type.
+   By default, <command>ANALYZE</> will attempt to gather statistics using
+   the type's <quote>equals</> and <quote>less-than</> operators, if there
+   is a default b-tree operator class for the type.  For non-scalar types
+   this behavior is likely to be unsuitable, so it can be overridden by
+   specifying a custom analysis function.  The analysis function must be
+   declared to take a single argument of type <type>internal</>, and return
+   a <type>boolean</> result.  The detailed API for analysis functions appears
+   in <filename>src/include/commands/vacuum.h</>.
+  </para>
+
   <para>
    While the details of the new type's internal representation are only
    known to the I/O functions and other functions you create to work with
@@ -341,6 +356,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><replaceable class="parameter">analyze_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of a function that performs statistical analysis for the
+      data type.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">internallength</replaceable></term>
     <listitem>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 905aa5b0b222f2efcfdc08204976ce1ebb0d2bce..63595962e4ea610b3430a14d2a1d4786709e321a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.258 2004/02/10 01:55:24 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.259 2004/02/12 23:41:02 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -689,6 +689,7 @@ AddNewRelationType(const char *typeName,
 			   F_RECORD_OUT,	/* output procedure */
 			   F_RECORD_RECV,	/* receive procedure */
 			   F_RECORD_SEND,	/* send procedure */
+			   InvalidOid,		/* analyze procedure - default */
 			   InvalidOid,		/* array element type - irrelevant */
 			   InvalidOid,		/* domain base type - irrelevant */
 			   NULL,			/* default type value - none */
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 802b01f759ee382803d00805f4a39338fe9fda13..efd2f61747c720711ca42719bec2c9e067ccd88f 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.92 2004/01/07 18:56:25 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.93 2004/02/12 23:41:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,6 +86,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
 	values[i++] = ObjectIdGetDatum(InvalidOid); /* typoutput */
 	values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */
 	values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */
+	values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */
 	values[i++] = CharGetDatum('i');	/* typalign */
 	values[i++] = CharGetDatum('p');	/* typstorage */
 	values[i++] = BoolGetDatum(false);	/* typnotnull */
@@ -121,6 +122,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
 								 InvalidOid,
 								 InvalidOid,
 								 InvalidOid,
+								 InvalidOid,
 								 NULL,
 								 false);
 
@@ -157,6 +159,7 @@ TypeCreate(const char *typeName,
 		   Oid outputProcedure,
 		   Oid receiveProcedure,
 		   Oid sendProcedure,
+		   Oid analyzeProcedure,
 		   Oid elementType,
 		   Oid baseType,
 		   const char *defaultTypeValue,		/* human readable rep */
@@ -236,6 +239,7 @@ TypeCreate(const char *typeName,
 	values[i++] = ObjectIdGetDatum(outputProcedure);	/* typoutput */
 	values[i++] = ObjectIdGetDatum(receiveProcedure);	/* typreceive */
 	values[i++] = ObjectIdGetDatum(sendProcedure);		/* typsend */
+	values[i++] = ObjectIdGetDatum(analyzeProcedure);	/* typanalyze */
 	values[i++] = CharGetDatum(alignment);		/* typalign */
 	values[i++] = CharGetDatum(storage);		/* typstorage */
 	values[i++] = BoolGetDatum(typeNotNull);	/* typnotnull */
@@ -332,6 +336,7 @@ TypeCreate(const char *typeName,
 								 outputProcedure,
 								 receiveProcedure,
 								 sendProcedure,
+								 analyzeProcedure,
 								 elementType,
 								 baseType,
 								 (defaultTypeBin ?
@@ -366,6 +371,7 @@ GenerateTypeDependencies(Oid typeNamespace,
 						 Oid outputProcedure,
 						 Oid receiveProcedure,
 						 Oid sendProcedure,
+						 Oid analyzeProcedure,
 						 Oid elementType,
 						 Oid baseType,
 						 Node *defaultExpr,
@@ -425,6 +431,14 @@ GenerateTypeDependencies(Oid typeNamespace,
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
+	if (OidIsValid(analyzeProcedure))
+	{
+		referenced.classId = RelOid_pg_proc;
+		referenced.objectId = analyzeProcedure;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
 	/*
 	 * If the type is a rowtype for a relation, mark it as internally
 	 * dependent on the relation, *unless* it is a stand-alone composite
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 0c713b3ca678cfba5877470d4731c999bf4cb0f2..eb8716b4880616e67a26a634a3afe9b61a544fb7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1,14 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * analyze.c
- *	  the postgres statistics generator
+ *	  the Postgres statistics generator
  *
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.67 2004/02/10 03:42:43 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.68 2004/02/12 23:41:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,8 +23,6 @@
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_operator.h"
-#include "catalog/pg_statistic.h"
-#include "catalog/pg_type.h"
 #include "commands/vacuum.h"
 #include "miscadmin.h"
 #include "parser/parse_oper.h"
@@ -38,91 +36,13 @@
 #include "utils/tuplesort.h"
 
 
-/*
- * Analysis algorithms supported
- */
-typedef enum
-{
-	ALG_MINIMAL = 1,			/* Compute only most-common-values */
-	ALG_SCALAR					/* Compute MCV, histogram, sort
-								 * correlation */
-} AlgCode;
-
-/*
- * To avoid consuming too much memory during analysis and/or too much space
- * in the resulting pg_statistic rows, we ignore varlena datums that are wider
- * than WIDTH_THRESHOLD (after detoasting!).  This is legitimate for MCV
- * and distinct-value calculations since a wide value is unlikely to be
- * duplicated at all, much less be a most-common value.  For the same reason,
- * ignoring wide values will not affect our estimates of histogram bin
- * boundaries very much.
- */
-#define WIDTH_THRESHOLD  1024
-
-/*
- * We build one of these structs for each attribute (column) that is to be
- * analyzed.  The struct and subsidiary data are in anl_context,
- * so they live until the end of the ANALYZE operation.
- */
-typedef struct
-{
-	/* These fields are set up by examine_attribute */
-	int			attnum;			/* attribute number */
-	AlgCode		algcode;		/* Which algorithm to use for this column */
-	int			minrows;		/* Minimum # of rows wanted for stats */
-	Form_pg_attribute attr;		/* copy of pg_attribute row for column */
-	Form_pg_type attrtype;		/* copy of pg_type row for column */
-	Oid			eqopr;			/* '=' operator for datatype, if any */
-	Oid			eqfunc;			/* and associated function */
-	Oid			ltopr;			/* '<' operator for datatype, if any */
-
-	/*
-	 * These fields are filled in by the actual statistics-gathering
-	 * routine
-	 */
-	bool		stats_valid;
-	float4		stanullfrac;	/* fraction of entries that are NULL */
-	int4		stawidth;		/* average width */
-	float4		stadistinct;	/* # distinct values */
-	int2		stakind[STATISTIC_NUM_SLOTS];
-	Oid			staop[STATISTIC_NUM_SLOTS];
-	int			numnumbers[STATISTIC_NUM_SLOTS];
-	float4	   *stanumbers[STATISTIC_NUM_SLOTS];
-	int			numvalues[STATISTIC_NUM_SLOTS];
-	Datum	   *stavalues[STATISTIC_NUM_SLOTS];
-} VacAttrStats;
-
-
-typedef struct
-{
-	Datum		value;			/* a data value */
-	int			tupno;			/* position index for tuple it came from */
-} ScalarItem;
-
-typedef struct
-{
-	int			count;			/* # of duplicates */
-	int			first;			/* values[] index of first occurrence */
-} ScalarMCVItem;
-
-
-#define swapInt(a,b)	do {int _tmp; _tmp=a; a=b; b=_tmp;} while(0)
-#define swapDatum(a,b)	do {Datum _tmp; _tmp=a; a=b; b=_tmp;} while(0)
-
-
 /* Default statistics target (GUC parameter) */
 int			default_statistics_target = 10;
 
-
 static int	elevel = -1;
 
 static MemoryContext anl_context = NULL;
 
-/* context information for compare_scalars() */
-static FmgrInfo *datumCmpFn;
-static SortFunctionKind datumCmpFnKind;
-static int *datumCmpTupnoLink;
-
 
 static VacAttrStats *examine_attribute(Relation onerel, int attnum);
 static int acquire_sample_rows(Relation onerel, HeapTuple *rows,
@@ -131,16 +51,10 @@ static double random_fract(void);
 static double init_selection_state(int n);
 static double select_next_random_record(double t, int n, double *stateptr);
 static int	compare_rows(const void *a, const void *b);
-static int	compare_scalars(const void *a, const void *b);
-static int	compare_mcvs(const void *a, const void *b);
-static void compute_minimal_stats(VacAttrStats *stats,
-					  TupleDesc tupDesc, double totalrows,
-					  HeapTuple *rows, int numrows);
-static void compute_scalar_stats(VacAttrStats *stats,
-					 TupleDesc tupDesc, double totalrows,
-					 HeapTuple *rows, int numrows);
 static void update_attstats(Oid relid, int natts, VacAttrStats **vacattrstats);
 
+static bool std_typanalyze(VacAttrStats *stats);
+
 
 /*
  *	analyze_rel() -- analyze one relation
@@ -345,19 +259,12 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
 		old_context = MemoryContextSwitchTo(col_context);
 		for (i = 0; i < attr_cnt; i++)
 		{
-			switch (vacattrstats[i]->algcode)
-			{
-				case ALG_MINIMAL:
-					compute_minimal_stats(vacattrstats[i],
-										  onerel->rd_att, totalrows,
-										  rows, numrows);
-					break;
-				case ALG_SCALAR:
-					compute_scalar_stats(vacattrstats[i],
-										 onerel->rd_att, totalrows,
-										 rows, numrows);
-					break;
-			}
+			(*vacattrstats[i]->compute_stats) (vacattrstats[i],
+											   vacattrstats[i]->tupattnum,
+											   onerel->rd_att,
+											   totalrows,
+											   rows,
+											   numrows);
 			MemoryContextResetAndDeleteChildren(col_context);
 		}
 		MemoryContextSwitchTo(old_context);
@@ -390,14 +297,11 @@ static VacAttrStats *
 examine_attribute(Relation onerel, int attnum)
 {
 	Form_pg_attribute attr = onerel->rd_att->attrs[attnum - 1];
-	Operator	func_operator;
 	HeapTuple	typtuple;
-	Oid			eqopr = InvalidOid;
-	Oid			eqfunc = InvalidOid;
-	Oid			ltopr = InvalidOid;
 	VacAttrStats *stats;
+	bool		ok;
 
-	/* Don't analyze dropped columns */
+	/* Never analyze dropped columns */
 	if (attr->attisdropped)
 		return NULL;
 
@@ -405,23 +309,10 @@ examine_attribute(Relation onerel, int attnum)
 	if (attr->attstattarget == 0)
 		return NULL;
 
-	/* If column has no "=" operator, we can't do much of anything */
-	func_operator = equality_oper(attr->atttypid, true);
-	if (func_operator != NULL)
-	{
-		eqopr = oprid(func_operator);
-		eqfunc = oprfuncid(func_operator);
-		ReleaseSysCache(func_operator);
-	}
-	if (!OidIsValid(eqfunc))
-		return NULL;
-
 	/*
-	 * If we have "=" then we're at least able to do the minimal
-	 * algorithm, so start filling in a VacAttrStats struct.
+	 * Create the VacAttrStats struct.
 	 */
 	stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats));
-	stats->attnum = attnum;
 	stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
 	memcpy(stats->attr, attr, ATTRIBUTE_TUPLE_SIZE);
 	typtuple = SearchSysCache(TYPEOID,
@@ -432,57 +323,25 @@ examine_attribute(Relation onerel, int attnum)
 	stats->attrtype = (Form_pg_type) palloc(sizeof(FormData_pg_type));
 	memcpy(stats->attrtype, GETSTRUCT(typtuple), sizeof(FormData_pg_type));
 	ReleaseSysCache(typtuple);
-	stats->eqopr = eqopr;
-	stats->eqfunc = eqfunc;
-
-	/* If the attstattarget column is negative, use the default value */
-	if (stats->attr->attstattarget < 0)
-		stats->attr->attstattarget = default_statistics_target;
-
-	/* Is there a "<" operator with suitable semantics? */
-	func_operator = ordering_oper(attr->atttypid, true);
-	if (func_operator != NULL)
-	{
-		ltopr = oprid(func_operator);
-		ReleaseSysCache(func_operator);
-	}
-	stats->ltopr = ltopr;
+	stats->anl_context = anl_context;
+	stats->tupattnum = attnum;
 
 	/*
-	 * Determine the algorithm to use (this will get more complicated
-	 * later)
+	 * Call the type-specific typanalyze function.  If none is specified,
+	 * use std_typanalyze().
 	 */
-	if (OidIsValid(ltopr))
-	{
-		/* Seems to be a scalar datatype */
-		stats->algcode = ALG_SCALAR;
-		/*--------------------
-		 * The following choice of minrows is based on the paper
-		 * "Random sampling for histogram construction: how much is enough?"
-		 * by Surajit Chaudhuri, Rajeev Motwani and Vivek Narasayya, in
-		 * Proceedings of ACM SIGMOD International Conference on Management
-		 * of Data, 1998, Pages 436-447.  Their Corollary 1 to Theorem 5
-		 * says that for table size n, histogram size k, maximum relative
-		 * error in bin size f, and error probability gamma, the minimum
-		 * random sample size is
-		 *		r = 4 * k * ln(2*n/gamma) / f^2
-		 * Taking f = 0.5, gamma = 0.01, n = 1 million rows, we obtain
-		 *		r = 305.82 * k
-		 * Note that because of the log function, the dependence on n is
-		 * quite weak; even at n = 1 billion, a 300*k sample gives <= 0.59
-		 * bin size error with probability 0.99.  So there's no real need to
-		 * scale for n, which is a good thing because we don't necessarily
-		 * know it at this point.
-		 *--------------------
-		 */
-		stats->minrows = 300 * stats->attr->attstattarget;
-	}
+	if (OidIsValid(stats->attrtype->typanalyze))
+		ok = DatumGetBool(OidFunctionCall1(stats->attrtype->typanalyze,
+										   PointerGetDatum(stats)));
 	else
+		ok = std_typanalyze(stats);
+
+	if (!ok || stats->compute_stats == NULL || stats->minrows <= 0)
 	{
-		/* Can't do much but the minimal stuff */
-		stats->algcode = ALG_MINIMAL;
-		/* Might as well use the same minrows as above */
-		stats->minrows = 300 * stats->attr->attstattarget;
+		pfree(stats->attrtype);
+		pfree(stats->attr);
+		pfree(stats);
+		return NULL;
 	}
 
 	return stats;
@@ -827,29 +686,327 @@ select_next_random_record(double t, int n, double *stateptr)
 }
 
 /*
- * qsort comparator for sorting rows[] array
+ * qsort comparator for sorting rows[] array
+ */
+static int
+compare_rows(const void *a, const void *b)
+{
+	HeapTuple	ha = *(HeapTuple *) a;
+	HeapTuple	hb = *(HeapTuple *) b;
+	BlockNumber ba = ItemPointerGetBlockNumber(&ha->t_self);
+	OffsetNumber oa = ItemPointerGetOffsetNumber(&ha->t_self);
+	BlockNumber bb = ItemPointerGetBlockNumber(&hb->t_self);
+	OffsetNumber ob = ItemPointerGetOffsetNumber(&hb->t_self);
+
+	if (ba < bb)
+		return -1;
+	if (ba > bb)
+		return 1;
+	if (oa < ob)
+		return -1;
+	if (oa > ob)
+		return 1;
+	return 0;
+}
+
+
+/*
+ *	update_attstats() -- update attribute statistics for one relation
+ *
+ *		Statistics are stored in several places: the pg_class row for the
+ *		relation has stats about the whole relation, and there is a
+ *		pg_statistic row for each (non-system) attribute that has ever
+ *		been analyzed.	The pg_class values are updated by VACUUM, not here.
+ *
+ *		pg_statistic rows are just added or updated normally.  This means
+ *		that pg_statistic will probably contain some deleted rows at the
+ *		completion of a vacuum cycle, unless it happens to get vacuumed last.
+ *
+ *		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.
+ *		Note analyze_rel() has seen to it that we won't come here when
+ *		vacuuming pg_statistic itself.
+ *
+ *		Note: if two backends concurrently try to analyze the same relation,
+ *		the second one is likely to fail here with a "tuple concurrently
+ *		updated" error.  This is slightly annoying, but no real harm is done.
+ *		We could prevent the problem by using a stronger lock on the
+ *		relation for ANALYZE (ie, ShareUpdateExclusiveLock instead
+ *		of AccessShareLock); but that cure seems worse than the disease,
+ *		especially now that ANALYZE doesn't start a new transaction
+ *		for each relation.	The lock could be held for a long time...
+ */
+static void
+update_attstats(Oid relid, int natts, VacAttrStats **vacattrstats)
+{
+	Relation	sd;
+	int			attno;
+
+	sd = heap_openr(StatisticRelationName, RowExclusiveLock);
+
+	for (attno = 0; attno < natts; attno++)
+	{
+		VacAttrStats *stats = vacattrstats[attno];
+		HeapTuple	stup,
+					oldtup;
+		int			i,
+					k,
+					n;
+		Datum		values[Natts_pg_statistic];
+		char		nulls[Natts_pg_statistic];
+		char		replaces[Natts_pg_statistic];
+
+		/* Ignore attr if we weren't able to collect stats */
+		if (!stats->stats_valid)
+			continue;
+
+		/*
+		 * Construct a new pg_statistic tuple
+		 */
+		for (i = 0; i < Natts_pg_statistic; ++i)
+		{
+			nulls[i] = ' ';
+			replaces[i] = 'r';
+		}
+
+		i = 0;
+		values[i++] = ObjectIdGetDatum(relid);	/* starelid */
+		values[i++] = Int16GetDatum(stats->attr->attnum);	/* staattnum */
+		values[i++] = Float4GetDatum(stats->stanullfrac);	/* stanullfrac */
+		values[i++] = Int32GetDatum(stats->stawidth);	/* stawidth */
+		values[i++] = Float4GetDatum(stats->stadistinct);	/* stadistinct */
+		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
+		{
+			values[i++] = Int16GetDatum(stats->stakind[k]);		/* stakindN */
+		}
+		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
+		{
+			values[i++] = ObjectIdGetDatum(stats->staop[k]);	/* staopN */
+		}
+		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
+		{
+			int			nnum = stats->numnumbers[k];
+
+			if (nnum > 0)
+			{
+				Datum	   *numdatums = (Datum *) palloc(nnum * sizeof(Datum));
+				ArrayType  *arry;
+
+				for (n = 0; n < nnum; n++)
+					numdatums[n] = Float4GetDatum(stats->stanumbers[k][n]);
+				/* XXX knows more than it should about type float4: */
+				arry = construct_array(numdatums, nnum,
+									   FLOAT4OID,
+									   sizeof(float4), false, 'i');
+				values[i++] = PointerGetDatum(arry);	/* stanumbersN */
+			}
+			else
+			{
+				nulls[i] = 'n';
+				values[i++] = (Datum) 0;
+			}
+		}
+		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
+		{
+			if (stats->numvalues[k] > 0)
+			{
+				ArrayType  *arry;
+
+				arry = construct_array(stats->stavalues[k],
+									   stats->numvalues[k],
+									   stats->attr->atttypid,
+									   stats->attrtype->typlen,
+									   stats->attrtype->typbyval,
+									   stats->attrtype->typalign);
+				values[i++] = PointerGetDatum(arry);	/* stavaluesN */
+			}
+			else
+			{
+				nulls[i] = 'n';
+				values[i++] = (Datum) 0;
+			}
+		}
+
+		/* Is there already a pg_statistic tuple for this attribute? */
+		oldtup = SearchSysCache(STATRELATT,
+								ObjectIdGetDatum(relid),
+								Int16GetDatum(stats->attr->attnum),
+								0, 0);
+
+		if (HeapTupleIsValid(oldtup))
+		{
+			/* Yes, replace it */
+			stup = heap_modifytuple(oldtup,
+									sd,
+									values,
+									nulls,
+									replaces);
+			ReleaseSysCache(oldtup);
+			simple_heap_update(sd, &stup->t_self, stup);
+		}
+		else
+		{
+			/* No, insert new tuple */
+			stup = heap_formtuple(sd->rd_att, values, nulls);
+			simple_heap_insert(sd, stup);
+		}
+
+		/* update indexes too */
+		CatalogUpdateIndexes(sd, stup);
+
+		heap_freetuple(stup);
+	}
+
+	heap_close(sd, RowExclusiveLock);
+}
+
+
+/*==========================================================================
+ *
+ * Code below this point represents the "standard" type-specific statistics
+ * analysis algorithms.  This code can be replaced on a per-data-type basis
+ * by setting a nonzero value in pg_type.typanalyze.
+ *
+ *==========================================================================
+ */
+
+
+/*
+ * To avoid consuming too much memory during analysis and/or too much space
+ * in the resulting pg_statistic rows, we ignore varlena datums that are wider
+ * than WIDTH_THRESHOLD (after detoasting!).  This is legitimate for MCV
+ * and distinct-value calculations since a wide value is unlikely to be
+ * duplicated at all, much less be a most-common value.  For the same reason,
+ * ignoring wide values will not affect our estimates of histogram bin
+ * boundaries very much.
+ */
+#define WIDTH_THRESHOLD  1024
+
+#define swapInt(a,b)	do {int _tmp; _tmp=a; a=b; b=_tmp;} while(0)
+#define swapDatum(a,b)	do {Datum _tmp; _tmp=a; a=b; b=_tmp;} while(0)
+
+/*
+ * Extra information used by the default analysis routines
+ */
+typedef struct
+{
+	Oid			eqopr;			/* '=' operator for datatype, if any */
+	Oid			eqfunc;			/* and associated function */
+	Oid			ltopr;			/* '<' operator for datatype, if any */
+} StdAnalyzeData;
+
+typedef struct
+{
+	Datum		value;			/* a data value */
+	int			tupno;			/* position index for tuple it came from */
+} ScalarItem;
+
+typedef struct
+{
+	int			count;			/* # of duplicates */
+	int			first;			/* values[] index of first occurrence */
+} ScalarMCVItem;
+
+
+/* context information for compare_scalars() */
+static FmgrInfo *datumCmpFn;
+static SortFunctionKind datumCmpFnKind;
+static int *datumCmpTupnoLink;
+
+
+static void compute_minimal_stats(VacAttrStats *stats, int attnum,
+					  TupleDesc tupDesc, double totalrows,
+					  HeapTuple *rows, int numrows);
+static void compute_scalar_stats(VacAttrStats *stats, int attnum,
+					 TupleDesc tupDesc, double totalrows,
+					 HeapTuple *rows, int numrows);
+static int	compare_scalars(const void *a, const void *b);
+static int	compare_mcvs(const void *a, const void *b);
+
+
+/*
+ * std_typanalyze -- the default type-specific typanalyze function
  */
-static int
-compare_rows(const void *a, const void *b)
+static bool
+std_typanalyze(VacAttrStats *stats)
 {
-	HeapTuple	ha = *(HeapTuple *) a;
-	HeapTuple	hb = *(HeapTuple *) b;
-	BlockNumber ba = ItemPointerGetBlockNumber(&ha->t_self);
-	OffsetNumber oa = ItemPointerGetOffsetNumber(&ha->t_self);
-	BlockNumber bb = ItemPointerGetBlockNumber(&hb->t_self);
-	OffsetNumber ob = ItemPointerGetOffsetNumber(&hb->t_self);
+	Form_pg_attribute attr = stats->attr;
+	Operator	func_operator;
+	Oid			eqopr = InvalidOid;
+	Oid			eqfunc = InvalidOid;
+	Oid			ltopr = InvalidOid;
+	StdAnalyzeData *mystats;
 
-	if (ba < bb)
-		return -1;
-	if (ba > bb)
-		return 1;
-	if (oa < ob)
-		return -1;
-	if (oa > ob)
-		return 1;
-	return 0;
-}
+	/* If the attstattarget column is negative, use the default value */
+	/* NB: it is okay to scribble on stats->attr since it's a copy */
+	if (attr->attstattarget < 0)
+		attr->attstattarget = default_statistics_target;
+
+	/* If column has no "=" operator, we can't do much of anything */
+	func_operator = equality_oper(attr->atttypid, true);
+	if (func_operator != NULL)
+	{
+		eqopr = oprid(func_operator);
+		eqfunc = oprfuncid(func_operator);
+		ReleaseSysCache(func_operator);
+	}
+	if (!OidIsValid(eqfunc))
+		return false;
+
+	/* Is there a "<" operator with suitable semantics? */
+	func_operator = ordering_oper(attr->atttypid, true);
+	if (func_operator != NULL)
+	{
+		ltopr = oprid(func_operator);
+		ReleaseSysCache(func_operator);
+	}
+
+	/* Save the operator info for compute_stats routines */
+	mystats = (StdAnalyzeData *) palloc(sizeof(StdAnalyzeData));
+	mystats->eqopr = eqopr;
+	mystats->eqfunc = eqfunc;
+	mystats->ltopr = ltopr;
+	stats->extra_data = mystats;
+
+	/*
+	 * Determine which standard statistics algorithm to use
+	 */
+	if (OidIsValid(ltopr))
+	{
+		/* Seems to be a scalar datatype */
+		stats->compute_stats = compute_scalar_stats;
+		/*--------------------
+		 * The following choice of minrows is based on the paper
+		 * "Random sampling for histogram construction: how much is enough?"
+		 * by Surajit Chaudhuri, Rajeev Motwani and Vivek Narasayya, in
+		 * Proceedings of ACM SIGMOD International Conference on Management
+		 * of Data, 1998, Pages 436-447.  Their Corollary 1 to Theorem 5
+		 * says that for table size n, histogram size k, maximum relative
+		 * error in bin size f, and error probability gamma, the minimum
+		 * random sample size is
+		 *		r = 4 * k * ln(2*n/gamma) / f^2
+		 * Taking f = 0.5, gamma = 0.01, n = 1 million rows, we obtain
+		 *		r = 305.82 * k
+		 * Note that because of the log function, the dependence on n is
+		 * quite weak; even at n = 1 billion, a 300*k sample gives <= 0.59
+		 * bin size error with probability 0.99.  So there's no real need to
+		 * scale for n, which is a good thing because we don't necessarily
+		 * know it at this point.
+		 *--------------------
+		 */
+		stats->minrows = 300 * attr->attstattarget;
+	}
+	else
+	{
+		/* Can't do much but the minimal stuff */
+		stats->compute_stats = compute_minimal_stats;
+		/* Might as well use the same minrows as above */
+		stats->minrows = 300 * attr->attstattarget;
+	}
 
+	return true;
+}
 
 /*
  *	compute_minimal_stats() -- compute minimal column statistics
@@ -867,7 +1024,7 @@ compare_rows(const void *a, const void *b)
  *	depend mainly on the length of the list we are willing to keep.
  */
 static void
-compute_minimal_stats(VacAttrStats *stats,
+compute_minimal_stats(VacAttrStats *stats, int attnum,
 					  TupleDesc tupDesc, double totalrows,
 					  HeapTuple *rows, int numrows)
 {
@@ -890,6 +1047,7 @@ compute_minimal_stats(VacAttrStats *stats,
 	int			track_cnt,
 				track_max;
 	int			num_mcv = stats->attr->attstattarget;
+	StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data;
 
 	/*
 	 * We track up to 2*n values for an n-element MCV list; but at least
@@ -901,7 +1059,7 @@ compute_minimal_stats(VacAttrStats *stats,
 	track = (TrackItem *) palloc(track_max * sizeof(TrackItem));
 	track_cnt = 0;
 
-	fmgr_info(stats->eqfunc, &f_cmpeq);
+	fmgr_info(mystats->eqfunc, &f_cmpeq);
 
 	for (i = 0; i < numrows; i++)
 	{
@@ -914,7 +1072,7 @@ compute_minimal_stats(VacAttrStats *stats,
 
 		vacuum_delay_point();
 
-		value = heap_getattr(tuple, stats->attnum, tupDesc, &isnull);
+		value = heap_getattr(tuple, attnum, tupDesc, &isnull);
 
 		/* Check for null/nonnull */
 		if (isnull)
@@ -1137,7 +1295,7 @@ compute_minimal_stats(VacAttrStats *stats,
 			float4	   *mcv_freqs;
 
 			/* Must copy the target values into anl_context */
-			old_context = MemoryContextSwitchTo(anl_context);
+			old_context = MemoryContextSwitchTo(stats->anl_context);
 			mcv_values = (Datum *) palloc(num_mcv * sizeof(Datum));
 			mcv_freqs = (float4 *) palloc(num_mcv * sizeof(float4));
 			for (i = 0; i < num_mcv; i++)
@@ -1150,7 +1308,7 @@ compute_minimal_stats(VacAttrStats *stats,
 			MemoryContextSwitchTo(old_context);
 
 			stats->stakind[0] = STATISTIC_KIND_MCV;
-			stats->staop[0] = stats->eqopr;
+			stats->staop[0] = mystats->eqopr;
 			stats->stanumbers[0] = mcv_freqs;
 			stats->numnumbers[0] = num_mcv;
 			stats->stavalues[0] = mcv_values;
@@ -1175,7 +1333,7 @@ compute_minimal_stats(VacAttrStats *stats,
  *	data values into order.
  */
 static void
-compute_scalar_stats(VacAttrStats *stats,
+compute_scalar_stats(VacAttrStats *stats, int attnum,
 					 TupleDesc tupDesc, double totalrows,
 					 HeapTuple *rows, int numrows)
 {
@@ -1199,12 +1357,13 @@ compute_scalar_stats(VacAttrStats *stats,
 	int			track_cnt = 0;
 	int			num_mcv = stats->attr->attstattarget;
 	int			num_bins = stats->attr->attstattarget;
+	StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data;
 
 	values = (ScalarItem *) palloc(numrows * sizeof(ScalarItem));
 	tupnoLink = (int *) palloc(numrows * sizeof(int));
 	track = (ScalarMCVItem *) palloc(num_mcv * sizeof(ScalarMCVItem));
 
-	SelectSortFunction(stats->ltopr, &cmpFn, &cmpFnKind);
+	SelectSortFunction(mystats->ltopr, &cmpFn, &cmpFnKind);
 	fmgr_info(cmpFn, &f_cmpfn);
 
 	/* Initial scan to find sortable values */
@@ -1216,7 +1375,7 @@ compute_scalar_stats(VacAttrStats *stats,
 
 		vacuum_delay_point();
 
-		value = heap_getattr(tuple, stats->attnum, tupDesc, &isnull);
+		value = heap_getattr(tuple, attnum, tupDesc, &isnull);
 
 		/* Check for null/nonnull */
 		if (isnull)
@@ -1469,7 +1628,7 @@ compute_scalar_stats(VacAttrStats *stats,
 			float4	   *mcv_freqs;
 
 			/* Must copy the target values into anl_context */
-			old_context = MemoryContextSwitchTo(anl_context);
+			old_context = MemoryContextSwitchTo(stats->anl_context);
 			mcv_values = (Datum *) palloc(num_mcv * sizeof(Datum));
 			mcv_freqs = (float4 *) palloc(num_mcv * sizeof(float4));
 			for (i = 0; i < num_mcv; i++)
@@ -1482,7 +1641,7 @@ compute_scalar_stats(VacAttrStats *stats,
 			MemoryContextSwitchTo(old_context);
 
 			stats->stakind[slot_idx] = STATISTIC_KIND_MCV;
-			stats->staop[slot_idx] = stats->eqopr;
+			stats->staop[slot_idx] = mystats->eqopr;
 			stats->stanumbers[slot_idx] = mcv_freqs;
 			stats->numnumbers[slot_idx] = num_mcv;
 			stats->stavalues[slot_idx] = mcv_values;
@@ -1555,7 +1714,7 @@ compute_scalar_stats(VacAttrStats *stats,
 			Assert(nvals >= num_hist);
 
 			/* Must copy the target values into anl_context */
-			old_context = MemoryContextSwitchTo(anl_context);
+			old_context = MemoryContextSwitchTo(stats->anl_context);
 			hist_values = (Datum *) palloc(num_hist * sizeof(Datum));
 			for (i = 0; i < num_hist; i++)
 			{
@@ -1569,7 +1728,7 @@ compute_scalar_stats(VacAttrStats *stats,
 			MemoryContextSwitchTo(old_context);
 
 			stats->stakind[slot_idx] = STATISTIC_KIND_HISTOGRAM;
-			stats->staop[slot_idx] = stats->ltopr;
+			stats->staop[slot_idx] = mystats->ltopr;
 			stats->stavalues[slot_idx] = hist_values;
 			stats->numvalues[slot_idx] = num_hist;
 			slot_idx++;
@@ -1584,7 +1743,7 @@ compute_scalar_stats(VacAttrStats *stats,
 						corr_x2sum;
 
 			/* Must copy the target values into anl_context */
-			old_context = MemoryContextSwitchTo(anl_context);
+			old_context = MemoryContextSwitchTo(stats->anl_context);
 			corrs = (float4 *) palloc(sizeof(float4));
 			MemoryContextSwitchTo(old_context);
 
@@ -1607,7 +1766,7 @@ compute_scalar_stats(VacAttrStats *stats,
 				(values_cnt * corr_x2sum - corr_xsum * corr_xsum);
 
 			stats->stakind[slot_idx] = STATISTIC_KIND_CORRELATION;
-			stats->staop[slot_idx] = stats->ltopr;
+			stats->staop[slot_idx] = mystats->ltopr;
 			stats->stanumbers[slot_idx] = corrs;
 			stats->numnumbers[slot_idx] = 1;
 			slot_idx++;
@@ -1665,155 +1824,3 @@ compare_mcvs(const void *a, const void *b)
 
 	return da - db;
 }
-
-
-/*
- *	update_attstats() -- update attribute statistics for one relation
- *
- *		Statistics are stored in several places: the pg_class row for the
- *		relation has stats about the whole relation, and there is a
- *		pg_statistic row for each (non-system) attribute that has ever
- *		been analyzed.	The pg_class values are updated by VACUUM, not here.
- *
- *		pg_statistic rows are just added or updated normally.  This means
- *		that pg_statistic will probably contain some deleted rows at the
- *		completion of a vacuum cycle, unless it happens to get vacuumed last.
- *
- *		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.
- *		Note analyze_rel() has seen to it that we won't come here when
- *		vacuuming pg_statistic itself.
- *
- *		Note: if two backends concurrently try to analyze the same relation,
- *		the second one is likely to fail here with a "tuple concurrently
- *		updated" error.  This is slightly annoying, but no real harm is done.
- *		We could prevent the problem by using a stronger lock on the
- *		relation for ANALYZE (ie, ShareUpdateExclusiveLock instead
- *		of AccessShareLock); but that cure seems worse than the disease,
- *		especially now that ANALYZE doesn't start a new transaction
- *		for each relation.	The lock could be held for a long time...
- */
-static void
-update_attstats(Oid relid, int natts, VacAttrStats **vacattrstats)
-{
-	Relation	sd;
-	int			attno;
-
-	sd = heap_openr(StatisticRelationName, RowExclusiveLock);
-
-	for (attno = 0; attno < natts; attno++)
-	{
-		VacAttrStats *stats = vacattrstats[attno];
-		HeapTuple	stup,
-					oldtup;
-		int			i,
-					k,
-					n;
-		Datum		values[Natts_pg_statistic];
-		char		nulls[Natts_pg_statistic];
-		char		replaces[Natts_pg_statistic];
-
-		/* Ignore attr if we weren't able to collect stats */
-		if (!stats->stats_valid)
-			continue;
-
-		/*
-		 * Construct a new pg_statistic tuple
-		 */
-		for (i = 0; i < Natts_pg_statistic; ++i)
-		{
-			nulls[i] = ' ';
-			replaces[i] = 'r';
-		}
-
-		i = 0;
-		values[i++] = ObjectIdGetDatum(relid);	/* starelid */
-		values[i++] = Int16GetDatum(stats->attnum);		/* staattnum */
-		values[i++] = Float4GetDatum(stats->stanullfrac);		/* stanullfrac */
-		values[i++] = Int32GetDatum(stats->stawidth);	/* stawidth */
-		values[i++] = Float4GetDatum(stats->stadistinct);		/* stadistinct */
-		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
-		{
-			values[i++] = Int16GetDatum(stats->stakind[k]);		/* stakindN */
-		}
-		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
-		{
-			values[i++] = ObjectIdGetDatum(stats->staop[k]);	/* staopN */
-		}
-		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
-		{
-			int			nnum = stats->numnumbers[k];
-
-			if (nnum > 0)
-			{
-				Datum	   *numdatums = (Datum *) palloc(nnum * sizeof(Datum));
-				ArrayType  *arry;
-
-				for (n = 0; n < nnum; n++)
-					numdatums[n] = Float4GetDatum(stats->stanumbers[k][n]);
-				/* XXX knows more than it should about type float4: */
-				arry = construct_array(numdatums, nnum,
-									   FLOAT4OID,
-									   sizeof(float4), false, 'i');
-				values[i++] = PointerGetDatum(arry);	/* stanumbersN */
-			}
-			else
-			{
-				nulls[i] = 'n';
-				values[i++] = (Datum) 0;
-			}
-		}
-		for (k = 0; k < STATISTIC_NUM_SLOTS; k++)
-		{
-			if (stats->numvalues[k] > 0)
-			{
-				ArrayType  *arry;
-
-				arry = construct_array(stats->stavalues[k],
-									   stats->numvalues[k],
-									   stats->attr->atttypid,
-									   stats->attrtype->typlen,
-									   stats->attrtype->typbyval,
-									   stats->attrtype->typalign);
-				values[i++] = PointerGetDatum(arry);	/* stavaluesN */
-			}
-			else
-			{
-				nulls[i] = 'n';
-				values[i++] = (Datum) 0;
-			}
-		}
-
-		/* Is there already a pg_statistic tuple for this attribute? */
-		oldtup = SearchSysCache(STATRELATT,
-								ObjectIdGetDatum(relid),
-								Int16GetDatum(stats->attnum),
-								0, 0);
-
-		if (HeapTupleIsValid(oldtup))
-		{
-			/* Yes, replace it */
-			stup = heap_modifytuple(oldtup,
-									sd,
-									values,
-									nulls,
-									replaces);
-			ReleaseSysCache(oldtup);
-			simple_heap_update(sd, &stup->t_self, stup);
-		}
-		else
-		{
-			/* No, insert new tuple */
-			stup = heap_formtuple(sd->rd_att, values, nulls);
-			simple_heap_insert(sd, stup);
-		}
-
-		/* update indexes too */
-		CatalogUpdateIndexes(sd, stup);
-
-		heap_freetuple(stup);
-	}
-
-	heap_close(sd, RowExclusiveLock);
-}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 530fb1f573cf1b6929df3be97be85a785d040003..d081c38b7042623d290007f02e053a099ae17cbe 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.52 2004/01/10 23:28:44 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.53 2004/02/12 23:41:02 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -77,6 +77,7 @@ static Oid	findTypeInputFunction(List *procname, Oid typeOid);
 static Oid	findTypeOutputFunction(List *procname, Oid typeOid);
 static Oid	findTypeReceiveFunction(List *procname, Oid typeOid);
 static Oid	findTypeSendFunction(List *procname, Oid typeOid);
+static Oid	findTypeAnalyzeFunction(List *procname, Oid typeOid);
 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
 static void domainOwnerCheck(HeapTuple tup, TypeName *typename);
 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
@@ -101,6 +102,7 @@ DefineType(List *names, List *parameters)
 	List	   *outputName = NIL;
 	List	   *receiveName = NIL;
 	List	   *sendName = NIL;
+	List	   *analyzeName = NIL;
 	char	   *defaultValue = NULL;
 	bool		byValue = false;
 	char		delimiter = DEFAULT_TYPDELIM;
@@ -110,6 +112,7 @@ DefineType(List *names, List *parameters)
 	Oid			outputOid;
 	Oid			receiveOid = InvalidOid;
 	Oid			sendOid = InvalidOid;
+	Oid			analyzeOid = InvalidOid;
 	char	   *shadow_type;
 	List	   *pl;
 	Oid			typoid;
@@ -151,6 +154,9 @@ DefineType(List *names, List *parameters)
 			receiveName = defGetQualifiedName(defel);
 		else if (strcasecmp(defel->defname, "send") == 0)
 			sendName = defGetQualifiedName(defel);
+		else if (strcasecmp(defel->defname, "analyze") == 0 ||
+				 strcasecmp(defel->defname, "analyse") == 0)
+			analyzeName = defGetQualifiedName(defel);
 		else if (strcasecmp(defel->defname, "delimiter") == 0)
 		{
 			char	   *p = defGetString(defel);
@@ -318,6 +324,13 @@ DefineType(List *names, List *parameters)
 							NameListToString(sendName))));
 	}
 
+	/*
+	 * Convert analysis function proc name to an OID. If no analysis function
+	 * is specified, we'll use zero to select the built-in default algorithm.
+	 */
+	if (analyzeName)
+		analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
+
 	/*
 	 * now have TypeCreate do all the real work.
 	 */
@@ -334,6 +347,7 @@ DefineType(List *names, List *parameters)
 				   outputOid,	/* output procedure */
 				   receiveOid,	/* receive procedure */
 				   sendOid,		/* send procedure */
+				   analyzeOid,	/* analyze procedure */
 				   elemType,	/* element type ID */
 				   InvalidOid,	/* base type ID (only for domains) */
 				   defaultValue,	/* default type value */
@@ -366,6 +380,7 @@ DefineType(List *names, List *parameters)
 			   F_ARRAY_OUT,		/* output procedure */
 			   F_ARRAY_RECV,	/* receive procedure */
 			   F_ARRAY_SEND,	/* send procedure */
+			   InvalidOid,		/* analyze procedure - default */
 			   typoid,			/* element type ID */
 			   InvalidOid,		/* base type ID */
 			   NULL,			/* never a default type value */
@@ -473,6 +488,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	Oid			outputProcedure;
 	Oid			receiveProcedure;
 	Oid			sendProcedure;
+	Oid			analyzeProcedure;
 	bool		byValue;
 	char		delimiter;
 	char		alignment;
@@ -562,6 +578,9 @@ DefineDomain(CreateDomainStmt *stmt)
 	receiveProcedure = baseType->typreceive;
 	sendProcedure = baseType->typsend;
 
+	/* Analysis function */
+	analyzeProcedure = baseType->typanalyze;
+
 	/* Inherited default value */
 	datum = SysCacheGetAttr(TYPEOID, typeTup,
 							Anum_pg_type_typdefault, &isnull);
@@ -714,6 +733,7 @@ DefineDomain(CreateDomainStmt *stmt)
 				   outputProcedure,		/* output procedure */
 				   receiveProcedure,	/* receive procedure */
 				   sendProcedure,		/* send procedure */
+				   analyzeProcedure,	/* analyze procedure */
 				   basetypelem, /* element type ID */
 				   basetypeoid, /* base type ID */
 				   defaultValue,	/* default type value (text) */
@@ -1033,6 +1053,35 @@ findTypeSendFunction(List *procname, Oid typeOid)
 	return InvalidOid;			/* keep compiler quiet */
 }
 
+static Oid
+findTypeAnalyzeFunction(List *procname, Oid typeOid)
+{
+	Oid			argList[FUNC_MAX_ARGS];
+	Oid			procOid;
+
+	/*
+	 * Analyze functions always take one INTERNAL argument and return bool.
+	 */
+	MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
+
+	argList[0] = INTERNALOID;
+
+	procOid = LookupFuncName(procname, 1, argList, true);
+	if (!OidIsValid(procOid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("function %s does not exist",
+						func_signature_string(procname, 1, argList))));
+
+	if (get_func_rettype(procOid) != BOOLOID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("type analyze function %s must return type \"boolean\"",
+						NameListToString(procname))));
+
+	return procOid;
+}
+
 
 /*-------------------------------------------------------------------
  * DefineCompositeType
@@ -1192,6 +1241,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
 							 typTup->typoutput,
 							 typTup->typreceive,
 							 typTup->typsend,
+							 typTup->typanalyze,
 							 typTup->typelem,
 							 typTup->typbasetype,
 							 defaultExpr,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index a6c8997c3943c7e59cf27d217670148352f5b370..096a175dc702900982d55427de7ad27e7612397f 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
  *	by PostgreSQL
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.363 2004/01/22 19:09:32 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.364 2004/02/12 23:41:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1648,9 +1648,9 @@ getTypes(int *numTypes)
 
 		/*
 		 * Make sure there are dependencies from the type to its input and
-		 * output functions.  (We don't worry about typsend/typreceive since
-		 * those are only valid in 7.4 and later, wherein the standard
-		 * dependency mechanism will pick them up.)
+		 * output functions.  (We don't worry about typsend, typreceive, or
+		 * typanalyze since those are only valid in 7.4 and later, wherein
+		 * the standard dependency mechanism will pick them up.)
 		 */
 		funcInfo = findFuncByOid(tinfo[i].typinput);
 		if (funcInfo)
@@ -4148,10 +4148,12 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 	char	   *typoutput;
 	char	   *typreceive;
 	char	   *typsend;
+	char	   *typanalyze;
 	Oid			typinputoid;
 	Oid			typoutputoid;
 	Oid			typreceiveoid;
 	Oid			typsendoid;
+	Oid			typanalyzeoid;
 	char	   *typdelim;
 	char	   *typdefault;
 	char	   *typbyval;
@@ -4162,14 +4164,32 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 	selectSourceSchema(tinfo->typnamespace->nspname);
 
 	/* Fetch type-specific details */
-	if (fout->remoteVersion >= 70400)
+	if (fout->remoteVersion >= 70500)
 	{
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, typreceive, typsend, "
+						  "typanalyze, "
 						  "typinput::pg_catalog.oid as typinputoid, "
 						  "typoutput::pg_catalog.oid as typoutputoid, "
 						  "typreceive::pg_catalog.oid as typreceiveoid, "
 						  "typsend::pg_catalog.oid as typsendoid, "
+						  "typanalyze::pg_catalog.oid as typanalyzeoid, "
+						  "typdelim, typdefault, typbyval, typalign, "
+						  "typstorage "
+						  "FROM pg_catalog.pg_type "
+						  "WHERE oid = '%u'::pg_catalog.oid",
+						  tinfo->dobj.catId.oid);
+	}
+	else if (fout->remoteVersion >= 70400)
+	{
+		appendPQExpBuffer(query, "SELECT typlen, "
+						  "typinput, typoutput, typreceive, typsend, "
+						  "'-' as typanalyze, "
+						  "typinput::pg_catalog.oid as typinputoid, "
+						  "typoutput::pg_catalog.oid as typoutputoid, "
+						  "typreceive::pg_catalog.oid as typreceiveoid, "
+						  "typsend::pg_catalog.oid as typsendoid, "
+						  "0 as typanalyzeoid, "
 						  "typdelim, typdefault, typbyval, typalign, "
 						  "typstorage "
 						  "FROM pg_catalog.pg_type "
@@ -4181,9 +4201,11 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, "
 						  "'-' as typreceive, '-' as typsend, "
+						  "'-' as typanalyze, "
 						  "typinput::pg_catalog.oid as typinputoid, "
 						  "typoutput::pg_catalog.oid as typoutputoid, "
 						  "0 as typreceiveoid, 0 as typsendoid, "
+						  "0 as typanalyzeoid, "
 						  "typdelim, typdefault, typbyval, typalign, "
 						  "typstorage "
 						  "FROM pg_catalog.pg_type "
@@ -4199,9 +4221,11 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, "
 						  "'-' as typreceive, '-' as typsend, "
+						  "'-' as typanalyze, "
 						  "typinput::oid as typinputoid, "
 						  "typoutput::oid as typoutputoid, "
 						  "0 as typreceiveoid, 0 as typsendoid, "
+						  "0 as typanalyzeoid, "
 						  "typdelim, typdefault, typbyval, typalign, "
 						  "typstorage "
 						  "FROM pg_type "
@@ -4213,9 +4237,11 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, "
 						  "'-' as typreceive, '-' as typsend, "
+						  "'-' as typanalyze, "
 						  "typinput::oid as typinputoid, "
 						  "typoutput::oid as typoutputoid, "
 						  "0 as typreceiveoid, 0 as typsendoid, "
+						  "0 as typanalyzeoid, "
 						  "typdelim, typdefault, typbyval, typalign, "
 						  "'p'::char as typstorage "
 						  "FROM pg_type "
@@ -4240,10 +4266,12 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 	typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
 	typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
 	typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
+	typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
 	typinputoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typinputoid")));
 	typoutputoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typoutputoid")));
 	typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
 	typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
+	typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
 	typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
 	if (PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
 		typdefault = NULL;
@@ -4270,13 +4298,15 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 
 	if (fout->remoteVersion >= 70300)
 	{
-		/* regproc result is correctly quoted in 7.3 */
+		/* regproc result is correctly quoted as of 7.3 */
 		appendPQExpBuffer(q, ",\n    INPUT = %s", typinput);
 		appendPQExpBuffer(q, ",\n    OUTPUT = %s", typoutput);
 		if (OidIsValid(typreceiveoid))
 			appendPQExpBuffer(q, ",\n    RECEIVE = %s", typreceive);
 		if (OidIsValid(typsendoid))
 			appendPQExpBuffer(q, ",\n    SEND = %s", typsend);
+		if (OidIsValid(typanalyzeoid))
+			appendPQExpBuffer(q, ",\n    ANALYZE = %s", typanalyze);
 	}
 	else
 	{
@@ -4284,7 +4314,7 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 		/* cannot combine these because fmtId uses static result area */
 		appendPQExpBuffer(q, ",\n    INPUT = %s", fmtId(typinput));
 		appendPQExpBuffer(q, ",\n    OUTPUT = %s", fmtId(typoutput));
-		/* no chance that receive/send need be printed */
+		/* no chance that receive/send/analyze need be printed */
 	}
 
 	if (typdefault != NULL)
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index fa6a7f1e605240796f63be6de3553ece0530a626..7fdadf5206a511d0216ca80e54268febb2806bc3 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.217 2004/02/03 08:29:56 joe Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.218 2004/02/12 23:41:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200402021
+#define CATALOG_VERSION_NO	200402121
 
 #endif
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 6098f92d8978b60d199f674c35239388510a820d..754878f0148be7680d3e42e4b8a8d076eb7647fe 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.107 2004/01/06 23:55:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.108 2004/02/12 23:41:04 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -240,14 +240,15 @@ typedef FormData_pg_attribute *Form_pg_attribute;
 { 1247, {"typoutput"},	   24, -1,	4, 12, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
 { 1247, {"typreceive"},    24, -1,	4, 13, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
 { 1247, {"typsend"},	   24, -1,	4, 14, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typalign"},	   18, -1,	1, 15, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typstorage"},    18, -1,	1, 16, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typnotnull"},    16, -1,	1, 17, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typbasetype"},   26, -1,	4, 18, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typtypmod"},	   23, -1,	4, 19, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typndims"},	   23, -1,	4, 20, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typdefaultbin"}, 25, -1, -1, 21, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
-{ 1247, {"typdefault"},    25, -1, -1, 22, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }
+{ 1247, {"typanalyze"},	   24, -1,	4, 15, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
+{ 1247, {"typalign"},	   18, -1,	1, 16, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
+{ 1247, {"typstorage"},    18, -1,	1, 17, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
+{ 1247, {"typnotnull"},    16, -1,	1, 18, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
+{ 1247, {"typbasetype"},   26, -1,	4, 19, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
+{ 1247, {"typtypmod"},	   23, -1,	4, 20, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
+{ 1247, {"typndims"},	   23, -1,	4, 21, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
+{ 1247, {"typdefaultbin"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
+{ 1247, {"typdefault"},    25, -1, -1, 23, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }
 
 
 DATA(insert ( 1247 typname			19 -1 NAMEDATALEN	1 0 -1 -1 f p f i t f f t 0));
@@ -264,14 +265,15 @@ DATA(insert ( 1247 typinput			24 -1 4  11 0 -1 -1 t p f i t f f t 0));
 DATA(insert ( 1247 typoutput		24 -1 4  12 0 -1 -1 t p f i t f f t 0));
 DATA(insert ( 1247 typreceive		24 -1 4  13 0 -1 -1 t p f i t f f t 0));
 DATA(insert ( 1247 typsend			24 -1 4  14 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typalign			18 -1 1  15 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typstorage		18 -1 1  16 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typnotnull		16 -1 1  17 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typbasetype		26 -1 4  18 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typtypmod		23 -1 4  19 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typndims			23 -1 4  20 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typdefaultbin	25 -1 -1 21 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1247 typdefault		25 -1 -1 22 0 -1 -1 f x f i f f f t 0));
+DATA(insert ( 1247 typanalyze		24 -1 4  15 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1247 typalign			18 -1 1  16 0 -1 -1 t p f c t f f t 0));
+DATA(insert ( 1247 typstorage		18 -1 1  17 0 -1 -1 t p f c t f f t 0));
+DATA(insert ( 1247 typnotnull		16 -1 1  18 0 -1 -1 t p f c t f f t 0));
+DATA(insert ( 1247 typbasetype		26 -1 4  19 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1247 typtypmod		23 -1 4  20 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1247 typndims			23 -1 4  21 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1247 typdefaultbin	25 -1 -1 22 0 -1 -1 f x f i f f f t 0));
+DATA(insert ( 1247 typdefault		25 -1 -1 23 0 -1 -1 f x f i f f f t 0));
 DATA(insert ( 1247 ctid				27 0  6  -1 0 -1 -1 f p f i t f f t 0));
 DATA(insert ( 1247 oid				26 0  4  -2 0 -1 -1 t p f i t f f t 0));
 DATA(insert ( 1247 xmin				28 0  4  -3 0 -1 -1 t p f i t f f t 0));
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 0743976ddcef3cee24e3c44a7a9b268d0144382d..eafe5ceb3285641c273afcd2a6a663fb1d7d41fc 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.79 2004/01/06 23:55:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.80 2004/02/12 23:41:04 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -134,7 +134,7 @@ typedef FormData_pg_class *Form_pg_class;
  * ----------------
  */
 
-DATA(insert OID = 1247 (  pg_type		PGNSP 71 PGUID 0 1247 0 0 0 0 f f r 22 0 0 0 0 0 t f f f _null_ ));
+DATA(insert OID = 1247 (  pg_type		PGNSP 71 PGUID 0 1247 0 0 0 0 f f r 23 0 0 0 0 0 t f f f _null_ ));
 DESCR("");
 DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 PGUID 0 1249 0 0 0 0 f f r 18 0 0 0 0 0 f f f f _null_ ));
 DESCR("");
diff --git a/src/include/catalog/pg_statistic.h b/src/include/catalog/pg_statistic.h
index 07a7f599461dd34a925188164f54bf120403c63b..254ffbfa8d22dbb546b478385fedcc1dc7366a91 100644
--- a/src/include/catalog/pg_statistic.h
+++ b/src/include/catalog/pg_statistic.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_statistic.h,v 1.23 2003/11/29 22:40:58 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_statistic.h,v 1.24 2004/02/12 23:41:04 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -164,11 +164,30 @@ typedef FormData_pg_statistic *Form_pg_statistic;
 /*
  * Currently, three statistical slot "kinds" are defined: most common values,
  * histogram, and correlation.	Additional "kinds" will probably appear in
- * future to help cope with non-scalar datatypes.
+ * future to help cope with non-scalar datatypes.  Also, custom data types
+ * can define their own "kind" codes by mutual agreement between a custom
+ * typanalyze routine and the selectivity estimation functions of the type's
+ * operators.
  *
  * Code reading the pg_statistic relation should not assume that a particular
  * data "kind" will appear in any particular slot.	Instead, search the
- * stakind fields to see if the desired data is available.
+ * stakind fields to see if the desired data is available.  (The standard
+ * function get_attstatsslot() may be used for this.)
+ */
+
+/*
+ * The present allocation of "kind" codes is:
+ *
+ *	1-99:		reserved for assignment by the core PostgreSQL project
+ *				(values in this range will be documented in this file)
+ *	100-199:	reserved for assignment by the PostGIS project
+ *				(values to be documented in PostGIS documentation)
+ *	200-9999:	reserved for future public assignments
+ *
+ * For private use you may choose a "kind" code at random in the range
+ * 10000-30000.  However, for code that is to be widely disseminated it is
+ * better to obtain a publicly defined "kind" code by request from the
+ * PostgreSQL Global Development Group.
  */
 
 /*
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 7b0e384f584f3be09d58d485a35b595dd1754888..6daf1579df0c4660221c21f287f1322dd859fd86 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.148 2003/11/29 22:40:58 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.149 2004/02/12 23:41:04 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -104,6 +104,11 @@ CATALOG(pg_type) BOOTSTRAP
 	regproc		typreceive;		/* binary format (optional) */
 	regproc		typsend;
 
+	/*
+	 * Custom ANALYZE procedure for the datatype (0 selects the default).
+	 */
+	regproc		typanalyze;
+
 	/* ----------------
 	 * typalign is the alignment required when storing a value of this
 	 * type.  It applies to storage on disk as well as most
@@ -200,7 +205,7 @@ typedef FormData_pg_type *Form_pg_type;
  *		compiler constants for pg_type
  * ----------------
  */
-#define Natts_pg_type					22
+#define Natts_pg_type					23
 #define Anum_pg_type_typname			1
 #define Anum_pg_type_typnamespace		2
 #define Anum_pg_type_typowner			3
@@ -215,14 +220,15 @@ typedef FormData_pg_type *Form_pg_type;
 #define Anum_pg_type_typoutput			12
 #define Anum_pg_type_typreceive			13
 #define Anum_pg_type_typsend			14
-#define Anum_pg_type_typalign			15
-#define Anum_pg_type_typstorage			16
-#define Anum_pg_type_typnotnull			17
-#define Anum_pg_type_typbasetype		18
-#define Anum_pg_type_typtypmod			19
-#define Anum_pg_type_typndims			20
-#define Anum_pg_type_typdefaultbin		21
-#define Anum_pg_type_typdefault			22
+#define Anum_pg_type_typanalyze			15
+#define Anum_pg_type_typalign			16
+#define Anum_pg_type_typstorage			17
+#define Anum_pg_type_typnotnull			18
+#define Anum_pg_type_typbasetype		19
+#define Anum_pg_type_typtypmod			20
+#define Anum_pg_type_typndims			21
+#define Anum_pg_type_typdefaultbin		22
+#define Anum_pg_type_typdefault			23
 
 
 /* ----------------
@@ -238,82 +244,82 @@ typedef FormData_pg_type *Form_pg_type;
 */
 
 /* OIDS 1 - 99 */
-DATA(insert OID = 16 (	bool	   PGNSP PGUID	1 t b t \054 0	 0 boolin boolout boolrecv boolsend c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 16 (	bool	   PGNSP PGUID	1 t b t \054 0	 0 boolin boolout boolrecv boolsend - c p f 0 -1 0 _null_ _null_ ));
 DESCR("boolean, 'true'/'false'");
 #define BOOLOID			16
 
-DATA(insert OID = 17 (	bytea	   PGNSP PGUID -1 f b t \054 0	0 byteain byteaout bytearecv byteasend i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 17 (	bytea	   PGNSP PGUID -1 f b t \054 0	0 byteain byteaout bytearecv byteasend - i x f 0 -1 0 _null_ _null_ ));
 DESCR("variable-length string, binary values escaped");
 #define BYTEAOID		17
 
-DATA(insert OID = 18 (	char	   PGNSP PGUID	1 t b t \054 0	 0 charin charout charrecv charsend c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 18 (	char	   PGNSP PGUID	1 t b t \054 0	 0 charin charout charrecv charsend - c p f 0 -1 0 _null_ _null_ ));
 DESCR("single character");
 #define CHAROID			18
 
-DATA(insert OID = 19 (	name	   PGNSP PGUID NAMEDATALEN f b t \054 0 18 namein nameout namerecv namesend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 19 (	name	   PGNSP PGUID NAMEDATALEN f b t \054 0 18 namein nameout namerecv namesend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("63-character type for storing system identifiers");
 #define NAMEOID			19
 
-DATA(insert OID = 20 (	int8	   PGNSP PGUID	8 f b t \054 0	 0 int8in int8out int8recv int8send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 20 (	int8	   PGNSP PGUID	8 f b t \054 0	 0 int8in int8out int8recv int8send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("~18 digit integer, 8-byte storage");
 #define INT8OID			20
 
-DATA(insert OID = 21 (	int2	   PGNSP PGUID	2 t b t \054 0	 0 int2in int2out int2recv int2send s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 21 (	int2	   PGNSP PGUID	2 t b t \054 0	 0 int2in int2out int2recv int2send - s p f 0 -1 0 _null_ _null_ ));
 DESCR("-32 thousand to 32 thousand, 2-byte storage");
 #define INT2OID			21
 
-DATA(insert OID = 22 (	int2vector PGNSP PGUID INDEX_MAX_KEYS*2 f b t \054 0  21 int2vectorin int2vectorout int2vectorrecv int2vectorsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 22 (	int2vector PGNSP PGUID INDEX_MAX_KEYS*2 f b t \054 0  21 int2vectorin int2vectorout int2vectorrecv int2vectorsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("array of INDEX_MAX_KEYS int2 integers, used in system tables");
 #define INT2VECTOROID	22
 
-DATA(insert OID = 23 (	int4	   PGNSP PGUID	4 t b t \054 0	 0 int4in int4out int4recv int4send i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 23 (	int4	   PGNSP PGUID	4 t b t \054 0	 0 int4in int4out int4recv int4send - i p f 0 -1 0 _null_ _null_ ));
 DESCR("-2 billion to 2 billion integer, 4-byte storage");
 #define INT4OID			23
 
-DATA(insert OID = 24 (	regproc    PGNSP PGUID	4 t b t \054 0	 0 regprocin regprocout regprocrecv regprocsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 24 (	regproc    PGNSP PGUID	4 t b t \054 0	 0 regprocin regprocout regprocrecv regprocsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered procedure");
 #define REGPROCOID		24
 
-DATA(insert OID = 25 (	text	   PGNSP PGUID -1 f b t \054 0	0 textin textout textrecv textsend i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 25 (	text	   PGNSP PGUID -1 f b t \054 0	0 textin textout textrecv textsend - i x f 0 -1 0 _null_ _null_ ));
 DESCR("variable-length string, no limit specified");
 #define TEXTOID			25
 
-DATA(insert OID = 26 (	oid		   PGNSP PGUID	4 t b t \054 0	 0 oidin oidout oidrecv oidsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 26 (	oid		   PGNSP PGUID	4 t b t \054 0	 0 oidin oidout oidrecv oidsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("object identifier(oid), maximum 4 billion");
 #define OIDOID			26
 
-DATA(insert OID = 27 (	tid		   PGNSP PGUID	6 f b t \054 0	 0 tidin tidout tidrecv tidsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 27 (	tid		   PGNSP PGUID	6 f b t \054 0	 0 tidin tidout tidrecv tidsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("(Block, offset), physical location of tuple");
 #define TIDOID		27
 
-DATA(insert OID = 28 (	xid		   PGNSP PGUID	4 t b t \054 0	 0 xidin xidout xidrecv xidsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 28 (	xid		   PGNSP PGUID	4 t b t \054 0	 0 xidin xidout xidrecv xidsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("transaction id");
 #define XIDOID 28
 
-DATA(insert OID = 29 (	cid		   PGNSP PGUID	4 t b t \054 0	 0 cidin cidout cidrecv cidsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 29 (	cid		   PGNSP PGUID	4 t b t \054 0	 0 cidin cidout cidrecv cidsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("command identifier type, sequence in transaction id");
 #define CIDOID 29
 
-DATA(insert OID = 30 (	oidvector  PGNSP PGUID INDEX_MAX_KEYS*4 f b t \054 0  26 oidvectorin oidvectorout oidvectorrecv oidvectorsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 30 (	oidvector  PGNSP PGUID INDEX_MAX_KEYS*4 f b t \054 0  26 oidvectorin oidvectorout oidvectorrecv oidvectorsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("array of INDEX_MAX_KEYS oids, used in system tables");
 #define OIDVECTOROID	30
 
-DATA(insert OID = 32 (	SET		   PGNSP PGUID -1 f b t \054 0	 0 unknownin unknownout - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 32 (	SET		   PGNSP PGUID -1 f b t \054 0	 0 unknownin unknownout - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("set of tuples");
 
-DATA(insert OID = 71 (	pg_type		 PGNSP PGUID 4 t c t \054 1247 0 record_in record_out record_recv record_send i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 75 (	pg_attribute PGNSP PGUID 4 t c t \054 1249 0 record_in record_out record_recv record_send i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 81 (	pg_proc		 PGNSP PGUID 4 t c t \054 1255 0 record_in record_out record_recv record_send i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 83 (	pg_class	 PGNSP PGUID 4 t c t \054 1259 0 record_in record_out record_recv record_send i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 86 (	pg_shadow	 PGNSP PGUID 4 t c t \054 1260 0 record_in record_out record_recv record_send i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 87 (	pg_group	 PGNSP PGUID 4 t c t \054 1261 0 record_in record_out record_recv record_send i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 88 (	pg_database  PGNSP PGUID 4 t c t \054 1262 0 record_in record_out record_recv record_send i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 71 (	pg_type		 PGNSP PGUID 4 t c t \054 1247 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 75 (	pg_attribute PGNSP PGUID 4 t c t \054 1249 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 81 (	pg_proc		 PGNSP PGUID 4 t c t \054 1255 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 83 (	pg_class	 PGNSP PGUID 4 t c t \054 1259 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 86 (	pg_shadow	 PGNSP PGUID 4 t c t \054 1260 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 87 (	pg_group	 PGNSP PGUID 4 t c t \054 1261 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 88 (	pg_database  PGNSP PGUID 4 t c t \054 1262 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 100 - 199 */
 
 /* OIDS 200 - 299 */
 
-DATA(insert OID = 210 (  smgr	   PGNSP PGUID 2 t b t \054 0 0 smgrin smgrout - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 210 (  smgr	   PGNSP PGUID 2 t b t \054 0 0 smgrin smgrout - - - s p f 0 -1 0 _null_ _null_ ));
 DESCR("storage manager");
 
 /* OIDS 300 - 399 */
@@ -323,192 +329,192 @@ DESCR("storage manager");
 /* OIDS 500 - 599 */
 
 /* OIDS 600 - 699 */
-DATA(insert OID = 600 (  point	   PGNSP PGUID 16 f b t \054 0 701 point_in point_out point_recv point_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 600 (  point	   PGNSP PGUID 16 f b t \054 0 701 point_in point_out point_recv point_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric point '(x, y)'");
 #define POINTOID		600
-DATA(insert OID = 601 (  lseg	   PGNSP PGUID 32 f b t \054 0 600 lseg_in lseg_out lseg_recv lseg_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 601 (  lseg	   PGNSP PGUID 32 f b t \054 0 600 lseg_in lseg_out lseg_recv lseg_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric line segment '(pt1,pt2)'");
 #define LSEGOID			601
-DATA(insert OID = 602 (  path	   PGNSP PGUID -1 f b t \054 0 0 path_in path_out path_recv path_send d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 602 (  path	   PGNSP PGUID -1 f b t \054 0 0 path_in path_out path_recv path_send - d x f 0 -1 0 _null_ _null_ ));
 DESCR("geometric path '(pt1,...)'");
 #define PATHOID			602
-DATA(insert OID = 603 (  box	   PGNSP PGUID 32 f b t \073 0 600 box_in box_out box_recv box_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 603 (  box	   PGNSP PGUID 32 f b t \073 0 600 box_in box_out box_recv box_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric box '(lower left,upper right)'");
 #define BOXOID			603
-DATA(insert OID = 604 (  polygon   PGNSP PGUID -1 f b t \054 0	 0 poly_in poly_out poly_recv poly_send d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 604 (  polygon   PGNSP PGUID -1 f b t \054 0	 0 poly_in poly_out poly_recv poly_send - d x f 0 -1 0 _null_ _null_ ));
 DESCR("geometric polygon '(pt1,...)'");
 #define POLYGONOID		604
 
-DATA(insert OID = 628 (  line	   PGNSP PGUID 32 f b t \054 0 701 line_in line_out line_recv line_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 628 (  line	   PGNSP PGUID 32 f b t \054 0 701 line_in line_out line_recv line_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric line (not implemented)'");
 #define LINEOID			628
-DATA(insert OID = 629 (  _line	   PGNSP PGUID	-1 f b t \054 0 628 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 629 (  _line	   PGNSP PGUID	-1 f b t \054 0 628 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
 DESCR("");
 
 /* OIDS 700 - 799 */
 
-DATA(insert OID = 700 (  float4    PGNSP PGUID	4 f b t \054 0	 0 float4in float4out float4recv float4send i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 700 (  float4    PGNSP PGUID	4 f b t \054 0	 0 float4in float4out float4recv float4send - i p f 0 -1 0 _null_ _null_ ));
 DESCR("single-precision floating point number, 4-byte storage");
 #define FLOAT4OID 700
-DATA(insert OID = 701 (  float8    PGNSP PGUID	8 f b t \054 0	 0 float8in float8out float8recv float8send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 701 (  float8    PGNSP PGUID	8 f b t \054 0	 0 float8in float8out float8recv float8send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("double-precision floating point number, 8-byte storage");
 #define FLOAT8OID 701
-DATA(insert OID = 702 (  abstime   PGNSP PGUID	4 t b t \054 0	 0 abstimein abstimeout abstimerecv abstimesend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 702 (  abstime   PGNSP PGUID	4 t b t \054 0	 0 abstimein abstimeout abstimerecv abstimesend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("absolute, limited-range date and time (Unix system time)");
 #define ABSTIMEOID		702
-DATA(insert OID = 703 (  reltime   PGNSP PGUID	4 t b t \054 0	 0 reltimein reltimeout reltimerecv reltimesend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 703 (  reltime   PGNSP PGUID	4 t b t \054 0	 0 reltimein reltimeout reltimerecv reltimesend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("relative, limited-range time interval (Unix delta time)");
 #define RELTIMEOID		703
-DATA(insert OID = 704 (  tinterval PGNSP PGUID 12 f b t \054 0	 0 tintervalin tintervalout tintervalrecv tintervalsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 704 (  tinterval PGNSP PGUID 12 f b t \054 0	 0 tintervalin tintervalout tintervalrecv tintervalsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("(abstime,abstime), time interval");
 #define TINTERVALOID	704
-DATA(insert OID = 705 (  unknown   PGNSP PGUID -1 f b t \054 0	 0 unknownin unknownout unknownrecv unknownsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 705 (  unknown   PGNSP PGUID -1 f b t \054 0	 0 unknownin unknownout unknownrecv unknownsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("");
 #define UNKNOWNOID		705
 
-DATA(insert OID = 718 (  circle    PGNSP PGUID	24 f b t \054 0 0 circle_in circle_out circle_recv circle_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 718 (  circle    PGNSP PGUID	24 f b t \054 0 0 circle_in circle_out circle_recv circle_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric circle '(center,radius)'");
 #define CIRCLEOID		718
-DATA(insert OID = 719 (  _circle   PGNSP PGUID	-1 f b t \054 0  718 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 790 (  money	   PGNSP PGUID	 4 f b t \054 0 0 cash_in cash_out cash_recv cash_send i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 719 (  _circle   PGNSP PGUID	-1 f b t \054 0  718 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 790 (  money	   PGNSP PGUID	 4 f b t \054 0 0 cash_in cash_out cash_recv cash_send - i p f 0 -1 0 _null_ _null_ ));
 DESCR("monetary amounts, $d,ddd.cc");
 #define CASHOID 790
-DATA(insert OID = 791 (  _money    PGNSP PGUID	-1 f b t \054 0  790 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 791 (  _money    PGNSP PGUID	-1 f b t \054 0  790 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr    PGNSP PGUID	6 f b t \054 0 0 macaddr_in macaddr_out macaddr_recv macaddr_send i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr    PGNSP PGUID	6 f b t \054 0 0 macaddr_in macaddr_out macaddr_recv macaddr_send - i p f 0 -1 0 _null_ _null_ ));
 DESCR("XX:XX:XX:XX:XX:XX, MAC address");
 #define MACADDROID 829
-DATA(insert OID = 869 ( inet	   PGNSP PGUID	-1 f b t \054 0 0 inet_in inet_out inet_recv inet_send i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 869 ( inet	   PGNSP PGUID	-1 f b t \054 0 0 inet_in inet_out inet_recv inet_send - i p f 0 -1 0 _null_ _null_ ));
 DESCR("IP address/netmask, host address, netmask optional");
 #define INETOID 869
-DATA(insert OID = 650 ( cidr	   PGNSP PGUID	-1 f b t \054 0 0 cidr_in cidr_out cidr_recv cidr_send i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 650 ( cidr	   PGNSP PGUID	-1 f b t \054 0 0 cidr_in cidr_out cidr_recv cidr_send - i p f 0 -1 0 _null_ _null_ ));
 DESCR("network IP address/netmask, network address");
 #define CIDROID 650
 
 /* OIDS 900 - 999 */
 
 /* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 (  _bool		 PGNSP PGUID -1 f b t \054 0	16 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1001 (  _bytea	 PGNSP PGUID -1 f b t \054 0	17 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1002 (  _char		 PGNSP PGUID -1 f b t \054 0	18 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1003 (  _name		 PGNSP PGUID -1 f b t \054 0	19 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1005 (  _int2		 PGNSP PGUID -1 f b t \054 0	21 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1006 (  _int2vector PGNSP PGUID -1 f b t \054 0	22 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1007 (  _int4		 PGNSP PGUID -1 f b t \054 0	23 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1008 (  _regproc	 PGNSP PGUID -1 f b t \054 0	24 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1009 (  _text		 PGNSP PGUID -1 f b t \054 0	25 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1028 (  _oid		 PGNSP PGUID -1 f b t \054 0	26 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1010 (  _tid		 PGNSP PGUID -1 f b t \054 0	27 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1011 (  _xid		 PGNSP PGUID -1 f b t \054 0	28 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1012 (  _cid		 PGNSP PGUID -1 f b t \054 0	29 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1013 (  _oidvector PGNSP PGUID -1 f b t \054 0	30 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1014 (  _bpchar	 PGNSP PGUID -1 f b t \054 0 1042 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1015 (  _varchar	 PGNSP PGUID -1 f b t \054 0 1043 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1016 (  _int8		 PGNSP PGUID -1 f b t \054 0	20 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1017 (  _point	 PGNSP PGUID -1 f b t \054 0 600 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1018 (  _lseg		 PGNSP PGUID -1 f b t \054 0 601 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1019 (  _path		 PGNSP PGUID -1 f b t \054 0 602 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1020 (  _box		 PGNSP PGUID -1 f b t \073 0 603 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1021 (  _float4	 PGNSP PGUID -1 f b t \054 0 700 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1022 (  _float8	 PGNSP PGUID -1 f b t \054 0 701 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1023 (  _abstime	 PGNSP PGUID -1 f b t \054 0 702 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1024 (  _reltime	 PGNSP PGUID -1 f b t \054 0 703 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1025 (  _tinterval PGNSP PGUID -1 f b t \054 0 704 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1027 (  _polygon	 PGNSP PGUID -1 f b t \054 0 604 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1033 (  aclitem	 PGNSP PGUID 12 f b t \054 0 0 aclitemin aclitemout - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1000 (  _bool		 PGNSP PGUID -1 f b t \054 0	16 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1001 (  _bytea	 PGNSP PGUID -1 f b t \054 0	17 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1002 (  _char		 PGNSP PGUID -1 f b t \054 0	18 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1003 (  _name		 PGNSP PGUID -1 f b t \054 0	19 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1005 (  _int2		 PGNSP PGUID -1 f b t \054 0	21 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1006 (  _int2vector PGNSP PGUID -1 f b t \054 0	22 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1007 (  _int4		 PGNSP PGUID -1 f b t \054 0	23 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1008 (  _regproc	 PGNSP PGUID -1 f b t \054 0	24 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1009 (  _text		 PGNSP PGUID -1 f b t \054 0	25 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1028 (  _oid		 PGNSP PGUID -1 f b t \054 0	26 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1010 (  _tid		 PGNSP PGUID -1 f b t \054 0	27 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1011 (  _xid		 PGNSP PGUID -1 f b t \054 0	28 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1012 (  _cid		 PGNSP PGUID -1 f b t \054 0	29 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1013 (  _oidvector PGNSP PGUID -1 f b t \054 0	30 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1014 (  _bpchar	 PGNSP PGUID -1 f b t \054 0 1042 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1015 (  _varchar	 PGNSP PGUID -1 f b t \054 0 1043 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1016 (  _int8		 PGNSP PGUID -1 f b t \054 0	20 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1017 (  _point	 PGNSP PGUID -1 f b t \054 0 600 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1018 (  _lseg		 PGNSP PGUID -1 f b t \054 0 601 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1019 (  _path		 PGNSP PGUID -1 f b t \054 0 602 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1020 (  _box		 PGNSP PGUID -1 f b t \073 0 603 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1021 (  _float4	 PGNSP PGUID -1 f b t \054 0 700 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1022 (  _float8	 PGNSP PGUID -1 f b t \054 0 701 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1023 (  _abstime	 PGNSP PGUID -1 f b t \054 0 702 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1024 (  _reltime	 PGNSP PGUID -1 f b t \054 0 703 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1025 (  _tinterval PGNSP PGUID -1 f b t \054 0 704 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1027 (  _polygon	 PGNSP PGUID -1 f b t \054 0 604 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1033 (  aclitem	 PGNSP PGUID 12 f b t \054 0 0 aclitemin aclitemout - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("access control list");
 #define ACLITEMOID		1033
-DATA(insert OID = 1034 (  _aclitem	 PGNSP PGUID -1 f b t \054 0 1033 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1040 (  _macaddr	 PGNSP PGUID -1 f b t \054 0  829 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1041 (  _inet    PGNSP PGUID -1 f b t \054 0	869 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 651  (  _cidr    PGNSP PGUID -1 f b t \054 0	650 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1042 ( bpchar		 PGNSP PGUID -1 f b t \054 0	0 bpcharin bpcharout bpcharrecv bpcharsend i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1034 (  _aclitem	 PGNSP PGUID -1 f b t \054 0 1033 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1040 (  _macaddr	 PGNSP PGUID -1 f b t \054 0  829 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1041 (  _inet    PGNSP PGUID -1 f b t \054 0	869 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 651  (  _cidr    PGNSP PGUID -1 f b t \054 0	650 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar		 PGNSP PGUID -1 f b t \054 0	0 bpcharin bpcharout bpcharrecv bpcharsend - i x f 0 -1 0 _null_ _null_ ));
 DESCR("char(length), blank-padded string, fixed storage length");
 #define BPCHAROID		1042
-DATA(insert OID = 1043 ( varchar	 PGNSP PGUID -1 f b t \054 0	0 varcharin varcharout varcharrecv varcharsend i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar	 PGNSP PGUID -1 f b t \054 0	0 varcharin varcharout varcharrecv varcharsend - i x f 0 -1 0 _null_ _null_ ));
 DESCR("varchar(length), non-blank-padded string, variable storage length");
 #define VARCHAROID		1043
 
-DATA(insert OID = 1082 ( date		 PGNSP PGUID	4 t b t \054 0	0 date_in date_out date_recv date_send i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1082 ( date		 PGNSP PGUID	4 t b t \054 0	0 date_in date_out date_recv date_send - i p f 0 -1 0 _null_ _null_ ));
 DESCR("ANSI SQL date");
 #define DATEOID			1082
-DATA(insert OID = 1083 ( time		 PGNSP PGUID	8 f b t \054 0	0 time_in time_out time_recv time_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1083 ( time		 PGNSP PGUID	8 f b t \054 0	0 time_in time_out time_recv time_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("hh:mm:ss, ANSI SQL time");
 #define TIMEOID			1083
 
 /* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp	 PGNSP PGUID	8 f b t \054 0	0 timestamp_in timestamp_out timestamp_recv timestamp_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp	 PGNSP PGUID	8 f b t \054 0	0 timestamp_in timestamp_out timestamp_recv timestamp_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("date and time");
 #define TIMESTAMPOID	1114
-DATA(insert OID = 1115 ( _timestamp  PGNSP PGUID	-1 f b t \054 0 1114 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1182 ( _date		 PGNSP PGUID	-1 f b t \054 0 1082 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1183 ( _time		 PGNSP PGUID	-1 f b t \054 0 1083 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID	8 f b t \054 0	0 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp  PGNSP PGUID	-1 f b t \054 0 1114 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1182 ( _date		 PGNSP PGUID	-1 f b t \054 0 1082 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1183 ( _time		 PGNSP PGUID	-1 f b t \054 0 1083 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID	8 f b t \054 0	0 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("date and time with time zone");
 #define TIMESTAMPTZOID	1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b t \054 0	1184 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1186 ( interval	 PGNSP PGUID 12 f b t \054 0	0 interval_in interval_out interval_recv interval_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b t \054 0	1184 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1186 ( interval	 PGNSP PGUID 12 f b t \054 0	0 interval_in interval_out interval_recv interval_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("@ <number> <units>, time interval");
 #define INTERVALOID		1186
-DATA(insert OID = 1187 ( _interval	 PGNSP PGUID	-1 f b t \054 0 1186 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval	 PGNSP PGUID	-1 f b t \054 0 1186 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 (  _numeric	 PGNSP PGUID -1 f b t \054 0	1700 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz		 PGNSP PGUID 12 f b t \054 0	0 timetz_in timetz_out timetz_recv timetz_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1231 (  _numeric	 PGNSP PGUID -1 f b t \054 0	1700 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz		 PGNSP PGUID 12 f b t \054 0	0 timetz_in timetz_out timetz_recv timetz_send - d p f 0 -1 0 _null_ _null_ ));
 DESCR("hh:mm:ss, ANSI SQL time");
 #define TIMETZOID		1266
-DATA(insert OID = 1270 ( _timetz	 PGNSP PGUID -1 f b t \054 0	1266 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz	 PGNSP PGUID -1 f b t \054 0	1266 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit		 PGNSP PGUID -1 f b t \054 0	0 bit_in bit_out bit_recv bit_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1560 ( bit		 PGNSP PGUID -1 f b t \054 0	0 bit_in bit_out bit_recv bit_send - i x f 0 -1 0 _null_ _null_ ));
 DESCR("fixed-length bit string");
 #define BITOID	 1560
-DATA(insert OID = 1561 ( _bit		 PGNSP PGUID -1 f b t \054 0	1560 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit		 PGNSP PGUID -1 f b t \054 0	0 varbit_in varbit_out varbit_recv varbit_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit		 PGNSP PGUID -1 f b t \054 0	1560 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit		 PGNSP PGUID -1 f b t \054 0	0 varbit_in varbit_out varbit_recv varbit_send - i x f 0 -1 0 _null_ _null_ ));
 DESCR("variable-length bit string");
 #define VARBITOID	  1562
-DATA(insert OID = 1563 ( _varbit	 PGNSP PGUID -1 f b t \054 0	1562 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit	 PGNSP PGUID -1 f b t \054 0	1562 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1600 - 1699 */
 
 /* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric	   PGNSP PGUID -1 f b t \054 0	0 numeric_in numeric_out numeric_recv numeric_send i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric	   PGNSP PGUID -1 f b t \054 0	0 numeric_in numeric_out numeric_recv numeric_send - i m f 0 -1 0 _null_ _null_ ));
 DESCR("numeric(precision, decimal), arbitrary precision number");
 #define NUMERICOID		1700
 
-DATA(insert OID = 1790 ( refcursor	   PGNSP PGUID -1 f b t \054 0	0 textin textout textrecv textsend i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor	   PGNSP PGUID -1 f b t \054 0	0 textin textout textrecv textsend - i x f 0 -1 0 _null_ _null_ ));
 DESCR("reference cursor (portal name)");
 #define REFCURSOROID	1790
 
 /* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor    PGNSP PGUID -1 f b t \054 0 1790 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor    PGNSP PGUID -1 f b t \054 0 1790 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 
-DATA(insert OID = 2202 ( regprocedure  PGNSP PGUID	4 t b t \054 0	 0 regprocedurein regprocedureout regprocedurerecv regproceduresend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure  PGNSP PGUID	4 t b t \054 0	 0 regprocedurein regprocedureout regprocedurerecv regproceduresend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered procedure (with args)");
 #define REGPROCEDUREOID 2202
 
-DATA(insert OID = 2203 ( regoper	   PGNSP PGUID	4 t b t \054 0	 0 regoperin regoperout regoperrecv regopersend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper	   PGNSP PGUID	4 t b t \054 0	 0 regoperin regoperout regoperrecv regopersend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered operator");
 #define REGOPEROID		2203
 
-DATA(insert OID = 2204 ( regoperator   PGNSP PGUID	4 t b t \054 0	 0 regoperatorin regoperatorout regoperatorrecv regoperatorsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator   PGNSP PGUID	4 t b t \054 0	 0 regoperatorin regoperatorout regoperatorrecv regoperatorsend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered operator (with args)");
 #define REGOPERATOROID	2204
 
-DATA(insert OID = 2205 ( regclass	   PGNSP PGUID	4 t b t \054 0	 0 regclassin regclassout regclassrecv regclasssend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass	   PGNSP PGUID	4 t b t \054 0	 0 regclassin regclassout regclassrecv regclasssend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered class");
 #define REGCLASSOID		2205
 
-DATA(insert OID = 2206 ( regtype	   PGNSP PGUID	4 t b t \054 0	 0 regtypein regtypeout regtyperecv regtypesend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype	   PGNSP PGUID	4 t b t \054 0	 0 regtypein regtypeout regtyperecv regtypesend - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered type");
 #define REGTYPEOID		2206
 
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b t \054 0 2202 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper	   PGNSP PGUID -1 f b t \054 0 2203 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator  PGNSP PGUID -1 f b t \054 0 2204 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass	   PGNSP PGUID -1 f b t \054 0 2205 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype	   PGNSP PGUID -1 f b t \054 0 2206 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b t \054 0 2202 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper	   PGNSP PGUID -1 f b t \054 0 2203 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator  PGNSP PGUID -1 f b t \054 0 2204 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass	   PGNSP PGUID -1 f b t \054 0 2205 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype	   PGNSP PGUID -1 f b t \054 0 2206 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 
 /*
  * pseudo-types
@@ -519,25 +525,25 @@ DATA(insert OID = 2211 ( _regtype	   PGNSP PGUID -1 f b t \054 0 2206 array_in a
  * argument and result types (if supported by the function's implementation
  * language).
  */
-DATA(insert OID = 2249 ( record			PGNSP PGUID  4 t p t \054 0 0 record_in record_out record_recv record_send	i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2249 ( record			PGNSP PGUID  4 t p t \054 0 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
 #define RECORDOID		2249
-DATA(insert OID = 2275 ( cstring		PGNSP PGUID -2 f p t \054 0 0 cstring_in cstring_out cstring_recv cstring_send	c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring		PGNSP PGUID -2 f p t \054 0 0 cstring_in cstring_out cstring_recv cstring_send - c p f 0 -1 0 _null_ _null_ ));
 #define CSTRINGOID		2275
-DATA(insert OID = 2276 ( any			PGNSP PGUID  4 t p t \054 0 0 any_in any_out - -	i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2276 ( any			PGNSP PGUID  4 t p t \054 0 0 any_in any_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYOID			2276
-DATA(insert OID = 2277 ( anyarray		PGNSP PGUID -1 f p t \054 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send	i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray		PGNSP PGUID -1 f p t \054 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - i x f 0 -1 0 _null_ _null_ ));
 #define ANYARRAYOID		2277
-DATA(insert OID = 2278 ( void			PGNSP PGUID  4 t p t \054 0 0 void_in void_out - -	i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2278 ( void			PGNSP PGUID  4 t p t \054 0 0 void_in void_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define VOIDOID			2278
-DATA(insert OID = 2279 ( trigger		PGNSP PGUID  4 t p t \054 0 0 trigger_in trigger_out - -	i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger		PGNSP PGUID  4 t p t \054 0 0 trigger_in trigger_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define TRIGGEROID		2279
-DATA(insert OID = 2280 ( language_handler	PGNSP PGUID  4 t p t \054 0 0 language_handler_in language_handler_out - -	i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler	PGNSP PGUID  4 t p t \054 0 0 language_handler_in language_handler_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define LANGUAGE_HANDLEROID		2280
-DATA(insert OID = 2281 ( internal		PGNSP PGUID  4 t p t \054 0 0 internal_in internal_out - -	i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2281 ( internal		PGNSP PGUID  4 t p t \054 0 0 internal_in internal_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define INTERNALOID		2281
-DATA(insert OID = 2282 ( opaque			PGNSP PGUID  4 t p t \054 0 0 opaque_in opaque_out - -	i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque			PGNSP PGUID  4 t p t \054 0 0 opaque_in opaque_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define OPAQUEOID		2282
-DATA(insert OID = 2283 ( anyelement		PGNSP PGUID  4 t p t \054 0 0 anyelement_in anyelement_out - -	i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement		PGNSP PGUID  4 t p t \054 0 0 anyelement_in anyelement_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYELEMENTOID	2283
 
 /*
@@ -557,6 +563,7 @@ extern Oid TypeCreate(const char *typeName,
 		   Oid outputProcedure,
 		   Oid receiveProcedure,
 		   Oid sendProcedure,
+		   Oid analyzeProcedure,
 		   Oid elementType,
 		   Oid baseType,
 		   const char *defaultTypeValue,
@@ -576,6 +583,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
 						 Oid outputProcedure,
 						 Oid receiveProcedure,
 						 Oid sendProcedure,
+						 Oid analyzeProcedure,
 						 Oid elementType,
 						 Oid baseType,
 						 Node *defaultExpr,
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 61985b0d395b53078b73aef5f5762d3ec5f77033..b80f06835637bfc596e99d5a51e9c05f0e1d7bc5 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.48 2004/02/10 03:42:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.49 2004/02/12 23:41:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,10 +23,86 @@
 #include "rusagestub.h"
 #endif
 
+#include "access/htup.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_statistic.h"
+#include "catalog/pg_type.h"
 #include "nodes/parsenodes.h"
 #include "utils/rel.h"
 
 
+/*----------
+ * ANALYZE builds one of these structs for each attribute (column) that is
+ * to be analyzed.  The struct and subsidiary data are in anl_context,
+ * so they live until the end of the ANALYZE operation.
+ *
+ * The type-specific typanalyze function is passed a pointer to this struct
+ * and must return TRUE to continue analysis, FALSE to skip analysis of this
+ * column.  In the TRUE case it must set the compute_stats and minrows fields,
+ * and can optionally set extra_data to pass additional info to compute_stats.
+ *
+ * The compute_stats routine will be called after sample rows have been
+ * gathered.  Aside from this struct, it is passed:
+ *		attnum: attribute number within the supplied tuples
+ *		tupDesc: tuple descriptor for the supplied tuples
+ *		totalrows: estimated total number of rows in relation
+ *		rows: an array of the sample tuples
+ *		numrows: the number of sample tuples
+ * Note that the passed attnum and tupDesc could possibly be different from
+ * what one would expect by looking at the pg_attribute row.  It is important
+ * to use these values for extracting attribute values from the given rows
+ * (and not for any other purpose).
+ *
+ * compute_stats should set stats_valid TRUE if it is able to compute
+ * any useful statistics.  If it does, the remainder of the struct holds
+ * the information to be stored in a pg_statistic row for the column.  Be
+ * careful to allocate any pointed-to data in anl_context, which will NOT
+ * be CurrentMemoryContext when compute_stats is called.
+ *----------
+ */
+typedef struct VacAttrStats
+{
+	/*
+	 * These fields are set up by the main ANALYZE code before invoking
+	 * the type-specific typanalyze function.
+	 */
+	Form_pg_attribute attr;		/* copy of pg_attribute row for column */
+	Form_pg_type attrtype;		/* copy of pg_type row for column */
+	MemoryContext anl_context;	/* where to save long-lived data */
+
+	/*
+	 * These fields must be filled in by the typanalyze routine,
+	 * unless it returns FALSE.
+	 */
+	void (*compute_stats) (struct VacAttrStats *stats, int attnum,
+						   TupleDesc tupDesc, double totalrows,
+						   HeapTuple *rows, int numrows);
+	int			minrows;		/* Minimum # of rows wanted for stats */
+	void	   *extra_data;		/* for extra type-specific data */
+
+	/*
+	 * These fields are to be filled in by the compute_stats routine.
+	 * (They are initialized to zero when the struct is created.)
+	 */
+	bool		stats_valid;
+	float4		stanullfrac;	/* fraction of entries that are NULL */
+	int4		stawidth;		/* average width of column values */
+	float4		stadistinct;	/* # distinct values */
+	int2		stakind[STATISTIC_NUM_SLOTS];
+	Oid			staop[STATISTIC_NUM_SLOTS];
+	int			numnumbers[STATISTIC_NUM_SLOTS];
+	float4	   *stanumbers[STATISTIC_NUM_SLOTS];
+	int			numvalues[STATISTIC_NUM_SLOTS];
+	Datum	   *stavalues[STATISTIC_NUM_SLOTS];
+
+	/*
+	 * These fields are private to the main ANALYZE code and should not
+	 * be looked at by type-specific functions.
+	 */
+	int			tupattnum;		/* attribute number within tuples */
+} VacAttrStats;
+
+
 /* State structure for vac_init_rusage/vac_show_rusage */
 typedef struct VacRUsage
 {