Add hooks for type-specific calculation of ANALYZE statistics. Idea and
authorTom Lane
Thu, 12 Feb 2004 23:41:04 +0000 (23:41 +0000)
committerTom Lane
Thu, 12 Feb 2004 23:41:04 +0000 (23:41 +0000)
coding by Mark Cave-Ayland, some kibitzing by Tom Lane.  initdb forced
due to new column in pg_type.

13 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/ref/create_type.sgml
src/backend/catalog/heap.c
src/backend/catalog/pg_type.c
src/backend/commands/analyze.c
src/backend/commands/typecmds.c
src/bin/pg_dump/pg_dump.c
src/include/catalog/catversion.h
src/include/catalog/pg_attribute.h
src/include/catalog/pg_class.h
src/include/catalog/pg_statistic.h
src/include/catalog/pg_type.h
src/include/commands/vacuum.h

index c2d62822e5ab3236be2bc1baf4aa2054417ef682..4baae556b317e960141cdea99aaf4f4d9f898dac 100644 (file)
@@ -1,6 +1,6 @@
 
 
 
       Output conversion function (binary format), or 0 if none
      
 
+     
+      typanalyze
+      regproc
+      pg_proc.oid
+      Custom ANALYZE function, or 0 to use the standard function
+     
+
      
       typalign
       char
index dfb7ab1510978ed257672436fa686995c78bac15..ba63f4378bbbae8f7e0ccae23297c9b4378a7c05 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -28,6 +28,7 @@ CREATE TYPE name (
     OUTPUT = output_function
     [ , RECEIVE = receive_function ]
     [ , SEND = send_function ]
+    [ , ANALYZE = analyze_function ]
     [ , INTERNALLENGTH = { internallength | VARIABLE } ]
     [ , PASSEDBYVALUE ]
     [ , ALIGNMENT = alignment ]
@@ -83,8 +84,9 @@ CREATE TYPE name (
    input_function and
    output_function
    are required, while the functions
-   receive_function and
-   send_function
+   receive_function,
+   send_function and
+   analyze_function
    are optional.  Generally these functions have to be coded in C
    or another low-level language.
   
@@ -152,6 +154,19 @@ CREATE TYPE name (
    shell entry with a complete type definition, and the new type can be used.
   
 
+  
+   The optional analyze_function
+   performs type-specific statistics collection for columns of the data type.
+   By default, ANALYZE will attempt to gather statistics using
+   the type's equals and 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 internal, and return
+   a boolean result.  The detailed API for analysis functions appears
+   in src/include/commands/vacuum.h.
+  
+
   
    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 name (
     
    
 
+   
+    analyze_function
+    
+     
+      The name of a function that performs statistical analysis for the
+      data type.
+     
+    
+   
+
    
     internallength
     
index 905aa5b0b222f2efcfdc08204976ce1ebb0d2bce..63595962e4ea610b3430a14d2a1d4786709e321a 100644 (file)
@@ -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 */
index 802b01f759ee382803d00805f4a39338fe9fda13..efd2f61747c720711ca42719bec2c9e067ccd88f 100644 (file)
@@ -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
index 0c713b3ca678cfba5877470d4731c999bf4cb0f2..eb8716b4880616e67a26a634a3afe9b61a544fb7 100644 (file)
@@ -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"
 #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);
-}
index 530fb1f573cf1b6929df3be97be85a785d040003..d081c38b7042623d290007f02e053a099ae17cbe 100644 (file)
@@ -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,
index a6c8997c3943c7e59cf27d217670148352f5b370..096a175dc702900982d55427de7ad27e7612397f 100644 (file)
@@ -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)
index fa6a7f1e605240796f63be6de3553ece0530a626..7fdadf5206a511d0216ca80e54268febb2806bc3 100644 (file)
@@ -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
index 6098f92d8978b60d199f674c35239388510a820d..754878f0148be7680d3e42e4b8a8d076eb7647fe 100644 (file)
@@ -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));
index 0743976ddcef3cee24e3c44a7a9b268d0144382d..eafe5ceb3285641c273afcd2a6a663fb1d7d41fc 100644 (file)
@@ -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("");
index 07a7f599461dd34a925188164f54bf120403c63b..254ffbfa8d22dbb546b478385fedcc1dc7366a91 100644 (file)
@@ -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.
  */
 
 /*
index 7b0e384f584f3be09d58d485a35b595dd1754888..6daf1579df0c4660221c21f287f1322dd859fd86 100644 (file)
@@ -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("@  , 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,
index 61985b0d395b53078b73aef5f5762d3ec5f77033..b80f06835637bfc596e99d5a51e9c05f0e1d7bc5 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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
 {