Commit 2e7a6889 authored by Tom Lane's avatar Tom Lane

Add aggsortop column to pg_aggregate, so that MIN/MAX optimization can

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.
parent 3803f243
<!--
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">
......@@ -250,11 +250,17 @@
<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>
......@@ -263,7 +269,7 @@
<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>
......
<!--
$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>
......
......@@ -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);
}
}
/*
......
......@@ -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 */
}
......
......@@ -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
}
......@@ -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
......
......@@ -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
......
......@@ -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
This diff is collapsed.
......@@ -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_ ));
......
......@@ -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);
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment