summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane2011-12-07 05:18:38 +0000
committerTom Lane2011-12-07 05:19:39 +0000
commitc6e3ac11b60ac4a8942ab964252d51c1c0bd8845 (patch)
treefa9ffffed5b31d01a007f447368fd9479bba3aef /src
parentd2a662182eac1069ff3874a1db499508a13c6bca (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/nbtree/nbtcompare.c106
-rw-r--r--src/backend/commands/analyze.c28
-rw-r--r--src/backend/commands/opclasscmds.c88
-rw-r--r--src/backend/executor/nodeMergeAppend.c77
-rw-r--r--src/backend/executor/nodeMergejoin.c146
-rw-r--r--src/backend/utils/adt/date.c23
-rw-r--r--src/backend/utils/adt/float.c37
-rw-r--r--src/backend/utils/adt/timestamp.c19
-rw-r--r--src/backend/utils/cache/lsyscache.c43
-rw-r--r--src/backend/utils/sort/Makefile2
-rw-r--r--src/backend/utils/sort/sortsupport.c160
-rw-r--r--src/backend/utils/sort/tuplesort.c143
-rw-r--r--src/bin/pg_dump/pg_dump.c30
-rw-r--r--src/include/access/nbtree.h15
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_am.h2
-rw-r--r--src/include/catalog/pg_amproc.h10
-rw-r--r--src/include/catalog/pg_proc.h18
-rw-r--r--src/include/nodes/execnodes.h5
-rw-r--r--src/include/utils/builtins.h15
-rw-r--r--src/include/utils/date.h1
-rw-r--r--src/include/utils/lsyscache.h4
-rw-r--r--src/include/utils/sortsupport.h156
-rw-r--r--src/include/utils/timestamp.h1
-rw-r--r--src/include/utils/tuplesort.h15
-rw-r--r--src/test/regress/expected/opr_sanity.out45
-rw-r--r--src/test/regress/sql/opr_sanity.sql43
27 files changed, 834 insertions, 400 deletions
diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
index 23f2b61fe90..f86b7551d2c 100644
--- a/src/backend/access/nbtree/nbtcompare.c
+++ b/src/backend/access/nbtree/nbtcompare.c
@@ -49,6 +49,7 @@
#include "postgres.h"
#include "utils/builtins.h"
+#include "utils/sortsupport.h"
Datum
@@ -69,6 +70,24 @@ btint2cmp(PG_FUNCTION_ARGS)
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
btint4cmp(PG_FUNCTION_ARGS)
{
@@ -83,6 +102,29 @@ btint4cmp(PG_FUNCTION_ARGS)
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
btint8cmp(PG_FUNCTION_ARGS)
{
@@ -97,6 +139,29 @@ btint8cmp(PG_FUNCTION_ARGS)
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
btint48cmp(PG_FUNCTION_ARGS)
{
@@ -195,6 +260,29 @@ btoidcmp(PG_FUNCTION_ARGS)
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
btoidvectorcmp(PG_FUNCTION_ARGS)
{
@@ -237,3 +325,21 @@ btnamecmp(PG_FUNCTION_ARGS)
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();
+}
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 314324618a8..c3d3958da76 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -47,8 +47,8 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
+#include "utils/sortsupport.h"
#include "utils/syscache.h"
-#include "utils/tuplesort.h"
#include "utils/timestamp.h"
#include "utils/tqual.h"
@@ -1774,8 +1774,7 @@ typedef struct
typedef struct
{
- FmgrInfo *cmpFn;
- int cmpFlags;
+ SortSupport ssup;
int *tupnoLink;
} CompareScalarsContext;
@@ -2222,9 +2221,7 @@ compute_scalar_stats(VacAttrStatsP stats,
bool is_varwidth = (!stats->attrtype->typbyval &&
stats->attrtype->typlen < 0);
double corr_xysum;
- Oid cmpFn;
- int cmpFlags;
- FmgrInfo f_cmpfn;
+ SortSupportData ssup;
ScalarItem *values;
int values_cnt = 0;
int *tupnoLink;
@@ -2238,8 +2235,13 @@ compute_scalar_stats(VacAttrStatsP stats,
tupnoLink = (int *) palloc(samplerows * sizeof(int));
track = (ScalarMCVItem *) palloc(num_mcv * sizeof(ScalarMCVItem));
- SelectSortFunction(mystats->ltopr, false, &cmpFn, &cmpFlags);
- fmgr_info(cmpFn, &f_cmpfn);
+ memset(&ssup, 0, sizeof(ssup));
+ 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 */
for (i = 0; i < samplerows; i++)
@@ -2307,8 +2309,7 @@ compute_scalar_stats(VacAttrStatsP stats,
CompareScalarsContext cxt;
/* Sort the collected values */
- cxt.cmpFn = &f_cmpfn;
- cxt.cmpFlags = cmpFlags;
+ cxt.ssup = &ssup;
cxt.tupnoLink = tupnoLink;
qsort_arg((void *) values, values_cnt, sizeof(ScalarItem),
compare_scalars, (void *) &cxt);
@@ -2712,12 +2713,9 @@ compare_scalars(const void *a, const void *b, void *arg)
Datum db = ((const ScalarItem *) b)->value;
int tb = ((const ScalarItem *) b)->tupno;
CompareScalarsContext *cxt = (CompareScalarsContext *) arg;
- int32 compare;
+ int compare;
- /* We always use the default collation for statistics */
- compare = ApplySortFunction(cxt->cmpFn, cxt->cmpFlags,
- DEFAULT_COLLATION_OID,
- da, false, db, false);
+ compare = ApplySortComparator(da, false, db, false, cxt->ssup);
if (compare != 0)
return compare;
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 0ef3584bb2f..b7ab0e3203e 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -19,6 +19,7 @@
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/nbtree.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
@@ -1151,27 +1152,48 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
procform = (Form_pg_proc) GETSTRUCT(proctup);
/*
- * btree support procs must be 2-arg procs returning int4; hash support
- * procs must be 1-arg procs returning int4; otherwise we don't know.
+ * btree comparison procs must be 2-arg procs returning int4, while btree
+ * 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 (procform->pronargs != 2)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree procedures must have two arguments")));
- if (procform->prorettype != INT4OID)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree procedures must return integer")));
+ if (member->number == BTORDER_PROC)
+ {
+ if (procform->pronargs != 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree comparison procedures must have two arguments")));
+ if (procform->prorettype != INT4OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree comparison procedures must return integer")));
- /*
- * If lefttype/righttype isn't specified, use the proc's input types
- */
- if (!OidIsValid(member->lefttype))
- member->lefttype = procform->proargtypes.values[0];
- if (!OidIsValid(member->righttype))
- member->righttype = procform->proargtypes.values[1];
+ /*
+ * If lefttype/righttype isn't specified, use the proc's input
+ * types
+ */
+ if (!OidIsValid(member->lefttype))
+ member->lefttype = procform->proargtypes.values[0];
+ if (!OidIsValid(member->righttype))
+ 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)
{
@@ -1192,23 +1214,21 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
if (!OidIsValid(member->righttype))
member->righttype = procform->proargtypes.values[0];
}
- else
- {
- /*
- * The default for GiST and GIN in CREATE OPERATOR CLASS is to use the
- * class' opcintype as lefttype and righttype. In CREATE or ALTER
- * OPERATOR FAMILY, opcintype isn't available, so make the user
- * specify the types.
- */
- if (!OidIsValid(member->lefttype))
- member->lefttype = typeoid;
- if (!OidIsValid(member->righttype))
- member->righttype = typeoid;
- if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("associated data types must be specified for index support procedure")));
- }
+
+ /*
+ * The default in CREATE OPERATOR CLASS is to use the class' opcintype as
+ * lefttype and righttype. In CREATE or ALTER OPERATOR FAMILY, opcintype
+ * isn't available, so make the user specify the types.
+ */
+ if (!OidIsValid(member->lefttype))
+ member->lefttype = typeoid;
+ if (!OidIsValid(member->righttype))
+ member->righttype = typeoid;
+
+ if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("associated data types must be specified for index support procedure")));
ReleaseSysCache(proctup);
}
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index 43059664b93..6065e2176ac 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -38,10 +38,8 @@
#include "postgres.h"
-#include "access/nbtree.h"
#include "executor/execdebug.h"
#include "executor/nodeMergeAppend.h"
-#include "utils/lsyscache.h"
/*
* 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
*/
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++)
{
- Oid sortFunction;
- bool reverse;
- int flags;
-
- if (!get_compare_function_for_ordering_op(node->sortOperators[i],
- &sortFunction, &reverse))
- elog(ERROR, "operator %u is not a valid ordering operator",
- node->sortOperators[i]);
-
- /* We use btree's conventions for encoding directionality */
- flags = 0;
- if (reverse)
- flags |= SK_BT_DESC;
- if (node->nullsFirst[i])
- flags |= SK_BT_NULLS_FIRST;
+ SortSupport sortKey = mergestate->ms_sortkeys + i;
- /*
- * 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);
+ sortKey->ssup_cxt = CurrentMemoryContext;
+ sortKey->ssup_collation = node->collations[i];
+ sortKey->ssup_nulls_first = node->nullsFirst[i];
+ sortKey->ssup_attno = node->sortColIdx[i];
+
+ PrepareSortSupportFromOrderingOp(node->sortOperators[i], sortKey);
}
/*
@@ -298,45 +276,22 @@ heap_compare_slots(MergeAppendState *node, SlotNumber slot1, SlotNumber slot2)
for (nkey = 0; nkey < node->ms_nkeys; nkey++)
{
- ScanKey scankey = node->ms_scankeys + nkey;
- AttrNumber attno = scankey->sk_attno;
+ SortSupport sortKey = node->ms_sortkeys + nkey;
+ AttrNumber attno = sortKey->ssup_attno;
Datum datum1,
datum2;
bool isNull1,
isNull2;
- int32 compare;
+ int compare;
datum1 = slot_getattr(s1, attno, &isNull1);
datum2 = slot_getattr(s2, attno, &isNull2);
- if (isNull1)
- {
- if (isNull2)
- 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 (scankey->sk_flags & SK_BT_DESC)
- compare = -compare;
- return compare;
- }
- }
+ compare = ApplySortComparator(datum1, isNull1,
+ datum2, isNull2,
+ sortKey);
+ if (compare != 0)
+ return compare;
}
return 0;
}
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index deaa79ed9fb..634931da425 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -95,8 +95,6 @@
#include "access/nbtree.h"
#include "executor/execdebug.h"
#include "executor/nodeMergejoin.h"
-#include "miscadmin.h"
-#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -135,13 +133,10 @@ typedef struct MergeJoinClauseData
bool risnull;
/*
- * The comparison strategy in use, and the lookup info to let us call the
- * btree comparison support function, and the collation to use.
+ * Everything we need to know to compare the left and right values is
+ * stored here.
*/
- bool reverse; /* if true, negate the cmpfn's output */
- bool nulls_first; /* if true, nulls sort low */
- FmgrInfo cmpfinfo;
- Oid collation;
+ SortSupportData ssup;
} MergeJoinClauseData;
/* Result type for MJEvalOuterValues and MJEvalInnerValues */
@@ -203,8 +198,7 @@ MJExamineQuals(List *mergeclauses,
int op_strategy;
Oid op_lefttype;
Oid op_righttype;
- RegProcedure cmpproc;
- AclResult aclresult;
+ Oid sortfunc;
if (!IsA(qual, 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->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 */
get_op_opfamily_properties(qual->opno, opfamily, false,
&op_strategy,
@@ -224,36 +229,30 @@ MJExamineQuals(List *mergeclauses,
elog(ERROR, "cannot merge using non-equality operator %u",
qual->opno);
- /* And get the matching support procedure (comparison function) */
- cmpproc = get_opfamily_proc(opfamily,
- op_lefttype,
- op_righttype,
- BTORDER_PROC);
- if (!RegProcedureIsValid(cmpproc)) /* should not happen */
- elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
- BTORDER_PROC, op_lefttype, op_righttype, opfamily);
-
- /* Check permission to call cmp function */
- 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;
+ /* And get the matching support or comparison function */
+ 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_righttype,
+ BTORDER_PROC);
+ if (!OidIsValid(sortfunc)) /* should not happen */
+ elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
+ BTORDER_PROC, op_lefttype, op_righttype, opfamily);
+ /* We'll use a shim to call the old-style btree comparator */
+ PrepareSortSupportComparisonShim(sortfunc, &clause->ssup);
+ }
iClause++;
}
@@ -310,7 +309,8 @@ MJEvalOuterValues(MergeJoinState *mergestate)
if (clause->lisnull)
{
/* 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;
else if (result == MJEVAL_MATCHABLE)
result = MJEVAL_NONMATCHABLE;
@@ -356,7 +356,8 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
if (clause->risnull)
{
/* 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;
else if (result == MJEVAL_MATCHABLE)
result = MJEVAL_NONMATCHABLE;
@@ -373,20 +374,19 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
*
* Compare the mergejoinable values of the current two input tuples
* 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
* for the current outer and inner tuples, respectively.
*/
-static int32
+static int
MJCompare(MergeJoinState *mergestate)
{
- int32 result = 0;
+ int result = 0;
bool nulleqnull = false;
ExprContext *econtext = mergestate->js.ps.ps_ExprContext;
int i;
MemoryContext oldContext;
- FunctionCallInfoData fcinfo;
/*
* 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++)
{
MergeJoinClause clause = &mergestate->mj_Clauses[i];
- Datum fresult;
-
- /*
- * Deal with null inputs.
- */
- if (clause->lisnull)
- {
- if (clause->risnull)
- {
- nulleqnull = true; /* NULL "=" NULL */
- 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;
- }
/*
- * OK to call the comparison function.
+ * Special case for NULL-vs-NULL, else use standard comparison.
*/
- 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)
+ if (clause->lisnull && clause->risnull)
{
- nulleqnull = true; /* treat like NULL = NULL */
+ nulleqnull = true; /* NULL "=" NULL */
continue;
}
- result = DatumGetInt32(fresult);
- if (clause->reverse)
- result = -result;
+ result = ApplySortComparator(clause->ldatum, clause->lisnull,
+ clause->rdatum, clause->risnull,
+ &clause->ssup);
if (result != 0)
break;
}
/*
- * If we had any null comparison results or NULL-vs-NULL inputs, we do not
- * want to report that the tuples are equal. Instead, if result is still
- * 0, change it to +1. This will result in advancing the inner side of
- * the join.
+ * If we had any NULL-vs-NULL inputs, we do not want to report that the
+ * tuples are equal. Instead, if result is still 0, change it to +1.
+ * This will result in advancing the inner side of the join.
*
* Likewise, if there was a constant-false joinqual, do not report
* equality. We have to check this as part of the mergequals, else the
@@ -647,7 +613,7 @@ ExecMergeJoin(MergeJoinState *node)
List *joinqual;
List *otherqual;
bool qualResult;
- int32 compareResult;
+ int compareResult;
PlanState *innerPlan;
TupleTableSlot *innerTupleSlot;
PlanState *outerPlan;
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 271b57a8302..ff4e3044f07 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -29,6 +29,7 @@
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/nabstime.h"
+#include "utils/sortsupport.h"
/*
* 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);
}
+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
date_finite(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index e3c84fd6c55..63b09a4ecac 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -23,6 +23,7 @@
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/sortsupport.h"
#ifndef M_PI
@@ -936,6 +937,24 @@ btfloat4cmp(PG_FUNCTION_ARGS)
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
*/
@@ -1032,6 +1051,24 @@ btfloat8cmp(PG_FUNCTION_ARGS)
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
btfloat48cmp(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 45e70029e53..450dcd47277 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -1830,6 +1830,25 @@ timestamp_cmp(PG_FUNCTION_ARGS)
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
timestamp_hash(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index cb341b8db67..7a4306e93f5 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -244,19 +244,22 @@ get_ordering_op_properties(Oid opno,
}
/*
- * get_compare_function_for_ordering_op
- * Get the OID of the datatype-specific btree comparison function
+ * get_sort_function_for_ordering_op
+ * 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).
*
- * *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 ">"
- * (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.
* (This indicates that the operator is not a valid ordering operator.)
*/
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 opcintype;
@@ -267,21 +270,31 @@ get_compare_function_for_ordering_op(Oid opno, Oid *cmpfunc, bool *reverse)
&opfamily, &opcintype, &strategy))
{
/* Found a suitable opfamily, get matching support function */
- *cmpfunc = get_opfamily_proc(opfamily,
- opcintype,
- opcintype,
- BTORDER_PROC);
-
- if (!OidIsValid(*cmpfunc)) /* should not happen */
- elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
- BTORDER_PROC, opcintype, opcintype, 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,
+ BTORDER_PROC);
+ if (!OidIsValid(*sortfunc)) /* should not happen */
+ elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
+ BTORDER_PROC, opcintype, opcintype, opfamily);
+ *issupport = false;
+ }
*reverse = (strategy == BTGreaterStrategyNumber);
return true;
}
/* ensure outputs are set on failure */
- *cmpfunc = InvalidOid;
-
+ *sortfunc = InvalidOid;
+ *issupport = false;
*reverse = false;
return false;
}
diff --git a/src/backend/utils/sort/Makefile b/src/backend/utils/sort/Makefile
index 9e20ecb3267..2ef4965ee6d 100644
--- a/src/backend/utils/sort/Makefile
+++ b/src/backend/utils/sort/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/utils/sort
top_builddir = ../../../..
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
diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c
new file mode 100644
index 00000000000..4a87f11827f
--- /dev/null
+++ b/src/backend/utils/sort/sortsupport.c
@@ -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);
+ }
+}
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 3505236e5d3..28d585fc3b5 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -112,6 +112,7 @@
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/rel.h"
+#include "utils/sortsupport.h"
#include "utils/tuplesort.h"
@@ -339,7 +340,7 @@ struct Tuplesortstate
* tuplesort_begin_heap and used only by the MinimalTuple routines.
*/
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
@@ -367,9 +368,7 @@ struct Tuplesortstate
* tuplesort_begin_datum and used only by the DatumTuple routines.
*/
Oid datumType;
- FmgrInfo sortOpFn; /* cached lookup data for sortOperator */
- int sortFnFlags; /* equivalent to sk_flags */
- Oid sortCollation; /* equivalent to sk_collation */
+ SortSupport datumKey;
/* we need typelen and byval in order to know how to copy the Datums. */
int datumTypeLen;
bool datumTypeByVal;
@@ -613,41 +612,23 @@ tuplesort_begin_heap(TupleDesc tupDesc,
state->reversedirection = reversedirection_heap;
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++)
{
- Oid sortFunction;
- bool reverse;
- int flags;
+ SortSupport sortKey = state->sortKeys + i;
AssertArg(attNums[i] != 0);
AssertArg(sortOperators[i] != 0);
- if (!get_compare_function_for_ordering_op(sortOperators[i],
- &sortFunction, &reverse))
- elog(ERROR, "operator %u is not a valid ordering operator",
- sortOperators[i]);
-
- /* We use btree's conventions for encoding directionality */
- flags = 0;
- if (reverse)
- flags |= SK_BT_DESC;
- if (nullsFirstFlags[i])
- flags |= SK_BT_NULLS_FIRST;
+ sortKey->ssup_cxt = CurrentMemoryContext;
+ sortKey->ssup_collation = sortCollations[i];
+ sortKey->ssup_nulls_first = nullsFirstFlags[i];
+ sortKey->ssup_attno = attNums[i];
- /*
- * 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);
+ PrepareSortSupportFromOrderingOp(sortOperators[i], sortKey);
}
MemoryContextSwitchTo(oldcontext);
@@ -799,8 +780,6 @@ tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation,
{
Tuplesortstate *state = tuplesort_begin_common(workMem, randomAccess);
MemoryContext oldcontext;
- Oid sortFunction;
- bool reverse;
int16 typlen;
bool typbyval;
@@ -829,18 +808,14 @@ tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation,
state->datumType = datumType;
- /* lookup the ordering function */
- if (!get_compare_function_for_ordering_op(sortOperator,
- &sortFunction, &reverse))
- elog(ERROR, "operator %u is not a valid ordering operator",
- sortOperator);
- fmgr_info(sortFunction, &state->sortOpFn);
+ /* Prepare SortSupport data */
+ state->datumKey = (SortSupport) palloc0(sizeof(SortSupportData));
- /* set ordering flags and collation */
- state->sortFnFlags = reverse ? SK_BT_DESC : 0;
- if (nullsFirstFlag)
- state->sortFnFlags |= SK_BT_NULLS_FIRST;
- state->sortCollation = sortCollation;
+ state->datumKey->ssup_cxt = CurrentMemoryContext;
+ state->datumKey->ssup_collation = sortCollation;
+ state->datumKey->ssup_nulls_first = nullsFirstFlag;
+
+ PrepareSortSupportFromOrderingOp(sortOperator, state->datumKey);
/* lookup necessary attributes of the datum type */
get_typlenbyval(datumType, &typlen, &typbyval);
@@ -2605,29 +2580,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.
*/
static inline Datum
@@ -2693,20 +2645,6 @@ inlineApplySortFunction(FmgrInfo *sortFunction, int sk_flags, Oid collation,
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
@@ -2715,7 +2653,7 @@ ApplySortFunction(FmgrInfo *sortFunction, int sortFlags, Oid collation,
static int
comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
{
- ScanKey scanKey = state->scanKeys;
+ SortSupport sortKey = state->sortKeys;
HeapTupleData ltup;
HeapTupleData rtup;
TupleDesc tupDesc;
@@ -2726,10 +2664,9 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
CHECK_FOR_INTERRUPTS();
/* Compare the leading sort key */
- compare = inlineApplySortFunction(&scanKey->sk_func, scanKey->sk_flags,
- scanKey->sk_collation,
- a->datum1, a->isnull1,
- b->datum1, b->isnull1);
+ compare = ApplySortComparator(a->datum1, a->isnull1,
+ b->datum1, b->isnull1,
+ sortKey);
if (compare != 0)
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_data = (HeapTupleHeader) ((char *) b->tuple - MINIMAL_TUPLE_OFFSET);
tupDesc = state->tupDesc;
- scanKey++;
- for (nkey = 1; nkey < state->nKeys; nkey++, scanKey++)
+ sortKey++;
+ for (nkey = 1; nkey < state->nKeys; nkey++, sortKey++)
{
- AttrNumber attno = scanKey->sk_attno;
+ AttrNumber attno = sortKey->ssup_attno;
Datum datum1,
datum2;
bool isnull1,
@@ -2751,10 +2688,9 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
datum1 = heap_getattr(&ltup, attno, tupDesc, &isnull1);
datum2 = heap_getattr(&rtup, attno, tupDesc, &isnull2);
- compare = inlineApplySortFunction(&scanKey->sk_func, scanKey->sk_flags,
- scanKey->sk_collation,
- datum1, isnull1,
- datum2, isnull2);
+ compare = ApplySortComparator(datum1, isnull1,
+ datum2, isnull2,
+ sortKey);
if (compare != 0)
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_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
stup->datum1 = heap_getattr(&htup,
- state->scanKeys[0].sk_attno,
+ state->sortKeys[0].ssup_attno,
state->tupDesc,
&stup->isnull1);
}
@@ -2833,7 +2769,7 @@ readtup_heap(Tuplesortstate *state, SortTuple *stup,
htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
stup->datum1 = heap_getattr(&htup,
- state->scanKeys[0].sk_attno,
+ state->sortKeys[0].ssup_attno,
state->tupDesc,
&stup->isnull1);
}
@@ -2841,12 +2777,13 @@ readtup_heap(Tuplesortstate *state, SortTuple *stup,
static void
reversedirection_heap(Tuplesortstate *state)
{
- ScanKey scanKey = state->scanKeys;
+ SortSupport sortKey = state->sortKeys;
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 */
CHECK_FOR_INTERRUPTS();
- return inlineApplySortFunction(&state->sortOpFn, state->sortFnFlags,
- state->sortCollation,
- a->datum1, a->isnull1,
- b->datum1, b->isnull1);
+ return ApplySortComparator(a->datum1, a->isnull1,
+ b->datum1, b->isnull1,
+ state->datumKey);
}
static void
@@ -3392,7 +3328,8 @@ readtup_datum(Tuplesortstate *state, SortTuple *stup,
static void
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;
}
/*
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index cbe529e1568..afeae6f9f7f 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -9892,6 +9892,8 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
int i_sortfamilynsp;
int i_amprocnum;
int i_amproc;
+ int i_amproclefttype;
+ int i_amprocrighttype;
char *opcintype;
char *opckeytype;
char *opcdefault;
@@ -9906,6 +9908,8 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
char *sortfamilynsp;
char *amprocnum;
char *amproc;
+ char *amproclefttype;
+ char *amprocrighttype;
bool needComma;
int i;
@@ -10150,13 +10154,21 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
*
* Print only those opfamily members that are tied to the opclass by
* 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);
if (g_fout->remoteVersion >= 80300)
{
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 "
"WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
"AND refobjid = '%u'::pg_catalog.oid "
@@ -10168,7 +10180,9 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
else
{
appendPQExpBuffer(query, "SELECT amprocnum, "
- "amproc::pg_catalog.regprocedure "
+ "amproc::pg_catalog.regprocedure, "
+ "'' AS amproclefttype, "
+ "'' AS amprocrighttype "
"FROM pg_catalog.pg_amproc "
"WHERE amopclaid = '%u'::pg_catalog.oid "
"ORDER BY amprocnum",
@@ -10182,17 +10196,25 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
i_amprocnum = PQfnumber(res, "amprocnum");
i_amproc = PQfnumber(res, "amproc");
+ i_amproclefttype = PQfnumber(res, "amproclefttype");
+ i_amprocrighttype = PQfnumber(res, "amprocrighttype");
for (i = 0; i < ntups; i++)
{
amprocnum = PQgetvalue(res, i, i_amprocnum);
amproc = PQgetvalue(res, i, i_amproc);
+ amproclefttype = PQgetvalue(res, i, i_amproclefttype);
+ amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
if (needComma)
appendPQExpBuffer(q, " ,\n ");
- appendPQExpBuffer(q, "FUNCTION %s %s",
- amprocnum, amproc);
+ appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
+
+ if (*amproclefttype && *amprocrighttype)
+ appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
+
+ appendPQExpBuffer(q, " %s", amproc);
needComma = true;
}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 347d9423ba3..9a751ce1004 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -417,13 +417,18 @@ typedef struct xl_btree_newroot
/*
* When a new operator class is declared, we require that the user
- * supply us with an amproc procedure for determining whether, for
- * two keys a and b, a < b, a = b, or a > b. This routine must
- * return < 0, 0, > 0, respectively, in these three cases. Since we
- * only have one such proc in amproc, it's number 1.
+ * supply us with an amproc procedure (BTORDER_PROC) for determining
+ * whether, for two keys a and b, a < b, a = b, or a > b. This routine
+ * must return < 0, 0, > 0, respectively, in these three cases. (It must
+ * 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
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 5a73726ba31..95c424b81fa 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201111271
+#define CATALOG_VERSION_NO 201112061
#endif
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 8b075d30d68..ddacdf274c4 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -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");
#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 ));
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index e5d43f7c1d3..8571dd08709 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -83,33 +83,43 @@ DATA(insert ( 426 1042 1042 1 1078 ));
DATA(insert ( 428 17 17 1 1954 ));
DATA(insert ( 429 18 18 1 358 ));
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 1184 1 2357 ));
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 1184 1 2526 ));
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 1114 1 2533 ));
DATA(insert ( 1970 700 700 1 354 ));
+DATA(insert ( 1970 700 700 2 3132 ));
DATA(insert ( 1970 700 701 1 2194 ));
DATA(insert ( 1970 701 701 1 355 ));
+DATA(insert ( 1970 701 701 2 3133 ));
DATA(insert ( 1970 701 700 1 2195 ));
DATA(insert ( 1974 869 869 1 926 ));
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 20 1 2192 ));
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 21 1 2191 ));
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 21 1 2193 ));
DATA(insert ( 1982 1186 1186 1 1315 ));
DATA(insert ( 1984 829 829 1 836 ));
DATA(insert ( 1986 19 19 1 359 ));
+DATA(insert ( 1986 19 19 2 3135 ));
DATA(insert ( 1988 1700 1700 1 1769 ));
DATA(insert ( 1989 26 26 1 356 ));
+DATA(insert ( 1989 26 26 2 3134 ));
DATA(insert ( 1991 30 30 1 404 ));
DATA(insert ( 2994 2249 2249 1 2987 ));
DATA(insert ( 1994 25 25 1 360 ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 28e53b7b7c3..7451a12908a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -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_ ));
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_ ));
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_ ));
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_ ));
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_ ));
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_ ));
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_ ));
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_ ));
@@ -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");
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");
+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_ ));
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_ ));
@@ -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 = 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");
+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 */
@@ -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?");
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");
+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_ ));
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_ ));
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 0a89f189d7c..f5935e2106f 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -20,6 +20,7 @@
#include "nodes/params.h"
#include "nodes/plannodes.h"
#include "utils/reltrigger.h"
+#include "utils/sortsupport.h"
#include "utils/tuplestore.h"
@@ -1087,7 +1088,7 @@ typedef struct AppendState
*
* nplans how many plans are in the array
* 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
* heap heap of active tuples (represented as array indexes)
* heap_size number of active heap entries
@@ -1101,7 +1102,7 @@ typedef struct MergeAppendState
PlanState **mergeplans; /* array of PlanStates for my inputs */
int ms_nplans;
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 */
int *ms_heap; /* array of length ms_nplans */
int ms_heap_size; /* current active length of ms_heap[] */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 47a14125c48..17550e0b3fd 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -279,7 +279,7 @@ extern void pg_lltoa(int64 ll, char *a);
/*
* 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 btint2cmp(PG_FUNCTION_ARGS);
@@ -304,6 +304,19 @@ extern Datum btcharcmp(PG_FUNCTION_ARGS);
extern Datum btnamecmp(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 */
extern PGDLLIMPORT int extra_float_digits;
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 9e10e1afce5..13e0db4e1cb 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -104,6 +104,7 @@ extern Datum date_le(PG_FUNCTION_ARGS);
extern Datum date_gt(PG_FUNCTION_ARGS);
extern Datum date_ge(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_larger(PG_FUNCTION_ARGS);
extern Datum date_smaller(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index d3ad4f14282..311aa25862e 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -48,8 +48,8 @@ extern Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
int16 strategy);
extern bool get_ordering_op_properties(Oid opno,
Oid *opfamily, Oid *opcintype, int16 *strategy);
-extern bool get_compare_function_for_ordering_op(Oid opno,
- Oid *cmpfunc, bool *reverse);
+extern bool get_sort_function_for_ordering_op(Oid opno, Oid *sortfunc,
+ bool *issupport, 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 List *get_mergejoin_opfamilies(Oid opno);
diff --git a/src/include/utils/sortsupport.h b/src/include/utils/sortsupport.h
new file mode 100644
index 00000000000..bd6dde34729
--- /dev/null
+++ b/src/include/utils/sortsupport.h
@@ -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 */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index dc91f3eaaa1..7661744e4d4 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -109,6 +109,7 @@ extern Datum timestamp_ge(PG_FUNCTION_ARGS);
extern Datum timestamp_gt(PG_FUNCTION_ARGS);
extern Datum timestamp_finite(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_smaller(PG_FUNCTION_ARGS);
extern Datum timestamp_larger(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h
index 1ebcbfe1724..5c48e0e10f5 100644
--- a/src/include/utils/tuplesort.h
+++ b/src/include/utils/tuplesort.h
@@ -116,19 +116,4 @@ extern void tuplesort_rescan(Tuplesortstate *state);
extern void tuplesort_markpos(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 */
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index b915134fff3..a0ffd77e0ed 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -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
-- 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
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' 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
WHERE p4.amprocfamily = p2.oid AND
p4.amproclefttype = p3.amproclefttype AND
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
--------+---------+----------------+-----------------
(0 rows)
-- 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(*)
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
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
HAVING count(*) != amsupport OR amprocfamily IS NULL;
amname | opcname | count
@@ -1230,7 +1220,7 @@ SELECT amname, opcname, count(*)
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
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
HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
OR amprocfamily IS NULL;
@@ -1261,19 +1251,22 @@ WHERE p1.amprocfamily = p3.oid AND p4.amprocfamily = p6.oid AND
(0 rows)
-- 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,
p2.oid, p2.proname,
p3.opfname
FROM pg_amproc AS p1, pg_proc AS p2, pg_opfamily AS p3
WHERE p3.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree')
AND p1.amprocfamily = p3.oid AND p1.amproc = p2.oid AND
- (amprocnum != 1
- OR proretset
- OR prorettype != 'int4'::regtype
- OR pronargs != 2
- OR proargtypes[0] != amproclefttype
- OR proargtypes[1] != amprocrighttype);
+ (CASE WHEN amprocnum = 1
+ THEN prorettype != 'int4'::regtype OR proretset OR pronargs != 2
+ OR proargtypes[0] != amproclefttype
+ 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
--------------+-----------+-----+---------+---------
(0 rows)
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index b0d143087e8..6a79ea180c1 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -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
-- 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
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' 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
WHERE p4.amprocfamily = p2.oid AND
p4.amproclefttype = p3.amproclefttype AND
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
--- 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(*)
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
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
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
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
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
HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
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);
-- 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,
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
WHERE p3.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree')
AND p1.amprocfamily = p3.oid AND p1.amproc = p2.oid AND
- (amprocnum != 1
- OR proretset
- OR prorettype != 'int4'::regtype
- OR pronargs != 2
- OR proargtypes[0] != amproclefttype
- OR proargtypes[1] != amprocrighttype);
+ (CASE WHEN amprocnum = 1
+ THEN prorettype != 'int4'::regtype OR proretset OR pronargs != 2
+ OR proargtypes[0] != amproclefttype
+ 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
-- of the form hash(lefttype) returns int4. There are several cases where