Add aggsortop column to pg_aggregate, so that MIN/MAX optimization can
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Apr 2005 04:26:34 +0000 (04:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Apr 2005 04:26:34 +0000 (04:26 +0000)
be supported for all datatypes.  Add CREATE AGGREGATE and pg_dump support
too.  Add specialized min/max aggregates for bpchar, instead of depending
on text's min/max, because otherwise the possible use of bpchar indexes
cannot be recognized.
initdb forced because of catalog changes.

15 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/ref/create_aggregate.sgml
src/backend/catalog/pg_aggregate.c
src/backend/commands/aggregatecmds.c
src/backend/optimizer/plan/planagg.c
src/backend/utils/adt/varchar.c
src/bin/pg_dump/pg_dump.c
src/include/catalog/catversion.h
src/include/catalog/pg_aggregate.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/test/regress/expected/oidjoins.out
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/oidjoins.sql
src/test/regress/sql/opr_sanity.sql

index 11d774410d37ae6beb40ce2de4f822f01b9db97c..5a171e9496938899b96c7ae75c773dfe0571e648 100644 (file)
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.99 2005/03/29 19:44:22 tgl Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.100 2005/04/12 04:26:13 tgl Exp $
  -->
 
 <chapter id="catalogs">
       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
       <entry>Final function (zero if none)</entry>
      </row>
+     <row>
+      <entry><structfield>aggsortop</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
+      <entry>Associated sort operator (zero if none)</entry>
+     </row>
      <row>
       <entry><structfield>aggtranstype</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
-      <entry>The type of the aggregate function's internal transition (state) data</entry>
+      <entry>Data type of the aggregate function's internal transition (state) data</entry>
      </row>
      <row>
       <entry><structfield>agginitval</structfield></entry>
       <entry>
        The initial value of the transition state.  This is a text
        field containing the initial value in its external string
-       representation.  If the value is null, the transition state
+       representation.  If this field is null, the transition state
        value starts out null.
       </entry>
      </row>
index 4ae1d1dd8cde855dbf5ffd73175e0b9a7a1da671..24e233f589442777a4a136a5761dbd8902d04ba6 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_aggregate.sgml,v 1.31 2005/01/04 00:39:53 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_aggregate.sgml,v 1.32 2005/04/12 04:26:15 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -26,6 +26,7 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
     STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
     [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
     [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
+    [ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
 )
 </synopsis>
  </refsynopsisdiv>
@@ -125,6 +126,29 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
    <function>avg</function> returns null when it sees there were zero
    input rows.
   </para>
+  
+  <para>
+   Aggregates that behave like <function>MIN</> or <function>MAX</> can
+   sometimes be optimized by looking into an index instead of scanning every
+   input row.  If this aggregate can be so optimized, indicate it by
+   specifying a <firstterm>sort operator</>.  The basic requirement is that
+   the aggregate must yield the first element in the sort ordering induced by
+   the operator; in other words
+<programlisting>
+SELECT agg(col) FROM tab;
+</programlisting>
+   must be equivalent to
+<programlisting>
+SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
+</programlisting>
+   Further assumptions are that the aggregate ignores null inputs, and that
+   it delivers a null result if and only if there were no non-null inputs.
+   Ordinarily, a datatype's <literal>&lt;</> operator is the proper sort
+   operator for <function>MIN</>, and <literal>&gt;</> is the proper sort
+   operator for <function>MAX</>.  Note that the optimization will never
+   actually take effect unless the specified operator is the LessThan or
+   GreaterThan strategy member of a btree index opclass.
+  </para>
  </refsect1>
 
  <refsect1>
@@ -211,6 +235,19 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">sort_operator</replaceable></term>
+    <listitem>
+     <para>
+      The associated sort operator for a <function>MIN</>- or
+      <function>MAX</>-like aggregate.
+      This is just an operator name (possibly schema-qualified).
+      The operator is assumed to have the same input datatypes as
+      the aggregate.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
 
   <para>
index 4428bc7ecba961a16ea01be44a8b429f0fb7b7db..c833f948a812c42694ff93c34771c77de61f98a3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.72 2005/03/31 22:46:06 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.73 2005/04/12 04:26:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,6 +26,7 @@
 #include "optimizer/cost.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_func.h"
+#include "parser/parse_oper.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -42,9 +43,10 @@ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
 void
 AggregateCreate(const char *aggName,
                Oid aggNamespace,
+               Oid aggBaseType,
                List *aggtransfnName,
                List *aggfinalfnName,
-               Oid aggBaseType,
+               List *aggsortopName,
                Oid aggTransType,
                const char *agginitval)
 {
@@ -55,6 +57,7 @@ AggregateCreate(const char *aggName,
    Form_pg_proc proc;
    Oid         transfn;
    Oid         finalfn = InvalidOid;   /* can be omitted */
+   Oid         sortop = InvalidOid;    /* can be omitted */
    Oid         rettype;
    Oid         finaltype;
    Oid         fnArgs[2];      /* we only deal with 1- and 2-arg fns */
@@ -167,6 +170,12 @@ AggregateCreate(const char *aggName,
        errdetail("An aggregate returning \"anyarray\" or \"anyelement\" "
                  "must have one of them as its base type.")));
 
+   /* handle sortop, if supplied */
+   if (aggsortopName)
+       sortop = LookupOperName(aggsortopName,
+                               aggBaseType, aggBaseType,
+                               false);
+
    /*
     * Everything looks okay.  Try to create the pg_proc entry for the
     * aggregate.  (This could fail if there's already a conflicting
@@ -207,6 +216,7 @@ AggregateCreate(const char *aggName,
    values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
    values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
    values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
+   values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
    values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
    if (agginitval)
        values[Anum_pg_aggregate_agginitval - 1] =
@@ -248,6 +258,15 @@ AggregateCreate(const char *aggName,
        referenced.objectSubId = 0;
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    }
+
+   /* Depends on sort operator, if any */
+   if (OidIsValid(sortop))
+   {
+       referenced.classId = get_system_catalog_relid(OperatorRelationName);
+       referenced.objectId = sortop;
+       referenced.objectSubId = 0;
+       recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+   }
 }
 
 /*
index 7d4cbe36422be079b4adceffd32ed5df0fe42703..32bf25389a486af82deeb0efd46fd28cc97d8122 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.23 2005/03/29 00:16:57 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.24 2005/04/12 04:26:20 tgl Exp $
  *
  * DESCRIPTION
  *   The "DefineFoo" routines take the parse tree and pick out the
@@ -51,6 +51,7 @@ DefineAggregate(List *names, List *parameters)
    AclResult   aclresult;
    List       *transfuncName = NIL;
    List       *finalfuncName = NIL;
+   List       *sortoperatorName = NIL;
    TypeName   *baseType = NULL;
    TypeName   *transType = NULL;
    char       *initval = NULL;
@@ -81,6 +82,8 @@ DefineAggregate(List *names, List *parameters)
            transfuncName = defGetQualifiedName(defel);
        else if (pg_strcasecmp(defel->defname, "finalfunc") == 0)
            finalfuncName = defGetQualifiedName(defel);
+       else if (pg_strcasecmp(defel->defname, "sortop") == 0)
+           sortoperatorName = defGetQualifiedName(defel);
        else if (pg_strcasecmp(defel->defname, "basetype") == 0)
            baseType = defGetTypeName(defel);
        else if (pg_strcasecmp(defel->defname, "stype") == 0)
@@ -143,9 +146,10 @@ DefineAggregate(List *names, List *parameters)
     */
    AggregateCreate(aggName,    /* aggregate name */
                    aggNamespace,       /* namespace */
+                   baseTypeId, /* type of data being aggregated */
                    transfuncName,      /* step function name */
                    finalfuncName,      /* final function name */
-                   baseTypeId, /* type of data being aggregated */
+                   sortoperatorName,   /* sort operator name */
                    transTypeId,    /* transition data type */
                    initval);   /* initial condition */
 }
index 299931c6156f1ba4cad456ec089ae8cd5fe9fc4d..b80f2c6900b7ccf6b03f0e24336dae23acccf7b5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.1 2005/04/11 23:06:55 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.2 2005/04/12 04:26:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -542,7 +542,6 @@ replace_aggs_with_params_mutator(Node *node,  List **context)
 static Oid
 fetch_agg_sort_op(Oid aggfnoid)
 {
-#ifdef NOT_YET
    HeapTuple   aggTuple;
    Form_pg_aggregate aggform;
    Oid         aggsortop;
@@ -558,18 +557,4 @@ fetch_agg_sort_op(Oid aggfnoid)
    ReleaseSysCache(aggTuple);
 
    return aggsortop;
-#else
-   /*
-    * XXX stub implementation for testing: hardwire a few cases.
-    */
-   if (aggfnoid == 2132)       /* min(int4) -> int4lt */
-       return 97;
-   if (aggfnoid == 2116)       /* max(int4) -> int4gt */
-       return 521;
-   if (aggfnoid == 2145)       /* min(text) -> text_lt */
-       return 664;
-   if (aggfnoid == 2129)       /* max(text) -> text_gt */
-       return 666;
-   return InvalidOid;
-#endif
 }
index eeb218bf57a63ff2380c559029a0497910b1c44b..17d775d23b9f7cd76218aeae1c30a19d7e913a51 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.108 2004/12/31 22:01:22 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.109 2005/04/12 04:26:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -687,6 +687,40 @@ bpcharcmp(PG_FUNCTION_ARGS)
    PG_RETURN_INT32(cmp);
 }
 
+Datum
+bpchar_larger(PG_FUNCTION_ARGS)
+{
+   BpChar     *arg1 = PG_GETARG_BPCHAR_P(0);
+   BpChar     *arg2 = PG_GETARG_BPCHAR_P(1);
+   int         len1,
+               len2;
+   int         cmp;
+
+   len1 = bcTruelen(arg1);
+   len2 = bcTruelen(arg2);
+
+   cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
+
+   PG_RETURN_BPCHAR_P((cmp >= 0) ? arg1 : arg2);
+}
+
+Datum
+bpchar_smaller(PG_FUNCTION_ARGS)
+{
+   BpChar     *arg1 = PG_GETARG_BPCHAR_P(0);
+   BpChar     *arg2 = PG_GETARG_BPCHAR_P(1);
+   int         len1,
+               len2;
+   int         cmp;
+
+   len1 = bcTruelen(arg1);
+   len2 = bcTruelen(arg2);
+
+   cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
+
+   PG_RETURN_BPCHAR_P((cmp <= 0) ? arg1 : arg2);
+}
+
 
 /*
  * bpchar needs a specialized hash function because we want to ignore
index 92ab99d01add5c1fec27b503206ddf89ebda21c3..323c3d9182d14894ca8960954203a9323e6b29b4 100644 (file)
@@ -12,7 +12,7 @@
  * by PostgreSQL
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.405 2005/04/01 18:35:41 tgl Exp $
+ *   $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.406 2005/04/12 04:26:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -6325,6 +6325,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
    int         ntups;
    int         i_aggtransfn;
    int         i_aggfinalfn;
+   int         i_aggsortop;
    int         i_aggtranstype;
    int         i_agginitval;
    int         i_anybasetype;
@@ -6332,6 +6333,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
    int         i_convertok;
    const char *aggtransfn;
    const char *aggfinalfn;
+   const char *aggsortop;
    const char *aggtranstype;
    const char *agginitval;
    bool        convertok;
@@ -6349,10 +6351,25 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
    selectSourceSchema(agginfo->aggfn.dobj.namespace->dobj.name);
 
    /* Get aggregate-specific details */
-   if (g_fout->remoteVersion >= 70300)
+   if (g_fout->remoteVersion >= 80100)
+   {
+       appendPQExpBuffer(query, "SELECT aggtransfn, "
+                         "aggfinalfn, aggtranstype::pg_catalog.regtype, "
+                         "aggsortop::pg_catalog.regoperator, "
+                         "agginitval, "
+                         "proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
+                   "proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
+                         "'t'::boolean as convertok "
+                 "from pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
+                         "where a.aggfnoid = p.oid "
+                         "and p.oid = '%u'::pg_catalog.oid",
+                         agginfo->aggfn.dobj.catId.oid);
+   }
+   else if (g_fout->remoteVersion >= 70300)
    {
        appendPQExpBuffer(query, "SELECT aggtransfn, "
                          "aggfinalfn, aggtranstype::pg_catalog.regtype, "
+                         "0 as aggsortop, "
                          "agginitval, "
                          "proargtypes[0] = 'pg_catalog.\"any\"'::pg_catalog.regtype as anybasetype, "
                    "proargtypes[0]::pg_catalog.regtype as fmtbasetype, "
@@ -6366,6 +6383,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
    {
        appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
                      "format_type(aggtranstype, NULL) as aggtranstype, "
+                         "0 as aggsortop, "
                          "agginitval, "
                          "aggbasetype = 0 as anybasetype, "
                          "CASE WHEN aggbasetype = 0 THEN '-' "
@@ -6380,6 +6398,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
        appendPQExpBuffer(query, "SELECT aggtransfn1 as aggtransfn, "
                          "aggfinalfn, "
                          "(select typname from pg_type where oid = aggtranstype1) as aggtranstype, "
+                         "0 as aggsortop, "
                          "agginitval1 as agginitval, "
                          "aggbasetype = 0 as anybasetype, "
                          "(select typname from pg_type where oid = aggbasetype) as fmtbasetype, "
@@ -6403,6 +6422,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 
    i_aggtransfn = PQfnumber(res, "aggtransfn");
    i_aggfinalfn = PQfnumber(res, "aggfinalfn");
+   i_aggsortop = PQfnumber(res, "aggsortop");
    i_aggtranstype = PQfnumber(res, "aggtranstype");
    i_agginitval = PQfnumber(res, "agginitval");
    i_anybasetype = PQfnumber(res, "anybasetype");
@@ -6411,6 +6431,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 
    aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
    aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
+   aggsortop = PQgetvalue(res, 0, i_aggsortop);
    aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
    agginitval = PQgetvalue(res, 0, i_agginitval);
    /* we save anybasetype for format_aggregate_signature */
@@ -6471,6 +6492,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
                          aggfinalfn);
    }
 
+   aggsortop = convertOperatorReference(aggsortop);
+   if (aggsortop)
+   {
+       appendPQExpBuffer(details, ",\n    SORTOP = %s",
+                         aggsortop);
+   }
+
    /*
     * DROP must be fully qualified in case same name appears in
     * pg_catalog
index 4243ce19b2820bb5cfe392a2d2aae591cc16e68a..cb3868ea1552403d4b4f1ef193d6d5fae163347e 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.263 2005/04/06 16:34:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.264 2005/04/12 04:26:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200504061
+#define CATALOG_VERSION_NO 200504111
 
 #endif
index 854e9e888f67072d75fbf4781ed0afdc5893e3e9..6e339c76873a04605344ebd00c97539c7cb9a426 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.49 2005/02/28 03:45:22 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.50 2005/04/12 04:26:28 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
  *
  * aggfnoid            pg_proc OID of the aggregate itself
  * aggtransfn          transition function
- * aggfinalfn          final function
+ * aggfinalfn          final function (0 if none)
+ * aggsortop           associated sort operator (0 if none)
  * aggtranstype        type of aggregate's transition (state) data
- * agginitval          initial value for transition state
+ * agginitval          initial value for transition state (can be NULL)
  * ----------------------------------------------------------------
  */
 CATALOG(pg_aggregate) BKI_WITHOUT_OIDS
@@ -45,6 +46,7 @@ CATALOG(pg_aggregate) BKI_WITHOUT_OIDS
    regproc     aggfnoid;
    regproc     aggtransfn;
    regproc     aggfinalfn;
+   Oid         aggsortop;
    Oid         aggtranstype;
    text        agginitval;     /* VARIABLE LENGTH FIELD */
 } FormData_pg_aggregate;
@@ -61,12 +63,13 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  * ----------------
  */
 
-#define Natts_pg_aggregate             5
+#define Natts_pg_aggregate             6
 #define Anum_pg_aggregate_aggfnoid     1
 #define Anum_pg_aggregate_aggtransfn   2
 #define Anum_pg_aggregate_aggfinalfn   3
-#define Anum_pg_aggregate_aggtranstype 4
-#define Anum_pg_aggregate_agginitval   5
+#define Anum_pg_aggregate_aggsortop        4
+#define Anum_pg_aggregate_aggtranstype 5
+#define Anum_pg_aggregate_agginitval   6
 
 
 /* ----------------
@@ -75,107 +78,110 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  */
 
 /* avg */
-DATA(insert ( 2100 int8_accum      numeric_avg     1231    "{0,0,0}" ));
-DATA(insert ( 2101 int4_avg_accum  int8_avg        1016    "{0,0}" ));
-DATA(insert ( 2102 int2_avg_accum  int8_avg        1016    "{0,0}" ));
-DATA(insert ( 2103 numeric_accum   numeric_avg     1231    "{0,0,0}" ));
-DATA(insert ( 2104 float4_accum    float8_avg      1022    "{0,0,0}" ));
-DATA(insert ( 2105 float8_accum    float8_avg      1022    "{0,0,0}" ));
-DATA(insert ( 2106 interval_accum  interval_avg    1187    "{0 second,0 second}" ));
+DATA(insert ( 2100 int8_accum      numeric_avg     0   1231    "{0,0,0}" ));
+DATA(insert ( 2101 int4_avg_accum  int8_avg        0   1016    "{0,0}" ));
+DATA(insert ( 2102 int2_avg_accum  int8_avg        0   1016    "{0,0}" ));
+DATA(insert ( 2103 numeric_accum   numeric_avg     0   1231    "{0,0,0}" ));
+DATA(insert ( 2104 float4_accum    float8_avg      0   1022    "{0,0,0}" ));
+DATA(insert ( 2105 float8_accum    float8_avg      0   1022    "{0,0,0}" ));
+DATA(insert ( 2106 interval_accum  interval_avg    0   1187    "{0 second,0 second}" ));
 
 /* sum */
-DATA(insert ( 2107 int8_sum        -               1700    _null_ ));
-DATA(insert ( 2108 int4_sum        -               20      _null_ ));
-DATA(insert ( 2109 int2_sum        -               20      _null_ ));
-DATA(insert ( 2110 float4pl        -               700     _null_ ));
-DATA(insert ( 2111 float8pl        -               701     _null_ ));
-DATA(insert ( 2112 cash_pl         -               790     _null_ ));
-DATA(insert ( 2113 interval_pl     -               1186    _null_ ));
-DATA(insert ( 2114 numeric_add     -               1700    _null_ ));
+DATA(insert ( 2107 int8_sum        -               0   1700    _null_ ));
+DATA(insert ( 2108 int4_sum        -               0   20      _null_ ));
+DATA(insert ( 2109 int2_sum        -               0   20      _null_ ));
+DATA(insert ( 2110 float4pl        -               0   700     _null_ ));
+DATA(insert ( 2111 float8pl        -               0   701     _null_ ));
+DATA(insert ( 2112 cash_pl         -               0   790     _null_ ));
+DATA(insert ( 2113 interval_pl     -               0   1186    _null_ ));
+DATA(insert ( 2114 numeric_add     -               0   1700    _null_ ));
 
 /* max */
-DATA(insert ( 2115 int8larger      -               20      _null_ ));
-DATA(insert ( 2116 int4larger      -               23      _null_ ));
-DATA(insert ( 2117 int2larger      -               21      _null_ ));
-DATA(insert ( 2118 oidlarger       -               26      _null_ ));
-DATA(insert ( 2119 float4larger    -               700     _null_ ));
-DATA(insert ( 2120 float8larger    -               701     _null_ ));
-DATA(insert ( 2121 int4larger      -               702     _null_ ));
-DATA(insert ( 2122 date_larger     -               1082    _null_ ));
-DATA(insert ( 2123 time_larger     -               1083    _null_ ));
-DATA(insert ( 2124 timetz_larger   -               1266    _null_ ));
-DATA(insert ( 2125 cashlarger      -               790     _null_ ));
-DATA(insert ( 2126 timestamp_larger    -           1114    _null_ ));
-DATA(insert ( 2127 timestamptz_larger  -           1184    _null_ ));
-DATA(insert ( 2128 interval_larger -               1186    _null_ ));
-DATA(insert ( 2129 text_larger     -               25      _null_ ));
-DATA(insert ( 2130 numeric_larger  -               1700    _null_ ));
-DATA(insert ( 2050 array_larger    -               2277    _null_ ));
+DATA(insert ( 2115 int8larger      -               413     20      _null_ ));
+DATA(insert ( 2116 int4larger      -               521     23      _null_ ));
+DATA(insert ( 2117 int2larger      -               520     21      _null_ ));
+DATA(insert ( 2118 oidlarger       -               610     26      _null_ ));
+DATA(insert ( 2119 float4larger    -               623     700     _null_ ));
+DATA(insert ( 2120 float8larger    -               674     701     _null_ ));
+DATA(insert ( 2121 int4larger      -               563     702     _null_ ));
+DATA(insert ( 2122 date_larger     -               1097    1082    _null_ ));
+DATA(insert ( 2123 time_larger     -               1112    1083    _null_ ));
+DATA(insert ( 2124 timetz_larger   -               1554    1266    _null_ ));
+DATA(insert ( 2125 cashlarger      -               903     790     _null_ ));
+DATA(insert ( 2126 timestamp_larger    -           2064    1114    _null_ ));
+DATA(insert ( 2127 timestamptz_larger  -           1324    1184    _null_ ));
+DATA(insert ( 2128 interval_larger -               1334    1186    _null_ ));
+DATA(insert ( 2129 text_larger     -               666     25      _null_ ));
+DATA(insert ( 2130 numeric_larger  -               1756    1700    _null_ ));
+DATA(insert ( 2050 array_larger    -               1073    2277    _null_ ));
+DATA(insert ( 2244 bpchar_larger   -               1060    1042    _null_ ));
 
 /* min */
-DATA(insert ( 2131 int8smaller     -               20      _null_ ));
-DATA(insert ( 2132 int4smaller     -               23      _null_ ));
-DATA(insert ( 2133 int2smaller     -               21      _null_ ));
-DATA(insert ( 2134 oidsmaller      -               26      _null_ ));
-DATA(insert ( 2135 float4smaller   -               700     _null_ ));
-DATA(insert ( 2136 float8smaller   -               701     _null_ ));
-DATA(insert ( 2137 int4smaller     -               702     _null_ ));
-DATA(insert ( 2138 date_smaller    -               1082    _null_ ));
-DATA(insert ( 2139 time_smaller    -               1083    _null_ ));
-DATA(insert ( 2140 timetz_smaller  -               1266    _null_ ));
-DATA(insert ( 2141 cashsmaller     -               790     _null_ ));
-DATA(insert ( 2142 timestamp_smaller   -           1114    _null_ ));
-DATA(insert ( 2143 timestamptz_smaller -           1184    _null_ ));
-DATA(insert ( 2144 interval_smaller    -           1186    _null_ ));
-DATA(insert ( 2145 text_smaller    -               25      _null_ ));
-DATA(insert ( 2146 numeric_smaller -               1700    _null_ ));
-DATA(insert ( 2051 array_smaller   -               2277    _null_ ));
+DATA(insert ( 2131 int8smaller     -               412     20      _null_ ));
+DATA(insert ( 2132 int4smaller     -               97      23      _null_ ));
+DATA(insert ( 2133 int2smaller     -               95      21      _null_ ));
+DATA(insert ( 2134 oidsmaller      -               609     26      _null_ ));
+DATA(insert ( 2135 float4smaller   -               622     700     _null_ ));
+DATA(insert ( 2136 float8smaller   -               672     701     _null_ ));
+DATA(insert ( 2137 int4smaller     -               562     702     _null_ ));
+DATA(insert ( 2138 date_smaller    -               1095    1082    _null_ ));
+DATA(insert ( 2139 time_smaller    -               1110    1083    _null_ ));
+DATA(insert ( 2140 timetz_smaller  -               1552    1266    _null_ ));
+DATA(insert ( 2141 cashsmaller     -               902     790     _null_ ));
+DATA(insert ( 2142 timestamp_smaller   -           2062    1114    _null_ ));
+DATA(insert ( 2143 timestamptz_smaller -           1322    1184    _null_ ));
+DATA(insert ( 2144 interval_smaller    -           1332    1186    _null_ ));
+DATA(insert ( 2145 text_smaller    -               664     25      _null_ ));
+DATA(insert ( 2146 numeric_smaller -               1754    1700    _null_ ));
+DATA(insert ( 2051 array_smaller   -               1072    2277    _null_ ));
+DATA(insert ( 2245 bpchar_smaller  -               1058    1042    _null_ ));
 
 /*
  * Using int8inc for count() is cheating a little, since it really only
  * takes 1 parameter not 2, but nodeAgg.c won't complain ...
  */
-DATA(insert ( 2147 int8inc     -                    20     0 ));
+DATA(insert ( 2147 int8inc     -                   0   20      0 ));
 
 /* variance */
-DATA(insert ( 2148 int8_accum  numeric_variance    1231    "{0,0,0}" ));
-DATA(insert ( 2149 int4_accum  numeric_variance    1231    "{0,0,0}" ));
-DATA(insert ( 2150 int2_accum  numeric_variance    1231    "{0,0,0}" ));
-DATA(insert ( 2151 float4_accum    float8_variance 1022    "{0,0,0}" ));
-DATA(insert ( 2152 float8_accum    float8_variance 1022    "{0,0,0}" ));
-DATA(insert ( 2153 numeric_accum   numeric_variance    1231    "{0,0,0}" ));
+DATA(insert ( 2148 int8_accum  numeric_variance    0   1231    "{0,0,0}" ));
+DATA(insert ( 2149 int4_accum  numeric_variance    0   1231    "{0,0,0}" ));
+DATA(insert ( 2150 int2_accum  numeric_variance    0   1231    "{0,0,0}" ));
+DATA(insert ( 2151 float4_accum    float8_variance 0   1022    "{0,0,0}" ));
+DATA(insert ( 2152 float8_accum    float8_variance 0   1022    "{0,0,0}" ));
+DATA(insert ( 2153 numeric_accum  numeric_variance 0   1231    "{0,0,0}" ));
 
 /* stddev */
-DATA(insert ( 2154 int8_accum  numeric_stddev      1231    "{0,0,0}" ));
-DATA(insert ( 2155 int4_accum  numeric_stddev      1231    "{0,0,0}" ));
-DATA(insert ( 2156 int2_accum  numeric_stddev      1231    "{0,0,0}" ));
-DATA(insert ( 2157 float4_accum    float8_stddev   1022    "{0,0,0}" ));
-DATA(insert ( 2158 float8_accum    float8_stddev   1022    "{0,0,0}" ));
-DATA(insert ( 2159 numeric_accum   numeric_stddev  1231    "{0,0,0}" ));
+DATA(insert ( 2154 int8_accum  numeric_stddev      0   1231    "{0,0,0}" ));
+DATA(insert ( 2155 int4_accum  numeric_stddev      0   1231    "{0,0,0}" ));
+DATA(insert ( 2156 int2_accum  numeric_stddev      0   1231    "{0,0,0}" ));
+DATA(insert ( 2157 float4_accum    float8_stddev   0   1022    "{0,0,0}" ));
+DATA(insert ( 2158 float8_accum    float8_stddev   0   1022    "{0,0,0}" ));
+DATA(insert ( 2159 numeric_accum   numeric_stddev  0   1231    "{0,0,0}" ));
 
 /* boolean-and and boolean-or */
-DATA(insert ( 2517 booland_statefunc   -               16  _null_ ));
-DATA(insert ( 2518 boolor_statefunc    -               16  _null_ ));
-DATA(insert ( 2519 booland_statefunc   -               16  _null_ ));
+DATA(insert ( 2517 booland_statefunc   -           0   16      _null_ ));
+DATA(insert ( 2518 boolor_statefunc    -           0   16      _null_ ));
+DATA(insert ( 2519 booland_statefunc   -           0   16      _null_ ));
 
 /* bitwise integer */
-DATA(insert ( 2236 int2and       -                 21       _null_ ));
-DATA(insert ( 2237 int2or        -                 21       _null_ ));
-DATA(insert ( 2238 int4and       -                 23       _null_ ));
-DATA(insert ( 2239 int4or        -                 23       _null_ ));
-DATA(insert ( 2240 int8and       -                 20       _null_ ));
-DATA(insert ( 2241 int8or        -                 20       _null_ ));
-DATA(insert ( 2242 bitand        -               1560       _null_ ));
-DATA(insert ( 2243 bitor         -               1560       _null_ ));
+DATA(insert ( 2236 int2and       -                 0   21      _null_ ));
+DATA(insert ( 2237 int2or        -                 0   21      _null_ ));
+DATA(insert ( 2238 int4and       -                 0   23      _null_ ));
+DATA(insert ( 2239 int4or        -                 0   23      _null_ ));
+DATA(insert ( 2240 int8and       -                 0   20      _null_ ));
+DATA(insert ( 2241 int8or        -                 0   20      _null_ ));
+DATA(insert ( 2242 bitand        -                 0   1560    _null_ ));
+DATA(insert ( 2243 bitor         -                 0   1560    _null_ ));
 
 /*
  * prototypes for functions in pg_aggregate.c
  */
 extern void AggregateCreate(const char *aggName,
                Oid aggNamespace,
+               Oid aggBaseType,
                List *aggtransfnName,
                List *aggfinalfnName,
-               Oid aggBaseType,
+               List *aggsortopName,
                Oid aggTransType,
                const char *agginitval);
 
index cb90a5e04c329b8072a6460df95f366671a6b4a0..d9bf85973cb88e20063b1af0aa29e9a3b39e612b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.357 2005/03/31 22:46:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.358 2005/04/12 04:26:28 tgl Exp $
  *
  * NOTES
  *   The script catalog/genbki.sh reads this file and generates .bki
@@ -1372,6 +1372,10 @@ DATA(insert OID = 1052 (  bpcharge          PGNSP PGUID 12 f f t f i 2 16 "1042 1042"
 DESCR("greater-than-or-equal");
 DATA(insert OID = 1053 (  bpcharne        PGNSP PGUID 12 f f t f i 2 16 "1042 1042" _null_ _null_ _null_ bpcharne - _null_ ));
 DESCR("not equal");
+DATA(insert OID = 1063 (  bpchar_larger       PGNSP PGUID 12 f f t f i 2 1042 "1042 1042" _null_ _null_ _null_ bpchar_larger - _null_ ));
+DESCR("larger of two");
+DATA(insert OID = 1064 (  bpchar_smaller   PGNSP PGUID 12 f f t f i 2 1042 "1042 1042" _null_ _null_ _null_ bpchar_smaller - _null_ ));
+DESCR("smaller of two");
 DATA(insert OID = 1078 (  bpcharcmp           PGNSP PGUID 12 f f t f i 2 23 "1042 1042" _null_ _null_ _null_ bpcharcmp - _null_ ));
 DESCR("less-equal-greater");
 DATA(insert OID = 1080 (  hashbpchar      PGNSP PGUID 12 f f t f i 1 23 "1042" _null_ _null_ _null_    hashbpchar - _null_ ));
@@ -3048,6 +3052,7 @@ DATA(insert OID = 2128 (  max             PGNSP PGUID 12 t f f f i 1 1186 "1186" _null_ _
 DATA(insert OID = 2129 (  max              PGNSP PGUID 12 t f f f i 1 25 "25" _null_ _null_ _null_ aggregate_dummy - _null_ ));
 DATA(insert OID = 2130 (  max              PGNSP PGUID 12 t f f f i 1 1700 "1700" _null_ _null_ _null_ aggregate_dummy - _null_ ));
 DATA(insert OID = 2050 (  max              PGNSP PGUID 12 t f f f i 1 2277 "2277" _null_ _null_ _null_ aggregate_dummy - _null_ ));
+DATA(insert OID = 2244 (  max              PGNSP PGUID 12 t f f f i 1 1042 "1042" _null_ _null_ _null_ aggregate_dummy - _null_ ));
 
 DATA(insert OID = 2131 (  min              PGNSP PGUID 12 t f f f i 1 20 "20" _null_ _null_ _null_ aggregate_dummy - _null_ ));
 DATA(insert OID = 2132 (  min              PGNSP PGUID 12 t f f f i 1 23 "23" _null_ _null_ _null_ aggregate_dummy - _null_ ));
@@ -3066,6 +3071,7 @@ DATA(insert OID = 2144 (  min             PGNSP PGUID 12 t f f f i 1 1186 "1186" _null_ _
 DATA(insert OID = 2145 (  min              PGNSP PGUID 12 t f f f i 1 25 "25" _null_ _null_ _null_ aggregate_dummy - _null_ ));
 DATA(insert OID = 2146 (  min              PGNSP PGUID 12 t f f f i 1 1700 "1700" _null_ _null_ _null_ aggregate_dummy - _null_ ));
 DATA(insert OID = 2051 (  min              PGNSP PGUID 12 t f f f i 1 2277 "2277" _null_ _null_ _null_ aggregate_dummy - _null_ ));
+DATA(insert OID = 2245 (  min              PGNSP PGUID 12 t f f f i 1 1042 "1042" _null_ _null_ _null_ aggregate_dummy - _null_ ));
 
 DATA(insert OID = 2147 (  count                PGNSP PGUID 12 t f f f i 1 20 "2276" _null_ _null_ _null_  aggregate_dummy - _null_ ));
 
index 4caa5b4cba2e35eb61ad0f34f6c49f0513e1c040..96e33786eec36e3ccb366b9d52805ec8b91f1c55 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.254 2005/03/29 00:17:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.255 2005/04/12 04:26:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -522,6 +522,8 @@ extern Datum bpcharle(PG_FUNCTION_ARGS);
 extern Datum bpchargt(PG_FUNCTION_ARGS);
 extern Datum bpcharge(PG_FUNCTION_ARGS);
 extern Datum bpcharcmp(PG_FUNCTION_ARGS);
+extern Datum bpchar_larger(PG_FUNCTION_ARGS);
+extern Datum bpchar_smaller(PG_FUNCTION_ARGS);
 extern Datum bpcharlen(PG_FUNCTION_ARGS);
 extern Datum bpcharoctetlen(PG_FUNCTION_ARGS);
 extern Datum hashbpchar(PG_FUNCTION_ARGS);
index feb912b87fed849fe9e2a6950cf384436fc61384..445f41ffb94e1130f35588ae78ef15e7ff781c58 100644 (file)
@@ -25,6 +25,14 @@ WHERE    aggfinalfn != 0 AND
 ------+------------
 (0 rows)
 
+SELECT ctid, aggsortop 
+FROM   pg_catalog.pg_aggregate fk 
+WHERE  aggsortop != 0 AND 
+   NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.aggsortop);
+ ctid | aggsortop 
+------+-----------
+(0 rows)
+
 SELECT ctid, aggtranstype 
 FROM   pg_catalog.pg_aggregate fk 
 WHERE  aggtranstype != 0 AND 
@@ -33,14 +41,6 @@ WHERE    aggtranstype != 0 AND
 ------+--------------
 (0 rows)
 
-SELECT ctid, amgettuple 
-FROM   pg_catalog.pg_am fk 
-WHERE  amgettuple != 0 AND 
-   NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
- ctid | amgettuple 
-------+------------
-(0 rows)
-
 SELECT ctid, aminsert 
 FROM   pg_catalog.pg_am fk 
 WHERE  aminsert != 0 AND 
@@ -57,6 +57,22 @@ WHERE    ambeginscan != 0 AND
 ------+-------------
 (0 rows)
 
+SELECT ctid, amgettuple 
+FROM   pg_catalog.pg_am fk 
+WHERE  amgettuple != 0 AND 
+   NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
+ ctid | amgettuple 
+------+------------
+(0 rows)
+
+SELECT ctid, amgetmulti 
+FROM   pg_catalog.pg_am fk 
+WHERE  amgetmulti != 0 AND 
+   NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetmulti);
+ ctid | amgetmulti 
+------+------------
+(0 rows)
+
 SELECT ctid, amrescan 
 FROM   pg_catalog.pg_am fk 
 WHERE  amrescan != 0 AND 
index 8740b0e938c01bd5ebc0faffd544a0fac6a1d2e5..e2e59d675e67a4dcb2d04d4ede786373bcdae871 100644 (file)
@@ -677,6 +677,53 @@ WHERE a.aggfnoid = p.oid AND
 ----------+---------+-----+---------
 (0 rows)
 
+-- Cross-check aggsortop (if present) against pg_operator.
+-- We expect to find only "<" for "min" and ">" for "max".
+SELECT DISTINCT proname, oprname
+FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p
+WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid
+ORDER BY 1;
+ proname | oprname 
+---------+---------
+ max     | >
+ min     | <
+(2 rows)
+
+-- Check datatypes match
+SELECT a.aggfnoid::oid, o.oid
+FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p
+WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND
+    (oprkind != 'b' OR oprresult != 'boolean'::regtype
+     OR oprleft != p.proargtypes[0] OR oprright != p.proargtypes[0]);
+ aggfnoid | oid 
+----------+-----
+(0 rows)
+
+-- Check operator is a suitable btree opclass member
+SELECT a.aggfnoid::oid, o.oid
+FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p
+WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND
+    NOT EXISTS(SELECT 1 FROM pg_amop ao, pg_opclass oc
+               WHERE amopclaid = oc.oid AND amopsubtype = 0
+                     AND amopopr = o.oid AND opcamid = 403
+                     AND opcintype = o.oprleft AND opcdefault);
+ aggfnoid | oid 
+----------+-----
+(0 rows)
+
+-- Check correspondence of btree strategies and names
+SELECT DISTINCT proname, oprname, amopstrategy
+FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p,
+     pg_amop as ao, pg_opclass oc
+WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND
+    amopclaid = oc.oid AND amopopr = o.oid AND opcamid = 403
+ORDER BY 1;
+ proname | oprname | amopstrategy 
+---------+---------+--------------
+ max     | >       |            5
+ min     | <       |            1
+(2 rows)
+
 -- **************** pg_opclass ****************
 -- Look for illegal values in pg_opclass fields
 SELECT p1.oid
index 9df84436bf1da5c8b260748357957b71c1d26ffe..910d55f9f94cfa3e9a93be87785de7c543e1c7b1 100644 (file)
@@ -13,14 +13,14 @@ SELECT  ctid, aggfinalfn
 FROM   pg_catalog.pg_aggregate fk 
 WHERE  aggfinalfn != 0 AND 
    NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aggfinalfn);
+SELECT ctid, aggsortop 
+FROM   pg_catalog.pg_aggregate fk 
+WHERE  aggsortop != 0 AND 
+   NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.aggsortop);
 SELECT ctid, aggtranstype 
 FROM   pg_catalog.pg_aggregate fk 
 WHERE  aggtranstype != 0 AND 
    NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.aggtranstype);
-SELECT ctid, amgettuple 
-FROM   pg_catalog.pg_am fk 
-WHERE  amgettuple != 0 AND 
-   NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
 SELECT ctid, aminsert 
 FROM   pg_catalog.pg_am fk 
 WHERE  aminsert != 0 AND 
@@ -29,6 +29,14 @@ SELECT   ctid, ambeginscan
 FROM   pg_catalog.pg_am fk 
 WHERE  ambeginscan != 0 AND 
    NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambeginscan);
+SELECT ctid, amgettuple 
+FROM   pg_catalog.pg_am fk 
+WHERE  amgettuple != 0 AND 
+   NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
+SELECT ctid, amgetmulti 
+FROM   pg_catalog.pg_am fk 
+WHERE  amgetmulti != 0 AND 
+   NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetmulti);
 SELECT ctid, amrescan 
 FROM   pg_catalog.pg_am fk 
 WHERE  amrescan != 0 AND 
index 84e9b9f738effc8e7f59e6abc80bd7e6604b1322..3b74c1bbd7b94bad1846e2c0deffb1e671c6ad52 100644 (file)
@@ -561,6 +561,41 @@ WHERE a.aggfnoid = p.oid AND
     a.agginitval IS NULL AND
     NOT binary_coercible(p.proargtypes[0], a.aggtranstype);
 
+-- Cross-check aggsortop (if present) against pg_operator.
+-- We expect to find only "<" for "min" and ">" for "max".
+
+SELECT DISTINCT proname, oprname
+FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p
+WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid
+ORDER BY 1;
+
+-- Check datatypes match
+
+SELECT a.aggfnoid::oid, o.oid
+FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p
+WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND
+    (oprkind != 'b' OR oprresult != 'boolean'::regtype
+     OR oprleft != p.proargtypes[0] OR oprright != p.proargtypes[0]);
+
+-- Check operator is a suitable btree opclass member
+
+SELECT a.aggfnoid::oid, o.oid
+FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p
+WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND
+    NOT EXISTS(SELECT 1 FROM pg_amop ao, pg_opclass oc
+               WHERE amopclaid = oc.oid AND amopsubtype = 0
+                     AND amopopr = o.oid AND opcamid = 403
+                     AND opcintype = o.oprleft AND opcdefault);
+
+-- Check correspondence of btree strategies and names
+
+SELECT DISTINCT proname, oprname, amopstrategy
+FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p,
+     pg_amop as ao, pg_opclass oc
+WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND
+    amopclaid = oc.oid AND amopopr = o.oid AND opcamid = 403
+ORDER BY 1;
+
 -- **************** pg_opclass ****************
 
 -- Look for illegal values in pg_opclass fields