OSDN Git Service

Add hooks for type-specific calculation of ANALYZE statistics. Idea and
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 12 Feb 2004 23:41:04 +0000 (23:41 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 c2d6282..4baae55 100644 (file)
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.82 2004/01/06 23:55:18 tgl Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.83 2004/02/12 23:41:00 tgl Exp $
  -->
 
 <chapter id="catalogs">
      </row>
 
      <row>
+      <entry><structfield>typanalyze</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Custom ANALYZE function, or 0 to use the standard function</entry>
+     </row>
+
+     <row>
       <entry><structfield>typalign</structfield></entry>
       <entry><type>char</type></entry>
       <entry></entry>
index dfb7ab1..ba63f43 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.48 2003/11/29 19:51:38 pgsql Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.49 2004/02/12 23:41:02 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -28,6 +28,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
     OUTPUT = <replaceable class="parameter">output_function</replaceable>
     [ , RECEIVE = <replaceable class="parameter">receive_function</replaceable> ]
     [ , SEND = <replaceable class="parameter">send_function</replaceable> ]
+    [ , ANALYZE = <replaceable class="parameter">analyze_function</replaceable> ]
     [ , INTERNALLENGTH = { <replaceable class="parameter">internallength</replaceable> | VARIABLE } ]
     [ , PASSEDBYVALUE ]
     [ , ALIGNMENT = <replaceable class="parameter">alignment</replaceable> ]
@@ -83,8 +84,9 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
    <replaceable class="parameter">input_function</replaceable> and
    <replaceable class="parameter">output_function</replaceable>
    are required, while the functions
-   <replaceable class="parameter">receive_function</replaceable> and
-   <replaceable class="parameter">send_function</replaceable>
+   <replaceable class="parameter">receive_function</replaceable>,
+   <replaceable class="parameter">send_function</replaceable> and
+   <replaceable class="parameter">analyze_function</replaceable>
    are optional.  Generally these functions have to be coded in C
    or another low-level language.
   </para>
@@ -153,6 +155,19 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
   </para>
 
   <para>
+   The optional <replaceable class="parameter">analyze_function</replaceable>
+   performs type-specific statistics collection for columns of the data type.
+   By default, <command>ANALYZE</> will attempt to gather statistics using
+   the type's <quote>equals</> and <quote>less-than</> operators, if there
+   is a default b-tree operator class for the type.  For non-scalar types
+   this behavior is likely to be unsuitable, so it can be overridden by
+   specifying a custom analysis function.  The analysis function must be
+   declared to take a single argument of type <type>internal</>, and return
+   a <type>boolean</> result.  The detailed API for analysis functions appears
+   in <filename>src/include/commands/vacuum.h</>.
+  </para>
+
+  <para>
    While the details of the new type's internal representation are only
    known to the I/O functions and other functions you create to work with
    the type, there are several properties of the internal representation
@@ -342,6 +357,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
    </varlistentry>
 
    <varlistentry>
+    <term><replaceable class="parameter">analyze_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of a function that performs statistical analysis for the
+      data type.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="parameter">internallength</replaceable></term>
     <listitem>
      <para>
index 905aa5b..6359596 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 802b01f..efd2f61 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 0c713b3..eb8716b 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 530fb1f..d081c38 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);
@@ -319,6 +325,13 @@ DefineType(List *names, List *parameters)
        }
 
        /*
+        * 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.
         */
        typoid =
@@ -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 a6c8997..096a175 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 fa6a7f1..7fdadf5 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 6098f92..754878f 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 0743976..eafe5ce 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 07a7f59..254ffbf 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 7b0e384..6daf157 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("@ <number> <units>, time interval");
 #define INTERVALOID            1186
