mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Create a "sort support" interface API for faster sorting.
This patch creates an API whereby a btree index opclass can optionally provide non-SQL-callable support functions for sorting. In the initial patch, we only use this to provide a directly-callable comparator function, which can be invoked with a bit less overhead than the traditional SQL-callable comparator. While that should be of value in itself, the real reason for doing this is to provide a datatype-extensible framework for more aggressive optimizations, as in Peter Geoghegan's recent work. Robert Haas and Tom Lane
This commit is contained in:
@ -140,11 +140,12 @@ ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="
|
|||||||
<para>
|
<para>
|
||||||
In an <literal>ADD FUNCTION</> clause, the operand data type(s) the
|
In an <literal>ADD FUNCTION</> clause, the operand data type(s) the
|
||||||
function is intended to support, if different from
|
function is intended to support, if different from
|
||||||
the input data type(s) of the function. For B-tree and hash indexes
|
the input data type(s) of the function. For B-tree comparison functions
|
||||||
it is not necessary to specify <replaceable
|
and hash functions it is not necessary to specify <replaceable
|
||||||
class="parameter">op_type</replaceable> since the function's input
|
class="parameter">op_type</replaceable> since the function's input
|
||||||
data type(s) are always the correct ones to use. For GIN and GiST
|
data type(s) are always the correct ones to use. For B-tree sort
|
||||||
indexes it is necessary to specify the input data type the function
|
support functions and all functions in GiST and GIN operator classes,
|
||||||
|
it is necessary to specify the operand data type(s) the function
|
||||||
is to be used with.
|
is to be used with.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
@ -169,13 +169,14 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
|
|||||||
<para>
|
<para>
|
||||||
In a <literal>FUNCTION</> clause, the operand data type(s) the
|
In a <literal>FUNCTION</> clause, the operand data type(s) the
|
||||||
function is intended to support, if different from
|
function is intended to support, if different from
|
||||||
the input data type(s) of the function (for B-tree and hash indexes)
|
the input data type(s) of the function (for B-tree comparison functions
|
||||||
or the class's data type (for GIN and GiST indexes). These defaults
|
and hash functions)
|
||||||
are always correct, so there is no point in specifying <replaceable
|
or the class's data type (for B-tree sort support functions and all
|
||||||
class="parameter">op_type</replaceable> in a <literal>FUNCTION</> clause
|
functions in GiST and GIN operator classes). These defaults
|
||||||
in <command>CREATE OPERATOR CLASS</>, but the option is provided
|
are correct, and so <replaceable
|
||||||
for consistency with the comparable syntax in
|
class="parameter">op_type</replaceable> need not be specified in
|
||||||
<command>ALTER OPERATOR FAMILY</>.
|
<literal>FUNCTION</> clauses, except for the case of a B-tree sort
|
||||||
|
support function that is meant to support cross-data-type comparisons.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -311,7 +311,8 @@
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
B-trees require a single support function, shown in <xref
|
B-trees require a single support function, and allow a second one to be
|
||||||
|
supplied at the operator class author's option, as shown in <xref
|
||||||
linkend="xindex-btree-support-table">.
|
linkend="xindex-btree-support-table">.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -333,12 +334,19 @@
|
|||||||
</entry>
|
</entry>
|
||||||
<entry>1</entry>
|
<entry>1</entry>
|
||||||
</row>
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
Return the addresses of C-callable sort support function(s),
|
||||||
|
as documented in <filename>utils/sortsupport.h</> (optional)
|
||||||
|
</entry>
|
||||||
|
<entry>2</entry>
|
||||||
|
</row>
|
||||||
</tbody>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Hash indexes likewise require one support function, shown in <xref
|
Hash indexes require one support function, shown in <xref
|
||||||
linkend="xindex-hash-support-table">.
|
linkend="xindex-hash-support-table">.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -363,6 +371,7 @@
|
|||||||
<para>
|
<para>
|
||||||
GiST indexes require seven support functions, with an optional eighth, as
|
GiST indexes require seven support functions, with an optional eighth, as
|
||||||
shown in <xref linkend="xindex-gist-support-table">.
|
shown in <xref linkend="xindex-gist-support-table">.
|
||||||
|
(For more information see <xref linkend="GiST">.)
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<table tocentry="1" id="xindex-gist-support-table">
|
<table tocentry="1" id="xindex-gist-support-table">
|
||||||
@ -418,9 +427,7 @@
|
|||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<entry><function>distance</></entry>
|
<entry><function>distance</></entry>
|
||||||
<entry>
|
<entry>determine distance from key to query value (optional)</entry>
|
||||||
(optional method) determine distance from key to query value
|
|
||||||
</entry>
|
|
||||||
<entry>8</entry>
|
<entry>8</entry>
|
||||||
</row>
|
</row>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -430,6 +437,7 @@
|
|||||||
<para>
|
<para>
|
||||||
GIN indexes require four support functions, with an optional fifth, as
|
GIN indexes require four support functions, with an optional fifth, as
|
||||||
shown in <xref linkend="xindex-gin-support-table">.
|
shown in <xref linkend="xindex-gin-support-table">.
|
||||||
|
(For more information see <xref linkend="GIN">.)
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<table tocentry="1" id="xindex-gin-support-table">
|
<table tocentry="1" id="xindex-gin-support-table">
|
||||||
@ -470,10 +478,10 @@
|
|||||||
<row>
|
<row>
|
||||||
<entry><function>comparePartial</></entry>
|
<entry><function>comparePartial</></entry>
|
||||||
<entry>
|
<entry>
|
||||||
(optional method) compare partial key from
|
compare partial key from
|
||||||
query and key from index, and return an integer less than zero, zero,
|
query and key from index, and return an integer less than zero, zero,
|
||||||
or greater than zero, indicating whether GIN should ignore this index
|
or greater than zero, indicating whether GIN should ignore this index
|
||||||
entry, treat the entry as a match, or stop the index scan
|
entry, treat the entry as a match, or stop the index scan (optional)
|
||||||
</entry>
|
</entry>
|
||||||
<entry>5</entry>
|
<entry>5</entry>
|
||||||
</row>
|
</row>
|
||||||
@ -486,7 +494,8 @@
|
|||||||
type the particular index method expects; for example in the case
|
type the particular index method expects; for example in the case
|
||||||
of the comparison function for B-trees, a signed integer. The number
|
of the comparison function for B-trees, a signed integer. The number
|
||||||
and types of the arguments to each support function are likewise
|
and types of the arguments to each support function are likewise
|
||||||
dependent on the index method. For B-tree and hash the support functions
|
dependent on the index method. For B-tree and hash the comparison and
|
||||||
|
hashing support functions
|
||||||
take the same input data types as do the operators included in the operator
|
take the same input data types as do the operators included in the operator
|
||||||
class, but this is not the case for most GIN and GiST support functions.
|
class, but this is not the case for most GIN and GiST support functions.
|
||||||
</para>
|
</para>
|
||||||
@ -748,7 +757,8 @@ DEFAULT FOR TYPE int8 USING btree FAMILY integer_ops AS
|
|||||||
OPERATOR 3 = ,
|
OPERATOR 3 = ,
|
||||||
OPERATOR 4 >= ,
|
OPERATOR 4 >= ,
|
||||||
OPERATOR 5 > ,
|
OPERATOR 5 > ,
|
||||||
FUNCTION 1 btint8cmp(int8, int8) ;
|
FUNCTION 1 btint8cmp(int8, int8) ,
|
||||||
|
FUNCTION 2 btint8sortsupport(internal) ;
|
||||||
|
|
||||||
CREATE OPERATOR CLASS int4_ops
|
CREATE OPERATOR CLASS int4_ops
|
||||||
DEFAULT FOR TYPE int4 USING btree FAMILY integer_ops AS
|
DEFAULT FOR TYPE int4 USING btree FAMILY integer_ops AS
|
||||||
@ -758,7 +768,8 @@ DEFAULT FOR TYPE int4 USING btree FAMILY integer_ops AS
|
|||||||
OPERATOR 3 = ,
|
OPERATOR 3 = ,
|
||||||
OPERATOR 4 >= ,
|
OPERATOR 4 >= ,
|
||||||
OPERATOR 5 > ,
|
OPERATOR 5 > ,
|
||||||
FUNCTION 1 btint4cmp(int4, int4) ;
|
FUNCTION 1 btint4cmp(int4, int4) ,
|
||||||
|
FUNCTION 2 btint4sortsupport(internal) ;
|
||||||
|
|
||||||
CREATE OPERATOR CLASS int2_ops
|
CREATE OPERATOR CLASS int2_ops
|
||||||
DEFAULT FOR TYPE int2 USING btree FAMILY integer_ops AS
|
DEFAULT FOR TYPE int2 USING btree FAMILY integer_ops AS
|
||||||
@ -768,7 +779,8 @@ DEFAULT FOR TYPE int2 USING btree FAMILY integer_ops AS
|
|||||||
OPERATOR 3 = ,
|
OPERATOR 3 = ,
|
||||||
OPERATOR 4 >= ,
|
OPERATOR 4 >= ,
|
||||||
OPERATOR 5 > ,
|
OPERATOR 5 > ,
|
||||||
FUNCTION 1 btint2cmp(int2, int2) ;
|
FUNCTION 1 btint2cmp(int2, int2) ,
|
||||||
|
FUNCTION 2 btint2sortsupport(internal) ;
|
||||||
|
|
||||||
ALTER OPERATOR FAMILY integer_ops USING btree ADD
|
ALTER OPERATOR FAMILY integer_ops USING btree ADD
|
||||||
-- cross-type comparisons int8 vs int2
|
-- cross-type comparisons int8 vs int2
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/sortsupport.h"
|
||||||
|
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
@ -69,6 +70,24 @@ btint2cmp(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_INT32((int32) a - (int32) b);
|
PG_RETURN_INT32((int32) a - (int32) b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
btint2fastcmp(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
int16 a = DatumGetInt16(x);
|
||||||
|
int16 b = DatumGetInt16(y);
|
||||||
|
|
||||||
|
return (int) a - (int) b;
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
btint2sortsupport(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
ssup->comparator = btint2fastcmp;
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
btint4cmp(PG_FUNCTION_ARGS)
|
btint4cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@ -83,6 +102,29 @@ btint4cmp(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_INT32(-1);
|
PG_RETURN_INT32(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
btint4fastcmp(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
int32 a = DatumGetInt32(x);
|
||||||
|
int32 b = DatumGetInt32(y);
|
||||||
|
|
||||||
|
if (a > b)
|
||||||
|
return 1;
|
||||||
|
else if (a == b)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
btint4sortsupport(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
ssup->comparator = btint4fastcmp;
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
btint8cmp(PG_FUNCTION_ARGS)
|
btint8cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@ -97,6 +139,29 @@ btint8cmp(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_INT32(-1);
|
PG_RETURN_INT32(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
btint8fastcmp(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
int64 a = DatumGetInt64(x);
|
||||||
|
int64 b = DatumGetInt64(y);
|
||||||
|
|
||||||
|
if (a > b)
|
||||||
|
return 1;
|
||||||
|
else if (a == b)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
btint8sortsupport(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
ssup->comparator = btint8fastcmp;
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
btint48cmp(PG_FUNCTION_ARGS)
|
btint48cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@ -195,6 +260,29 @@ btoidcmp(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_INT32(-1);
|
PG_RETURN_INT32(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
btoidfastcmp(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
Oid a = DatumGetObjectId(x);
|
||||||
|
Oid b = DatumGetObjectId(y);
|
||||||
|
|
||||||
|
if (a > b)
|
||||||
|
return 1;
|
||||||
|
else if (a == b)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
btoidsortsupport(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
ssup->comparator = btoidfastcmp;
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
btoidvectorcmp(PG_FUNCTION_ARGS)
|
btoidvectorcmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@ -237,3 +325,21 @@ btnamecmp(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
PG_RETURN_INT32(strncmp(NameStr(*a), NameStr(*b), NAMEDATALEN));
|
PG_RETURN_INT32(strncmp(NameStr(*a), NameStr(*b), NAMEDATALEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
btnamefastcmp(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
Name a = DatumGetName(x);
|
||||||
|
Name b = DatumGetName(y);
|
||||||
|
|
||||||
|
return strncmp(NameStr(*a), NameStr(*b), NAMEDATALEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
btnamesortsupport(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
ssup->comparator = btnamefastcmp;
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
@ -47,8 +47,8 @@
|
|||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/pg_rusage.h"
|
#include "utils/pg_rusage.h"
|
||||||
|
#include "utils/sortsupport.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
#include "utils/tuplesort.h"
|
|
||||||
#include "utils/timestamp.h"
|
#include "utils/timestamp.h"
|
||||||
#include "utils/tqual.h"
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
@ -1774,8 +1774,7 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
FmgrInfo *cmpFn;
|
SortSupport ssup;
|
||||||
int cmpFlags;
|
|
||||||
int *tupnoLink;
|
int *tupnoLink;
|
||||||
} CompareScalarsContext;
|
} CompareScalarsContext;
|
||||||
|
|
||||||
@ -2222,9 +2221,7 @@ compute_scalar_stats(VacAttrStatsP stats,
|
|||||||
bool is_varwidth = (!stats->attrtype->typbyval &&
|
bool is_varwidth = (!stats->attrtype->typbyval &&
|
||||||
stats->attrtype->typlen < 0);
|
stats->attrtype->typlen < 0);
|
||||||
double corr_xysum;
|
double corr_xysum;
|
||||||
Oid cmpFn;
|
SortSupportData ssup;
|
||||||
int cmpFlags;
|
|
||||||
FmgrInfo f_cmpfn;
|
|
||||||
ScalarItem *values;
|
ScalarItem *values;
|
||||||
int values_cnt = 0;
|
int values_cnt = 0;
|
||||||
int *tupnoLink;
|
int *tupnoLink;
|
||||||
@ -2238,8 +2235,13 @@ compute_scalar_stats(VacAttrStatsP stats,
|
|||||||
tupnoLink = (int *) palloc(samplerows * sizeof(int));
|
tupnoLink = (int *) palloc(samplerows * sizeof(int));
|
||||||
track = (ScalarMCVItem *) palloc(num_mcv * sizeof(ScalarMCVItem));
|
track = (ScalarMCVItem *) palloc(num_mcv * sizeof(ScalarMCVItem));
|
||||||
|
|
||||||
SelectSortFunction(mystats->ltopr, false, &cmpFn, &cmpFlags);
|
memset(&ssup, 0, sizeof(ssup));
|
||||||
fmgr_info(cmpFn, &f_cmpfn);
|
ssup.ssup_cxt = CurrentMemoryContext;
|
||||||
|
/* We always use the default collation for statistics */
|
||||||
|
ssup.ssup_collation = DEFAULT_COLLATION_OID;
|
||||||
|
ssup.ssup_nulls_first = false;
|
||||||
|
|
||||||
|
PrepareSortSupportFromOrderingOp(mystats->ltopr, &ssup);
|
||||||
|
|
||||||
/* Initial scan to find sortable values */
|
/* Initial scan to find sortable values */
|
||||||
for (i = 0; i < samplerows; i++)
|
for (i = 0; i < samplerows; i++)
|
||||||
@ -2307,8 +2309,7 @@ compute_scalar_stats(VacAttrStatsP stats,
|
|||||||
CompareScalarsContext cxt;
|
CompareScalarsContext cxt;
|
||||||
|
|
||||||
/* Sort the collected values */
|
/* Sort the collected values */
|
||||||
cxt.cmpFn = &f_cmpfn;
|
cxt.ssup = &ssup;
|
||||||
cxt.cmpFlags = cmpFlags;
|
|
||||||
cxt.tupnoLink = tupnoLink;
|
cxt.tupnoLink = tupnoLink;
|
||||||
qsort_arg((void *) values, values_cnt, sizeof(ScalarItem),
|
qsort_arg((void *) values, values_cnt, sizeof(ScalarItem),
|
||||||
compare_scalars, (void *) &cxt);
|
compare_scalars, (void *) &cxt);
|
||||||
@ -2712,12 +2713,9 @@ compare_scalars(const void *a, const void *b, void *arg)
|
|||||||
Datum db = ((const ScalarItem *) b)->value;
|
Datum db = ((const ScalarItem *) b)->value;
|
||||||
int tb = ((const ScalarItem *) b)->tupno;
|
int tb = ((const ScalarItem *) b)->tupno;
|
||||||
CompareScalarsContext *cxt = (CompareScalarsContext *) arg;
|
CompareScalarsContext *cxt = (CompareScalarsContext *) arg;
|
||||||
int32 compare;
|
int compare;
|
||||||
|
|
||||||
/* We always use the default collation for statistics */
|
compare = ApplySortComparator(da, false, db, false, cxt->ssup);
|
||||||
compare = ApplySortFunction(cxt->cmpFn, cxt->cmpFlags,
|
|
||||||
DEFAULT_COLLATION_OID,
|
|
||||||
da, false, db, false);
|
|
||||||
if (compare != 0)
|
if (compare != 0)
|
||||||
return compare;
|
return compare;
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "access/genam.h"
|
#include "access/genam.h"
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
|
#include "access/nbtree.h"
|
||||||
#include "access/sysattr.h"
|
#include "access/sysattr.h"
|
||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
@ -1151,28 +1152,49 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
|
|||||||
procform = (Form_pg_proc) GETSTRUCT(proctup);
|
procform = (Form_pg_proc) GETSTRUCT(proctup);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* btree support procs must be 2-arg procs returning int4; hash support
|
* btree comparison procs must be 2-arg procs returning int4, while btree
|
||||||
* procs must be 1-arg procs returning int4; otherwise we don't know.
|
* sortsupport procs must take internal and return void. hash support
|
||||||
|
* procs must be 1-arg procs returning int4. Otherwise we don't know.
|
||||||
*/
|
*/
|
||||||
if (amoid == BTREE_AM_OID)
|
if (amoid == BTREE_AM_OID)
|
||||||
|
{
|
||||||
|
if (member->number == BTORDER_PROC)
|
||||||
{
|
{
|
||||||
if (procform->pronargs != 2)
|
if (procform->pronargs != 2)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
errmsg("btree procedures must have two arguments")));
|
errmsg("btree comparison procedures must have two arguments")));
|
||||||
if (procform->prorettype != INT4OID)
|
if (procform->prorettype != INT4OID)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
errmsg("btree procedures must return integer")));
|
errmsg("btree comparison procedures must return integer")));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If lefttype/righttype isn't specified, use the proc's input types
|
* If lefttype/righttype isn't specified, use the proc's input
|
||||||
|
* types
|
||||||
*/
|
*/
|
||||||
if (!OidIsValid(member->lefttype))
|
if (!OidIsValid(member->lefttype))
|
||||||
member->lefttype = procform->proargtypes.values[0];
|
member->lefttype = procform->proargtypes.values[0];
|
||||||
if (!OidIsValid(member->righttype))
|
if (!OidIsValid(member->righttype))
|
||||||
member->righttype = procform->proargtypes.values[1];
|
member->righttype = procform->proargtypes.values[1];
|
||||||
}
|
}
|
||||||
|
else if (member->number == BTSORTSUPPORT_PROC)
|
||||||
|
{
|
||||||
|
if (procform->pronargs != 1 ||
|
||||||
|
procform->proargtypes.values[0] != INTERNALOID)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("btree sort support procedures must accept type \"internal\"")));
|
||||||
|
if (procform->prorettype != VOIDOID)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("btree sort support procedures must return void")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Can't infer lefttype/righttype from proc, so use default rule
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (amoid == HASH_AM_OID)
|
else if (amoid == HASH_AM_OID)
|
||||||
{
|
{
|
||||||
if (procform->pronargs != 1)
|
if (procform->pronargs != 1)
|
||||||
@ -1192,23 +1214,21 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
|
|||||||
if (!OidIsValid(member->righttype))
|
if (!OidIsValid(member->righttype))
|
||||||
member->righttype = procform->proargtypes.values[0];
|
member->righttype = procform->proargtypes.values[0];
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
* The default for GiST and GIN in CREATE OPERATOR CLASS is to use the
|
* The default in CREATE OPERATOR CLASS is to use the class' opcintype as
|
||||||
* class' opcintype as lefttype and righttype. In CREATE or ALTER
|
* lefttype and righttype. In CREATE or ALTER OPERATOR FAMILY, opcintype
|
||||||
* OPERATOR FAMILY, opcintype isn't available, so make the user
|
* isn't available, so make the user specify the types.
|
||||||
* specify the types.
|
|
||||||
*/
|
*/
|
||||||
if (!OidIsValid(member->lefttype))
|
if (!OidIsValid(member->lefttype))
|
||||||
member->lefttype = typeoid;
|
member->lefttype = typeoid;
|
||||||
if (!OidIsValid(member->righttype))
|
if (!OidIsValid(member->righttype))
|
||||||
member->righttype = typeoid;
|
member->righttype = typeoid;
|
||||||
|
|
||||||
if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
|
if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
errmsg("associated data types must be specified for index support procedure")));
|
errmsg("associated data types must be specified for index support procedure")));
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseSysCache(proctup);
|
ReleaseSysCache(proctup);
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,8 @@
|
|||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/nbtree.h"
|
|
||||||
#include "executor/execdebug.h"
|
#include "executor/execdebug.h"
|
||||||
#include "executor/nodeMergeAppend.h"
|
#include "executor/nodeMergeAppend.h"
|
||||||
#include "utils/lsyscache.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It gets quite confusing having a heap array (indexed by integers) which
|
* It gets quite confusing having a heap array (indexed by integers) which
|
||||||
@ -128,38 +126,18 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
|
|||||||
* initialize sort-key information
|
* initialize sort-key information
|
||||||
*/
|
*/
|
||||||
mergestate->ms_nkeys = node->numCols;
|
mergestate->ms_nkeys = node->numCols;
|
||||||
mergestate->ms_scankeys = palloc0(sizeof(ScanKeyData) * node->numCols);
|
mergestate->ms_sortkeys = palloc0(sizeof(SortSupportData) * node->numCols);
|
||||||
|
|
||||||
for (i = 0; i < node->numCols; i++)
|
for (i = 0; i < node->numCols; i++)
|
||||||
{
|
{
|
||||||
Oid sortFunction;
|
SortSupport sortKey = mergestate->ms_sortkeys + i;
|
||||||
bool reverse;
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
if (!get_compare_function_for_ordering_op(node->sortOperators[i],
|
sortKey->ssup_cxt = CurrentMemoryContext;
|
||||||
&sortFunction, &reverse))
|
sortKey->ssup_collation = node->collations[i];
|
||||||
elog(ERROR, "operator %u is not a valid ordering operator",
|
sortKey->ssup_nulls_first = node->nullsFirst[i];
|
||||||
node->sortOperators[i]);
|
sortKey->ssup_attno = node->sortColIdx[i];
|
||||||
|
|
||||||
/* We use btree's conventions for encoding directionality */
|
PrepareSortSupportFromOrderingOp(node->sortOperators[i], sortKey);
|
||||||
flags = 0;
|
|
||||||
if (reverse)
|
|
||||||
flags |= SK_BT_DESC;
|
|
||||||
if (node->nullsFirst[i])
|
|
||||||
flags |= SK_BT_NULLS_FIRST;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We needn't fill in sk_strategy or sk_subtype since these scankeys
|
|
||||||
* will never be passed to an index.
|
|
||||||
*/
|
|
||||||
ScanKeyEntryInitialize(&mergestate->ms_scankeys[i],
|
|
||||||
flags,
|
|
||||||
node->sortColIdx[i],
|
|
||||||
InvalidStrategy,
|
|
||||||
InvalidOid,
|
|
||||||
node->collations[i],
|
|
||||||
sortFunction,
|
|
||||||
(Datum) 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -298,46 +276,23 @@ heap_compare_slots(MergeAppendState *node, SlotNumber slot1, SlotNumber slot2)
|
|||||||
|
|
||||||
for (nkey = 0; nkey < node->ms_nkeys; nkey++)
|
for (nkey = 0; nkey < node->ms_nkeys; nkey++)
|
||||||
{
|
{
|
||||||
ScanKey scankey = node->ms_scankeys + nkey;
|
SortSupport sortKey = node->ms_sortkeys + nkey;
|
||||||
AttrNumber attno = scankey->sk_attno;
|
AttrNumber attno = sortKey->ssup_attno;
|
||||||
Datum datum1,
|
Datum datum1,
|
||||||
datum2;
|
datum2;
|
||||||
bool isNull1,
|
bool isNull1,
|
||||||
isNull2;
|
isNull2;
|
||||||
int32 compare;
|
int compare;
|
||||||
|
|
||||||
datum1 = slot_getattr(s1, attno, &isNull1);
|
datum1 = slot_getattr(s1, attno, &isNull1);
|
||||||
datum2 = slot_getattr(s2, attno, &isNull2);
|
datum2 = slot_getattr(s2, attno, &isNull2);
|
||||||
|
|
||||||
if (isNull1)
|
compare = ApplySortComparator(datum1, isNull1,
|
||||||
{
|
datum2, isNull2,
|
||||||
if (isNull2)
|
sortKey);
|
||||||
continue; /* NULL "=" NULL */
|
|
||||||
else if (scankey->sk_flags & SK_BT_NULLS_FIRST)
|
|
||||||
return -1; /* NULL "<" NOT_NULL */
|
|
||||||
else
|
|
||||||
return 1; /* NULL ">" NOT_NULL */
|
|
||||||
}
|
|
||||||
else if (isNull2)
|
|
||||||
{
|
|
||||||
if (scankey->sk_flags & SK_BT_NULLS_FIRST)
|
|
||||||
return 1; /* NOT_NULL ">" NULL */
|
|
||||||
else
|
|
||||||
return -1; /* NOT_NULL "<" NULL */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
compare = DatumGetInt32(FunctionCall2Coll(&scankey->sk_func,
|
|
||||||
scankey->sk_collation,
|
|
||||||
datum1, datum2));
|
|
||||||
if (compare != 0)
|
if (compare != 0)
|
||||||
{
|
|
||||||
if (scankey->sk_flags & SK_BT_DESC)
|
|
||||||
compare = -compare;
|
|
||||||
return compare;
|
return compare;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,8 +95,6 @@
|
|||||||
#include "access/nbtree.h"
|
#include "access/nbtree.h"
|
||||||
#include "executor/execdebug.h"
|
#include "executor/execdebug.h"
|
||||||
#include "executor/nodeMergejoin.h"
|
#include "executor/nodeMergejoin.h"
|
||||||
#include "miscadmin.h"
|
|
||||||
#include "utils/acl.h"
|
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
@ -135,13 +133,10 @@ typedef struct MergeJoinClauseData
|
|||||||
bool risnull;
|
bool risnull;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The comparison strategy in use, and the lookup info to let us call the
|
* Everything we need to know to compare the left and right values is
|
||||||
* btree comparison support function, and the collation to use.
|
* stored here.
|
||||||
*/
|
*/
|
||||||
bool reverse; /* if true, negate the cmpfn's output */
|
SortSupportData ssup;
|
||||||
bool nulls_first; /* if true, nulls sort low */
|
|
||||||
FmgrInfo cmpfinfo;
|
|
||||||
Oid collation;
|
|
||||||
} MergeJoinClauseData;
|
} MergeJoinClauseData;
|
||||||
|
|
||||||
/* Result type for MJEvalOuterValues and MJEvalInnerValues */
|
/* Result type for MJEvalOuterValues and MJEvalInnerValues */
|
||||||
@ -203,8 +198,7 @@ MJExamineQuals(List *mergeclauses,
|
|||||||
int op_strategy;
|
int op_strategy;
|
||||||
Oid op_lefttype;
|
Oid op_lefttype;
|
||||||
Oid op_righttype;
|
Oid op_righttype;
|
||||||
RegProcedure cmpproc;
|
Oid sortfunc;
|
||||||
AclResult aclresult;
|
|
||||||
|
|
||||||
if (!IsA(qual, OpExpr))
|
if (!IsA(qual, OpExpr))
|
||||||
elog(ERROR, "mergejoin clause is not an OpExpr");
|
elog(ERROR, "mergejoin clause is not an OpExpr");
|
||||||
@ -215,6 +209,17 @@ MJExamineQuals(List *mergeclauses,
|
|||||||
clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent);
|
clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent);
|
||||||
clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent);
|
clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent);
|
||||||
|
|
||||||
|
/* Set up sort support data */
|
||||||
|
clause->ssup.ssup_cxt = CurrentMemoryContext;
|
||||||
|
clause->ssup.ssup_collation = collation;
|
||||||
|
if (opstrategy == BTLessStrategyNumber)
|
||||||
|
clause->ssup.ssup_reverse = false;
|
||||||
|
else if (opstrategy == BTGreaterStrategyNumber)
|
||||||
|
clause->ssup.ssup_reverse = true;
|
||||||
|
else /* planner screwed up */
|
||||||
|
elog(ERROR, "unsupported mergejoin strategy %d", opstrategy);
|
||||||
|
clause->ssup.ssup_nulls_first = nulls_first;
|
||||||
|
|
||||||
/* Extract the operator's declared left/right datatypes */
|
/* Extract the operator's declared left/right datatypes */
|
||||||
get_op_opfamily_properties(qual->opno, opfamily, false,
|
get_op_opfamily_properties(qual->opno, opfamily, false,
|
||||||
&op_strategy,
|
&op_strategy,
|
||||||
@ -224,36 +229,30 @@ MJExamineQuals(List *mergeclauses,
|
|||||||
elog(ERROR, "cannot merge using non-equality operator %u",
|
elog(ERROR, "cannot merge using non-equality operator %u",
|
||||||
qual->opno);
|
qual->opno);
|
||||||
|
|
||||||
/* And get the matching support procedure (comparison function) */
|
/* And get the matching support or comparison function */
|
||||||
cmpproc = get_opfamily_proc(opfamily,
|
sortfunc = get_opfamily_proc(opfamily,
|
||||||
|
op_lefttype,
|
||||||
|
op_righttype,
|
||||||
|
BTSORTSUPPORT_PROC);
|
||||||
|
if (OidIsValid(sortfunc))
|
||||||
|
{
|
||||||
|
/* The sort support function should provide a comparator */
|
||||||
|
OidFunctionCall1(sortfunc, PointerGetDatum(&clause->ssup));
|
||||||
|
Assert(clause->ssup.comparator != NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* opfamily doesn't provide sort support, get comparison func */
|
||||||
|
sortfunc = get_opfamily_proc(opfamily,
|
||||||
op_lefttype,
|
op_lefttype,
|
||||||
op_righttype,
|
op_righttype,
|
||||||
BTORDER_PROC);
|
BTORDER_PROC);
|
||||||
if (!RegProcedureIsValid(cmpproc)) /* should not happen */
|
if (!OidIsValid(sortfunc)) /* should not happen */
|
||||||
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
|
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
|
||||||
BTORDER_PROC, op_lefttype, op_righttype, opfamily);
|
BTORDER_PROC, op_lefttype, op_righttype, opfamily);
|
||||||
|
/* We'll use a shim to call the old-style btree comparator */
|
||||||
/* Check permission to call cmp function */
|
PrepareSortSupportComparisonShim(sortfunc, &clause->ssup);
|
||||||
aclresult = pg_proc_aclcheck(cmpproc, GetUserId(), ACL_EXECUTE);
|
}
|
||||||
if (aclresult != ACLCHECK_OK)
|
|
||||||
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
||||||
get_func_name(cmpproc));
|
|
||||||
|
|
||||||
/* Set up the fmgr lookup information */
|
|
||||||
fmgr_info(cmpproc, &(clause->cmpfinfo));
|
|
||||||
|
|
||||||
/* Fill the additional comparison-strategy flags */
|
|
||||||
if (opstrategy == BTLessStrategyNumber)
|
|
||||||
clause->reverse = false;
|
|
||||||
else if (opstrategy == BTGreaterStrategyNumber)
|
|
||||||
clause->reverse = true;
|
|
||||||
else /* planner screwed up */
|
|
||||||
elog(ERROR, "unsupported mergejoin strategy %d", opstrategy);
|
|
||||||
|
|
||||||
clause->nulls_first = nulls_first;
|
|
||||||
|
|
||||||
/* ... and the collation too */
|
|
||||||
clause->collation = collation;
|
|
||||||
|
|
||||||
iClause++;
|
iClause++;
|
||||||
}
|
}
|
||||||
@ -310,7 +309,8 @@ MJEvalOuterValues(MergeJoinState *mergestate)
|
|||||||
if (clause->lisnull)
|
if (clause->lisnull)
|
||||||
{
|
{
|
||||||
/* match is impossible; can we end the join early? */
|
/* match is impossible; can we end the join early? */
|
||||||
if (i == 0 && !clause->nulls_first && !mergestate->mj_FillOuter)
|
if (i == 0 && !clause->ssup.ssup_nulls_first &&
|
||||||
|
!mergestate->mj_FillOuter)
|
||||||
result = MJEVAL_ENDOFJOIN;
|
result = MJEVAL_ENDOFJOIN;
|
||||||
else if (result == MJEVAL_MATCHABLE)
|
else if (result == MJEVAL_MATCHABLE)
|
||||||
result = MJEVAL_NONMATCHABLE;
|
result = MJEVAL_NONMATCHABLE;
|
||||||
@ -356,7 +356,8 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
|
|||||||
if (clause->risnull)
|
if (clause->risnull)
|
||||||
{
|
{
|
||||||
/* match is impossible; can we end the join early? */
|
/* match is impossible; can we end the join early? */
|
||||||
if (i == 0 && !clause->nulls_first && !mergestate->mj_FillInner)
|
if (i == 0 && !clause->ssup.ssup_nulls_first &&
|
||||||
|
!mergestate->mj_FillInner)
|
||||||
result = MJEVAL_ENDOFJOIN;
|
result = MJEVAL_ENDOFJOIN;
|
||||||
else if (result == MJEVAL_MATCHABLE)
|
else if (result == MJEVAL_MATCHABLE)
|
||||||
result = MJEVAL_NONMATCHABLE;
|
result = MJEVAL_NONMATCHABLE;
|
||||||
@ -373,20 +374,19 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
|
|||||||
*
|
*
|
||||||
* Compare the mergejoinable values of the current two input tuples
|
* Compare the mergejoinable values of the current two input tuples
|
||||||
* and return 0 if they are equal (ie, the mergejoin equalities all
|
* and return 0 if they are equal (ie, the mergejoin equalities all
|
||||||
* succeed), +1 if outer > inner, -1 if outer < inner.
|
* succeed), >0 if outer > inner, <0 if outer < inner.
|
||||||
*
|
*
|
||||||
* MJEvalOuterValues and MJEvalInnerValues must already have been called
|
* MJEvalOuterValues and MJEvalInnerValues must already have been called
|
||||||
* for the current outer and inner tuples, respectively.
|
* for the current outer and inner tuples, respectively.
|
||||||
*/
|
*/
|
||||||
static int32
|
static int
|
||||||
MJCompare(MergeJoinState *mergestate)
|
MJCompare(MergeJoinState *mergestate)
|
||||||
{
|
{
|
||||||
int32 result = 0;
|
int result = 0;
|
||||||
bool nulleqnull = false;
|
bool nulleqnull = false;
|
||||||
ExprContext *econtext = mergestate->js.ps.ps_ExprContext;
|
ExprContext *econtext = mergestate->js.ps.ps_ExprContext;
|
||||||
int i;
|
int i;
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
FunctionCallInfoData fcinfo;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call the comparison functions in short-lived context, in case they leak
|
* Call the comparison functions in short-lived context, in case they leak
|
||||||
@ -399,62 +399,28 @@ MJCompare(MergeJoinState *mergestate)
|
|||||||
for (i = 0; i < mergestate->mj_NumClauses; i++)
|
for (i = 0; i < mergestate->mj_NumClauses; i++)
|
||||||
{
|
{
|
||||||
MergeJoinClause clause = &mergestate->mj_Clauses[i];
|
MergeJoinClause clause = &mergestate->mj_Clauses[i];
|
||||||
Datum fresult;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deal with null inputs.
|
* Special case for NULL-vs-NULL, else use standard comparison.
|
||||||
*/
|
*/
|
||||||
if (clause->lisnull)
|
if (clause->lisnull && clause->risnull)
|
||||||
{
|
|
||||||
if (clause->risnull)
|
|
||||||
{
|
{
|
||||||
nulleqnull = true; /* NULL "=" NULL */
|
nulleqnull = true; /* NULL "=" NULL */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (clause->nulls_first)
|
|
||||||
result = -1; /* NULL "<" NOT_NULL */
|
|
||||||
else
|
|
||||||
result = 1; /* NULL ">" NOT_NULL */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (clause->risnull)
|
|
||||||
{
|
|
||||||
if (clause->nulls_first)
|
|
||||||
result = 1; /* NOT_NULL ">" NULL */
|
|
||||||
else
|
|
||||||
result = -1; /* NOT_NULL "<" NULL */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
result = ApplySortComparator(clause->ldatum, clause->lisnull,
|
||||||
* OK to call the comparison function.
|
clause->rdatum, clause->risnull,
|
||||||
*/
|
&clause->ssup);
|
||||||
InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2,
|
|
||||||
clause->collation, NULL, NULL);
|
|
||||||
fcinfo.arg[0] = clause->ldatum;
|
|
||||||
fcinfo.arg[1] = clause->rdatum;
|
|
||||||
fcinfo.argnull[0] = false;
|
|
||||||
fcinfo.argnull[1] = false;
|
|
||||||
fresult = FunctionCallInvoke(&fcinfo);
|
|
||||||
if (fcinfo.isnull)
|
|
||||||
{
|
|
||||||
nulleqnull = true; /* treat like NULL = NULL */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result = DatumGetInt32(fresult);
|
|
||||||
|
|
||||||
if (clause->reverse)
|
|
||||||
result = -result;
|
|
||||||
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we had any null comparison results or NULL-vs-NULL inputs, we do not
|
* If we had any NULL-vs-NULL inputs, we do not want to report that the
|
||||||
* want to report that the tuples are equal. Instead, if result is still
|
* tuples are equal. Instead, if result is still 0, change it to +1.
|
||||||
* 0, change it to +1. This will result in advancing the inner side of
|
* This will result in advancing the inner side of the join.
|
||||||
* the join.
|
|
||||||
*
|
*
|
||||||
* Likewise, if there was a constant-false joinqual, do not report
|
* Likewise, if there was a constant-false joinqual, do not report
|
||||||
* equality. We have to check this as part of the mergequals, else the
|
* equality. We have to check this as part of the mergequals, else the
|
||||||
@ -647,7 +613,7 @@ ExecMergeJoin(MergeJoinState *node)
|
|||||||
List *joinqual;
|
List *joinqual;
|
||||||
List *otherqual;
|
List *otherqual;
|
||||||
bool qualResult;
|
bool qualResult;
|
||||||
int32 compareResult;
|
int compareResult;
|
||||||
PlanState *innerPlan;
|
PlanState *innerPlan;
|
||||||
TupleTableSlot *innerTupleSlot;
|
TupleTableSlot *innerTupleSlot;
|
||||||
PlanState *outerPlan;
|
PlanState *outerPlan;
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "utils/date.h"
|
#include "utils/date.h"
|
||||||
#include "utils/datetime.h"
|
#include "utils/datetime.h"
|
||||||
#include "utils/nabstime.h"
|
#include "utils/nabstime.h"
|
||||||
|
#include "utils/sortsupport.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* gcc's -ffast-math switch breaks routines that expect exact results from
|
* gcc's -ffast-math switch breaks routines that expect exact results from
|
||||||
@ -320,6 +321,28 @@ date_cmp(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_INT32(0);
|
PG_RETURN_INT32(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
date_fastcmp(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
DateADT a = DatumGetDateADT(x);
|
||||||
|
DateADT b = DatumGetDateADT(y);
|
||||||
|
|
||||||
|
if (a < b)
|
||||||
|
return -1;
|
||||||
|
else if (a > b)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
date_sortsupport(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
ssup->comparator = date_fastcmp;
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
date_finite(PG_FUNCTION_ARGS)
|
date_finite(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/sortsupport.h"
|
||||||
|
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
@ -936,6 +937,24 @@ btfloat4cmp(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_INT32(float4_cmp_internal(arg1, arg2));
|
PG_RETURN_INT32(float4_cmp_internal(arg1, arg2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
btfloat4fastcmp(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
float4 arg1 = DatumGetFloat4(x);
|
||||||
|
float4 arg2 = DatumGetFloat4(y);
|
||||||
|
|
||||||
|
return float4_cmp_internal(arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
btfloat4sortsupport(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
ssup->comparator = btfloat4fastcmp;
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* float8{eq,ne,lt,le,gt,ge} - float8/float8 comparison operations
|
* float8{eq,ne,lt,le,gt,ge} - float8/float8 comparison operations
|
||||||
*/
|
*/
|
||||||
@ -1032,6 +1051,24 @@ btfloat8cmp(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
|
PG_RETURN_INT32(float8_cmp_internal(arg1, arg2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
btfloat8fastcmp(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
float8 arg1 = DatumGetFloat8(x);
|
||||||
|
float8 arg2 = DatumGetFloat8(y);
|
||||||
|
|
||||||
|
return float8_cmp_internal(arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
btfloat8sortsupport(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
ssup->comparator = btfloat8fastcmp;
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
btfloat48cmp(PG_FUNCTION_ARGS)
|
btfloat48cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
@ -1830,6 +1830,25 @@ timestamp_cmp(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
|
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* note: this is used for timestamptz also */
|
||||||
|
static int
|
||||||
|
timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
Timestamp a = DatumGetTimestamp(x);
|
||||||
|
Timestamp b = DatumGetTimestamp(y);
|
||||||
|
|
||||||
|
return timestamp_cmp_internal(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
timestamp_sortsupport(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
ssup->comparator = timestamp_fastcmp;
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
timestamp_hash(PG_FUNCTION_ARGS)
|
timestamp_hash(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
33
src/backend/utils/cache/lsyscache.c
vendored
33
src/backend/utils/cache/lsyscache.c
vendored
@ -244,19 +244,22 @@ get_ordering_op_properties(Oid opno,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_compare_function_for_ordering_op
|
* get_sort_function_for_ordering_op
|
||||||
* Get the OID of the datatype-specific btree comparison function
|
* Get the OID of the datatype-specific btree sort support function,
|
||||||
|
* or if there is none, the btree comparison function,
|
||||||
* associated with an ordering operator (a "<" or ">" operator).
|
* associated with an ordering operator (a "<" or ">" operator).
|
||||||
*
|
*
|
||||||
* *cmpfunc receives the comparison function OID.
|
* *sortfunc receives the support or comparison function OID.
|
||||||
|
* *issupport is set TRUE if it's a support func, FALSE if a comparison func.
|
||||||
* *reverse is set FALSE if the operator is "<", TRUE if it's ">"
|
* *reverse is set FALSE if the operator is "<", TRUE if it's ">"
|
||||||
* (indicating the comparison result must be negated before use).
|
* (indicating that comparison results must be negated before use).
|
||||||
*
|
*
|
||||||
* Returns TRUE if successful, FALSE if no btree function can be found.
|
* Returns TRUE if successful, FALSE if no btree function can be found.
|
||||||
* (This indicates that the operator is not a valid ordering operator.)
|
* (This indicates that the operator is not a valid ordering operator.)
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
get_compare_function_for_ordering_op(Oid opno, Oid *cmpfunc, bool *reverse)
|
get_sort_function_for_ordering_op(Oid opno, Oid *sortfunc,
|
||||||
|
bool *issupport, bool *reverse)
|
||||||
{
|
{
|
||||||
Oid opfamily;
|
Oid opfamily;
|
||||||
Oid opcintype;
|
Oid opcintype;
|
||||||
@ -267,21 +270,31 @@ get_compare_function_for_ordering_op(Oid opno, Oid *cmpfunc, bool *reverse)
|
|||||||
&opfamily, &opcintype, &strategy))
|
&opfamily, &opcintype, &strategy))
|
||||||
{
|
{
|
||||||
/* Found a suitable opfamily, get matching support function */
|
/* Found a suitable opfamily, get matching support function */
|
||||||
*cmpfunc = get_opfamily_proc(opfamily,
|
*sortfunc = get_opfamily_proc(opfamily,
|
||||||
|
opcintype,
|
||||||
|
opcintype,
|
||||||
|
BTSORTSUPPORT_PROC);
|
||||||
|
if (OidIsValid(*sortfunc))
|
||||||
|
*issupport = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* opfamily doesn't provide sort support, get comparison func */
|
||||||
|
*sortfunc = get_opfamily_proc(opfamily,
|
||||||
opcintype,
|
opcintype,
|
||||||
opcintype,
|
opcintype,
|
||||||
BTORDER_PROC);
|
BTORDER_PROC);
|
||||||
|
if (!OidIsValid(*sortfunc)) /* should not happen */
|
||||||
if (!OidIsValid(*cmpfunc)) /* should not happen */
|
|
||||||
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
|
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
|
||||||
BTORDER_PROC, opcintype, opcintype, opfamily);
|
BTORDER_PROC, opcintype, opcintype, opfamily);
|
||||||
|
*issupport = false;
|
||||||
|
}
|
||||||
*reverse = (strategy == BTGreaterStrategyNumber);
|
*reverse = (strategy == BTGreaterStrategyNumber);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ensure outputs are set on failure */
|
/* ensure outputs are set on failure */
|
||||||
*cmpfunc = InvalidOid;
|
*sortfunc = InvalidOid;
|
||||||
|
*issupport = false;
|
||||||
*reverse = false;
|
*reverse = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,6 @@ subdir = src/backend/utils/sort
|
|||||||
top_builddir = ../../../..
|
top_builddir = ../../../..
|
||||||
include $(top_builddir)/src/Makefile.global
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
OBJS = logtape.o tuplesort.o tuplestore.o
|
OBJS = logtape.o sortsupport.o tuplesort.o tuplestore.o
|
||||||
|
|
||||||
include $(top_srcdir)/src/backend/common.mk
|
include $(top_srcdir)/src/backend/common.mk
|
||||||
|
160
src/backend/utils/sort/sortsupport.c
Normal file
160
src/backend/utils/sort/sortsupport.c
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* sortsupport.c
|
||||||
|
* Support routines for accelerated sorting.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* src/backend/utils/sort/sortsupport.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "fmgr.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/sortsupport.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Info needed to use an old-style comparison function as a sort comparator */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
FunctionCallInfoData fcinfo; /* reusable callinfo structure */
|
||||||
|
FmgrInfo flinfo; /* lookup data for comparison function */
|
||||||
|
} SortShimExtra;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sortsupport.h defines inline versions of these functions if allowed by the
|
||||||
|
* compiler; in which case the definitions below are skipped.
|
||||||
|
*/
|
||||||
|
#ifndef USE_INLINE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apply a sort comparator function and return a 3-way comparison result.
|
||||||
|
* This takes care of handling reverse-sort and NULLs-ordering properly.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ApplySortComparator(Datum datum1, bool isNull1,
|
||||||
|
Datum datum2, bool isNull2,
|
||||||
|
SortSupport ssup)
|
||||||
|
{
|
||||||
|
int compare;
|
||||||
|
|
||||||
|
if (isNull1)
|
||||||
|
{
|
||||||
|
if (isNull2)
|
||||||
|
compare = 0; /* NULL "=" NULL */
|
||||||
|
else if (ssup->ssup_nulls_first)
|
||||||
|
compare = -1; /* NULL "<" NOT_NULL */
|
||||||
|
else
|
||||||
|
compare = 1; /* NULL ">" NOT_NULL */
|
||||||
|
}
|
||||||
|
else if (isNull2)
|
||||||
|
{
|
||||||
|
if (ssup->ssup_nulls_first)
|
||||||
|
compare = 1; /* NOT_NULL ">" NULL */
|
||||||
|
else
|
||||||
|
compare = -1; /* NOT_NULL "<" NULL */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
compare = (*ssup->comparator) (datum1, datum2, ssup);
|
||||||
|
if (ssup->ssup_reverse)
|
||||||
|
compare = -compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
return compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ! USE_INLINE */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shim function for calling an old-style comparator
|
||||||
|
*
|
||||||
|
* This is essentially an inlined version of FunctionCall2Coll(), except
|
||||||
|
* we assume that the FunctionCallInfoData was already mostly set up by
|
||||||
|
* PrepareSortSupportComparisonShim.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
comparison_shim(Datum x, Datum y, SortSupport ssup)
|
||||||
|
{
|
||||||
|
SortShimExtra *extra = (SortShimExtra *) ssup->ssup_extra;
|
||||||
|
Datum result;
|
||||||
|
|
||||||
|
extra->fcinfo.arg[0] = x;
|
||||||
|
extra->fcinfo.arg[1] = y;
|
||||||
|
|
||||||
|
/* just for paranoia's sake, we reset isnull each time */
|
||||||
|
extra->fcinfo.isnull = false;
|
||||||
|
|
||||||
|
result = FunctionCallInvoke(&extra->fcinfo);
|
||||||
|
|
||||||
|
/* Check for null result, since caller is clearly not expecting one */
|
||||||
|
if (extra->fcinfo.isnull)
|
||||||
|
elog(ERROR, "function %u returned NULL", extra->flinfo.fn_oid);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up a shim function to allow use of an old-style btree comparison
|
||||||
|
* function as if it were a sort support comparator.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
|
||||||
|
{
|
||||||
|
SortShimExtra *extra;
|
||||||
|
|
||||||
|
extra = (SortShimExtra *) MemoryContextAlloc(ssup->ssup_cxt,
|
||||||
|
sizeof(SortShimExtra));
|
||||||
|
|
||||||
|
/* Lookup the comparison function */
|
||||||
|
fmgr_info_cxt(cmpFunc, &extra->flinfo, ssup->ssup_cxt);
|
||||||
|
|
||||||
|
/* We can initialize the callinfo just once and re-use it */
|
||||||
|
InitFunctionCallInfoData(extra->fcinfo, &extra->flinfo, 2,
|
||||||
|
ssup->ssup_collation, NULL, NULL);
|
||||||
|
extra->fcinfo.argnull[0] = false;
|
||||||
|
extra->fcinfo.argnull[1] = false;
|
||||||
|
|
||||||
|
ssup->ssup_extra = extra;
|
||||||
|
ssup->comparator = comparison_shim;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill in SortSupport given an ordering operator (btree "<" or ">" operator).
|
||||||
|
*
|
||||||
|
* Caller must previously have zeroed the SortSupportData structure and then
|
||||||
|
* filled in ssup_cxt, ssup_collation, and ssup_nulls_first. This will fill
|
||||||
|
* in ssup_reverse as well as the comparator function pointer.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup)
|
||||||
|
{
|
||||||
|
Oid sortFunction;
|
||||||
|
bool issupport;
|
||||||
|
|
||||||
|
if (!get_sort_function_for_ordering_op(orderingOp,
|
||||||
|
&sortFunction,
|
||||||
|
&issupport,
|
||||||
|
&ssup->ssup_reverse))
|
||||||
|
elog(ERROR, "operator %u is not a valid ordering operator",
|
||||||
|
orderingOp);
|
||||||
|
|
||||||
|
if (issupport)
|
||||||
|
{
|
||||||
|
/* The sort support function should provide a comparator */
|
||||||
|
OidFunctionCall1(sortFunction, PointerGetDatum(ssup));
|
||||||
|
Assert(ssup->comparator != NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We'll use a shim to call the old-style btree comparator */
|
||||||
|
PrepareSortSupportComparisonShim(sortFunction, ssup);
|
||||||
|
}
|
||||||
|
}
|
@ -112,6 +112,7 @@
|
|||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/pg_rusage.h"
|
#include "utils/pg_rusage.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
#include "utils/sortsupport.h"
|
||||||
#include "utils/tuplesort.h"
|
#include "utils/tuplesort.h"
|
||||||
|
|
||||||
|
|
||||||
@ -339,7 +340,7 @@ struct Tuplesortstate
|
|||||||
* tuplesort_begin_heap and used only by the MinimalTuple routines.
|
* tuplesort_begin_heap and used only by the MinimalTuple routines.
|
||||||
*/
|
*/
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
ScanKey scanKeys; /* array of length nKeys */
|
SortSupport sortKeys; /* array of length nKeys */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These variables are specific to the CLUSTER case; they are set by
|
* These variables are specific to the CLUSTER case; they are set by
|
||||||
@ -367,9 +368,7 @@ struct Tuplesortstate
|
|||||||
* tuplesort_begin_datum and used only by the DatumTuple routines.
|
* tuplesort_begin_datum and used only by the DatumTuple routines.
|
||||||
*/
|
*/
|
||||||
Oid datumType;
|
Oid datumType;
|
||||||
FmgrInfo sortOpFn; /* cached lookup data for sortOperator */
|
SortSupport datumKey;
|
||||||
int sortFnFlags; /* equivalent to sk_flags */
|
|
||||||
Oid sortCollation; /* equivalent to sk_collation */
|
|
||||||
/* we need typelen and byval in order to know how to copy the Datums. */
|
/* we need typelen and byval in order to know how to copy the Datums. */
|
||||||
int datumTypeLen;
|
int datumTypeLen;
|
||||||
bool datumTypeByVal;
|
bool datumTypeByVal;
|
||||||
@ -613,41 +612,23 @@ tuplesort_begin_heap(TupleDesc tupDesc,
|
|||||||
state->reversedirection = reversedirection_heap;
|
state->reversedirection = reversedirection_heap;
|
||||||
|
|
||||||
state->tupDesc = tupDesc; /* assume we need not copy tupDesc */
|
state->tupDesc = tupDesc; /* assume we need not copy tupDesc */
|
||||||
state->scanKeys = (ScanKey) palloc0(nkeys * sizeof(ScanKeyData));
|
|
||||||
|
/* Prepare SortSupport data for each column */
|
||||||
|
state->sortKeys = (SortSupport) palloc0(nkeys * sizeof(SortSupportData));
|
||||||
|
|
||||||
for (i = 0; i < nkeys; i++)
|
for (i = 0; i < nkeys; i++)
|
||||||
{
|
{
|
||||||
Oid sortFunction;
|
SortSupport sortKey = state->sortKeys + i;
|
||||||
bool reverse;
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
AssertArg(attNums[i] != 0);
|
AssertArg(attNums[i] != 0);
|
||||||
AssertArg(sortOperators[i] != 0);
|
AssertArg(sortOperators[i] != 0);
|
||||||
|
|
||||||
if (!get_compare_function_for_ordering_op(sortOperators[i],
|
sortKey->ssup_cxt = CurrentMemoryContext;
|
||||||
&sortFunction, &reverse))
|
sortKey->ssup_collation = sortCollations[i];
|
||||||
elog(ERROR, "operator %u is not a valid ordering operator",
|
sortKey->ssup_nulls_first = nullsFirstFlags[i];
|
||||||
sortOperators[i]);
|
sortKey->ssup_attno = attNums[i];
|
||||||
|
|
||||||
/* We use btree's conventions for encoding directionality */
|
PrepareSortSupportFromOrderingOp(sortOperators[i], sortKey);
|
||||||
flags = 0;
|
|
||||||
if (reverse)
|
|
||||||
flags |= SK_BT_DESC;
|
|
||||||
if (nullsFirstFlags[i])
|
|
||||||
flags |= SK_BT_NULLS_FIRST;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We needn't fill in sk_strategy or sk_subtype since these scankeys
|
|
||||||
* will never be passed to an index.
|
|
||||||
*/
|
|
||||||
ScanKeyEntryInitialize(&state->scanKeys[i],
|
|
||||||
flags,
|
|
||||||
attNums[i],
|
|
||||||
InvalidStrategy,
|
|
||||||
InvalidOid,
|
|
||||||
sortCollations[i],
|
|
||||||
sortFunction,
|
|
||||||
(Datum) 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
@ -799,8 +780,6 @@ tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation,
|
|||||||
{
|
{
|
||||||
Tuplesortstate *state = tuplesort_begin_common(workMem, randomAccess);
|
Tuplesortstate *state = tuplesort_begin_common(workMem, randomAccess);
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
Oid sortFunction;
|
|
||||||
bool reverse;
|
|
||||||
int16 typlen;
|
int16 typlen;
|
||||||
bool typbyval;
|
bool typbyval;
|
||||||
|
|
||||||
@ -829,18 +808,14 @@ tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation,
|
|||||||
|
|
||||||
state->datumType = datumType;
|
state->datumType = datumType;
|
||||||
|
|
||||||
/* lookup the ordering function */
|
/* Prepare SortSupport data */
|
||||||
if (!get_compare_function_for_ordering_op(sortOperator,
|
state->datumKey = (SortSupport) palloc0(sizeof(SortSupportData));
|
||||||
&sortFunction, &reverse))
|
|
||||||
elog(ERROR, "operator %u is not a valid ordering operator",
|
|
||||||
sortOperator);
|
|
||||||
fmgr_info(sortFunction, &state->sortOpFn);
|
|
||||||
|
|
||||||
/* set ordering flags and collation */
|
state->datumKey->ssup_cxt = CurrentMemoryContext;
|
||||||
state->sortFnFlags = reverse ? SK_BT_DESC : 0;
|
state->datumKey->ssup_collation = sortCollation;
|
||||||
if (nullsFirstFlag)
|
state->datumKey->ssup_nulls_first = nullsFirstFlag;
|
||||||
state->sortFnFlags |= SK_BT_NULLS_FIRST;
|
|
||||||
state->sortCollation = sortCollation;
|
PrepareSortSupportFromOrderingOp(sortOperator, state->datumKey);
|
||||||
|
|
||||||
/* lookup necessary attributes of the datum type */
|
/* lookup necessary attributes of the datum type */
|
||||||
get_typlenbyval(datumType, &typlen, &typbyval);
|
get_typlenbyval(datumType, &typlen, &typbyval);
|
||||||
@ -2604,29 +2579,6 @@ markrunend(Tuplesortstate *state, int tapenum)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set up for an external caller of ApplySortFunction. This function
|
|
||||||
* basically just exists to localize knowledge of the encoding of sk_flags
|
|
||||||
* used in this module.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
SelectSortFunction(Oid sortOperator,
|
|
||||||
bool nulls_first,
|
|
||||||
Oid *sortFunction,
|
|
||||||
int *sortFlags)
|
|
||||||
{
|
|
||||||
bool reverse;
|
|
||||||
|
|
||||||
if (!get_compare_function_for_ordering_op(sortOperator,
|
|
||||||
sortFunction, &reverse))
|
|
||||||
elog(ERROR, "operator %u is not a valid ordering operator",
|
|
||||||
sortOperator);
|
|
||||||
|
|
||||||
*sortFlags = reverse ? SK_BT_DESC : 0;
|
|
||||||
if (nulls_first)
|
|
||||||
*sortFlags |= SK_BT_NULLS_FIRST;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inline-able copy of FunctionCall2Coll() to save some cycles in sorting.
|
* Inline-able copy of FunctionCall2Coll() to save some cycles in sorting.
|
||||||
*/
|
*/
|
||||||
@ -2693,20 +2645,6 @@ inlineApplySortFunction(FmgrInfo *sortFunction, int sk_flags, Oid collation,
|
|||||||
return compare;
|
return compare;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Non-inline ApplySortFunction() --- this is needed only to conform to
|
|
||||||
* C99's brain-dead notions about how to implement inline functions...
|
|
||||||
*/
|
|
||||||
int32
|
|
||||||
ApplySortFunction(FmgrInfo *sortFunction, int sortFlags, Oid collation,
|
|
||||||
Datum datum1, bool isNull1,
|
|
||||||
Datum datum2, bool isNull2)
|
|
||||||
{
|
|
||||||
return inlineApplySortFunction(sortFunction, sortFlags, collation,
|
|
||||||
datum1, isNull1,
|
|
||||||
datum2, isNull2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Routines specialized for HeapTuple (actually MinimalTuple) case
|
* Routines specialized for HeapTuple (actually MinimalTuple) case
|
||||||
@ -2715,7 +2653,7 @@ ApplySortFunction(FmgrInfo *sortFunction, int sortFlags, Oid collation,
|
|||||||
static int
|
static int
|
||||||
comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
|
comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
|
||||||
{
|
{
|
||||||
ScanKey scanKey = state->scanKeys;
|
SortSupport sortKey = state->sortKeys;
|
||||||
HeapTupleData ltup;
|
HeapTupleData ltup;
|
||||||
HeapTupleData rtup;
|
HeapTupleData rtup;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
@ -2726,10 +2664,9 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
|
|||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
/* Compare the leading sort key */
|
/* Compare the leading sort key */
|
||||||
compare = inlineApplySortFunction(&scanKey->sk_func, scanKey->sk_flags,
|
compare = ApplySortComparator(a->datum1, a->isnull1,
|
||||||
scanKey->sk_collation,
|
b->datum1, b->isnull1,
|
||||||
a->datum1, a->isnull1,
|
sortKey);
|
||||||
b->datum1, b->isnull1);
|
|
||||||
if (compare != 0)
|
if (compare != 0)
|
||||||
return compare;
|
return compare;
|
||||||
|
|
||||||
@ -2739,10 +2676,10 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
|
|||||||
rtup.t_len = ((MinimalTuple) b->tuple)->t_len + MINIMAL_TUPLE_OFFSET;
|
rtup.t_len = ((MinimalTuple) b->tuple)->t_len + MINIMAL_TUPLE_OFFSET;
|
||||||
rtup.t_data = (HeapTupleHeader) ((char *) b->tuple - MINIMAL_TUPLE_OFFSET);
|
rtup.t_data = (HeapTupleHeader) ((char *) b->tuple - MINIMAL_TUPLE_OFFSET);
|
||||||
tupDesc = state->tupDesc;
|
tupDesc = state->tupDesc;
|
||||||
scanKey++;
|
sortKey++;
|
||||||
for (nkey = 1; nkey < state->nKeys; nkey++, scanKey++)
|
for (nkey = 1; nkey < state->nKeys; nkey++, sortKey++)
|
||||||
{
|
{
|
||||||
AttrNumber attno = scanKey->sk_attno;
|
AttrNumber attno = sortKey->ssup_attno;
|
||||||
Datum datum1,
|
Datum datum1,
|
||||||
datum2;
|
datum2;
|
||||||
bool isnull1,
|
bool isnull1,
|
||||||
@ -2751,10 +2688,9 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
|
|||||||
datum1 = heap_getattr(<up, attno, tupDesc, &isnull1);
|
datum1 = heap_getattr(<up, attno, tupDesc, &isnull1);
|
||||||
datum2 = heap_getattr(&rtup, attno, tupDesc, &isnull2);
|
datum2 = heap_getattr(&rtup, attno, tupDesc, &isnull2);
|
||||||
|
|
||||||
compare = inlineApplySortFunction(&scanKey->sk_func, scanKey->sk_flags,
|
compare = ApplySortComparator(datum1, isnull1,
|
||||||
scanKey->sk_collation,
|
datum2, isnull2,
|
||||||
datum1, isnull1,
|
sortKey);
|
||||||
datum2, isnull2);
|
|
||||||
if (compare != 0)
|
if (compare != 0)
|
||||||
return compare;
|
return compare;
|
||||||
}
|
}
|
||||||
@ -2781,7 +2717,7 @@ copytup_heap(Tuplesortstate *state, SortTuple *stup, void *tup)
|
|||||||
htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
|
htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
|
||||||
htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
|
htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
|
||||||
stup->datum1 = heap_getattr(&htup,
|
stup->datum1 = heap_getattr(&htup,
|
||||||
state->scanKeys[0].sk_attno,
|
state->sortKeys[0].ssup_attno,
|
||||||
state->tupDesc,
|
state->tupDesc,
|
||||||
&stup->isnull1);
|
&stup->isnull1);
|
||||||
}
|
}
|
||||||
@ -2833,7 +2769,7 @@ readtup_heap(Tuplesortstate *state, SortTuple *stup,
|
|||||||
htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
|
htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
|
||||||
htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
|
htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
|
||||||
stup->datum1 = heap_getattr(&htup,
|
stup->datum1 = heap_getattr(&htup,
|
||||||
state->scanKeys[0].sk_attno,
|
state->sortKeys[0].ssup_attno,
|
||||||
state->tupDesc,
|
state->tupDesc,
|
||||||
&stup->isnull1);
|
&stup->isnull1);
|
||||||
}
|
}
|
||||||
@ -2841,12 +2777,13 @@ readtup_heap(Tuplesortstate *state, SortTuple *stup,
|
|||||||
static void
|
static void
|
||||||
reversedirection_heap(Tuplesortstate *state)
|
reversedirection_heap(Tuplesortstate *state)
|
||||||
{
|
{
|
||||||
ScanKey scanKey = state->scanKeys;
|
SortSupport sortKey = state->sortKeys;
|
||||||
int nkey;
|
int nkey;
|
||||||
|
|
||||||
for (nkey = 0; nkey < state->nKeys; nkey++, scanKey++)
|
for (nkey = 0; nkey < state->nKeys; nkey++, sortKey++)
|
||||||
{
|
{
|
||||||
scanKey->sk_flags ^= (SK_BT_DESC | SK_BT_NULLS_FIRST);
|
sortKey->ssup_reverse = !sortKey->ssup_reverse;
|
||||||
|
sortKey->ssup_nulls_first = !sortKey->ssup_nulls_first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3297,10 +3234,9 @@ comparetup_datum(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
|
|||||||
/* Allow interrupting long sorts */
|
/* Allow interrupting long sorts */
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
return inlineApplySortFunction(&state->sortOpFn, state->sortFnFlags,
|
return ApplySortComparator(a->datum1, a->isnull1,
|
||||||
state->sortCollation,
|
b->datum1, b->isnull1,
|
||||||
a->datum1, a->isnull1,
|
state->datumKey);
|
||||||
b->datum1, b->isnull1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -3392,7 +3328,8 @@ readtup_datum(Tuplesortstate *state, SortTuple *stup,
|
|||||||
static void
|
static void
|
||||||
reversedirection_datum(Tuplesortstate *state)
|
reversedirection_datum(Tuplesortstate *state)
|
||||||
{
|
{
|
||||||
state->sortFnFlags ^= (SK_BT_DESC | SK_BT_NULLS_FIRST);
|
state->datumKey->ssup_reverse = !state->datumKey->ssup_reverse;
|
||||||
|
state->datumKey->ssup_nulls_first = !state->datumKey->ssup_nulls_first;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -9892,6 +9892,8 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
|
|||||||
int i_sortfamilynsp;
|
int i_sortfamilynsp;
|
||||||
int i_amprocnum;
|
int i_amprocnum;
|
||||||
int i_amproc;
|
int i_amproc;
|
||||||
|
int i_amproclefttype;
|
||||||
|
int i_amprocrighttype;
|
||||||
char *opcintype;
|
char *opcintype;
|
||||||
char *opckeytype;
|
char *opckeytype;
|
||||||
char *opcdefault;
|
char *opcdefault;
|
||||||
@ -9906,6 +9908,8 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
|
|||||||
char *sortfamilynsp;
|
char *sortfamilynsp;
|
||||||
char *amprocnum;
|
char *amprocnum;
|
||||||
char *amproc;
|
char *amproc;
|
||||||
|
char *amproclefttype;
|
||||||
|
char *amprocrighttype;
|
||||||
bool needComma;
|
bool needComma;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -10150,13 +10154,21 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
|
|||||||
*
|
*
|
||||||
* Print only those opfamily members that are tied to the opclass by
|
* Print only those opfamily members that are tied to the opclass by
|
||||||
* pg_depend entries.
|
* pg_depend entries.
|
||||||
|
*
|
||||||
|
* We print the amproclefttype/amprocrighttype even though in most cases
|
||||||
|
* the backend could deduce the right values, because of the corner case
|
||||||
|
* of a btree sort support function for a cross-type comparison. That's
|
||||||
|
* only allowed in 9.2 and later, but for simplicity print them in all
|
||||||
|
* versions that have the columns.
|
||||||
*/
|
*/
|
||||||
resetPQExpBuffer(query);
|
resetPQExpBuffer(query);
|
||||||
|
|
||||||
if (g_fout->remoteVersion >= 80300)
|
if (g_fout->remoteVersion >= 80300)
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(query, "SELECT amprocnum, "
|
appendPQExpBuffer(query, "SELECT amprocnum, "
|
||||||
"amproc::pg_catalog.regprocedure "
|
"amproc::pg_catalog.regprocedure, "
|
||||||
|
"amproclefttype::pg_catalog.regtype, "
|
||||||
|
"amprocrighttype::pg_catalog.regtype "
|
||||||
"FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
|
"FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
|
||||||
"WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
|
"WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
|
||||||
"AND refobjid = '%u'::pg_catalog.oid "
|
"AND refobjid = '%u'::pg_catalog.oid "
|
||||||
@ -10168,7 +10180,9 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(query, "SELECT amprocnum, "
|
appendPQExpBuffer(query, "SELECT amprocnum, "
|
||||||
"amproc::pg_catalog.regprocedure "
|
"amproc::pg_catalog.regprocedure, "
|
||||||
|
"'' AS amproclefttype, "
|
||||||
|
"'' AS amprocrighttype "
|
||||||
"FROM pg_catalog.pg_amproc "
|
"FROM pg_catalog.pg_amproc "
|
||||||
"WHERE amopclaid = '%u'::pg_catalog.oid "
|
"WHERE amopclaid = '%u'::pg_catalog.oid "
|
||||||
"ORDER BY amprocnum",
|
"ORDER BY amprocnum",
|
||||||
@ -10182,17 +10196,25 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
|
|||||||
|
|
||||||
i_amprocnum = PQfnumber(res, "amprocnum");
|
i_amprocnum = PQfnumber(res, "amprocnum");
|
||||||
i_amproc = PQfnumber(res, "amproc");
|
i_amproc = PQfnumber(res, "amproc");
|
||||||
|
i_amproclefttype = PQfnumber(res, "amproclefttype");
|
||||||
|
i_amprocrighttype = PQfnumber(res, "amprocrighttype");
|
||||||
|
|
||||||
for (i = 0; i < ntups; i++)
|
for (i = 0; i < ntups; i++)
|
||||||
{
|
{
|
||||||
amprocnum = PQgetvalue(res, i, i_amprocnum);
|
amprocnum = PQgetvalue(res, i, i_amprocnum);
|
||||||
amproc = PQgetvalue(res, i, i_amproc);
|
amproc = PQgetvalue(res, i, i_amproc);
|
||||||
|
amproclefttype = PQgetvalue(res, i, i_amproclefttype);
|
||||||
|
amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
|
||||||
|
|
||||||
if (needComma)
|
if (needComma)
|
||||||
appendPQExpBuffer(q, " ,\n ");
|
appendPQExpBuffer(q, " ,\n ");
|
||||||
|
|
||||||
appendPQExpBuffer(q, "FUNCTION %s %s",
|
appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
|
||||||
amprocnum, amproc);
|
|
||||||
|
if (*amproclefttype && *amprocrighttype)
|
||||||
|
appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
|
||||||
|
|
||||||
|
appendPQExpBuffer(q, " %s", amproc);
|
||||||
|
|
||||||
needComma = true;
|
needComma = true;
|
||||||
}
|
}
|
||||||
|
@ -417,13 +417,18 @@ typedef struct xl_btree_newroot
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* When a new operator class is declared, we require that the user
|
* When a new operator class is declared, we require that the user
|
||||||
* supply us with an amproc procedure for determining whether, for
|
* supply us with an amproc procedure (BTORDER_PROC) for determining
|
||||||
* two keys a and b, a < b, a = b, or a > b. This routine must
|
* whether, for two keys a and b, a < b, a = b, or a > b. This routine
|
||||||
* return < 0, 0, > 0, respectively, in these three cases. Since we
|
* must return < 0, 0, > 0, respectively, in these three cases. (It must
|
||||||
* only have one such proc in amproc, it's number 1.
|
* not return INT_MIN, since we may negate the result before using it.)
|
||||||
|
*
|
||||||
|
* To facilitate accelerated sorting, an operator class may choose to
|
||||||
|
* offer a second procedure (BTSORTSUPPORT_PROC). For full details, see
|
||||||
|
* src/include/utils/sortsupport.h.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define BTORDER_PROC 1
|
#define BTORDER_PROC 1
|
||||||
|
#define BTSORTSUPPORT_PROC 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to be able to tell the difference between read and write
|
* We need to be able to tell the difference between read and write
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201111271
|
#define CATALOG_VERSION_NO 201112061
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -117,7 +117,7 @@ typedef FormData_pg_am *Form_pg_am;
|
|||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DATA(insert OID = 403 ( btree 5 1 t f t t t t t t t f t t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbuildempty btbulkdelete btvacuumcleanup btcostestimate btoptions ));
|
DATA(insert OID = 403 ( btree 5 2 t f t t t t t t t f t t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbuildempty btbulkdelete btvacuumcleanup btcostestimate btoptions ));
|
||||||
DESCR("b-tree index access method");
|
DESCR("b-tree index access method");
|
||||||
#define BTREE_AM_OID 403
|
#define BTREE_AM_OID 403
|
||||||
DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
|
DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
|
||||||
|
@ -83,33 +83,43 @@ DATA(insert ( 426 1042 1042 1 1078 ));
|
|||||||
DATA(insert ( 428 17 17 1 1954 ));
|
DATA(insert ( 428 17 17 1 1954 ));
|
||||||
DATA(insert ( 429 18 18 1 358 ));
|
DATA(insert ( 429 18 18 1 358 ));
|
||||||
DATA(insert ( 434 1082 1082 1 1092 ));
|
DATA(insert ( 434 1082 1082 1 1092 ));
|
||||||
|
DATA(insert ( 434 1082 1082 2 3136 ));
|
||||||
DATA(insert ( 434 1082 1114 1 2344 ));
|
DATA(insert ( 434 1082 1114 1 2344 ));
|
||||||
DATA(insert ( 434 1082 1184 1 2357 ));
|
DATA(insert ( 434 1082 1184 1 2357 ));
|
||||||
DATA(insert ( 434 1114 1114 1 2045 ));
|
DATA(insert ( 434 1114 1114 1 2045 ));
|
||||||
|
DATA(insert ( 434 1114 1114 2 3137 ));
|
||||||
DATA(insert ( 434 1114 1082 1 2370 ));
|
DATA(insert ( 434 1114 1082 1 2370 ));
|
||||||
DATA(insert ( 434 1114 1184 1 2526 ));
|
DATA(insert ( 434 1114 1184 1 2526 ));
|
||||||
DATA(insert ( 434 1184 1184 1 1314 ));
|
DATA(insert ( 434 1184 1184 1 1314 ));
|
||||||
|
DATA(insert ( 434 1184 1184 2 3137 ));
|
||||||
DATA(insert ( 434 1184 1082 1 2383 ));
|
DATA(insert ( 434 1184 1082 1 2383 ));
|
||||||
DATA(insert ( 434 1184 1114 1 2533 ));
|
DATA(insert ( 434 1184 1114 1 2533 ));
|
||||||
DATA(insert ( 1970 700 700 1 354 ));
|
DATA(insert ( 1970 700 700 1 354 ));
|
||||||
|
DATA(insert ( 1970 700 700 2 3132 ));
|
||||||
DATA(insert ( 1970 700 701 1 2194 ));
|
DATA(insert ( 1970 700 701 1 2194 ));
|
||||||
DATA(insert ( 1970 701 701 1 355 ));
|
DATA(insert ( 1970 701 701 1 355 ));
|
||||||
|
DATA(insert ( 1970 701 701 2 3133 ));
|
||||||
DATA(insert ( 1970 701 700 1 2195 ));
|
DATA(insert ( 1970 701 700 1 2195 ));
|
||||||
DATA(insert ( 1974 869 869 1 926 ));
|
DATA(insert ( 1974 869 869 1 926 ));
|
||||||
DATA(insert ( 1976 21 21 1 350 ));
|
DATA(insert ( 1976 21 21 1 350 ));
|
||||||
|
DATA(insert ( 1976 21 21 2 3129 ));
|
||||||
DATA(insert ( 1976 21 23 1 2190 ));
|
DATA(insert ( 1976 21 23 1 2190 ));
|
||||||
DATA(insert ( 1976 21 20 1 2192 ));
|
DATA(insert ( 1976 21 20 1 2192 ));
|
||||||
DATA(insert ( 1976 23 23 1 351 ));
|
DATA(insert ( 1976 23 23 1 351 ));
|
||||||
|
DATA(insert ( 1976 23 23 2 3130 ));
|
||||||
DATA(insert ( 1976 23 20 1 2188 ));
|
DATA(insert ( 1976 23 20 1 2188 ));
|
||||||
DATA(insert ( 1976 23 21 1 2191 ));
|
DATA(insert ( 1976 23 21 1 2191 ));
|
||||||
DATA(insert ( 1976 20 20 1 842 ));
|
DATA(insert ( 1976 20 20 1 842 ));
|
||||||
|
DATA(insert ( 1976 20 20 2 3131 ));
|
||||||
DATA(insert ( 1976 20 23 1 2189 ));
|
DATA(insert ( 1976 20 23 1 2189 ));
|
||||||
DATA(insert ( 1976 20 21 1 2193 ));
|
DATA(insert ( 1976 20 21 1 2193 ));
|
||||||
DATA(insert ( 1982 1186 1186 1 1315 ));
|
DATA(insert ( 1982 1186 1186 1 1315 ));
|
||||||
DATA(insert ( 1984 829 829 1 836 ));
|
DATA(insert ( 1984 829 829 1 836 ));
|
||||||
DATA(insert ( 1986 19 19 1 359 ));
|
DATA(insert ( 1986 19 19 1 359 ));
|
||||||
|
DATA(insert ( 1986 19 19 2 3135 ));
|
||||||
DATA(insert ( 1988 1700 1700 1 1769 ));
|
DATA(insert ( 1988 1700 1700 1 1769 ));
|
||||||
DATA(insert ( 1989 26 26 1 356 ));
|
DATA(insert ( 1989 26 26 1 356 ));
|
||||||
|
DATA(insert ( 1989 26 26 2 3134 ));
|
||||||
DATA(insert ( 1991 30 30 1 404 ));
|
DATA(insert ( 1991 30 30 1 404 ));
|
||||||
DATA(insert ( 2994 2249 2249 1 2987 ));
|
DATA(insert ( 2994 2249 2249 1 2987 ));
|
||||||
DATA(insert ( 1994 25 25 1 360 ));
|
DATA(insert ( 1994 25 25 1 360 ));
|
||||||
|
@ -566,16 +566,28 @@ DESCR("I/O");
|
|||||||
|
|
||||||
DATA(insert OID = 350 ( btint2cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "21 21" _null_ _null_ _null_ _null_ btint2cmp _null_ _null_ _null_ ));
|
DATA(insert OID = 350 ( btint2cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "21 21" _null_ _null_ _null_ _null_ btint2cmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3129 ( btint2sortsupport PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2278 "2281" _null_ _null_ _null_ _null_ btint2sortsupport _null_ _null_ _null_ ));
|
||||||
|
DESCR("sort support");
|
||||||
DATA(insert OID = 351 ( btint4cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "23 23" _null_ _null_ _null_ _null_ btint4cmp _null_ _null_ _null_ ));
|
DATA(insert OID = 351 ( btint4cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "23 23" _null_ _null_ _null_ _null_ btint4cmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3130 ( btint4sortsupport PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2278 "2281" _null_ _null_ _null_ _null_ btint4sortsupport _null_ _null_ _null_ ));
|
||||||
|
DESCR("sort support");
|
||||||
DATA(insert OID = 842 ( btint8cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "20 20" _null_ _null_ _null_ _null_ btint8cmp _null_ _null_ _null_ ));
|
DATA(insert OID = 842 ( btint8cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "20 20" _null_ _null_ _null_ _null_ btint8cmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3131 ( btint8sortsupport PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2278 "2281" _null_ _null_ _null_ _null_ btint8sortsupport _null_ _null_ _null_ ));
|
||||||
|
DESCR("sort support");
|
||||||
DATA(insert OID = 354 ( btfloat4cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "700 700" _null_ _null_ _null_ _null_ btfloat4cmp _null_ _null_ _null_ ));
|
DATA(insert OID = 354 ( btfloat4cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "700 700" _null_ _null_ _null_ _null_ btfloat4cmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3132 ( btfloat4sortsupport PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2278 "2281" _null_ _null_ _null_ _null_ btfloat4sortsupport _null_ _null_ _null_ ));
|
||||||
|
DESCR("sort support");
|
||||||
DATA(insert OID = 355 ( btfloat8cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "701 701" _null_ _null_ _null_ _null_ btfloat8cmp _null_ _null_ _null_ ));
|
DATA(insert OID = 355 ( btfloat8cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "701 701" _null_ _null_ _null_ _null_ btfloat8cmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3133 ( btfloat8sortsupport PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2278 "2281" _null_ _null_ _null_ _null_ btfloat8sortsupport _null_ _null_ _null_ ));
|
||||||
|
DESCR("sort support");
|
||||||
DATA(insert OID = 356 ( btoidcmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "26 26" _null_ _null_ _null_ _null_ btoidcmp _null_ _null_ _null_ ));
|
DATA(insert OID = 356 ( btoidcmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "26 26" _null_ _null_ _null_ _null_ btoidcmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3134 ( btoidsortsupport PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2278 "2281" _null_ _null_ _null_ _null_ btoidsortsupport _null_ _null_ _null_ ));
|
||||||
|
DESCR("sort support");
|
||||||
DATA(insert OID = 404 ( btoidvectorcmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "30 30" _null_ _null_ _null_ _null_ btoidvectorcmp _null_ _null_ _null_ ));
|
DATA(insert OID = 404 ( btoidvectorcmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "30 30" _null_ _null_ _null_ _null_ btoidvectorcmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
DATA(insert OID = 357 ( btabstimecmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "702 702" _null_ _null_ _null_ _null_ btabstimecmp _null_ _null_ _null_ ));
|
DATA(insert OID = 357 ( btabstimecmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "702 702" _null_ _null_ _null_ _null_ btabstimecmp _null_ _null_ _null_ ));
|
||||||
@ -584,6 +596,8 @@ DATA(insert OID = 358 ( btcharcmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23
|
|||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
DATA(insert OID = 359 ( btnamecmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "19 19" _null_ _null_ _null_ _null_ btnamecmp _null_ _null_ _null_ ));
|
DATA(insert OID = 359 ( btnamecmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "19 19" _null_ _null_ _null_ _null_ btnamecmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3135 ( btnamesortsupport PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2278 "2281" _null_ _null_ _null_ _null_ btnamesortsupport _null_ _null_ _null_ ));
|
||||||
|
DESCR("sort support");
|
||||||
DATA(insert OID = 360 ( bttextcmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "25 25" _null_ _null_ _null_ _null_ bttextcmp _null_ _null_ _null_ ));
|
DATA(insert OID = 360 ( bttextcmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "25 25" _null_ _null_ _null_ _null_ bttextcmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
DATA(insert OID = 377 ( cash_cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "790 790" _null_ _null_ _null_ _null_ cash_cmp _null_ _null_ _null_ ));
|
DATA(insert OID = 377 ( cash_cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "790 790" _null_ _null_ _null_ _null_ cash_cmp _null_ _null_ _null_ ));
|
||||||
@ -1122,6 +1136,8 @@ DATA(insert OID = 1090 ( date_ge PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16
|
|||||||
DATA(insert OID = 1091 ( date_ne PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "1082 1082" _null_ _null_ _null_ _null_ date_ne _null_ _null_ _null_ ));
|
DATA(insert OID = 1091 ( date_ne PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "1082 1082" _null_ _null_ _null_ _null_ date_ne _null_ _null_ _null_ ));
|
||||||
DATA(insert OID = 1092 ( date_cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "1082 1082" _null_ _null_ _null_ _null_ date_cmp _null_ _null_ _null_ ));
|
DATA(insert OID = 1092 ( date_cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "1082 1082" _null_ _null_ _null_ _null_ date_cmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3136 ( date_sortsupport PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2278 "2281" _null_ _null_ _null_ _null_ date_sortsupport _null_ _null_ _null_ ));
|
||||||
|
DESCR("sort support");
|
||||||
|
|
||||||
/* OIDS 1100 - 1199 */
|
/* OIDS 1100 - 1199 */
|
||||||
|
|
||||||
@ -2769,6 +2785,8 @@ DATA(insert OID = 2044 ( overlaps PGNSP PGUID 14 1 0 0 0 f f f f f i 4 0 16 "1
|
|||||||
DESCR("intervals overlap?");
|
DESCR("intervals overlap?");
|
||||||
DATA(insert OID = 2045 ( timestamp_cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "1114 1114" _null_ _null_ _null_ _null_ timestamp_cmp _null_ _null_ _null_ ));
|
DATA(insert OID = 2045 ( timestamp_cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "1114 1114" _null_ _null_ _null_ _null_ timestamp_cmp _null_ _null_ _null_ ));
|
||||||
DESCR("less-equal-greater");
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3137 ( timestamp_sortsupport PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2278 "2281" _null_ _null_ _null_ _null_ timestamp_sortsupport _null_ _null_ _null_ ));
|
||||||
|
DESCR("sort support");
|
||||||
DATA(insert OID = 2046 ( time PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 1083 "1266" _null_ _null_ _null_ _null_ timetz_time _null_ _null_ _null_ ));
|
DATA(insert OID = 2046 ( time PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 1083 "1266" _null_ _null_ _null_ _null_ timetz_time _null_ _null_ _null_ ));
|
||||||
DESCR("convert time with time zone to time");
|
DESCR("convert time with time zone to time");
|
||||||
DATA(insert OID = 2047 ( timetz PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 1266 "1083" _null_ _null_ _null_ _null_ time_timetz _null_ _null_ _null_ ));
|
DATA(insert OID = 2047 ( timetz PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 1266 "1083" _null_ _null_ _null_ _null_ time_timetz _null_ _null_ _null_ ));
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "nodes/params.h"
|
#include "nodes/params.h"
|
||||||
#include "nodes/plannodes.h"
|
#include "nodes/plannodes.h"
|
||||||
#include "utils/reltrigger.h"
|
#include "utils/reltrigger.h"
|
||||||
|
#include "utils/sortsupport.h"
|
||||||
#include "utils/tuplestore.h"
|
#include "utils/tuplestore.h"
|
||||||
|
|
||||||
|
|
||||||
@ -1087,7 +1088,7 @@ typedef struct AppendState
|
|||||||
*
|
*
|
||||||
* nplans how many plans are in the array
|
* nplans how many plans are in the array
|
||||||
* nkeys number of sort key columns
|
* nkeys number of sort key columns
|
||||||
* scankeys sort keys in ScanKey representation
|
* sortkeys sort keys in SortSupport representation
|
||||||
* slots current output tuple of each subplan
|
* slots current output tuple of each subplan
|
||||||
* heap heap of active tuples (represented as array indexes)
|
* heap heap of active tuples (represented as array indexes)
|
||||||
* heap_size number of active heap entries
|
* heap_size number of active heap entries
|
||||||
@ -1101,7 +1102,7 @@ typedef struct MergeAppendState
|
|||||||
PlanState **mergeplans; /* array of PlanStates for my inputs */
|
PlanState **mergeplans; /* array of PlanStates for my inputs */
|
||||||
int ms_nplans;
|
int ms_nplans;
|
||||||
int ms_nkeys;
|
int ms_nkeys;
|
||||||
ScanKey ms_scankeys; /* array of length ms_nkeys */
|
SortSupport ms_sortkeys; /* array of length ms_nkeys */
|
||||||
TupleTableSlot **ms_slots; /* array of length ms_nplans */
|
TupleTableSlot **ms_slots; /* array of length ms_nplans */
|
||||||
int *ms_heap; /* array of length ms_nplans */
|
int *ms_heap; /* array of length ms_nplans */
|
||||||
int ms_heap_size; /* current active length of ms_heap[] */
|
int ms_heap_size; /* current active length of ms_heap[] */
|
||||||
|
@ -279,7 +279,7 @@ extern void pg_lltoa(int64 ll, char *a);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Per-opclass comparison functions for new btrees. These are
|
* Per-opclass comparison functions for new btrees. These are
|
||||||
* stored in pg_amproc and defined in access/nbtree/nbtcompare.c
|
* stored in pg_amproc; most are defined in access/nbtree/nbtcompare.c
|
||||||
*/
|
*/
|
||||||
extern Datum btboolcmp(PG_FUNCTION_ARGS);
|
extern Datum btboolcmp(PG_FUNCTION_ARGS);
|
||||||
extern Datum btint2cmp(PG_FUNCTION_ARGS);
|
extern Datum btint2cmp(PG_FUNCTION_ARGS);
|
||||||
@ -304,6 +304,19 @@ extern Datum btcharcmp(PG_FUNCTION_ARGS);
|
|||||||
extern Datum btnamecmp(PG_FUNCTION_ARGS);
|
extern Datum btnamecmp(PG_FUNCTION_ARGS);
|
||||||
extern Datum bttextcmp(PG_FUNCTION_ARGS);
|
extern Datum bttextcmp(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per-opclass sort support functions for new btrees. Like the
|
||||||
|
* functions above, these are stored in pg_amproc; most are defined in
|
||||||
|
* access/nbtree/nbtcompare.c
|
||||||
|
*/
|
||||||
|
extern Datum btint2sortsupport(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum btint4sortsupport(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum btint8sortsupport(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum btfloat4sortsupport(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum btfloat8sortsupport(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum btoidsortsupport(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum btnamesortsupport(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
/* float.c */
|
/* float.c */
|
||||||
extern PGDLLIMPORT int extra_float_digits;
|
extern PGDLLIMPORT int extra_float_digits;
|
||||||
|
|
||||||
|
@ -104,6 +104,7 @@ extern Datum date_le(PG_FUNCTION_ARGS);
|
|||||||
extern Datum date_gt(PG_FUNCTION_ARGS);
|
extern Datum date_gt(PG_FUNCTION_ARGS);
|
||||||
extern Datum date_ge(PG_FUNCTION_ARGS);
|
extern Datum date_ge(PG_FUNCTION_ARGS);
|
||||||
extern Datum date_cmp(PG_FUNCTION_ARGS);
|
extern Datum date_cmp(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum date_sortsupport(PG_FUNCTION_ARGS);
|
||||||
extern Datum date_finite(PG_FUNCTION_ARGS);
|
extern Datum date_finite(PG_FUNCTION_ARGS);
|
||||||
extern Datum date_larger(PG_FUNCTION_ARGS);
|
extern Datum date_larger(PG_FUNCTION_ARGS);
|
||||||
extern Datum date_smaller(PG_FUNCTION_ARGS);
|
extern Datum date_smaller(PG_FUNCTION_ARGS);
|
||||||
|
@ -48,8 +48,8 @@ extern Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
|
|||||||
int16 strategy);
|
int16 strategy);
|
||||||
extern bool get_ordering_op_properties(Oid opno,
|
extern bool get_ordering_op_properties(Oid opno,
|
||||||
Oid *opfamily, Oid *opcintype, int16 *strategy);
|
Oid *opfamily, Oid *opcintype, int16 *strategy);
|
||||||
extern bool get_compare_function_for_ordering_op(Oid opno,
|
extern bool get_sort_function_for_ordering_op(Oid opno, Oid *sortfunc,
|
||||||
Oid *cmpfunc, bool *reverse);
|
bool *issupport, bool *reverse);
|
||||||
extern Oid get_equality_op_for_ordering_op(Oid opno, bool *reverse);
|
extern Oid get_equality_op_for_ordering_op(Oid opno, bool *reverse);
|
||||||
extern Oid get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type);
|
extern Oid get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type);
|
||||||
extern List *get_mergejoin_opfamilies(Oid opno);
|
extern List *get_mergejoin_opfamilies(Oid opno);
|
||||||
|
156
src/include/utils/sortsupport.h
Normal file
156
src/include/utils/sortsupport.h
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* sortsupport.h
|
||||||
|
* Framework for accelerated sorting.
|
||||||
|
*
|
||||||
|
* Traditionally, PostgreSQL has implemented sorting by repeatedly invoking
|
||||||
|
* an SQL-callable comparison function "cmp(x, y) returns int" on pairs of
|
||||||
|
* values to be compared, where the comparison function is the BTORDER_PROC
|
||||||
|
* pg_amproc support function of the appropriate btree index opclass.
|
||||||
|
*
|
||||||
|
* This file defines alternative APIs that allow sorting to be performed with
|
||||||
|
* reduced overhead. To support lower-overhead sorting, a btree opclass may
|
||||||
|
* provide a BTSORTSUPPORT_PROC pg_amproc entry, which must take a single
|
||||||
|
* argument of type internal and return void. The argument is actually a
|
||||||
|
* pointer to a SortSupportData struct, which is defined below.
|
||||||
|
*
|
||||||
|
* If provided, the BTSORTSUPPORT function will be called during sort setup,
|
||||||
|
* and it must initialize the provided struct with pointers to function(s)
|
||||||
|
* that can be called to perform sorting. This API is defined to allow
|
||||||
|
* multiple acceleration mechanisms to be supported, but no opclass is
|
||||||
|
* required to provide all of them. The BTSORTSUPPORT function should
|
||||||
|
* simply not set any function pointers for mechanisms it doesn't support.
|
||||||
|
* (However, all opclasses that provide BTSORTSUPPORT are required to provide
|
||||||
|
* the comparator function.)
|
||||||
|
*
|
||||||
|
* All sort support functions will be passed the address of the
|
||||||
|
* SortSupportData struct when called, so they can use it to store
|
||||||
|
* additional private data as needed. In particular, for collation-aware
|
||||||
|
* datatypes, the ssup_collation field is set before calling BTSORTSUPPORT
|
||||||
|
* and is available to all support functions. Additional opclass-dependent
|
||||||
|
* data can be stored using the ssup_extra field. Any such data
|
||||||
|
* should be allocated in the ssup_cxt memory context.
|
||||||
|
*
|
||||||
|
* Note: since pg_amproc functions are indexed by (lefttype, righttype)
|
||||||
|
* it is possible to associate a BTSORTSUPPORT function with a cross-type
|
||||||
|
* comparison. This could sensibly be used to provide a fast comparator
|
||||||
|
* function for such cases, but probably not any other acceleration method.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* src/include/utils/sortsupport.h
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef SORTSUPPORT_H
|
||||||
|
#define SORTSUPPORT_H
|
||||||
|
|
||||||
|
#include "access/attnum.h"
|
||||||
|
|
||||||
|
typedef struct SortSupportData *SortSupport;
|
||||||
|
|
||||||
|
typedef struct SortSupportData
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* These fields are initialized before calling the BTSORTSUPPORT function
|
||||||
|
* and should not be changed later.
|
||||||
|
*/
|
||||||
|
MemoryContext ssup_cxt; /* Context containing sort info */
|
||||||
|
Oid ssup_collation; /* Collation to use, or InvalidOid */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Additional sorting parameters; but unlike ssup_collation, these can
|
||||||
|
* be changed after BTSORTSUPPORT is called, so don't use them in
|
||||||
|
* selecting sort support functions.
|
||||||
|
*/
|
||||||
|
bool ssup_reverse; /* descending-order sort? */
|
||||||
|
bool ssup_nulls_first; /* sort nulls first? */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These fields are workspace for callers, and should not be touched by
|
||||||
|
* opclass-specific functions.
|
||||||
|
*/
|
||||||
|
AttrNumber ssup_attno; /* column number to sort */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ssup_extra is zeroed before calling the BTSORTSUPPORT function, and
|
||||||
|
* is not touched subsequently by callers.
|
||||||
|
*/
|
||||||
|
void *ssup_extra; /* Workspace for opclass functions */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function pointers are zeroed before calling the BTSORTSUPPORT function,
|
||||||
|
* and must be set by it for any acceleration methods it wants to supply.
|
||||||
|
* The comparator pointer must be set, others are optional.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Comparator function has the same API as the traditional btree
|
||||||
|
* comparison function, ie, return <0, 0, or >0 according as x is less
|
||||||
|
* than, equal to, or greater than y. Note that x and y are guaranteed
|
||||||
|
* not null, and there is no way to return null either. Do not return
|
||||||
|
* INT_MIN, as callers are allowed to negate the result before using it.
|
||||||
|
*/
|
||||||
|
int (*comparator) (Datum x, Datum y, SortSupport ssup);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Additional sort-acceleration functions might be added here later.
|
||||||
|
*/
|
||||||
|
} SortSupportData;
|
||||||
|
|
||||||
|
|
||||||
|
/* ApplySortComparator should be inlined if possible */
|
||||||
|
#ifdef USE_INLINE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apply a sort comparator function and return a 3-way comparison result.
|
||||||
|
* This takes care of handling reverse-sort and NULLs-ordering properly.
|
||||||
|
*/
|
||||||
|
static inline int
|
||||||
|
ApplySortComparator(Datum datum1, bool isNull1,
|
||||||
|
Datum datum2, bool isNull2,
|
||||||
|
SortSupport ssup)
|
||||||
|
{
|
||||||
|
int compare;
|
||||||
|
|
||||||
|
if (isNull1)
|
||||||
|
{
|
||||||
|
if (isNull2)
|
||||||
|
compare = 0; /* NULL "=" NULL */
|
||||||
|
else if (ssup->ssup_nulls_first)
|
||||||
|
compare = -1; /* NULL "<" NOT_NULL */
|
||||||
|
else
|
||||||
|
compare = 1; /* NULL ">" NOT_NULL */
|
||||||
|
}
|
||||||
|
else if (isNull2)
|
||||||
|
{
|
||||||
|
if (ssup->ssup_nulls_first)
|
||||||
|
compare = 1; /* NOT_NULL ">" NULL */
|
||||||
|
else
|
||||||
|
compare = -1; /* NOT_NULL "<" NULL */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
compare = (*ssup->comparator) (datum1, datum2, ssup);
|
||||||
|
if (ssup->ssup_reverse)
|
||||||
|
compare = -compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
return compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
extern int ApplySortComparator(Datum datum1, bool isNull1,
|
||||||
|
Datum datum2, bool isNull2,
|
||||||
|
SortSupport ssup);
|
||||||
|
|
||||||
|
#endif /* USE_INLINE */
|
||||||
|
|
||||||
|
/* Other functions in utils/sort/sortsupport.c */
|
||||||
|
extern void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup);
|
||||||
|
extern void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup);
|
||||||
|
|
||||||
|
#endif /* SORTSUPPORT_H */
|
@ -109,6 +109,7 @@ extern Datum timestamp_ge(PG_FUNCTION_ARGS);
|
|||||||
extern Datum timestamp_gt(PG_FUNCTION_ARGS);
|
extern Datum timestamp_gt(PG_FUNCTION_ARGS);
|
||||||
extern Datum timestamp_finite(PG_FUNCTION_ARGS);
|
extern Datum timestamp_finite(PG_FUNCTION_ARGS);
|
||||||
extern Datum timestamp_cmp(PG_FUNCTION_ARGS);
|
extern Datum timestamp_cmp(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum timestamp_sortsupport(PG_FUNCTION_ARGS);
|
||||||
extern Datum timestamp_hash(PG_FUNCTION_ARGS);
|
extern Datum timestamp_hash(PG_FUNCTION_ARGS);
|
||||||
extern Datum timestamp_smaller(PG_FUNCTION_ARGS);
|
extern Datum timestamp_smaller(PG_FUNCTION_ARGS);
|
||||||
extern Datum timestamp_larger(PG_FUNCTION_ARGS);
|
extern Datum timestamp_larger(PG_FUNCTION_ARGS);
|
||||||
|
@ -116,19 +116,4 @@ extern void tuplesort_rescan(Tuplesortstate *state);
|
|||||||
extern void tuplesort_markpos(Tuplesortstate *state);
|
extern void tuplesort_markpos(Tuplesortstate *state);
|
||||||
extern void tuplesort_restorepos(Tuplesortstate *state);
|
extern void tuplesort_restorepos(Tuplesortstate *state);
|
||||||
|
|
||||||
/* Setup for ApplySortFunction */
|
|
||||||
extern void SelectSortFunction(Oid sortOperator, bool nulls_first,
|
|
||||||
Oid *sortFunction,
|
|
||||||
int *sortFlags);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Apply a sort function (by now converted to fmgr lookup form)
|
|
||||||
* and return a 3-way comparison result. This takes care of handling
|
|
||||||
* reverse-sort and NULLs-ordering properly.
|
|
||||||
*/
|
|
||||||
extern int32 ApplySortFunction(FmgrInfo *sortFunction, int sortFlags,
|
|
||||||
Oid collation,
|
|
||||||
Datum datum1, bool isNull1,
|
|
||||||
Datum datum2, bool isNull2);
|
|
||||||
|
|
||||||
#endif /* TUPLESORT_H */
|
#endif /* TUPLESORT_H */
|
||||||
|
@ -1186,40 +1186,30 @@ WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
|
|||||||
|
|
||||||
-- Detect missing pg_amproc entries: should have as many support functions
|
-- Detect missing pg_amproc entries: should have as many support functions
|
||||||
-- as AM expects for each datatype combination supported by the opfamily.
|
-- as AM expects for each datatype combination supported by the opfamily.
|
||||||
-- GiST/GIN are special cases because each has an optional support function.
|
-- btree/GiST/GIN each allow one optional support function, though.
|
||||||
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
|
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
|
||||||
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
|
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
|
||||||
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
|
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
|
||||||
p1.amname <> 'gist' AND p1.amname <> 'gin' AND
|
|
||||||
p1.amsupport != (SELECT count(*) FROM pg_amproc AS p4
|
|
||||||
WHERE p4.amprocfamily = p2.oid AND
|
|
||||||
p4.amproclefttype = p3.amproclefttype AND
|
|
||||||
p4.amprocrighttype = p3.amprocrighttype);
|
|
||||||
amname | opfname | amproclefttype | amprocrighttype
|
|
||||||
--------+---------+----------------+-----------------
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
-- Similar check for GiST/GIN, allowing one optional proc
|
|
||||||
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
|
|
||||||
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
|
|
||||||
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
|
|
||||||
(p1.amname = 'gist' OR p1.amname = 'gin') AND
|
|
||||||
(SELECT count(*) FROM pg_amproc AS p4
|
(SELECT count(*) FROM pg_amproc AS p4
|
||||||
WHERE p4.amprocfamily = p2.oid AND
|
WHERE p4.amprocfamily = p2.oid AND
|
||||||
p4.amproclefttype = p3.amproclefttype AND
|
p4.amproclefttype = p3.amproclefttype AND
|
||||||
p4.amprocrighttype = p3.amprocrighttype)
|
p4.amprocrighttype = p3.amprocrighttype)
|
||||||
NOT IN (p1.amsupport, p1.amsupport - 1);
|
NOT BETWEEN
|
||||||
|
(CASE WHEN p1.amname IN ('btree', 'gist', 'gin') THEN p1.amsupport - 1
|
||||||
|
ELSE p1.amsupport END)
|
||||||
|
AND p1.amsupport;
|
||||||
amname | opfname | amproclefttype | amprocrighttype
|
amname | opfname | amproclefttype | amprocrighttype
|
||||||
--------+---------+----------------+-----------------
|
--------+---------+----------------+-----------------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
-- Also, check if there are any pg_opclass entries that don't seem to have
|
-- Also, check if there are any pg_opclass entries that don't seem to have
|
||||||
-- pg_amproc support. Again, GiST/GIN have to be checked specially.
|
-- pg_amproc support. Again, opclasses with an optional support proc have
|
||||||
|
-- to be checked specially.
|
||||||
SELECT amname, opcname, count(*)
|
SELECT amname, opcname, count(*)
|
||||||
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
|
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
|
||||||
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
|
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
|
||||||
amproclefttype = amprocrighttype AND amproclefttype = opcintype
|
amproclefttype = amprocrighttype AND amproclefttype = opcintype
|
||||||
WHERE am.amname <> 'gist' AND am.amname <> 'gin'
|
WHERE am.amname <> 'btree' AND am.amname <> 'gist' AND am.amname <> 'gin'
|
||||||
GROUP BY amname, amsupport, opcname, amprocfamily
|
GROUP BY amname, amsupport, opcname, amprocfamily
|
||||||
HAVING count(*) != amsupport OR amprocfamily IS NULL;
|
HAVING count(*) != amsupport OR amprocfamily IS NULL;
|
||||||
amname | opcname | count
|
amname | opcname | count
|
||||||
@ -1230,7 +1220,7 @@ SELECT amname, opcname, count(*)
|
|||||||
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
|
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
|
||||||
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
|
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
|
||||||
amproclefttype = amprocrighttype AND amproclefttype = opcintype
|
amproclefttype = amprocrighttype AND amproclefttype = opcintype
|
||||||
WHERE am.amname = 'gist' OR am.amname = 'gin'
|
WHERE am.amname = 'btree' OR am.amname = 'gist' OR am.amname = 'gin'
|
||||||
GROUP BY amname, amsupport, opcname, amprocfamily
|
GROUP BY amname, amsupport, opcname, amprocfamily
|
||||||
HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
|
HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
|
||||||
OR amprocfamily IS NULL;
|
OR amprocfamily IS NULL;
|
||||||
@ -1261,19 +1251,22 @@ WHERE p1.amprocfamily = p3.oid AND p4.amprocfamily = p6.oid AND
|
|||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
-- For btree, though, we can do better since we know the support routines
|
-- For btree, though, we can do better since we know the support routines
|
||||||
-- must be of the form cmp(lefttype, righttype) returns int4.
|
-- must be of the form cmp(lefttype, righttype) returns int4
|
||||||
|
-- or sortsupport(internal) returns void.
|
||||||
SELECT p1.amprocfamily, p1.amprocnum,
|
SELECT p1.amprocfamily, p1.amprocnum,
|
||||||
p2.oid, p2.proname,
|
p2.oid, p2.proname,
|
||||||
p3.opfname
|
p3.opfname
|
||||||
FROM pg_amproc AS p1, pg_proc AS p2, pg_opfamily AS p3
|
FROM pg_amproc AS p1, pg_proc AS p2, pg_opfamily AS p3
|
||||||
WHERE p3.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree')
|
WHERE p3.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree')
|
||||||
AND p1.amprocfamily = p3.oid AND p1.amproc = p2.oid AND
|
AND p1.amprocfamily = p3.oid AND p1.amproc = p2.oid AND
|
||||||
(amprocnum != 1
|
(CASE WHEN amprocnum = 1
|
||||||
OR proretset
|
THEN prorettype != 'int4'::regtype OR proretset OR pronargs != 2
|
||||||
OR prorettype != 'int4'::regtype
|
|
||||||
OR pronargs != 2
|
|
||||||
OR proargtypes[0] != amproclefttype
|
OR proargtypes[0] != amproclefttype
|
||||||
OR proargtypes[1] != amprocrighttype);
|
OR proargtypes[1] != amprocrighttype
|
||||||
|
WHEN amprocnum = 2
|
||||||
|
THEN prorettype != 'void'::regtype OR proretset OR pronargs != 1
|
||||||
|
OR proargtypes[0] != 'internal'::regtype
|
||||||
|
ELSE true END);
|
||||||
amprocfamily | amprocnum | oid | proname | opfname
|
amprocfamily | amprocnum | oid | proname | opfname
|
||||||
--------------+-----------+-----+---------+---------
|
--------------+-----------+-----+---------+---------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
@ -926,37 +926,29 @@ WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
|
|||||||
|
|
||||||
-- Detect missing pg_amproc entries: should have as many support functions
|
-- Detect missing pg_amproc entries: should have as many support functions
|
||||||
-- as AM expects for each datatype combination supported by the opfamily.
|
-- as AM expects for each datatype combination supported by the opfamily.
|
||||||
-- GiST/GIN are special cases because each has an optional support function.
|
-- btree/GiST/GIN each allow one optional support function, though.
|
||||||
|
|
||||||
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
|
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
|
||||||
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
|
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
|
||||||
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
|
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
|
||||||
p1.amname <> 'gist' AND p1.amname <> 'gin' AND
|
|
||||||
p1.amsupport != (SELECT count(*) FROM pg_amproc AS p4
|
|
||||||
WHERE p4.amprocfamily = p2.oid AND
|
|
||||||
p4.amproclefttype = p3.amproclefttype AND
|
|
||||||
p4.amprocrighttype = p3.amprocrighttype);
|
|
||||||
|
|
||||||
-- Similar check for GiST/GIN, allowing one optional proc
|
|
||||||
|
|
||||||
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
|
|
||||||
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
|
|
||||||
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
|
|
||||||
(p1.amname = 'gist' OR p1.amname = 'gin') AND
|
|
||||||
(SELECT count(*) FROM pg_amproc AS p4
|
(SELECT count(*) FROM pg_amproc AS p4
|
||||||
WHERE p4.amprocfamily = p2.oid AND
|
WHERE p4.amprocfamily = p2.oid AND
|
||||||
p4.amproclefttype = p3.amproclefttype AND
|
p4.amproclefttype = p3.amproclefttype AND
|
||||||
p4.amprocrighttype = p3.amprocrighttype)
|
p4.amprocrighttype = p3.amprocrighttype)
|
||||||
NOT IN (p1.amsupport, p1.amsupport - 1);
|
NOT BETWEEN
|
||||||
|
(CASE WHEN p1.amname IN ('btree', 'gist', 'gin') THEN p1.amsupport - 1
|
||||||
|
ELSE p1.amsupport END)
|
||||||
|
AND p1.amsupport;
|
||||||
|
|
||||||
-- Also, check if there are any pg_opclass entries that don't seem to have
|
-- Also, check if there are any pg_opclass entries that don't seem to have
|
||||||
-- pg_amproc support. Again, GiST/GIN have to be checked specially.
|
-- pg_amproc support. Again, opclasses with an optional support proc have
|
||||||
|
-- to be checked specially.
|
||||||
|
|
||||||
SELECT amname, opcname, count(*)
|
SELECT amname, opcname, count(*)
|
||||||
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
|
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
|
||||||
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
|
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
|
||||||
amproclefttype = amprocrighttype AND amproclefttype = opcintype
|
amproclefttype = amprocrighttype AND amproclefttype = opcintype
|
||||||
WHERE am.amname <> 'gist' AND am.amname <> 'gin'
|
WHERE am.amname <> 'btree' AND am.amname <> 'gist' AND am.amname <> 'gin'
|
||||||
GROUP BY amname, amsupport, opcname, amprocfamily
|
GROUP BY amname, amsupport, opcname, amprocfamily
|
||||||
HAVING count(*) != amsupport OR amprocfamily IS NULL;
|
HAVING count(*) != amsupport OR amprocfamily IS NULL;
|
||||||
|
|
||||||
@ -964,7 +956,7 @@ SELECT amname, opcname, count(*)
|
|||||||
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
|
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
|
||||||
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
|
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
|
||||||
amproclefttype = amprocrighttype AND amproclefttype = opcintype
|
amproclefttype = amprocrighttype AND amproclefttype = opcintype
|
||||||
WHERE am.amname = 'gist' OR am.amname = 'gin'
|
WHERE am.amname = 'btree' OR am.amname = 'gist' OR am.amname = 'gin'
|
||||||
GROUP BY amname, amsupport, opcname, amprocfamily
|
GROUP BY amname, amsupport, opcname, amprocfamily
|
||||||
HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
|
HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
|
||||||
OR amprocfamily IS NULL;
|
OR amprocfamily IS NULL;
|
||||||
@ -990,7 +982,8 @@ WHERE p1.amprocfamily = p3.oid AND p4.amprocfamily = p6.oid AND
|
|||||||
(p2.proretset OR p5.proretset OR p2.pronargs != p5.pronargs);
|
(p2.proretset OR p5.proretset OR p2.pronargs != p5.pronargs);
|
||||||
|
|
||||||
-- For btree, though, we can do better since we know the support routines
|
-- For btree, though, we can do better since we know the support routines
|
||||||
-- must be of the form cmp(lefttype, righttype) returns int4.
|
-- must be of the form cmp(lefttype, righttype) returns int4
|
||||||
|
-- or sortsupport(internal) returns void.
|
||||||
|
|
||||||
SELECT p1.amprocfamily, p1.amprocnum,
|
SELECT p1.amprocfamily, p1.amprocnum,
|
||||||
p2.oid, p2.proname,
|
p2.oid, p2.proname,
|
||||||
@ -998,12 +991,14 @@ SELECT p1.amprocfamily, p1.amprocnum,
|
|||||||
FROM pg_amproc AS p1, pg_proc AS p2, pg_opfamily AS p3
|
FROM pg_amproc AS p1, pg_proc AS p2, pg_opfamily AS p3
|
||||||
WHERE p3.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree')
|
WHERE p3.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree')
|
||||||
AND p1.amprocfamily = p3.oid AND p1.amproc = p2.oid AND
|
AND p1.amprocfamily = p3.oid AND p1.amproc = p2.oid AND
|
||||||
(amprocnum != 1
|
(CASE WHEN amprocnum = 1
|
||||||
OR proretset
|
THEN prorettype != 'int4'::regtype OR proretset OR pronargs != 2
|
||||||
OR prorettype != 'int4'::regtype
|
|
||||||
OR pronargs != 2
|
|
||||||
OR proargtypes[0] != amproclefttype
|
OR proargtypes[0] != amproclefttype
|
||||||
OR proargtypes[1] != amprocrighttype);
|
OR proargtypes[1] != amprocrighttype
|
||||||
|
WHEN amprocnum = 2
|
||||||
|
THEN prorettype != 'void'::regtype OR proretset OR pronargs != 1
|
||||||
|
OR proargtypes[0] != 'internal'::regtype
|
||||||
|
ELSE true END);
|
||||||
|
|
||||||
-- For hash we can also do a little better: the support routines must be
|
-- For hash we can also do a little better: the support routines must be
|
||||||
-- of the form hash(lefttype) returns int4. There are several cases where
|
-- of the form hash(lefttype) returns int4. There are several cases where
|
||||||
|
Reference in New Issue
Block a user