-DATA(insert OID = 1187 ( _interval      PGNSP PGUID    -1 f b t \054 0 1186 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval      PGNSP PGUID    -1 f b t \054 0 1186 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 (  _numeric      PGNSP PGUID -1 f b t \054 0    1700 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz                 PGNSP PGUID 12 f b t \054 0    0 timetz_in timetz_out timetz_recv timetz_send d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1231 (  _numeric      PGNSP PGUID -1 f b t \054 0    1700 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz                 PGNSP PGUID 12 f b t \054 0    0 timetz_in timetz_out timetz_recv timetz_send d p f 0 -1 0 _null_ _null_ ));
 DESCR("hh:mm:ss, ANSI SQL time");
 #define TIMETZOID              1266
-DATA(insert OID = 1270 ( _timetz        PGNSP PGUID -1 f b t \054 0    1266 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz        PGNSP PGUID -1 f b t \054 0    1266 array_in array_out array_recv array_send d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit            PGNSP PGUID -1 f b t \054 0    0 bit_in bit_out bit_recv bit_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1560 ( bit            PGNSP PGUID -1 f b t \054 0    0 bit_in bit_out bit_recv bit_send i x f 0 -1 0 _null_ _null_ ));
 DESCR("fixed-length bit string");
 #define BITOID  1560
-DATA(insert OID = 1561 ( _bit           PGNSP PGUID -1 f b t \054 0    1560 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit                 PGNSP PGUID -1 f b t \054 0    0 varbit_in varbit_out varbit_recv varbit_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit           PGNSP PGUID -1 f b t \054 0    1560 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit                 PGNSP PGUID -1 f b t \054 0    0 varbit_in varbit_out varbit_recv varbit_send i x f 0 -1 0 _null_ _null_ ));
 DESCR("variable-length bit string");
 #define VARBITOID        1562
-DATA(insert OID = 1563 ( _varbit        PGNSP PGUID -1 f b t \054 0    1562 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit        PGNSP PGUID -1 f b t \054 0    1562 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1600 - 1699 */
 
 /* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric          PGNSP PGUID -1 f b t \054 0  0 numeric_in numeric_out numeric_recv numeric_send i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric          PGNSP PGUID -1 f b t \054 0  0 numeric_in numeric_out numeric_recv numeric_send i m f 0 -1 0 _null_ _null_ ));
 DESCR("numeric(precision, decimal), arbitrary precision number");
 #define NUMERICOID             1700
 
-DATA(insert OID = 1790 ( refcursor        PGNSP PGUID -1 f b t \054 0  0 textin textout textrecv textsend i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor        PGNSP PGUID -1 f b t \054 0  0 textin textout textrecv textsend i x f 0 -1 0 _null_ _null_ ));
 DESCR("reference cursor (portal name)");
 #define REFCURSOROID   1790
 
 /* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor    PGNSP PGUID -1 f b t \054 0 1790 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor    PGNSP PGUID -1 f b t \054 0 1790 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
 
-DATA(insert OID = 2202 ( regprocedure  PGNSP PGUID     4 t b t \054 0   0 regprocedurein regprocedureout regprocedurerecv regproceduresend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure  PGNSP PGUID     4 t b t \054 0   0 regprocedurein regprocedureout regprocedurerecv regproceduresend i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered procedure (with args)");
 #define REGPROCEDUREOID 2202
 
-DATA(insert OID = 2203 ( regoper          PGNSP PGUID  4 t b t \054 0   0 regoperin regoperout regoperrecv regopersend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper          PGNSP PGUID  4 t b t \054 0   0 regoperin regoperout regoperrecv regopersend i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered operator");
 #define REGOPEROID             2203
 
-DATA(insert OID = 2204 ( regoperator   PGNSP PGUID     4 t b t \054 0   0 regoperatorin regoperatorout regoperatorrecv regoperatorsend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator   PGNSP PGUID     4 t b t \054 0   0 regoperatorin regoperatorout regoperatorrecv regoperatorsend i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered operator (with args)");
 #define REGOPERATOROID 2204
 
-DATA(insert OID = 2205 ( regclass         PGNSP PGUID  4 t b t \054 0   0 regclassin regclassout regclassrecv regclasssend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass         PGNSP PGUID  4 t b t \054 0   0 regclassin regclassout regclassrecv regclasssend i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered class");
 #define REGCLASSOID            2205
 
-DATA(insert OID = 2206 ( regtype          PGNSP PGUID  4 t b t \054 0   0 regtypein regtypeout regtyperecv regtypesend i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype          PGNSP PGUID  4 t b t \054 0   0 regtypein regtypeout regtyperecv regtypesend i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered type");
 #define REGTYPEOID             2206
 
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b t \054 0 2202 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper         PGNSP PGUID -1 f b t \054 0 2203 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator  PGNSP PGUID -1 f b t \054 0 2204 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass        PGNSP PGUID -1 f b t \054 0 2205 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype         PGNSP PGUID -1 f b t \054 0 2206 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b t \054 0 2202 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper         PGNSP PGUID -1 f b t \054 0 2203 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator  PGNSP PGUID -1 f b t \054 0 2204 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass        PGNSP PGUID -1 f b t \054 0 2205 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype         PGNSP PGUID -1 f b t \054 0 2206 array_in array_out array_recv array_send i x f 0 -1 0 _null_ _null_ ));
 
 /*
  * pseudo-types
@@ -519,25 +525,25 @@ DATA(insert OID = 2211 ( _regtype    PGNSP PGUID -1 f b t \054 0 2206 array_in a
  * argument and result types (if supported by the function's implementation
  * language).
  */
-DATA(insert OID = 2249 ( record                        PGNSP PGUID  4 t p t \054 0 0 record_in record_out record_recv record_send      i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2249 ( record                        PGNSP PGUID  4 t p t \054 0 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
 #define RECORDOID              2249
-DATA(insert OID = 2275 ( cstring               PGNSP PGUID -2 f p t \054 0 0 cstring_in cstring_out cstring_recv cstring_send  c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring               PGNSP PGUID -2 f p t \054 0 0 cstring_in cstring_out cstring_recv cstring_send - c p f 0 -1 0 _null_ _null_ ));
 #define CSTRINGOID             2275
-DATA(insert OID = 2276 ( any                   PGNSP PGUID  4 t p t \054 0 0 any_in any_out - -        i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2276 ( any                   PGNSP PGUID  4 t p t \054 0 0 any_in any_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYOID                 2276
-DATA(insert OID = 2277 ( anyarray              PGNSP PGUID -1 f p t \054 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send      i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray              PGNSP PGUID -1 f p t \054 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - i x f 0 -1 0 _null_ _null_ ));
 #define ANYARRAYOID            2277
-DATA(insert OID = 2278 ( void                  PGNSP PGUID  4 t p t \054 0 0 void_in void_out - -      i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2278 ( void                  PGNSP PGUID  4 t p t \054 0 0 void_in void_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define VOIDOID                        2278
-DATA(insert OID = 2279 ( trigger               PGNSP PGUID  4 t p t \054 0 0 trigger_in trigger_out - -        i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger               PGNSP PGUID  4 t p t \054 0 0 trigger_in trigger_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define TRIGGEROID             2279
-DATA(insert OID = 2280 ( language_handler      PGNSP PGUID  4 t p t \054 0 0 language_handler_in language_handler_out - -      i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler      PGNSP PGUID  4 t p t \054 0 0 language_handler_in language_handler_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define LANGUAGE_HANDLEROID            2280
-DATA(insert OID = 2281 ( internal              PGNSP PGUID  4 t p t \054 0 0 internal_in internal_out - -      i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2281 ( internal              PGNSP PGUID  4 t p t \054 0 0 internal_in internal_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define INTERNALOID            2281
-DATA(insert OID = 2282 ( opaque                        PGNSP PGUID  4 t p t \054 0 0 opaque_in opaque_out - -  i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque                        PGNSP PGUID  4 t p t \054 0 0 opaque_in opaque_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define OPAQUEOID              2282
-DATA(insert OID = 2283 ( anyelement            PGNSP PGUID  4 t p t \054 0 0 anyelement_in anyelement_out - -  i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement            PGNSP PGUID  4 t p t \054 0 0 anyelement_in anyelement_out - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYELEMENTOID  2283
 
 /*
@@ -557,6 +563,7 @@ extern Oid TypeCreate(const char *typeName,
                   Oid outputProcedure,
                   Oid receiveProcedure,
                   Oid sendProcedure,
+                  Oid analyzeProcedure,
                   Oid elementType,
                   Oid baseType,
                   const char *defaultTypeValue,
@@ -576,6 +583,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
                                                 Oid outputProcedure,
                                                 Oid receiveProcedure,
                                                 Oid sendProcedure,
+                                                Oid analyzeProcedure,
                                                 Oid elementType,
                                                 Oid baseType,
                                                 Node *defaultExpr,
index 61985b0..b80f068 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
 {