[PATCH] kNN for btree
Hi hackers,
I'd like to present a series of patches that implements k-Nearest
Neighbors (kNN)
search forbtree, which can be usedto speed up ORDER BY distance queries
like this:
SELECT * FROM eventsORDER BY date <-> '2000-01-01'::date ASC LIMIT 100;
Now only GiST supports kNN, but kNN on btree can be emulated using
contrib/btree_gist.
Scanning algorithm
==================
Algorithm is very simple: we use bidirectional B-tree index scan
starting at the point
from which we measure the distance(target point).At each step, we advance
this scan in the directionthat has the nearest point. Butwhen the
target point
does not fall into the scannedrange, we don't even need to
useabidirectional
scan here --- wecan use ordinaryunidirectional scaninthe right direction.
Performance results
===================
Test database istaken from original kNN-GiST presentation (PGCon2010).
Test query
SELECT * FROM events ORDER BY date <-> '1957-10-04'::date ASC LIMIT k;
can be optimizedto the next rather complicated UNION form, whichnolonger
requireskNN:
WITH
t1 AS (SELECT * FROM events WHERE date >= '1957-10-04'::date ORDER BY
date ASC LIMIT k),
t2 AS (SELECT * FROM events WHERE date < '1957-10-04'::date ORDER BY
date DESC LIMIT k),
t AS (SELECT * FROM t1 UNION SELECT * FROM t2)
SELECT * FROM t ORDER BY date <-> '1957-10-04'::date ASC LIMIT k;
In each cell of this table shown query execution time in milliseconds and
the number of accessed blocks:
k | kNN-btree | kNN-GiST| Opt. query | Seq.scan
| | (btree_gist) | withUNION | with sort
-------|--------------|--------------|---------------|------------
1 | 0.0414| 0.0794| 0.0608 |41.11824
10 | 0.0487 | 0.0919 | 0.09717 |41.81824
100 | 0.10747 | 0.192 52| 0.342104 |42.31824
1000 | 0.735573 | 0.913 650 | 2.9701160 |43.51824
10000 | 5.070 5622| 6.240 6760| 36.300 11031 | 54.1 1824
100000 | 49.600 51608| 61.900 64194 | 295.100 94980| 115.0 1824
As you can see, kNN-btree can be two times faster than kNN-GiST(btree_gist)
when k < 1000,but the number of blocks readis roughly the same.
Implementation details
======================
A brief description is given below for each of the patches:
1. Introduce amcanorderbyop() function
This patch transformsexisting boolean AMpropertyamcanorderbyop into a
method
(function pointer).This is necessarybecause, unlike GiST,kNN for
btreesupports
only a one ordering operator onthe first index column and we need a
different pathkey
matching logicfor btree (there was acorresponding comment
inmatch_pathkeys_to_index()).
GiST-specific logic has been moved from match_pathkeys_to_index() to
gistcanorderbyop().
2. Extract substructure BTScanState from BTScanOpaque
This refactoringis necessary for bidirectional kNN-scanimplementation. Now,
BTScanOpaque'ssubstructure BTScanState containing only thefields related
toscanpositionis passed to some functions where thewhole BTScanOpaque
waspassedpreviously.
3. Extract get_index_column_opclass(),
get_opclass_opfamily_and_input_type().
Extracted two simple common functions usedingistproperty() and
btproperty() (see the next patch).
4. Add kNN supportto btree
* Added additional optional BTScanState to BTScanOpaque for
bidirectional kNN scan.
* Implemented bidirectional kNN scan.
* Implemented logic for selecting kNN strategy
* Implemented btcanorderbyop(), updated btproperty() and btvalidate()
B-tree user interface functions have not been altered because ordering
operators
are used directly.
5. Add distance operators for sometypes
These operators for integer, float, date, time, timestamp, interval,
cash and oidtypes
havebeencopied fromcontrib/btree_gistand added to the existing btree
opclasses
as ordering operators. Their btree_gist duplicates areremoved in the
next patch.
6. Remove duplicate distance operators from contrib/btree_gist.
References to their own distance operators in btree_gist opclassesare
replaced
with references to the built-in operatorsand thanduplicate operators are
dropped.
But if the user is using somewhere these operators, upgrade of btree_gist
from 1.3 to 1.4 should fail.
7. Add regression tests for btree kNN.
Tests were added only after the built-in distance operators were added.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-introduce-amcanorderby-function-v01.patchtext/x-patch; name=0001-introduce-amcanorderby-function-v01.patchDownload
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 06077af..cb0f5f9 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,7 +109,6 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BLOOM_NSTRATEGIES;
amroutine->amsupport = BLOOM_NPROC;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -138,6 +137,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = blendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 3fce672..e85e224 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -82,7 +82,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -111,6 +110,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = brinendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 3909638..c7ff0fd 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -39,7 +39,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GINNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -68,6 +67,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 4092a8b..504d9ee 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -59,7 +59,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GISTNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -88,6 +87,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = gistendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amcanorderbyop = gistcanorderbyop;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index f92baed..159987e 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -19,6 +19,7 @@
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/pg_opclass.h"
+#include "optimizer/paths.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
@@ -964,3 +965,31 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+/* gistcanorderbyop() */
+Expr *
+gistcanorderbyop(IndexOptInfo *index, PathKey *pathkey, int pathkeyno,
+ Expr *orderby_clause, int *indexcol_p)
+{
+ int indexcol;
+
+ /*
+ * We allow any column of the GiST index to match each pathkey;
+ * they don't have to match left-to-right as you might expect.
+ */
+
+ for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
+ {
+ Expr *expr = match_clause_to_ordering_op(index,
+ indexcol,
+ orderby_clause,
+ pathkey->pk_opfamily);
+ if (expr)
+ {
+ *indexcol_p = indexcol;
+ return expr;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 0cbf6b0..8cdefac 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -56,7 +56,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = HTMaxStrategyNumber;
amroutine->amsupport = HASHNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -85,6 +84,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = hashendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index c6eed63..9e4384a 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -88,7 +88,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BTMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = true;
amroutine->amcanmulticol = true;
@@ -117,6 +116,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index b9e4940..f18b0bc 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -38,7 +38,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = SPGISTNProc;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -67,6 +66,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amendscan = spgendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 0a5c050..b9b4701 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -17,6 +17,7 @@
#include <math.h>
+#include "access/amapi.h"
#include "access/stratnum.h"
#include "access/sysattr.h"
#include "catalog/pg_am.h"
@@ -166,8 +167,6 @@ static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
List **orderby_clauses_p,
List **clause_columns_p);
-static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
- int indexcol, Expr *clause, Oid pk_opfamily);
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
EquivalenceClass *ec, EquivalenceMember *em,
void *arg);
@@ -2473,12 +2472,15 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
List *orderby_clauses = NIL;
List *clause_columns = NIL;
ListCell *lc1;
+ int pathkeyno = 1;
+ amcanorderbyop_function amcanorderbyop =
+ (amcanorderbyop_function) index->amcanorderbyop;
*orderby_clauses_p = NIL; /* set default results */
*clause_columns_p = NIL;
- /* Only indexes with the amcanorderbyop property are interesting here */
- if (!index->amcanorderbyop)
+ /* Only indexes with the amcanorderbyop function are interesting here */
+ if (!amcanorderbyop)
return;
foreach(lc1, pathkeys)
@@ -2512,42 +2514,29 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
foreach(lc2, pathkey->pk_eclass->ec_members)
{
EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+ Expr *expr;
int indexcol;
/* No possibility of match if it references other relations */
if (!bms_equal(member->em_relids, index->rel->relids))
continue;
- /*
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, which is the sole existing AM supporting
- * amcanorderbyop. We might need different logic in future for
- * other implementations.
- */
- for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
- {
- Expr *expr;
+ expr = amcanorderbyop(index, pathkey, pathkeyno, member->em_expr,
+ &indexcol);
- expr = match_clause_to_ordering_op(index,
- indexcol,
- member->em_expr,
- pathkey->pk_opfamily);
- if (expr)
- {
- orderby_clauses = lappend(orderby_clauses, expr);
- clause_columns = lappend_int(clause_columns, indexcol);
- found = true;
- break;
- }
+ if (expr)
+ {
+ orderby_clauses = lappend(orderby_clauses, expr);
+ clause_columns = lappend_int(clause_columns, indexcol);
+ found = true;
+ break; /* don't want to look at remaining members */
}
-
- if (found) /* don't want to look at remaining members */
- break;
}
if (!found) /* fail if no match for this pathkey */
return;
+
+ pathkeyno++;
}
*orderby_clauses_p = orderby_clauses; /* success! */
@@ -2579,7 +2568,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
* If successful, return 'clause' as-is if the indexkey is on the left,
* otherwise a commuted copy of 'clause'. If no match, return NULL.
*/
-static Expr *
+Expr *
match_clause_to_ordering_op(IndexOptInfo *index,
int indexcol,
Expr *clause,
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 7836e6b..93bda0f 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -237,7 +237,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* We copy just the fields we need, not all of rd_amroutine */
amroutine = indexRelation->rd_amroutine;
- info->amcanorderbyop = amroutine->amcanorderbyop;
+ info->amcanorderbyop = (void (*)()) amroutine->amcanorderbyop;
info->amoptionalkey = amroutine->amoptionalkey;
info->amsearcharray = amroutine->amsearcharray;
info->amsearchnulls = amroutine->amsearchnulls;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 48b0cc0..436c5f8 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -21,6 +21,9 @@
*/
struct PlannerInfo;
struct IndexPath;
+struct IndexOptInfo;
+struct PathKey;
+struct Expr;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
@@ -137,6 +140,12 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* does AM support ORDER BY result of an operator on indexed column? */
+typedef struct Expr *(*amcanorderbyop_function) (struct IndexOptInfo *index,
+ struct PathKey *pathkey,
+ int pathkeyno,
+ struct Expr *orderby_clause,
+ int *indexcol_p);
/*
* API struct for an index AM. Note this must be stored in a single palloc'd
@@ -155,8 +164,6 @@ typedef struct IndexAmRoutine
uint16 amsupport;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
- /* does AM support ORDER BY result of an operator on indexed column? */
- bool amcanorderbyop;
/* does AM support backward scanning? */
bool amcanbackward;
/* does AM support UNIQUE indexes? */
@@ -196,6 +203,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amcanorderbyop_function amcanorderbyop; /* can be NULL */
} IndexAmRoutine;
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 72f52d4..0d35646 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -538,6 +538,11 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
extern XLogRecPtr gistGetFakeLSN(Relation rel);
+extern struct Expr *gistcanorderbyop(struct IndexOptInfo *index,
+ struct PathKey *pathkey, int pathkeyno,
+ struct Expr *orderby_clause,
+ int *indexcol_p);
+
/* gistvacuum.c */
extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index e1d31c7..ccf282a 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -616,7 +616,6 @@ typedef struct IndexOptInfo
bool hypothetical; /* true if index doesn't really exist */
/* Remaining fields are copied from the index AM's API struct: */
- bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
@@ -624,6 +623,7 @@ typedef struct IndexOptInfo
bool amhasgetbitmap; /* does AM have amgetbitmap interface? */
/* Rather than include amapi.h here, we declare amcostestimate like this */
void (*amcostestimate) (); /* AM's cost estimator */
+ void (*amcanorderbyop) (); /* does AM support order by operator result? */
} IndexOptInfo;
/*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 81a9be7..bb7611e 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -79,6 +79,10 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
int indexcol,
List **indexcolnos,
bool *var_on_left_p);
+extern Expr *match_clause_to_ordering_op(IndexOptInfo *index,
+ int indexcol,
+ Expr *clause,
+ Oid pk_opfamily);
/*
* tidpath.h
0002-extract-structure-BTScanState-v01.patchtext/x-patch; name=0002-extract-structure-BTScanState-v01.patchDownload
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 9e4384a..a05f0c2 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -295,6 +295,7 @@ bool
btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
bool res;
/* btree indexes are never lossy */
@@ -305,7 +306,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(so->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
@@ -322,7 +323,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(so->currPos))
+ if (!BTScanPosIsValid(state->currPos))
res = _bt_first(scan, dir);
else
{
@@ -340,11 +341,11 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* trying to optimize that, so we don't detect it, but instead
* just forget any excess entries.
*/
- if (so->killedItems == NULL)
- so->killedItems = (int *)
+ if (state->killedItems == NULL)
+ state->killedItems = (int *)
palloc(MaxIndexTuplesPerPage * sizeof(int));
- if (so->numKilled < MaxIndexTuplesPerPage)
- so->killedItems[so->numKilled++] = so->currPos.itemIndex;
+ if (state->numKilled < MaxIndexTuplesPerPage)
+ state->killedItems[so->state.numKilled++] = state->currPos.itemIndex;
}
/*
@@ -369,6 +370,7 @@ int64
btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
int64 ntids = 0;
ItemPointer heapTid;
@@ -401,7 +403,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* Advance to next tuple within page. This is the same as the
* easy case in _bt_next().
*/
- if (++so->currPos.itemIndex > so->currPos.lastItem)
+ if (++currPos->itemIndex > currPos->lastItem)
{
/* let _bt_next do the heavy lifting */
if (!_bt_next(scan, ForwardScanDirection))
@@ -409,7 +411,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
/* Save tuple ID, and continue scanning */
- heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
+ heapTid = &currPos->items[currPos->itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
}
@@ -437,8 +439,8 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
/* allocate private workspace */
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
- BTScanPosInvalidate(so->currPos);
- BTScanPosInvalidate(so->markPos);
+ BTScanPosInvalidate(so->state.currPos);
+ BTScanPosInvalidate(so->state.markPos);
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
else
@@ -449,15 +451,15 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
so->arrayKeys = NULL;
so->arrayContext = NULL;
- so->killedItems = NULL; /* until needed */
- so->numKilled = 0;
+ so->state.killedItems = NULL; /* until needed */
+ so->state.numKilled = 0;
/*
* We don't know yet whether the scan will be index-only, so we do not
* allocate the tuple workspace arrays until btrescan. However, we set up
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
- so->currTuples = so->markTuples = NULL;
+ so->state.currTuples = so->state.markTuples = NULL;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -466,6 +468,45 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
return scan;
}
+static void
+_bt_release_current_position(BTScanState state, Relation indexRelation,
+ bool invalidate)
+{
+ /* we aren't holding any read locks, but gotta drop the pins */
+ if (BTScanPosIsValid(state->currPos))
+ {
+ /* Before leaving current page, deal with any killed items */
+ if (state->numKilled > 0)
+ _bt_killitems(state, indexRelation);
+
+ BTScanPosUnpinIfPinned(state->currPos);
+
+ if (invalidate)
+ BTScanPosInvalidate(state->currPos);
+ }
+}
+
+static void
+_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
+{
+ /* No need to invalidate positions, if the RAM is about to be freed. */
+ _bt_release_current_position(state, scan->indexRelation, !free);
+
+ state->markItemIndex = -1;
+ BTScanPosUnpinIfPinned(state->markPos);
+
+ if (free)
+ {
+ if (state->killedItems != NULL)
+ pfree(state->killedItems);
+ if (state->currTuples != NULL)
+ pfree(state->currTuples);
+ /* markTuples should not be pfree'd (_bt_allocate_tuple_workspaces) */
+ }
+ else
+ BTScanPosInvalidate(state->markPos);
+}
+
/*
* btrescan() -- rescan an index relation
*/
@@ -474,20 +515,9 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
ScanKey orderbys, int norderbys)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
- BTScanPosInvalidate(so->markPos);
+ _bt_release_scan_state(scan, state, false);
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
@@ -505,11 +535,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
* a SIGSEGV is not possible. Yeah, this is ugly as sin, but it beats
* adding special-case treatment for name_ops elsewhere.
*/
- if (scan->xs_want_itup && so->currTuples == NULL)
- {
- so->currTuples = (char *) palloc(BLCKSZ * 2);
- so->markTuples = so->currTuples + BLCKSZ;
- }
+ if (scan->xs_want_itup && state->currTuples == NULL)
+ _bt_allocate_tuple_workspaces(state);
/*
* Reset the scan keys. Note that keys ordering stuff moved to _bt_first.
@@ -533,19 +560,7 @@ btendscan(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
-
- /* No need to invalidate positions, the RAM is about to be freed. */
+ _bt_release_scan_state(scan, &so->state, true);
/* Release storage */
if (so->keyData != NULL)
@@ -553,24 +568,15 @@ btendscan(IndexScanDesc scan)
/* so->arrayKeyData and so->arrayKeys are in arrayContext */
if (so->arrayContext != NULL)
MemoryContextDelete(so->arrayContext);
- if (so->killedItems != NULL)
- pfree(so->killedItems);
- if (so->currTuples != NULL)
- pfree(so->currTuples);
- /* so->markTuples should not be pfree'd, see btrescan */
+
pfree(so);
}
-/*
- * btmarkpos() -- save current scan position
- */
-void
-btmarkpos(IndexScanDesc scan)
+static void
+_bt_mark_current_position(BTScanState state)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
-
/* There may be an old mark with a pin (but no lock). */
- BTScanPosUnpinIfPinned(so->markPos);
+ BTScanPosUnpinIfPinned(state->markPos);
/*
* Just record the current itemIndex. If we later step to next page
@@ -578,32 +584,34 @@ btmarkpos(IndexScanDesc scan)
* the currPos struct in markPos. If (as often happens) the mark is moved
* before we leave the page, we don't have to do that work.
*/
- if (BTScanPosIsValid(so->currPos))
- so->markItemIndex = so->currPos.itemIndex;
+ if (BTScanPosIsValid(state->currPos))
+ state->markItemIndex = state->currPos.itemIndex;
else
{
- BTScanPosInvalidate(so->markPos);
- so->markItemIndex = -1;
+ BTScanPosInvalidate(state->markPos);
+ state->markItemIndex = -1;
}
-
- /* Also record the current positions of any array keys */
- if (so->numArrayKeys)
- _bt_mark_array_keys(scan);
}
/*
- * btrestrpos() -- restore scan to last saved position
+ * btmarkpos() -- save current scan position
*/
void
-btrestrpos(IndexScanDesc scan)
+btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* Restore the marked positions of any array keys */
+ _bt_mark_current_position(&so->state);
+
+ /* Also record the current positions of any array keys */
if (so->numArrayKeys)
- _bt_restore_array_keys(scan);
+ _bt_mark_array_keys(scan);
+}
- if (so->markItemIndex >= 0)
+static void
+_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
+{
+ if (state->markItemIndex >= 0)
{
/*
* The scan has never moved to a new page since the last mark. Just
@@ -612,7 +620,7 @@ btrestrpos(IndexScanDesc scan)
* NB: In this case we can't count on anything in so->markPos to be
* accurate.
*/
- so->currPos.itemIndex = so->markItemIndex;
+ state->currPos.itemIndex = state->markItemIndex;
}
else
{
@@ -622,32 +630,40 @@ btrestrpos(IndexScanDesc scan)
* locks, but if we're still holding the pin for the current position,
* we must drop it.
*/
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
+ _bt_release_current_position(state, scan->indexRelation,
+ !BTScanPosIsValid(state->markPos));
- if (BTScanPosIsValid(so->markPos))
+ if (BTScanPosIsValid(state->markPos))
{
/* bump pin on mark buffer for assignment to current buffer */
- if (BTScanPosIsPinned(so->markPos))
- IncrBufferRefCount(so->markPos.buf);
- memcpy(&so->currPos, &so->markPos,
+ if (BTScanPosIsPinned(state->markPos))
+ IncrBufferRefCount(state->markPos.buf);
+ memcpy(&state->currPos, &state->markPos,
offsetof(BTScanPosData, items[1]) +
- so->markPos.lastItem * sizeof(BTScanPosItem));
- if (so->currTuples)
- memcpy(so->currTuples, so->markTuples,
- so->markPos.nextTupleOffset);
+ state->markPos.lastItem * sizeof(BTScanPosItem));
+ if (state->currTuples)
+ memcpy(state->currTuples, state->markTuples,
+ state->markPos.nextTupleOffset);
}
- else
- BTScanPosInvalidate(so->currPos);
}
}
/*
+ * btrestrpos() -- restore scan to last saved position
+ */
+void
+btrestrpos(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
+ /* Restore the marked positions of any array keys */
+ if (so->numArrayKeys)
+ _bt_restore_array_keys(scan);
+
+ _bt_restore_marked_position(scan, &so->state);
+}
+
+/*
* Bulk deletion of all index entries pointing to a set of heap tuples.
* The set of target tuples is specified via a callback routine that tells
* whether any given heap tuple (identified by ItemPointer) is being deleted.
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 4fba75a..264d581 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -25,11 +25,11 @@
#include "utils/tqual.h"
-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir,
+static bool _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
OffsetNumber offnum);
-static void _bt_saveitem(BTScanOpaque so, int itemIndex,
+static void _bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup);
-static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
+static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
@@ -509,6 +509,58 @@ _bt_compare(Relation rel,
}
/*
+ * _bt_return_current_item() -- Prepare current scan state item for return.
+ *
+ * This function is used only in "return _bt_return_current_item();" statements
+ * and always returns true.
+ */
+static inline bool
+_bt_return_current_item(IndexScanDesc scan, BTScanState state)
+{
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+
+ scan->xs_ctup.t_self = currItem->heapTid;
+
+ if (scan->xs_want_itup)
+ scan->xs_itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+
+ return true;
+}
+
+/*
+ * _bt_load_first_page() -- Load data from the first page of the scan.
+ *
+ * Caller must have pinned and read-locked state->currPos.buf.
+ *
+ * On success exit, state->currPos is updated to contain data from the next
+ * interesting page. For success on a scan using a non-MVCC snapshot we hold
+ * a pin, but not a read lock, on that page. If we do not hold the pin, we
+ * set state->currPos.buf to InvalidBuffer. We return true to indicate success.
+ *
+ * If there are no more matching records in the given direction at all,
+ * we drop all locks and pins, set state->currPos.buf to InvalidBuffer,
+ * and return false.
+ */
+static bool
+_bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
+{
+ if (!_bt_readpage(scan, state, dir, offnum))
+ {
+ /*
+ * There's no actually-matching data on this page. Try to advance to
+ * the next page. Return false if there's no matching data at all.
+ */
+ LockBuffer(state->currPos.buf, BUFFER_LOCK_UNLOCK);
+ return _bt_steppage(scan, state, dir);
+ }
+
+ /* Drop the lock, and maybe the pin, on the current page */
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
+ return true;
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -533,6 +585,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
BTStack stack;
OffsetNumber offnum;
@@ -545,9 +598,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
int keysCount = 0;
int i;
StrategyNumber strat_total;
- BTScanPosItem *currItem;
- Assert(!BTScanPosIsValid(so->currPos));
+ Assert(!BTScanPosIsValid(*currPos));
pgstat_count_index_scan(rel);
@@ -1002,16 +1054,16 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ currPos->moreLeft = false;
+ currPos->moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ currPos->moreLeft = true;
+ currPos->moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- Assert(so->markItemIndex == -1);
+ so->state.numKilled = 0; /* just paranoia */
+ Assert(so->state.markItemIndex == -1);
/* position to the precise item on the page */
offnum = _bt_binsrch(rel, buf, keysCount, scankeys, nextkey);
@@ -1038,35 +1090,35 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
offnum = OffsetNumberPrev(offnum);
/* remember which buffer we have pinned, if any */
- Assert(!BTScanPosIsValid(so->currPos));
- so->currPos.buf = buf;
+ Assert(!BTScanPosIsValid(*currPos));
+ currPos->buf = buf;
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, offnum))
+ if (!_bt_load_first_page(scan, &so->state, dir, offnum))
+ return false;
+
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
+}
+
+/*
+ * Advance to next tuple on current page; or if there's no more,
+ * try to step to the next page with data.
+ */
+static bool
+_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
+{
+ if (ScanDirectionIsForward(dir))
{
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
+ if (++state->currPos.itemIndex <= state->currPos.lastItem)
+ return true;
}
else
{
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ if (--state->currPos.itemIndex >= state->currPos.firstItem)
+ return true;
}
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_steppage(scan, state, dir);
}
/*
@@ -1087,44 +1139,20 @@ bool
_bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- BTScanPosItem *currItem;
- /*
- * Advance to next tuple on current page; or if there's no more, try to
- * step to the next page with data.
- */
- if (ScanDirectionIsForward(dir))
- {
- if (++so->currPos.itemIndex > so->currPos.lastItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
- else
- {
- if (--so->currPos.itemIndex < so->currPos.firstItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
+ if (!_bt_next_item(scan, &so->state, dir))
+ return false;
/* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_return_current_item(scan, &so->state);
}
/*
* _bt_readpage() -- Load data from current index page into so->currPos
*
- * Caller must have pinned and read-locked so->currPos.buf; the buffer's state
- * is not changed here. Also, currPos.moreLeft and moreRight must be valid;
- * they are updated as appropriate. All other fields of so->currPos are
+ * Caller must have pinned and read-locked pos->buf; the buffer's state
+ * is not changed here. Also, pos->moreLeft and moreRight must be valid;
+ * they are updated as appropriate. All other fields of pos are
* initialized from scratch here.
*
* We scan the current page starting at offnum and moving in the indicated
@@ -1135,9 +1163,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
* Returns true if any matching items found on the page, false if none.
*/
static bool
-_bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
+_bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
@@ -1150,9 +1179,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We must have the buffer pinned and locked, but the usual macro can't be
* used here; this function is what makes it good for currPos.
*/
- Assert(BufferIsValid(so->currPos.buf));
+ Assert(BufferIsValid(pos->buf));
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
@@ -1161,30 +1190,30 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We note the buffer's block number so that we can release the pin later.
* This allows us to re-read the buffer if it is needed again for hinting.
*/
- so->currPos.currPage = BufferGetBlockNumber(so->currPos.buf);
+ pos->currPage = BufferGetBlockNumber(pos->buf);
/*
* We save the LSN of the page as we read it, so that we know whether it
* safe to apply LP_DEAD hints to the page later. This allows us to drop
* the pin for MVCC scans, which allows vacuum to avoid blocking.
*/
- so->currPos.lsn = PageGetLSN(page);
+ pos->lsn = PageGetLSN(page);
/*
* we must save the page's right-link while scanning it; this tells us
* where to step right to after we're done with these items. There is no
* corresponding need for the left-link, since splits always go right.
*/
- so->currPos.nextPage = opaque->btpo_next;
+ pos->nextPage = opaque->btpo_next;
/* initialize tuple workspace to empty */
- so->currPos.nextTupleOffset = 0;
+ pos->nextTupleOffset = 0;
/*
* Now that the current page has been made consistent, the macro should be
* good.
*/
- Assert(BTScanPosIsPinned(so->currPos));
+ Assert(BTScanPosIsPinned(*pos));
if (ScanDirectionIsForward(dir))
{
@@ -1199,13 +1228,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (itup != NULL)
{
/* tuple passes all scan key conditions, so remember it */
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
itemIndex++;
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreRight = false;
+ pos->moreRight = false;
break;
}
@@ -1213,9 +1242,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex <= MaxIndexTuplesPerPage);
- so->currPos.firstItem = 0;
- so->currPos.lastItem = itemIndex - 1;
- so->currPos.itemIndex = 0;
+ pos->firstItem = 0;
+ pos->lastItem = itemIndex - 1;
+ pos->itemIndex = 0;
}
else
{
@@ -1231,12 +1260,12 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
{
/* tuple passes all scan key conditions, so remember it */
itemIndex--;
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreLeft = false;
+ pos->moreLeft = false;
break;
}
@@ -1244,30 +1273,31 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex >= 0);
- so->currPos.firstItem = itemIndex;
- so->currPos.lastItem = MaxIndexTuplesPerPage - 1;
- so->currPos.itemIndex = MaxIndexTuplesPerPage - 1;
+ pos->firstItem = itemIndex;
+ pos->lastItem = MaxIndexTuplesPerPage - 1;
+ pos->itemIndex = MaxIndexTuplesPerPage - 1;
}
- return (so->currPos.firstItem <= so->currPos.lastItem);
+ return (pos->firstItem <= pos->lastItem);
}
/* Save an index item into so->currPos.items[itemIndex] */
static void
-_bt_saveitem(BTScanOpaque so, int itemIndex,
+_bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup)
{
- BTScanPosItem *currItem = &so->currPos.items[itemIndex];
+ BTScanPosItem *currItem = &state->currPos.items[itemIndex];
currItem->heapTid = itup->t_tid;
currItem->indexOffset = offnum;
- if (so->currTuples)
+ if (state->currTuples)
{
Size itupsz = IndexTupleSize(itup);
- currItem->tupleOffset = so->currPos.nextTupleOffset;
- memcpy(so->currTuples + so->currPos.nextTupleOffset, itup, itupsz);
- so->currPos.nextTupleOffset += MAXALIGN(itupsz);
+ currItem->tupleOffset = state->currPos.nextTupleOffset;
+ memcpy(state->currTuples + state->currPos.nextTupleOffset,
+ itup, itupsz);
+ state->currPos.nextTupleOffset += MAXALIGN(itupsz);
}
}
@@ -1287,66 +1317,64 @@ _bt_saveitem(BTScanOpaque so, int itemIndex,
* locks and pins, set so->currPos.buf to InvalidBuffer, and return FALSE.
*/
static bool
-_bt_steppage(IndexScanDesc scan, ScanDirection dir)
+_bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
- Relation rel;
+ BTScanPos currPos = &state->currPos;
+ Relation rel = scan->indexRelation;
Page page;
BTPageOpaque opaque;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(*currPos));
/* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
+ if (state->numKilled > 0)
+ _bt_killitems(state, rel);
/*
* Before we modify currPos, make a copy of the page data if there was a
* mark position that needs it.
*/
- if (so->markItemIndex >= 0)
+ if (state->markItemIndex >= 0)
{
/* bump pin on current buffer for assignment to mark buffer */
- if (BTScanPosIsPinned(so->currPos))
- IncrBufferRefCount(so->currPos.buf);
- memcpy(&so->markPos, &so->currPos,
+ if (BTScanPosIsPinned(*currPos))
+ IncrBufferRefCount(currPos->buf);
+ memcpy(&state->markPos, currPos,
offsetof(BTScanPosData, items[1]) +
- so->currPos.lastItem * sizeof(BTScanPosItem));
- if (so->markTuples)
- memcpy(so->markTuples, so->currTuples,
- so->currPos.nextTupleOffset);
- so->markPos.itemIndex = so->markItemIndex;
- so->markItemIndex = -1;
+ currPos->lastItem * sizeof(BTScanPosItem));
+ if (state->markTuples)
+ memcpy(state->markTuples, state->currTuples,
+ currPos->nextTupleOffset);
+ state->markPos.itemIndex = state->markItemIndex;
+ state->markItemIndex = -1;
}
- rel = scan->indexRelation;
-
if (ScanDirectionIsForward(dir))
{
/* Walk right to the next page with data */
/* We must rely on the previously saved nextPage link! */
- BlockNumber blkno = so->currPos.nextPage;
+ BlockNumber blkno = currPos->nextPage;
/* Remember we left a page with data */
- so->currPos.moreLeft = true;
+ currPos->moreLeft = true;
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
for (;;)
{
/* if we're at end of scan, give up */
- if (blkno == P_NONE || !so->currPos.moreRight)
+ if (blkno == P_NONE || !currPos->moreRight)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* check for interrupts while we're not holding any buffer lock */
CHECK_FOR_INTERRUPTS();
/* step right one page */
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
/* check for deleted page */
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
@@ -1354,19 +1382,19 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
PredicateLockPage(rel, blkno, scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreRight if we can stop */
- if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque)))
+ if (_bt_readpage(scan, state, dir, P_FIRSTDATAKEY(opaque)))
break;
}
/* nope, keep going */
blkno = opaque->btpo_next;
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
}
}
else
{
/* Remember we left a page with data */
- so->currPos.moreRight = true;
+ currPos->moreRight = true;
/*
* Walk left to the next page with data. This is much more complex
@@ -1390,29 +1418,28 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* is MVCC the page cannot move past the half-dead state to fully
* deleted.
*/
- if (BTScanPosIsPinned(so->currPos))
- LockBuffer(so->currPos.buf, BT_READ);
+ if (BTScanPosIsPinned(*currPos))
+ LockBuffer(currPos->buf, BT_READ);
else
- so->currPos.buf = _bt_getbuf(rel, so->currPos.currPage, BT_READ);
+ currPos->buf = _bt_getbuf(rel, currPos->currPage, BT_READ);
for (;;)
{
/* Done if we know there are no matching keys to the left */
- if (!so->currPos.moreLeft)
+ if (!currPos->moreLeft)
{
- _bt_relbuf(rel, so->currPos.buf);
- BTScanPosInvalidate(so->currPos);
+ _bt_relbuf(rel, currPos->buf);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* Step to next physical page */
- so->currPos.buf = _bt_walk_left(rel, so->currPos.buf,
- scan->xs_snapshot);
+ currPos->buf = _bt_walk_left(rel, currPos->buf, scan->xs_snapshot);
/* if we're physically at end of index, return failure */
- if (so->currPos.buf == InvalidBuffer)
+ if (currPos->buf == InvalidBuffer)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1421,22 +1448,22 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* it's not half-dead and contains matching tuples. Else loop back
* and do it all again.
*/
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
{
- PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
+ PredicateLockPage(rel, BufferGetBlockNumber(currPos->buf), scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreLeft if we can stop */
- if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page)))
+ if (_bt_readpage(scan, state, dir, PageGetMaxOffsetNumber(page)))
break;
}
}
}
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, currPos);
return true;
}
@@ -1661,11 +1688,11 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
Page page;
BTPageOpaque opaque;
OffsetNumber start;
- BTScanPosItem *currItem;
/*
* Scan down to the leftmost or rightmost leaf page. This is a simplified
@@ -1681,7 +1708,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* exists.
*/
PredicateLockRelation(rel, scan->xs_snapshot);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1710,46 +1737,25 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
}
/* remember which buffer we have pinned */
- so->currPos.buf = buf;
+ currPos->buf = buf;
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ currPos->moreLeft = false;
+ currPos->moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ currPos->moreLeft = true;
+ currPos->moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- so->markItemIndex = -1; /* ditto */
+ so->state.numKilled = 0; /* just paranoia */
+ so->state.markItemIndex = -1; /* ditto */
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, start))
- {
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
- }
- else
- {
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
- }
-
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
+ if (!_bt_load_first_page(scan, &so->state, dir, start))
+ return false;
- return true;
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index da0f330..ebcba7e 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1725,26 +1725,26 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
* away and the TID was re-used by a completely different heap tuple.
*/
void
-_bt_killitems(IndexScanDesc scan)
+_bt_killitems(BTScanState state, Relation indexRelation)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
OffsetNumber maxoff;
int i;
- int numKilled = so->numKilled;
+ int numKilled = state->numKilled;
bool killedsomething = false;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(state->currPos));
/*
* Always reset the scan state, so we don't look for same items on other
* pages.
*/
- so->numKilled = 0;
+ state->numKilled = 0;
- if (BTScanPosIsPinned(so->currPos))
+ if (BTScanPosIsPinned(*pos))
{
/*
* We have held the pin on this page since we read the index tuples,
@@ -1752,28 +1752,28 @@ _bt_killitems(IndexScanDesc scan)
* re-use of any TID on the page, so there is no need to check the
* LSN.
*/
- LockBuffer(so->currPos.buf, BT_READ);
+ LockBuffer(pos->buf, BT_READ);
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
}
else
{
Buffer buf;
/* Attempt to re-read the buffer, getting pin and lock. */
- buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, BT_READ);
+ buf = _bt_getbuf(indexRelation, pos->currPage, BT_READ);
/* It might not exist anymore; in which case we can't hint it. */
if (!BufferIsValid(buf))
return;
page = BufferGetPage(buf);
- if (PageGetLSN(page) == so->currPos.lsn)
- so->currPos.buf = buf;
+ if (PageGetLSN(page) == pos->lsn)
+ pos->buf = buf;
else
{
/* Modified while not pinned means hinting is not safe. */
- _bt_relbuf(scan->indexRelation, buf);
+ _bt_relbuf(indexRelation, buf);
return;
}
}
@@ -1784,12 +1784,12 @@ _bt_killitems(IndexScanDesc scan)
for (i = 0; i < numKilled; i++)
{
- int itemIndex = so->killedItems[i];
- BTScanPosItem *kitem = &so->currPos.items[itemIndex];
+ int itemIndex = state->killedItems[i];
+ BTScanPosItem *kitem = &pos->items[itemIndex];
OffsetNumber offnum = kitem->indexOffset;
- Assert(itemIndex >= so->currPos.firstItem &&
- itemIndex <= so->currPos.lastItem);
+ Assert(itemIndex >= pos->firstItem &&
+ itemIndex <= pos->lastItem);
if (offnum < minoff)
continue; /* pure paranoia */
while (offnum <= maxoff)
@@ -1817,10 +1817,10 @@ _bt_killitems(IndexScanDesc scan)
if (killedsomething)
{
opaque->btpo_flags |= BTP_HAS_GARBAGE;
- MarkBufferDirtyHint(so->currPos.buf, true);
+ MarkBufferDirtyHint(pos->buf, true);
}
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(pos->buf, BUFFER_LOCK_UNLOCK);
}
@@ -2065,3 +2065,14 @@ btproperty(Oid index_oid, int attno,
return false; /* punt to generic code */
}
}
+
+/*
+ * _bt_allocate_tuple_workspaces() -- Allocate buffers for saving index tuples
+ * in index-only scans.
+ */
+void
+_bt_allocate_tuple_workspaces(BTScanState state)
+{
+ state->currTuples = (char *) palloc(BLCKSZ * 2);
+ state->markTuples = state->currTuples + BLCKSZ;
+}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 181f3ac..4427bd3 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -598,20 +598,8 @@ typedef struct BTArrayKeyInfo
Datum *elem_values; /* array of num_elems Datums */
} BTArrayKeyInfo;
-typedef struct BTScanOpaqueData
+typedef struct BTScanStateData
{
- /* these fields are set by _bt_preprocess_keys(): */
- bool qual_ok; /* false if qual can never be satisfied */
- int numberOfKeys; /* number of preprocessed scan keys */
- ScanKey keyData; /* array of preprocessed scan keys */
-
- /* workspace for SK_SEARCHARRAY support */
- ScanKey arrayKeyData; /* modified copy of scan->keyData */
- int numArrayKeys; /* number of equality-type array keys (-1 if
- * there are any unsatisfiable array keys) */
- BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
- MemoryContext arrayContext; /* scan-lifespan context for array data */
-
/* info about killed items if any (killedItems is NULL if never used) */
int *killedItems; /* currPos.items indexes of killed items */
int numKilled; /* number of currently stored items */
@@ -636,6 +624,23 @@ typedef struct BTScanOpaqueData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+} BTScanStateData, *BTScanState;
+
+typedef struct BTScanOpaqueData
+{
+ /* these fields are set by _bt_preprocess_keys(): */
+ bool qual_ok; /* false if qual can never be satisfied */
+ int numberOfKeys; /* number of preprocessed scan keys */
+ ScanKey keyData; /* array of preprocessed scan keys */
+
+ /* workspace for SK_SEARCHARRAY support */
+ ScanKey arrayKeyData; /* modified copy of scan->keyData */
+ int numArrayKeys; /* number of equality-type array keys (-1 if
+ * there are any unsatisfiable array keys) */
+ BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
+ MemoryContext arrayContext; /* scan-lifespan context for array data */
+
+ BTScanStateData state;
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -740,7 +745,7 @@ extern void _bt_preprocess_keys(IndexScanDesc scan);
extern IndexTuple _bt_checkkeys(IndexScanDesc scan,
Page page, OffsetNumber offnum,
ScanDirection dir, bool *continuescan);
-extern void _bt_killitems(IndexScanDesc scan);
+extern void _bt_killitems(BTScanState state, Relation indexRelation);
extern BTCycleId _bt_vacuum_cycleid(Relation rel);
extern BTCycleId _bt_start_vacuum(Relation rel);
extern void _bt_end_vacuum(Relation rel);
@@ -751,6 +756,7 @@ extern bytea *btoptions(Datum reloptions, bool validate);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
+extern void _bt_allocate_tuple_workspaces(BTScanState state);
/*
* prototypes for functions in nbtvalidate.c
0003-extract-get_index_column_opclass-and-get_opclass_opfamily_and_input_type-v01.patchtext/x-patch; name=0003-extract-get_index_column_opclass-and-get_opclass_opfamily_and_input_type-v01.patchDownload
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 159987e..e1b1a18 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -24,6 +24,7 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
+#include "utils/lsyscache.h"
/*
@@ -852,12 +853,6 @@ gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull)
{
- HeapTuple tuple;
- Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
- Form_pg_opclass rd_opclass;
- Datum datum;
- bool disnull;
- oidvector *indclass;
Oid opclass,
opfamily,
opcintype;
@@ -891,49 +886,28 @@ gistproperty(Oid index_oid, int attno,
}
/* First we need to know the column's opclass. */
-
- tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
- if (!HeapTupleIsValid(tuple))
+ opclass = get_index_column_opclass(index_oid, attno);
+ if (!OidIsValid(opclass))
{
*isnull = true;
return true;
}
- rd_index = (Form_pg_index) GETSTRUCT(tuple);
-
- /* caller is supposed to guarantee this */
- Assert(attno > 0 && attno <= rd_index->indnatts);
-
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indclass, &disnull);
- Assert(!disnull);
-
- indclass = ((oidvector *) DatumGetPointer(datum));
- opclass = indclass->values[attno - 1];
-
- ReleaseSysCache(tuple);
/* Now look up the opclass family and input datatype. */
-
- tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
- if (!HeapTupleIsValid(tuple))
+ if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
{
*isnull = true;
return true;
}
- rd_opclass = (Form_pg_opclass) GETSTRUCT(tuple);
-
- opfamily = rd_opclass->opcfamily;
- opcintype = rd_opclass->opcintype;
-
- ReleaseSysCache(tuple);
/* And now we can check whether the function is provided. */
-
*res = SearchSysCacheExists4(AMPROCNUM,
ObjectIdGetDatum(opfamily),
ObjectIdGetDatum(opcintype),
ObjectIdGetDatum(opcintype),
Int16GetDatum(procno));
+ isnull = false;
+
return true;
}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index aff88a5..26c81c9 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1050,6 +1050,32 @@ get_opclass_input_type(Oid opclass)
return result;
}
+/*
+ * get_opclass_family_and_input_type
+ *
+ * Returns the OID of the operator family the opclass belongs to,
+ * the OID of the datatype the opclass indexes
+ */
+bool
+get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
+{
+ HeapTuple tp;
+ Form_pg_opclass cla_tup;
+
+ tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
+ if (!HeapTupleIsValid(tp))
+ return false;
+
+ cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
+
+ *opfamily = cla_tup->opcfamily;
+ *opcintype = cla_tup->opcintype;
+
+ ReleaseSysCache(tp);
+
+ return true;
+}
+
/* ---------- OPERATOR CACHE ---------- */
/*
@@ -3061,3 +3087,45 @@ get_range_subtype(Oid rangeOid)
else
return InvalidOid;
}
+
+/* ---------- PG_INDEX CACHE ---------- */
+
+/*
+ * get_index_column_opclass
+ *
+ * Given the index OID and column number,
+ * return opclass of the index column
+ * or InvalidOid if the index was not found.
+ */
+Oid
+get_index_column_opclass(Oid index_oid, int attno)
+{
+ HeapTuple tuple;
+ Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
+ Datum datum;
+ bool isnull;
+ oidvector *indclass;
+ Oid opclass;
+
+ /* First we need to know the column's opclass. */
+
+ tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
+ if (!HeapTupleIsValid(tuple))
+ return InvalidOid;
+
+ rd_index = (Form_pg_index) GETSTRUCT(tuple);
+
+ /* caller is supposed to guarantee this */
+ Assert(attno > 0 && attno <= rd_index->indnatts);
+
+ datum = SysCacheGetAttr(INDEXRELID, tuple,
+ Anum_pg_index_indclass, &isnull);
+ Assert(!isnull);
+
+ indclass = ((oidvector *) DatumGetPointer(datum));
+ opclass = indclass->values[attno - 1];
+
+ ReleaseSysCache(tuple);
+
+ return opclass;
+}
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index b6d1fca..618c4e8 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -73,6 +73,8 @@ extern char *get_constraint_name(Oid conoid);
extern char *get_language_name(Oid langoid, bool missing_ok);
extern Oid get_opclass_family(Oid opclass);
extern Oid get_opclass_input_type(Oid opclass);
+extern bool get_opclass_opfamily_and_input_type(Oid opclass,
+ Oid *opfamily, Oid *opcintype);
extern RegProcedure get_opcode(Oid opno);
extern char *get_opname(Oid opno);
extern Oid get_op_rettype(Oid opno);
@@ -159,6 +161,7 @@ extern void free_attstatsslot(Oid atttype,
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
0004-add-kNN-support-to-btree-v01.patchtext/x-patch; name=0004-add-kNN-support-to-btree-v01.patchDownload
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 271c135..480bfd0 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -248,7 +248,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
</para>
<para>
- GiST indexes are also capable of optimizing <quote>nearest-neighbor</>
+ B-tree and GiST indexes are also capable of optimizing <quote>nearest-neighbor</>
searches, such as
<programlisting><![CDATA[
SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 333a36c..6708653 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -1171,7 +1171,7 @@ ALTER OPERATOR FAMILY integer_ops USING btree ADD
<title>Ordering Operators</title>
<para>
- Some index access methods (currently, only GiST) support the concept of
+ Some index access methods (currently, only B-tree and GiST) support the concept of
<firstterm>ordering operators</>. What we have been discussing so far
are <firstterm>search operators</>. A search operator is one for which
the index can be searched to find all rows satisfying
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
index a3f11da..764c0a9 100644
--- a/src/backend/access/nbtree/README
+++ b/src/backend/access/nbtree/README
@@ -624,6 +624,24 @@ item is irrelevant, and need not be stored at all. This arrangement
corresponds to the fact that an L&Y non-leaf page has one more pointer
than key.
+Nearest-neighbor search
+-----------------------
+
+There is a special scan strategy for nearest-neighbor (kNN) search,
+that is used in queries with ORDER BY distance clauses like this:
+SELECT * FROM tab WHERE col > const1 ORDER BY col <-> const2 LIMIT k.
+But, unlike GiST, B-tree supports only a one ordering operator on the
+first index column.
+
+At the beginning of kNN scan, we need to determine which strategy we
+will use --- a special bidirectional or a ordinary unidirectional.
+If the point from which we measure the distance falls into the scan range,
+we use bidirectional scan starting from this point, else we use simple
+unidirectional scan in the right direction. Algorithm of a bidirectional
+scan is very simple: at each step we advancing scan in that direction,
+which has the nearest point.
+
+
Notes to Operator Class Implementors
------------------------------------
@@ -660,6 +678,18 @@ procedure, of course.)
The other three operators are defined in terms of these two in the obvious way,
and must act consistently with them.
+To implement the distance ordered (nearest-neighbor) search, we only need
+to define a distance operator (usually it called <->) with a correpsonding
+operator family for distance comparison in the index's operator class.
+These operators must satisfy the following assumptions for all non-null
+values A,B,C of the datatype:
+
+ A <-> B = B <-> A symmetric law
+ if A = B, then A <-> C = B <-> C distance equivalence
+ if (A <= B and B <= C) or (A >= B and B >= C),
+ then A <-> B <= A <-> C monotonicity
+
+
For an operator family supporting multiple datatypes, the above laws must hold
when A,B,C are taken from any datatypes in the family. The transitive laws
are the trickiest to ensure, as in cross-type situations they represent
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index a05f0c2..74641d2 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -23,11 +23,15 @@
#include "access/xlog.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "optimizer/paths.h"
#include "storage/indexfsm.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "tcop/tcopprot.h" /* pgrminclude ignore */
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
@@ -75,6 +79,10 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
+static Expr *btcanorderbyop(IndexOptInfo *index,
+ PathKey *pathkey, int pathkeyno,
+ Expr *expr, int *indexcol_p);
+
/*
* Btree handler function: return IndexAmRoutine with access method parameters
@@ -85,7 +93,7 @@ bthandler(PG_FUNCTION_ARGS)
{
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
- amroutine->amstrategies = BTMaxStrategyNumber;
+ amroutine->amstrategies = BtreeMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
amroutine->amcanbackward = true;
@@ -116,7 +124,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
- amroutine->amcanorderbyop = NULL;
+ amroutine->amcanorderbyop = btcanorderbyop;
PG_RETURN_POINTER(amroutine);
}
@@ -300,6 +308,10 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
/* btree indexes are never lossy */
scan->xs_recheck = false;
+ scan->xs_recheckorderby = false;
+
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
/*
* If we have any array keys, initialize them during first call for a
@@ -323,7 +335,8 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(state->currPos))
+ if (!BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
res = _bt_first(scan, dir);
else
{
@@ -431,9 +444,6 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
IndexScanDesc scan;
BTScanOpaque so;
- /* no order by operators allowed */
- Assert(norderbys == 0);
-
/* get the scan */
scan = RelationGetIndexScan(rel, nkeys, norderbys);
@@ -460,6 +470,9 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
so->state.currTuples = so->state.markTuples = NULL;
+ so->knnState = NULL;
+ so->distanceTypeByVal = true;
+ so->scanDirection = NoMovementScanDirection;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -489,6 +502,8 @@ _bt_release_current_position(BTScanState state, Relation indexRelation,
static void
_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
/* No need to invalidate positions, if the RAM is about to be freed. */
_bt_release_current_position(state, scan->indexRelation, !free);
@@ -505,6 +520,14 @@ _bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
}
else
BTScanPosInvalidate(state->markPos);
+
+ if (!so->distanceTypeByVal)
+ {
+ pfree(DatumGetPointer(state->currDistance));
+ state->currDistance = PointerGetDatum(NULL);
+ pfree(DatumGetPointer(state->markDistance));
+ state->markDistance = PointerGetDatum(NULL);
+ }
}
/*
@@ -519,6 +542,13 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
_bt_release_scan_state(scan, state, false);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ so->knnState = NULL;
+ }
+
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
* not already done in a previous rescan call. To save on palloc
@@ -548,6 +578,14 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
scan->numberOfKeys * sizeof(ScanKeyData));
so->numberOfKeys = 0; /* until _bt_preprocess_keys sets it */
+ if (orderbys && scan->numberOfOrderBys > 0)
+ memmove(scan->orderByData,
+ orderbys,
+ scan->numberOfOrderBys * sizeof(ScanKeyData));
+
+ so->scanDirection = NoMovementScanDirection;
+ so->distanceTypeByVal = true;
+
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
_bt_preprocess_array_keys(scan);
}
@@ -562,6 +600,12 @@ btendscan(IndexScanDesc scan)
_bt_release_scan_state(scan, &so->state, true);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ }
+
/* Release storage */
if (so->keyData != NULL)
pfree(so->keyData);
@@ -573,7 +617,7 @@ btendscan(IndexScanDesc scan)
}
static void
-_bt_mark_current_position(BTScanState state)
+_bt_mark_current_position(BTScanOpaque so, BTScanState state)
{
/* There may be an old mark with a pin (but no lock). */
BTScanPosUnpinIfPinned(state->markPos);
@@ -591,6 +635,21 @@ _bt_mark_current_position(BTScanState state)
BTScanPosInvalidate(state->markPos);
state->markItemIndex = -1;
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->markDistance));
+
+ state->markIsNull = !BTScanPosIsValid(state->currPos) ||
+ state->currIsNull;
+
+ state->markDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->currDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -601,7 +660,13 @@ btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_mark_current_position(&so->state);
+ _bt_mark_current_position(so, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_mark_current_position(so, so->knnState);
+ so->markRightIsNearest = so->currRightIsNearest;
+ }
/* Also record the current positions of any array keys */
if (so->numArrayKeys)
@@ -611,6 +676,8 @@ btmarkpos(IndexScanDesc scan)
static void
_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
if (state->markItemIndex >= 0)
{
/*
@@ -646,6 +713,19 @@ _bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
state->markPos.nextTupleOffset);
}
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currIsNull = state->markIsNull;
+ state->currDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->markDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -661,6 +741,12 @@ btrestrpos(IndexScanDesc scan)
_bt_restore_array_keys(scan);
_bt_restore_marked_position(scan, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_restore_marked_position(scan, so->knnState);
+ so->currRightIsNearest = so->markRightIsNearest;
+ }
}
/*
@@ -1148,3 +1234,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+/*
+ * btcanorderbyop() -- Check whether KNN-search strategy is applicable to
+ * the given ORDER BY distance operator.
+ */
+static Expr *
+btcanorderbyop(IndexOptInfo *index, PathKey *pathkey, int pathkeyno,
+ Expr *expr, int *indexcol_p)
+{
+ if (pathkeyno > 1)
+ return NULL; /* only one ORDER BY clause is supported */
+
+ expr = match_clause_to_ordering_op(index,
+ 0, /* ORDER BY distance to the first
+ * index column is only supported */
+ expr,
+ pathkey->pk_opfamily);
+
+ if (expr)
+ *indexcol_p = 0;
+
+ return expr;
+}
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 264d581..4b08d8b 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -561,6 +561,127 @@ _bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
}
/*
+ * _bt_calc_current_dist() -- Calculate distance from the current item
+ * of the scan state to the target order-by ScanKey argument.
+ */
+static void
+_bt_calc_current_dist(IndexScanDesc scan, BTScanState state)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+ IndexTuple itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+ ScanKey scankey = &scan->orderByData[0];
+ Datum value;
+
+ value = index_getattr(itup, 1, scan->xs_itupdesc, &state->currIsNull);
+
+ if (state->currIsNull)
+ return; /* NULL distance */
+
+ value = FunctionCall2Coll(&scankey->sk_func,
+ scankey->sk_collation,
+ value,
+ scankey->sk_argument);
+
+ /* free previous distance value for by-ref types */
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currDistance = value;
+}
+
+/*
+ * _bt_compare_current_dist() -- Compare current distances of the left and right scan states.
+ *
+ * NULL distances are considered to be greater than any non-NULL distances.
+ *
+ * Returns true if right distance is lesser than left, otherwise false.
+ */
+static bool
+_bt_compare_current_dist(BTScanOpaque so, BTScanState rstate, BTScanState lstate)
+{
+ if (lstate->currIsNull)
+ return true; /* non-NULL < NULL */
+
+ if (rstate->currIsNull)
+ return false; /* NULL > non-NULL */
+
+ return DatumGetBool(FunctionCall2Coll(&so->distanceCmpProc,
+ InvalidOid, /* XXX collation for distance comparison */
+ rstate->currDistance,
+ lstate->currDistance));
+}
+
+/*
+ * _bt_init_knn_scan() -- Init additional scan state for KNN search.
+ *
+ * Caller must pin and read-lock scan->state.currPos.buf buffer.
+ *
+ * If empty result was found returned false.
+ * Otherwise prepared current item, and returned true.
+ */
+static bool
+_bt_init_knn_scan(IndexScanDesc scan, OffsetNumber offnum)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+ Buffer buf = rstate->currPos.buf;
+ bool left,
+ right;
+
+ lstate = so->knnState = (BTScanState) palloc(sizeof(BTScanStateData));
+ _bt_allocate_tuple_workspaces(lstate);
+
+ if (!scan->xs_want_itup)
+ {
+ /* We need to request index tuples for distance comparison. */
+ scan->xs_want_itup = true;
+ _bt_allocate_tuple_workspaces(rstate);
+ }
+
+ /* Bump pin and lock count before BTScanPosData copying. */
+ IncrBufferRefCount(buf);
+ LockBuffer(buf, BT_READ);
+
+ memcpy(&lstate->currPos, &rstate->currPos, sizeof(BTScanPosData));
+ lstate->currPos.moreLeft = true;
+ lstate->currPos.moreRight = false;
+
+ BTScanPosInvalidate(lstate->markPos);
+ lstate->markItemIndex = -1;
+ lstate->killedItems = NULL;
+ lstate->numKilled = 0;
+ lstate->currDistance = PointerGetDatum(NULL);
+ lstate->markDistance = PointerGetDatum(NULL);
+
+ /* Load first pages from the both scans. */
+ right = _bt_load_first_page(scan, rstate, ForwardScanDirection, offnum);
+ left = _bt_load_first_page(scan, lstate, BackwardScanDirection,
+ OffsetNumberPrev(offnum));
+
+ if (!left && !right)
+ return false; /* empty result */
+
+ if (left && right)
+ {
+ /*
+ * We have found items in both scan directions,
+ * determine nearest item to return.
+ */
+ _bt_calc_current_dist(scan, rstate);
+ _bt_calc_current_dist(scan, lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+
+ /* Reset right flag if the left item is nearer. */
+ right = so->currRightIsNearest;
+ }
+
+ /* Return current item of the selected scan direction. */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -663,7 +784,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*----------
*/
strat_total = BTEqualStrategyNumber;
- if (so->numberOfKeys > 0)
+
+ if (scan->numberOfOrderBys > 0)
+ {
+ if (_bt_process_orderings(scan, startKeys, &keysCount, notnullkeys))
+ /* use bidirectional KNN scan */
+ strat_total = BtreeKNNSearchStrategyNumber;
+
+ /* use selected KNN scan direction */
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
+ }
+
+ if (so->numberOfKeys > 0 &&
+ /* startKeys for KNN search already have been initialized */
+ strat_total != BtreeKNNSearchStrategyNumber)
{
AttrNumber curattr;
ScanKey chosen;
@@ -1003,6 +1138,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
break;
case BTGreaterEqualStrategyNumber:
+ case BtreeKNNSearchStrategyNumber:
/*
* Find first item >= scankey. (This is only used for forward
@@ -1093,16 +1229,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(!BTScanPosIsValid(*currPos));
currPos->buf = buf;
+ if (strat_total == BtreeKNNSearchStrategyNumber)
+ return _bt_init_knn_scan(scan, offnum);
+
if (!_bt_load_first_page(scan, &so->state, dir, offnum))
- return false;
+ return false; /* empty result */
/* OK, currPos->itemIndex says what to return */
return _bt_return_current_item(scan, &so->state);
}
/*
- * Advance to next tuple on current page; or if there's no more,
- * try to step to the next page with data.
+ * _bt_next_item() -- Advance to next tuple on current page;
+ * or if there's no more, try to step to the next page with data.
+ *
+ * If there are no more matching records in the given direction
*/
static bool
_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
@@ -1122,6 +1263,52 @@ _bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
}
/*
+ * _bt_next_nearest() -- Return next nearest item from bidirectional KNN scan.
+ */
+static bool
+_bt_next_nearest(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state;
+ BTScanState lstate = so->knnState;
+ bool right = BTScanPosIsValid(rstate->currPos);
+ bool left = BTScanPosIsValid(lstate->currPos);
+ bool advanceRight;
+
+ if (right && left)
+ advanceRight = so->currRightIsNearest;
+ else if (right)
+ advanceRight = true;
+ else if (left)
+ advanceRight = false;
+ else
+ return false; /* end of the scan */
+
+ *(advanceRight ? &right : &left) =
+ _bt_next_item(scan,
+ advanceRight ? rstate : lstate,
+ advanceRight ? ForwardScanDirection :
+ BackwardScanDirection);
+
+ if (!left && !right)
+ return false; /* end of the scan */
+
+ if (left && right)
+ {
+ /*
+ * If there are items in both scans we must recalculate distance
+ * in the advanced scan.
+ */
+ _bt_calc_current_dist(scan, advanceRight ? rstate : lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+ right = so->currRightIsNearest;
+ }
+
+ /* return nearest item */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_next() -- Get the next item in a scan.
*
* On entry, so->currPos describes the current page, which may be pinned
@@ -1140,6 +1327,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ if (so->knnState)
+ /* return next neareset item from KNN scan */
+ return _bt_next_nearest(scan);
+
if (!_bt_next_item(scan, &so->state, dir))
return false;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index ebcba7e..e5e855e 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -20,11 +20,13 @@
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "catalog/pg_amop.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
typedef struct BTSortArrayContext
@@ -2061,6 +2063,34 @@ btproperty(Oid index_oid, int attno,
*res = true;
return true;
+ case AMPROP_DISTANCE_ORDERABLE:
+ {
+ Oid opclass,
+ opfamily,
+ opcindtype;
+
+ /* answer only for columns, not AM or whole index */
+ if (attno == 0)
+ return false;
+
+ opclass = get_index_column_opclass(index_oid, attno);
+
+ if (!OidIsValid(opclass) ||
+ !get_opclass_opfamily_and_input_type(opclass,
+ &opfamily, &opcindtype))
+ {
+ *isnull = true;
+ return true;
+ }
+
+ *res = SearchSysCacheExists(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(opcindtype),
+ ObjectIdGetDatum(opcindtype),
+ Int16GetDatum(BtreeKNNSearchStrategyNumber));
+ return true;
+ }
+
default:
return false; /* punt to generic code */
}
@@ -2076,3 +2106,242 @@ _bt_allocate_tuple_workspaces(BTScanState state)
state->currTuples = (char *) palloc(BLCKSZ * 2);
state->markTuples = state->currTuples + BLCKSZ;
}
+
+static Oid
+_bt_get_sortfamily_for_ordering_key(Relation rel, ScanKey skey)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid sortfamily;
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(rel->rd_opfamily[skey->sk_attno - 1]),
+ ObjectIdGetDatum(rel->rd_opcintype[skey->sk_attno - 1]),
+ ObjectIdGetDatum(skey->sk_subtype),
+ Int16GetDatum(skey->sk_strategy));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ sortfamily = amop_tup->amopsortfamily;
+ ReleaseSysCache(tp);
+
+ return sortfamily;
+}
+
+static bool
+_bt_compare_row_key_with_ordering_key(ScanKey row, ScanKey ord, bool *result)
+{
+ ScanKey subkey = (ScanKey) DatumGetPointer(row->sk_argument);
+ int32 cmpresult;
+
+ Assert(subkey->sk_attno == 1);
+ Assert(subkey->sk_flags & SK_ROW_MEMBER);
+
+ if (subkey->sk_flags & SK_ISNULL)
+ return false;
+
+ /* Perform the test --- three-way comparison not bool operator */
+ cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
+ subkey->sk_collation,
+ ord->sk_argument,
+ subkey->sk_argument));
+
+ if (subkey->sk_flags & SK_BT_DESC)
+ cmpresult = -cmpresult;
+
+ /*
+ * At this point cmpresult indicates the overall result of the row
+ * comparison, and subkey points to the deciding column (or the last
+ * column if the result is "=").
+ */
+ switch (subkey->sk_strategy)
+ {
+ /* EQ and NE cases aren't allowed here */
+ case BTLessStrategyNumber:
+ *result = cmpresult < 0;
+ break;
+ case BTLessEqualStrategyNumber:
+ *result = cmpresult <= 0;
+ break;
+ case BTGreaterEqualStrategyNumber:
+ *result = cmpresult >= 0;
+ break;
+ case BTGreaterStrategyNumber:
+ *result = cmpresult > 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized RowCompareType: %d",
+ (int) subkey->sk_strategy);
+ *result = false; /* keep compiler quiet */
+ }
+
+ return true;
+}
+
+/* _bt_select_knn_search_strategy() -- Determine which KNN scan strategy to use:
+ * bidirectional or unidirectional. We are checking here if the
+ * ordering scankey argument falls into the scan range: if it falls
+ * we must use bidirectional scan, otherwise we use unidirectional.
+ *
+ * Returns BtreeKNNSearchStrategyNumber for bidirectional scan or
+ * strategy number of non-matched scankey for unidirectional.
+ */
+static StrategyNumber
+_bt_select_knn_search_strategy(IndexScanDesc scan, ScanKey ord)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey cond;
+
+ for (cond = so->keyData; cond < so->keyData + so->numberOfKeys; cond++)
+ {
+ bool result;
+
+ if (cond->sk_attno != 1)
+ break; /* only interesting in the first index attribute */
+
+ if (cond->sk_strategy == BTEqualStrategyNumber)
+ /* always use simple unidirectional scan for equals operators */
+ return BTEqualStrategyNumber;
+
+ if (cond->sk_flags & SK_ROW_HEADER)
+ {
+ if (!_bt_compare_row_key_with_ordering_key(cond, ord, &result))
+ return BTEqualStrategyNumber; /* ROW(fist_index_attr, ...) IS NULL */
+ }
+ else
+ {
+ if (!_bt_compare_scankey_args(scan, cond, ord, cond, &result))
+ elog(ERROR, "could not compare ordering key");
+ }
+
+ if (!result)
+ /*
+ * Ordering scankey argument is out of scan range,
+ * use unidirectional scan.
+ */
+ return cond->sk_strategy;
+ }
+
+ return BtreeKNNSearchStrategyNumber; /* use bidirectional scan */
+}
+
+/*
+ * _bt_init_distance_comparison() -- Init distance typlen/typbyval and its
+ * comparison procedure.
+ */
+static void
+_bt_init_distance_comparison(IndexScanDesc scan, ScanKey ord)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ RegProcedure opcode;
+ Oid sortfamily;
+ Oid opno;
+ Oid distanceType;
+
+ distanceType = get_func_rettype(ord->sk_func.fn_oid);
+
+ sortfamily = _bt_get_sortfamily_for_ordering_key(scan->indexRelation, ord);
+
+ if (!OidIsValid(sortfamily))
+ elog(ERROR, "could not find sort family for btree ordering operator");
+
+ opno = get_opfamily_member(sortfamily,
+ distanceType,
+ distanceType,
+ BTLessEqualStrategyNumber);
+
+ if (!OidIsValid(opno))
+ elog(ERROR, "could not find operator for btree distance comparison");
+
+ opcode = get_opcode(opno);
+
+ if (!RegProcedureIsValid(opcode))
+ elog(ERROR,
+ "could not find procedure for btree distance comparison operator");
+
+ fmgr_info(opcode, &so->distanceCmpProc);
+
+ get_typlenbyval(distanceType, &so->distanceTypeLen, &so->distanceTypeByVal);
+
+ if (!so->distanceTypeByVal)
+ {
+ so->state.currDistance = PointerGetDatum(NULL);
+ so->state.markDistance = PointerGetDatum(NULL);
+ }
+}
+
+/*
+ * _bt_process_orderings() -- Process ORDER BY distance scankeys and
+ * select corresponding KNN strategy.
+ *
+ * If bidirectional scan is selected then one scankey is initialized
+ * using bufKeys and placed into startKeys/keysCount, true is returned.
+ *
+ * Otherwise, so->scanDirection is set and false is returned.
+ */
+bool
+_bt_process_orderings(IndexScanDesc scan, ScanKey *startKeys, int *keysCount,
+ ScanKeyData bufKeys[])
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey ord = scan->orderByData;
+
+ if (scan->numberOfOrderBys > 1 || ord->sk_attno != 1)
+ /* it should not happen, see btcanorderbyop() */
+ elog(ERROR, "only one btree ordering operator "
+ "for the first index column is supported");
+
+ Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
+
+ switch (_bt_select_knn_search_strategy(scan, ord))
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ /*
+ * Ordering key argument is greater than all values in scan range.
+ * select backward scan direction.
+ */
+ so->scanDirection = BackwardScanDirection;
+ return false;
+
+ case BTEqualStrategyNumber:
+ /* Use default unidirectional scan direction. */
+ return false;
+
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ /*
+ * Ordering key argument is lesser than all values in scan range.
+ * select forward scan direction.
+ */
+ so->scanDirection = ForwardScanDirection;
+ return false;
+
+ case BtreeKNNSearchStrategyNumber:
+ /*
+ * Ordering key argument falls into scan range,
+ * use bidirectional scan.
+ */
+ break;
+ }
+
+ _bt_init_distance_comparison(scan, ord);
+
+ /* Init btree search key with ordering key argument. */
+ ScanKeyEntryInitialize(&bufKeys[0],
+ (scan->indexRelation->rd_indoption[ord->sk_attno - 1] <<
+ SK_BT_INDOPTION_SHIFT) |
+ SK_ORDER_BY |
+ SK_SEARCHNULL /* only for invalid procedure oid, see
+ * assert in ScanKeyEntryInitialize */,
+ ord->sk_attno,
+ BtreeKNNSearchStrategyNumber,
+ ord->sk_subtype,
+ ord->sk_collation,
+ InvalidOid,
+ ord->sk_argument);
+
+ startKeys[(*keysCount)++] = &bufKeys[0];
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 7b443f2..a06b697 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -22,8 +22,16 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
+#define BTRequiredOperatorSet \
+ ((1 << BTLessStrategyNumber) | \
+ (1 << BTLessEqualStrategyNumber) | \
+ (1 << BTEqualStrategyNumber) | \
+ (1 << BTGreaterEqualStrategyNumber) | \
+ (1 << BTGreaterStrategyNumber))
+
/*
* Validator for a btree opclass.
@@ -122,10 +130,11 @@ btvalidate(Oid opclassoid)
{
HeapTuple oprtup = &oprlist->members[i]->tuple;
Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ Oid op_rettype;
/* Check that only allowed strategy numbers exist */
if (oprform->amopstrategy < 1 ||
- oprform->amopstrategy > BTMaxStrategyNumber)
+ oprform->amopstrategy > BtreeMaxStrategyNumber)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -136,20 +145,29 @@ btvalidate(Oid opclassoid)
result = false;
}
- /* btree doesn't support ORDER BY operators */
- if (oprform->amoppurpose != AMOP_SEARCH ||
- OidIsValid(oprform->amopsortfamily))
+ /* btree supports ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH)
{
- ereport(INFO,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree operator family \"%s\" contains invalid ORDER BY specification for operator %s",
- opfamilyname,
- format_operator(oprform->amopopr))));
- result = false;
+ /* ... and operator result must match the claimed btree opfamily */
+ op_rettype = get_op_rettype(oprform->amopopr);
+ if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
+ {
+ ereport(INFO,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree operator family %s contains invalid ORDER BY specification for operator %s",
+ opfamilyname,
+ format_operator(oprform->amopopr))));
+ result = false;
+ }
+ }
+ else
+ {
+ /* Search operators must always return bool */
+ op_rettype = BOOLOID;
}
/* Check operator signature --- same for all btree strategies */
- if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ if (!check_amop_signature(oprform->amopopr, op_rettype,
oprform->amoplefttype,
oprform->amoprighttype))
{
@@ -188,12 +206,8 @@ btvalidate(Oid opclassoid)
* or support functions for this datatype pair. The only thing that
* is considered optional is the sortsupport function.
*/
- if (thisgroup->operatorset !=
- ((1 << BTLessStrategyNumber) |
- (1 << BTLessEqualStrategyNumber) |
- (1 << BTEqualStrategyNumber) |
- (1 << BTGreaterEqualStrategyNumber) |
- (1 << BTGreaterStrategyNumber)))
+ if ((thisgroup->operatorset & BTRequiredOperatorSet) !=
+ BTRequiredOperatorSet)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index b9b4701..9ebe29b 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -982,6 +982,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
* if we are only trying to build bitmap indexscans, nor if we have to
* assume the scan is unordered.
*/
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
!found_lower_saop_clause &&
has_useful_pathkeys(root, rel));
@@ -992,10 +996,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
ForwardScanDirection);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
}
- else if (index->amcanorderbyop && pathkeys_possibly_useful)
+
+ if (useful_pathkeys == NIL &&
+ index->amcanorderbyop && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -1006,12 +1010,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
else
useful_pathkeys = NIL;
}
- else
- {
- useful_pathkeys = NIL;
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
- }
/*
* 3. Check if an index-only scan is possible. If we're not building
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 4427bd3..f2bd19f 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -624,6 +624,12 @@ typedef struct BTScanStateData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+
+ /* KNN-search fields: */
+ Datum currDistance; /* current distance */
+ Datum markDistance; /* marked distance */
+ bool currIsNull; /* current item is NULL */
+ bool markIsNull; /* marked item is NULL */
} BTScanStateData, *BTScanState;
typedef struct BTScanOpaqueData
@@ -640,7 +646,17 @@ typedef struct BTScanOpaqueData
BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
MemoryContext arrayContext; /* scan-lifespan context for array data */
- BTScanStateData state;
+ BTScanStateData state; /* main scan state */
+
+ /* KNN-search fields: */
+ BTScanState knnState; /* optional scan state for KNN search */
+ ScanDirection scanDirection; /* selected scan direction for
+ * unidirectional KNN scan */
+ FmgrInfo distanceCmpProc; /* distance comparison procedure */
+ int16 distanceTypeLen; /* distance typlen */
+ bool distanceTypeByVal; /* distance typebyval */
+ bool currRightIsNearest; /* current right item is nearest */
+ bool markRightIsNearest; /* marked right item is nearest */
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -757,6 +773,8 @@ extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
extern void _bt_allocate_tuple_workspaces(BTScanState state);
+extern bool _bt_process_orderings(IndexScanDesc scan,
+ ScanKey *startKeys, int *keysCount, ScanKeyData bufKeys[]);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/include/access/stratnum.h b/src/include/access/stratnum.h
index 489e5c5..0852f8a 100644
--- a/src/include/access/stratnum.h
+++ b/src/include/access/stratnum.h
@@ -32,7 +32,10 @@ typedef uint16 StrategyNumber;
#define BTGreaterEqualStrategyNumber 4
#define BTGreaterStrategyNumber 5
-#define BTMaxStrategyNumber 5
+#define BTMaxStrategyNumber 5 /* number of canonical B-tree strategies */
+
+#define BtreeKNNSearchStrategyNumber 6 /* for <-> (distance) */
+#define BtreeMaxStrategyNumber 6 /* number of extended B-tree strategies */
/*
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index b01be59..b03374e 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -347,10 +347,10 @@ ROLLBACK;
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
ERROR: access method "invalid_index_method" does not exist
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 6, must be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 0, must be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 7, must be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 0, must be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -400,7 +400,6 @@ DROP OPERATOR FAMILY alt_opf9 USING gist;
-- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
-ERROR: access method "btree" does not support ordering operators
DROP OPERATOR FAMILY alt_opf10 USING btree;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
diff --git a/src/test/regress/expected/amutils.out b/src/test/regress/expected/amutils.out
index 74f7c9f..55296b7 100644
--- a/src/test/regress/expected/amutils.out
+++ b/src/test/regress/expected/amutils.out
@@ -24,7 +24,7 @@ select prop,
nulls_first | | | f
nulls_last | | | t
orderable | | | t
- distance_orderable | | | f
+ distance_orderable | | | t
returnable | | | t
search_array | | | t
search_nulls | | | t
@@ -97,7 +97,7 @@ select prop,
nulls_first | f | f | f | f | f | f
nulls_last | t | f | f | f | f | f
orderable | t | f | f | f | f | f
- distance_orderable | f | f | t | f | f | f
+ distance_orderable | t | f | t | f | f | f
returnable | t | f | f | t | f | f
search_array | t | f | f | f | f | f
search_nulls | t | f | t | t | f | t
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index c9ea479..53cdda0 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -295,8 +295,8 @@ ROLLBACK;
-- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
0005-add-distance-operators-v01.patchtext/x-patch; name=0005-add-distance-operators-v01.patchDownload
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index a146b0a..1247d8e 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -30,6 +30,7 @@
#include "utils/numeric.h"
#include "utils/pg_locale.h"
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
/*************************************************************************
* Private routines
@@ -1157,3 +1158,23 @@ int8_cash(PG_FUNCTION_ARGS)
PG_RETURN_CASH(result);
}
+
+Datum
+cash_dist(PG_FUNCTION_ARGS)
+{
+ Cash a = PG_GETARG_CASH(0);
+ Cash b = PG_GETARG_CASH(1);
+ Cash r;
+ Cash ra;
+
+ r = a - b;
+ ra = Abs(r);
+
+ /* Overflow check. */
+ if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("money out of range")));
+
+ PG_RETURN_CASH(ra);
+}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 96cfacd..a94a0df 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -561,6 +561,17 @@ date_mii(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(result);
}
+Datum
+date_dist(PG_FUNCTION_ARGS)
+{
+ /* we assume the difference can't overflow */
+ Datum diff = DirectFunctionCall2(date_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+}
+
/*
* Internal routines for promoting date to timestamp and timestamp with
* time zone
@@ -2061,6 +2072,16 @@ time_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+Datum
+time_dist(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(time_mi_time,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
+
/*****************************************************************************
* Time With Time Zone ADT
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 86b46de..4c023f5 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -3585,6 +3585,32 @@ width_bucket_float8(PG_FUNCTION_ARGS)
PG_RETURN_INT32(result);
}
+Datum
+float4_dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float4 r;
+
+ r = a - b;
+ CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
+
+ PG_RETURN_FLOAT4(Abs(r));
+}
+
+Datum
+float8_dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r;
+
+ r = a - b;
+ CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
/* ========== PRIVATE ROUTINES ========== */
#ifndef HAVE_CBRT
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index bd4422e..43dcf40 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -1395,3 +1395,43 @@ generate_series_step_int4(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+Datum
+int2_dist(PG_FUNCTION_ARGS)
+{
+ int16 a = PG_GETARG_INT16(0);
+ int16 b = PG_GETARG_INT16(1);
+ int16 r;
+ int16 ra;
+
+ r = a - b;
+ ra = Abs(r);
+
+ /* Overflow check. */
+ if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+
+ PG_RETURN_INT16(ra);
+}
+
+Datum
+int4_dist(PG_FUNCTION_ARGS)
+{
+ int32 a = PG_GETARG_INT32(0);
+ int32 b = PG_GETARG_INT32(1);
+ int32 r;
+ int32 ra;
+
+ r = a - b;
+ ra = Abs(r);
+
+ /* Overflow check. */
+ if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ PG_RETURN_INT32(ra);
+}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index b7aa0ad..f61d5a9 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1508,3 +1508,23 @@ generate_series_step_int8(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+Datum
+int8_dist(PG_FUNCTION_ARGS)
+{
+ int64 a = PG_GETARG_INT64(0);
+ int64 b = PG_GETARG_INT64(1);
+ int64 r;
+ int64 ra;
+
+ r = a - b;
+ ra = Abs(r);
+
+ /* Overflow check. */
+ if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+
+ PG_RETURN_INT64(ra);
+}
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index fd12382..0640c65 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -452,3 +452,17 @@ oidvectorgt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+
+Datum
+oid_dist(PG_FUNCTION_ARGS)
+{
+ Oid a = PG_GETARG_OID(0);
+ Oid b = PG_GETARG_OID(1);
+ Oid res;
+
+ if (a < b)
+ res = b - a;
+ else
+ res = a - b;
+ PG_RETURN_OID(res);
+}
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index a87f982..9ecaba6 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2922,6 +2922,62 @@ timestamp_mi(PG_FUNCTION_ARGS)
PG_RETURN_INTERVAL_P(result);
}
+Datum
+ts_dist(PG_FUNCTION_ARGS)
+{
+ Timestamp a = PG_GETARG_TIMESTAMP(0);
+ Timestamp b = PG_GETARG_TIMESTAMP(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+#ifdef HAVE_INT64_TIMESTAMP
+ p->time = PG_INT64_MAX;
+#else
+ p->time = DBL_MAX;
+#endif
+ PG_RETURN_INTERVAL_P(p);
+ }
+ else
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+
+Datum
+tstz_dist(PG_FUNCTION_ARGS)
+{
+ TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
+ TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+#ifdef HAVE_INT64_TIMESTAMP
+ p->time = PG_INT64_MAX;
+#else
+ p->time = DBL_MAX;
+#endif
+ PG_RETURN_INTERVAL_P(p);
+ }
+
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+
/*
* interval_justify_interval()
*
@@ -3725,6 +3781,29 @@ interval_avg(PG_FUNCTION_ARGS)
Float8GetDatum((double) N.time));
}
+Interval *
+abs_interval(Interval *a)
+{
+ static Interval zero = {0, 0, 0};
+
+ if (DatumGetBool(DirectFunctionCall2(interval_lt,
+ IntervalPGetDatum(a),
+ IntervalPGetDatum(&zero))))
+ a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
+ IntervalPGetDatum(a)));
+
+ return a;
+}
+
+Datum
+interval_dist(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(interval_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
/* timestamp_age()
* Calculate time difference while retaining year/month fields.
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index 0251664..44e2caf 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -105,6 +105,7 @@ DATA(insert ( 1976 21 21 2 s 522 403 0 ));
DATA(insert ( 1976 21 21 3 s 94 403 0 ));
DATA(insert ( 1976 21 21 4 s 524 403 0 ));
DATA(insert ( 1976 21 21 5 s 520 403 0 ));
+DATA(insert ( 1976 21 21 6 o 3365 403 1976 ));
/* crosstype operators int24 */
DATA(insert ( 1976 21 23 1 s 534 403 0 ));
DATA(insert ( 1976 21 23 2 s 540 403 0 ));
@@ -123,6 +124,7 @@ DATA(insert ( 1976 23 23 2 s 523 403 0 ));
DATA(insert ( 1976 23 23 3 s 96 403 0 ));
DATA(insert ( 1976 23 23 4 s 525 403 0 ));
DATA(insert ( 1976 23 23 5 s 521 403 0 ));
+DATA(insert ( 1976 23 23 6 o 3366 403 1976 ));
/* crosstype operators int42 */
DATA(insert ( 1976 23 21 1 s 535 403 0 ));
DATA(insert ( 1976 23 21 2 s 541 403 0 ));
@@ -141,6 +143,7 @@ DATA(insert ( 1976 20 20 2 s 414 403 0 ));
DATA(insert ( 1976 20 20 3 s 410 403 0 ));
DATA(insert ( 1976 20 20 4 s 415 403 0 ));
DATA(insert ( 1976 20 20 5 s 413 403 0 ));
+DATA(insert ( 1976 20 20 6 o 3367 403 1976 ));
/* crosstype operators int82 */
DATA(insert ( 1976 20 21 1 s 1870 403 0 ));
DATA(insert ( 1976 20 21 2 s 1872 403 0 ));
@@ -163,6 +166,7 @@ DATA(insert ( 1989 26 26 2 s 611 403 0 ));
DATA(insert ( 1989 26 26 3 s 607 403 0 ));
DATA(insert ( 1989 26 26 4 s 612 403 0 ));
DATA(insert ( 1989 26 26 5 s 610 403 0 ));
+DATA(insert ( 1989 26 26 6 o 3368 403 1989 ));
/*
* btree tid_ops
@@ -194,6 +198,7 @@ DATA(insert ( 1970 700 700 2 s 624 403 0 ));
DATA(insert ( 1970 700 700 3 s 620 403 0 ));
DATA(insert ( 1970 700 700 4 s 625 403 0 ));
DATA(insert ( 1970 700 700 5 s 623 403 0 ));
+DATA(insert ( 1970 700 700 6 o 3369 403 1970 ));
/* crosstype operators float48 */
DATA(insert ( 1970 700 701 1 s 1122 403 0 ));
DATA(insert ( 1970 700 701 2 s 1124 403 0 ));
@@ -206,6 +211,7 @@ DATA(insert ( 1970 701 701 2 s 673 403 0 ));
DATA(insert ( 1970 701 701 3 s 670 403 0 ));
DATA(insert ( 1970 701 701 4 s 675 403 0 ));
DATA(insert ( 1970 701 701 5 s 674 403 0 ));
+DATA(insert ( 1970 701 701 6 o 3370 403 1970 ));
/* crosstype operators float84 */
DATA(insert ( 1970 701 700 1 s 1132 403 0 ));
DATA(insert ( 1970 701 700 2 s 1134 403 0 ));
@@ -283,6 +289,7 @@ DATA(insert ( 434 1082 1082 2 s 1096 403 0 ));
DATA(insert ( 434 1082 1082 3 s 1093 403 0 ));
DATA(insert ( 434 1082 1082 4 s 1098 403 0 ));
DATA(insert ( 434 1082 1082 5 s 1097 403 0 ));
+DATA(insert ( 434 1082 1082 6 o 3372 403 1976 ));
/* crosstype operators vs timestamp */
DATA(insert ( 434 1082 1114 1 s 2345 403 0 ));
DATA(insert ( 434 1082 1114 2 s 2346 403 0 ));
@@ -301,6 +308,7 @@ DATA(insert ( 434 1114 1114 2 s 2063 403 0 ));
DATA(insert ( 434 1114 1114 3 s 2060 403 0 ));
DATA(insert ( 434 1114 1114 4 s 2065 403 0 ));
DATA(insert ( 434 1114 1114 5 s 2064 403 0 ));
+DATA(insert ( 434 1114 1114 6 o 3374 403 1982 ));
/* crosstype operators vs date */
DATA(insert ( 434 1114 1082 1 s 2371 403 0 ));
DATA(insert ( 434 1114 1082 2 s 2372 403 0 ));
@@ -319,6 +327,7 @@ DATA(insert ( 434 1184 1184 2 s 1323 403 0 ));
DATA(insert ( 434 1184 1184 3 s 1320 403 0 ));
DATA(insert ( 434 1184 1184 4 s 1325 403 0 ));
DATA(insert ( 434 1184 1184 5 s 1324 403 0 ));
+DATA(insert ( 434 1184 1184 6 o 3375 403 1982 ));
/* crosstype operators vs date */
DATA(insert ( 434 1184 1082 1 s 2384 403 0 ));
DATA(insert ( 434 1184 1082 2 s 2385 403 0 ));
@@ -341,6 +350,7 @@ DATA(insert ( 1996 1083 1083 2 s 1111 403 0 ));
DATA(insert ( 1996 1083 1083 3 s 1108 403 0 ));
DATA(insert ( 1996 1083 1083 4 s 1113 403 0 ));
DATA(insert ( 1996 1083 1083 5 s 1112 403 0 ));
+DATA(insert ( 1996 1083 1083 6 o 3373 403 1982 ));
/*
* btree timetz_ops
@@ -361,6 +371,7 @@ DATA(insert ( 1982 1186 1186 2 s 1333 403 0 ));
DATA(insert ( 1982 1186 1186 3 s 1330 403 0 ));
DATA(insert ( 1982 1186 1186 4 s 1335 403 0 ));
DATA(insert ( 1982 1186 1186 5 s 1334 403 0 ));
+DATA(insert ( 1982 1186 1186 6 o 3376 403 1982 ));
/*
* btree macaddr
@@ -451,6 +462,7 @@ DATA(insert ( 2099 790 790 2 s 904 403 0 ));
DATA(insert ( 2099 790 790 3 s 900 403 0 ));
DATA(insert ( 2099 790 790 4 s 905 403 0 ));
DATA(insert ( 2099 790 790 5 s 903 403 0 ));
+DATA(insert ( 2099 790 790 6 o 3371 403 2099 ));
/*
* btree reltime_ops
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index aeb7927..5a7e6b2 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1618,6 +1618,32 @@ DESCR("greater than or equal");
DATA(insert OID = 3228 ( "-" PGNSP PGUID b f f 3220 3220 1700 0 0 pg_lsn_mi - - ));
DESCR("minus");
+/* distance operators */
+DATA(insert OID = 3365 ( "<->" PGNSP PGUID b f f 21 21 21 3365 0 int2_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3366 ( "<->" PGNSP PGUID b f f 23 23 23 3366 0 int4_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3367 ( "<->" PGNSP PGUID b f f 20 20 20 3367 0 int8_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3368 ( "<->" PGNSP PGUID b f f 26 26 26 3368 0 oid_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3369 ( "<->" PGNSP PGUID b f f 700 700 700 3369 0 float4_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3370 ( "<->" PGNSP PGUID b f f 701 701 701 3370 0 float8_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3371 ( "<->" PGNSP PGUID b f f 790 790 790 3371 0 cash_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3372 ( "<->" PGNSP PGUID b f f 1082 1082 23 3372 0 date_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3373 ( "<->" PGNSP PGUID b f f 1083 1083 1186 3373 0 time_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3374 ( "<->" PGNSP PGUID b f f 1114 1114 1186 3374 0 ts_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3375 ( "<->" PGNSP PGUID b f f 1184 1184 1186 3375 0 tstz_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3376 ( "<->" PGNSP PGUID b f f 1186 1186 1186 3376 0 interval_dist - - ));
+DESCR("distance between");
+
/* enum operators */
DATA(insert OID = 3516 ( "=" PGNSP PGUID b t t 3500 3500 16 3516 3517 enum_eq eqsel eqjoinsel ));
DESCR("equal");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 37e022d..9451b84 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5345,6 +5345,21 @@ DESCR("pg_controldata recovery state information as a function");
DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
DESCR("pg_controldata init state information as a function");
+/* distance functions */
+DATA(insert OID = 3353 ( int2_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 21 "21 21" _null_ _null_ _null_ _null_ _null_ int2_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3354 ( int4_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 23 "23 23" _null_ _null_ _null_ _null_ _null_ int4_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3355 ( int8_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "20 20" _null_ _null_ _null_ _null_ _null_ int8_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3356 ( oid_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 26 "26 26" _null_ _null_ _null_ _null_ _null_ oid_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3357 ( float4_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 700 "700 700" _null_ _null_ _null_ _null_ _null_ float4_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3358 ( float8_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 701 "701 701" _null_ _null_ _null_ _null_ _null_ float8_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3359 ( cash_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 790 "790 790" _null_ _null_ _null_ _null_ _null_ cash_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3360 ( date_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 23 "1082 1082" _null_ _null_ _null_ _null_ _null_ date_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3361 ( time_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1186 "1083 1083" _null_ _null_ _null_ _null_ _null_ time_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3362 ( ts_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1186 "1114 1114" _null_ _null_ _null_ _null_ _null_ ts_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3363 ( tstz_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1186 "1184 1184" _null_ _null_ _null_ _null_ _null_ tstz_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3364 ( interval_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1186 "1186 1186" _null_ _null_ _null_ _null_ _null_ interval_dist _null_ _null_ _null_ ));
+
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index e1bb344..ed61bb8 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -253,6 +253,8 @@ extern Datum int2larger(PG_FUNCTION_ARGS);
extern Datum int2smaller(PG_FUNCTION_ARGS);
extern Datum int4larger(PG_FUNCTION_ARGS);
extern Datum int4smaller(PG_FUNCTION_ARGS);
+extern Datum int2_dist(PG_FUNCTION_ARGS);
+extern Datum int4_dist(PG_FUNCTION_ARGS);
extern Datum int4and(PG_FUNCTION_ARGS);
extern Datum int4or(PG_FUNCTION_ARGS);
@@ -391,6 +393,8 @@ extern Datum float8lt(PG_FUNCTION_ARGS);
extern Datum float8le(PG_FUNCTION_ARGS);
extern Datum float8gt(PG_FUNCTION_ARGS);
extern Datum float8ge(PG_FUNCTION_ARGS);
+extern Datum float4_dist(PG_FUNCTION_ARGS);
+extern Datum float8_dist(PG_FUNCTION_ARGS);
extern Datum ftod(PG_FUNCTION_ARGS);
extern Datum i4tod(PG_FUNCTION_ARGS);
extern Datum i2tod(PG_FUNCTION_ARGS);
@@ -546,6 +550,7 @@ extern Datum oidvectorlt(PG_FUNCTION_ARGS);
extern Datum oidvectorle(PG_FUNCTION_ARGS);
extern Datum oidvectorge(PG_FUNCTION_ARGS);
extern Datum oidvectorgt(PG_FUNCTION_ARGS);
+extern Datum oid_dist(PG_FUNCTION_ARGS);
extern oidvector *buildoidvector(const Oid *oids, int n);
extern Oid oidparse(Node *node);
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 3a491f9..9d609b2 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -70,4 +70,6 @@ extern Datum numeric_cash(PG_FUNCTION_ARGS);
extern Datum int4_cash(PG_FUNCTION_ARGS);
extern Datum int8_cash(PG_FUNCTION_ARGS);
+extern Datum cash_dist(PG_FUNCTION_ARGS);
+
#endif /* CASH_H */
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index b5cc6f3..d56c3c1 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -115,6 +115,7 @@ extern Datum date_smaller(PG_FUNCTION_ARGS);
extern Datum date_mi(PG_FUNCTION_ARGS);
extern Datum date_pli(PG_FUNCTION_ARGS);
extern Datum date_mii(PG_FUNCTION_ARGS);
+extern Datum date_dist(PG_FUNCTION_ARGS);
extern Datum date_eq_timestamp(PG_FUNCTION_ARGS);
extern Datum date_ne_timestamp(PG_FUNCTION_ARGS);
extern Datum date_lt_timestamp(PG_FUNCTION_ARGS);
@@ -180,6 +181,7 @@ extern Datum interval_time(PG_FUNCTION_ARGS);
extern Datum time_pl_interval(PG_FUNCTION_ARGS);
extern Datum time_mi_interval(PG_FUNCTION_ARGS);
extern Datum time_part(PG_FUNCTION_ARGS);
+extern Datum time_dist(PG_FUNCTION_ARGS);
extern Datum timetz_in(PG_FUNCTION_ARGS);
extern Datum timetz_out(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index a7cc2c5..fff7e1d 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -349,4 +349,6 @@ extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
extern Datum pg_timezone_abbrevs(PG_FUNCTION_ARGS);
extern Datum pg_timezone_names(PG_FUNCTION_ARGS);
+extern Interval *abs_interval(Interval *a);
+
#endif /* DATETIME_H */
diff --git a/src/include/utils/int8.h b/src/include/utils/int8.h
index 627505e..163f0fd 100644
--- a/src/include/utils/int8.h
+++ b/src/include/utils/int8.h
@@ -126,4 +126,6 @@ extern Datum oidtoi8(PG_FUNCTION_ARGS);
extern Datum generate_series_int8(PG_FUNCTION_ARGS);
extern Datum generate_series_step_int8(PG_FUNCTION_ARGS);
+extern Datum int8_dist(PG_FUNCTION_ARGS);
+
#endif /* INT8_H */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index 3ec8ecd..32b8842 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -213,6 +213,10 @@ extern Datum pg_conf_load_time(PG_FUNCTION_ARGS);
extern Datum generate_series_timestamp(PG_FUNCTION_ARGS);
extern Datum generate_series_timestamptz(PG_FUNCTION_ARGS);
+extern Datum ts_dist(PG_FUNCTION_ARGS);
+extern Datum tstz_dist(PG_FUNCTION_ARGS);
+extern Datum interval_dist(PG_FUNCTION_ARGS);
+
/* Internal routines (not fmgr-callable) */
extern int32 anytimestamp_typmod_check(bool istz, int32 typmod);
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 0bcec13..fb44ae8 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1721,6 +1721,7 @@ ORDER BY 1, 2, 3;
403 | 5 | *>
403 | 5 | >
403 | 5 | ~>~
+ 403 | 6 | <->
405 | 1 | =
783 | 1 | <<
783 | 1 | @@
@@ -1828,7 +1829,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
0006-remove-distance-operators-from-btree_gist-v01.patchtext/x-patch; name=0006-remove-distance-operators-from-btree_gist-v01.patchDownload
diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile
index d36f517..c650581 100644
--- a/contrib/btree_gist/Makefile
+++ b/contrib/btree_gist/Makefile
@@ -10,7 +10,8 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
EXTENSION = btree_gist
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
- btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql
+ btree_gist--1.1--1.2.sql btree_gist--1.2--1.3.sql \
+ btree_gist--1.3--1.4.sql btree_gist--1.4.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c
index aa14735..6ebeda1 100644
--- a/contrib/btree_gist/btree_cash.c
+++ b/contrib/btree_gist/btree_cash.c
@@ -90,27 +90,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(cash_dist);
-Datum
-cash_dist(PG_FUNCTION_ARGS)
-{
- Cash a = PG_GETARG_CASH(0);
- Cash b = PG_GETARG_CASH(1);
- Cash r;
- Cash ra;
-
- r = a - b;
- ra = Abs(r);
-
- /* Overflow check. */
- if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("money out of range")));
-
- PG_RETURN_CASH(ra);
-}
-
/**************************************************
* Cash ops
**************************************************/
diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c
index bb516a9..e1bd413 100644
--- a/contrib/btree_gist/btree_date.c
+++ b/contrib/btree_gist/btree_date.c
@@ -108,19 +108,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(date_dist);
-Datum
-date_dist(PG_FUNCTION_ARGS)
-{
- /* we assume the difference can't overflow */
- Datum diff = DirectFunctionCall2(date_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
-}
-
-
/**************************************************
* date ops
**************************************************/
diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c
index 13dc4a5..c798b42 100644
--- a/contrib/btree_gist/btree_float4.c
+++ b/contrib/btree_gist/btree_float4.c
@@ -89,21 +89,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(float4_dist);
-Datum
-float4_dist(PG_FUNCTION_ARGS)
-{
- float4 a = PG_GETARG_FLOAT4(0);
- float4 b = PG_GETARG_FLOAT4(1);
- float4 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT4(Abs(r));
-}
-
-
/**************************************************
* float4 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c
index c3a2415..c332c2d 100644
--- a/contrib/btree_gist/btree_float8.c
+++ b/contrib/btree_gist/btree_float8.c
@@ -96,21 +96,6 @@ static const gbtree_ninfo tinfo =
gbt_float8_dist
};
-
-PG_FUNCTION_INFO_V1(float8_dist);
-Datum
-float8_dist(PG_FUNCTION_ARGS)
-{
- float8 a = PG_GETARG_FLOAT8(0);
- float8 b = PG_GETARG_FLOAT8(1);
- float8 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT8(Abs(r));
-}
-
/**************************************************
* float8 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_gist--1.2.sql b/contrib/btree_gist/btree_gist--1.2.sql
deleted file mode 100644
index 1efe753..0000000
--- a/contrib/btree_gist/btree_gist--1.2.sql
+++ /dev/null
@@ -1,1570 +0,0 @@
-/* contrib/btree_gist/btree_gist--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
-
-CREATE FUNCTION gbtreekey4_in(cstring)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey4_out(gbtreekey4)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey4 (
- INTERNALLENGTH = 4,
- INPUT = gbtreekey4_in,
- OUTPUT = gbtreekey4_out
-);
-
-CREATE FUNCTION gbtreekey8_in(cstring)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey8_out(gbtreekey8)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey8 (
- INTERNALLENGTH = 8,
- INPUT = gbtreekey8_in,
- OUTPUT = gbtreekey8_out
-);
-
-CREATE FUNCTION gbtreekey16_in(cstring)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey16_out(gbtreekey16)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey16 (
- INTERNALLENGTH = 16,
- INPUT = gbtreekey16_in,
- OUTPUT = gbtreekey16_out
-);
-
-CREATE FUNCTION gbtreekey32_in(cstring)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey32_out(gbtreekey32)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey32 (
- INTERNALLENGTH = 32,
- INPUT = gbtreekey32_in,
- OUTPUT = gbtreekey32_out
-);
-
-CREATE FUNCTION gbtreekey_var_in(cstring)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey_var (
- INTERNALLENGTH = VARIABLE,
- INPUT = gbtreekey_var_in,
- OUTPUT = gbtreekey_var_out,
- STORAGE = EXTENDED
-);
-
---distance operators
-
-CREATE FUNCTION cash_dist(money, money)
-RETURNS money
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = money,
- RIGHTARG = money,
- PROCEDURE = cash_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION date_dist(date, date)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = date,
- RIGHTARG = date,
- PROCEDURE = date_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float4_dist(float4, float4)
-RETURNS float4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float4,
- RIGHTARG = float4,
- PROCEDURE = float4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float8_dist(float8, float8)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float8,
- RIGHTARG = float8,
- PROCEDURE = float8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int2_dist(int2, int2)
-RETURNS int2
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int2,
- RIGHTARG = int2,
- PROCEDURE = int2_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int4_dist(int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int4,
- RIGHTARG = int4,
- PROCEDURE = int4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int8_dist(int8, int8)
-RETURNS int8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int8,
- RIGHTARG = int8,
- PROCEDURE = int8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION interval_dist(interval, interval)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = interval,
- RIGHTARG = interval,
- PROCEDURE = interval_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION oid_dist(oid, oid)
-RETURNS oid
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = oid,
- RIGHTARG = oid,
- PROCEDURE = oid_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION time_dist(time, time)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = time,
- RIGHTARG = time,
- PROCEDURE = time_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION ts_dist(timestamp, timestamp)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamp,
- RIGHTARG = timestamp,
- PROCEDURE = ts_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION tstz_dist(timestamptz, timestamptz)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamptz,
- RIGHTARG = timestamptz,
- PROCEDURE = tstz_dist,
- COMMUTATOR = '<->'
-);
-
-
---
---
---
--- oid ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_oid_ops
-DEFAULT FOR TYPE oid USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
- FUNCTION 2 gbt_oid_union (internal, internal),
- FUNCTION 3 gbt_oid_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
- FUNCTION 6 gbt_oid_picksplit (internal, internal),
- FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
--- Add operators that are new in 9.1. We do it like this, leaving them
--- "loose" in the operator family rather than bound into the opclass, because
--- that's the only state that can be reproduced during an upgrade from 9.0.
-ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
- OPERATOR 6 <> (oid, oid) ,
- OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
- FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
- -- Also add support function for index-only-scans, added in 9.5.
- FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
-
-
---
---
---
--- int2 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_union(internal, internal)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int2_ops
-DEFAULT FOR TYPE int2 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
- FUNCTION 2 gbt_int2_union (internal, internal),
- FUNCTION 3 gbt_int2_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int2_picksplit (internal, internal),
- FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
- STORAGE gbtreekey4;
-
-ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
- OPERATOR 6 <> (int2, int2) ,
- OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
- FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
-
---
---
---
--- int4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int4_ops
-DEFAULT FOR TYPE int4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
- FUNCTION 2 gbt_int4_union (internal, internal),
- FUNCTION 3 gbt_int4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int4_picksplit (internal, internal),
- FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
- OPERATOR 6 <> (int4, int4) ,
- OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
- FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
-
-
---
---
---
--- int8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int8_ops
-DEFAULT FOR TYPE int8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
- FUNCTION 2 gbt_int8_union (internal, internal),
- FUNCTION 3 gbt_int8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int8_picksplit (internal, internal),
- FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
- OPERATOR 6 <> (int8, int8) ,
- OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
- FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
-
---
---
---
--- float4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float4_ops
-DEFAULT FOR TYPE float4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
- FUNCTION 2 gbt_float4_union (internal, internal),
- FUNCTION 3 gbt_float4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float4_picksplit (internal, internal),
- FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
- OPERATOR 6 <> (float4, float4) ,
- OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
- FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
-
---
---
---
--- float8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float8_ops
-DEFAULT FOR TYPE float8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
- FUNCTION 2 gbt_float8_union (internal, internal),
- FUNCTION 3 gbt_float8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float8_picksplit (internal, internal),
- FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
- OPERATOR 6 <> (float8, float8) ,
- OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
- FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
-
---
---
---
--- timestamp ops
---
---
---
-
-CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamp_ops
-DEFAULT FOR TYPE timestamp USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_ts_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
- OPERATOR 6 <> (timestamp, timestamp) ,
- OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
- FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamptz_ops
-DEFAULT FOR TYPE timestamptz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_tstz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
- OPERATOR 6 <> (timestamptz, timestamptz) ,
- OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
- FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
-
---
---
---
--- time ops
---
---
---
-
-CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_time_ops
-DEFAULT FOR TYPE time USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_time_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
- OPERATOR 6 <> (time, time) ,
- OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
- FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
-
-
-CREATE OPERATOR CLASS gist_timetz_ops
-DEFAULT FOR TYPE timetz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_timetz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
- OPERATOR 6 <> (timetz, timetz) ;
- -- no 'fetch' function, as the compress function is lossy.
-
-
---
---
---
--- date ops
---
---
---
-
-CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_date_ops
-DEFAULT FOR TYPE date USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
- FUNCTION 2 gbt_date_union (internal, internal),
- FUNCTION 3 gbt_date_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_date_penalty (internal, internal, internal),
- FUNCTION 6 gbt_date_picksplit (internal, internal),
- FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
- OPERATOR 6 <> (date, date) ,
- OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
- FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
-
-
---
---
---
--- interval ops
---
---
---
-
-CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_union(internal, internal)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_interval_ops
-DEFAULT FOR TYPE interval USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
- FUNCTION 2 gbt_intv_union (internal, internal),
- FUNCTION 3 gbt_intv_compress (internal),
- FUNCTION 4 gbt_intv_decompress (internal),
- FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
- FUNCTION 6 gbt_intv_picksplit (internal, internal),
- FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
- STORAGE gbtreekey32;
-
-ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
- OPERATOR 6 <> (interval, interval) ,
- OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
- FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
-
-
---
---
---
--- cash ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cash_ops
-DEFAULT FOR TYPE money USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
- FUNCTION 2 gbt_cash_union (internal, internal),
- FUNCTION 3 gbt_cash_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
- FUNCTION 6 gbt_cash_picksplit (internal, internal),
- FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
- OPERATOR 6 <> (money, money) ,
- OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
- FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
- FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
-
-
---
---
---
--- macaddr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_macaddr_ops
-DEFAULT FOR TYPE macaddr USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
- FUNCTION 2 gbt_macad_union (internal, internal),
- FUNCTION 3 gbt_macad_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
- FUNCTION 6 gbt_macad_picksplit (internal, internal),
- FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
- OPERATOR 6 <> (macaddr, macaddr) ,
- FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
-
-
---
---
---
--- text/ bpchar ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_text_ops
-DEFAULT FOR TYPE text USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_text_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
- OPERATOR 6 <> (text, text) ,
- FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
-
-
----- Create the operator class
-CREATE OPERATOR CLASS gist_bpchar_ops
-DEFAULT FOR TYPE bpchar USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_bpchar_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
- OPERATOR 6 <> (bpchar, bpchar) ,
- FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
-
---
---
--- bytea ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bytea_ops
-DEFAULT FOR TYPE bytea USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
- FUNCTION 2 gbt_bytea_union (internal, internal),
- FUNCTION 3 gbt_bytea_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bytea_picksplit (internal, internal),
- FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
- OPERATOR 6 <> (bytea, bytea) ,
- FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
-
-
---
---
---
--- numeric ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_numeric_ops
-DEFAULT FOR TYPE numeric USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
- FUNCTION 2 gbt_numeric_union (internal, internal),
- FUNCTION 3 gbt_numeric_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
- FUNCTION 6 gbt_numeric_picksplit (internal, internal),
- FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
- OPERATOR 6 <> (numeric, numeric) ,
- FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
-
-
---
---
--- bit ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bit_ops
-DEFAULT FOR TYPE bit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
- OPERATOR 6 <> (bit, bit) ,
- FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
-
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_vbit_ops
-DEFAULT FOR TYPE varbit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
- OPERATOR 6 <> (varbit, varbit) ,
- FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
-
-
---
---
---
--- inet/cidr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_inet_ops
-DEFAULT FOR TYPE inet USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cidr_ops
-DEFAULT FOR TYPE cidr USING gist
-AS
- OPERATOR 1 < (inet, inet) ,
- OPERATOR 2 <= (inet, inet) ,
- OPERATOR 3 = (inet, inet) ,
- OPERATOR 4 >= (inet, inet) ,
- OPERATOR 5 > (inet, inet) ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
diff --git a/contrib/btree_gist/btree_gist--1.3--1.4.sql b/contrib/btree_gist/btree_gist--1.3--1.4.sql
new file mode 100644
index 0000000..4062aed
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.3--1.4.sql
@@ -0,0 +1,150 @@
+/* contrib/btree_gist/btree_gist--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.4'" to load this file. \quit
+
+-- update references to distance operators in pg_amop and pg_depend
+
+WITH
+btree_ops AS (
+ SELECT
+ amoplefttype, amoprighttype, amopopr
+ FROM
+ pg_amop
+ JOIN pg_am ON pg_am.oid = amopmethod
+ JOIN pg_opfamily ON pg_opfamily.oid = amopfamily
+ JOIN pg_namespace ON pg_namespace.oid = opfnamespace
+ WHERE
+ nspname = 'pg_catalog'
+ AND opfname IN (
+ 'integer_ops',
+ 'oid_ops',
+ 'money_ops',
+ 'float_ops',
+ 'datetime_ops',
+ 'time_ops',
+ 'interval_ops'
+ )
+ AND amname = 'btree'
+ AND amoppurpose = 'o'
+ AND amopstrategy = 6
+ ),
+gist_ops AS (
+ SELECT
+ pg_amop.oid AS oid, amoplefttype, amoprighttype, amopopr
+ FROM
+ pg_amop
+ JOIN pg_am ON amopmethod = pg_am.oid
+ JOIN pg_opfamily ON amopfamily = pg_opfamily.oid
+ JOIN pg_namespace ON pg_namespace.oid = opfnamespace
+ WHERE
+ nspname = current_schema()
+ AND opfname IN (
+ 'gist_oid_ops',
+ 'gist_int2_ops',
+ 'gist_int4_ops',
+ 'gist_int8_ops',
+ 'gist_float4_ops',
+ 'gist_float8_ops',
+ 'gist_timestamp_ops',
+ 'gist_timestamptz_ops',
+ 'gist_time_ops',
+ 'gist_date_ops',
+ 'gist_interval_ops',
+ 'gist_cash_ops'
+ )
+ AND amname = 'gist'
+ AND amoppurpose = 'o'
+ AND amopstrategy = 15
+ ),
+depend_update_data(gist_amop, gist_amopopr, btree_amopopr) AS (
+ SELECT
+ gist_ops.oid, gist_ops.amopopr, btree_ops.amopopr
+ FROM
+ btree_ops JOIN gist_ops USING (amoplefttype, amoprighttype)
+),
+amop_update_data AS (
+ UPDATE
+ pg_depend
+ SET
+ refobjid = btree_amopopr
+ FROM
+ depend_update_data
+ WHERE
+ objid = gist_amop AND refobjid = gist_amopopr
+ RETURNING
+ depend_update_data.*
+)
+UPDATE
+ pg_amop
+SET
+ amopopr = btree_amopopr
+FROM
+ amop_update_data
+WHERE
+ pg_amop.oid = gist_amop;
+
+-- disable implicit pg_catalog search
+
+DO
+$$
+BEGIN
+ EXECUTE 'SET LOCAL search_path TO ' || current_schema() || ', pg_catalog';
+END
+$$;
+
+-- drop distance operators
+
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int2, int2);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int4, int4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int8, int8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float4, float4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float8, float8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (oid, oid);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (money, money);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (date, date);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (time, time);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (interval, interval);
+
+DROP OPERATOR <-> (int2, int2);
+DROP OPERATOR <-> (int4, int4);
+DROP OPERATOR <-> (int8, int8);
+DROP OPERATOR <-> (float4, float4);
+DROP OPERATOR <-> (float8, float8);
+DROP OPERATOR <-> (oid, oid);
+DROP OPERATOR <-> (money, money);
+DROP OPERATOR <-> (date, date);
+DROP OPERATOR <-> (time, time);
+DROP OPERATOR <-> (timestamp, timestamp);
+DROP OPERATOR <-> (timestamptz, timestamptz);
+DROP OPERATOR <-> (interval, interval);
+
+-- drop distance functions
+
+ALTER EXTENSION btree_gist DROP FUNCTION int2_dist(int2, int2);
+ALTER EXTENSION btree_gist DROP FUNCTION int4_dist(int4, int4);
+ALTER EXTENSION btree_gist DROP FUNCTION int8_dist(int8, int8);
+ALTER EXTENSION btree_gist DROP FUNCTION float4_dist(float4, float4);
+ALTER EXTENSION btree_gist DROP FUNCTION float8_dist(float8, float8);
+ALTER EXTENSION btree_gist DROP FUNCTION oid_dist(oid, oid);
+ALTER EXTENSION btree_gist DROP FUNCTION cash_dist(money, money);
+ALTER EXTENSION btree_gist DROP FUNCTION date_dist(date, date);
+ALTER EXTENSION btree_gist DROP FUNCTION time_dist(time, time);
+ALTER EXTENSION btree_gist DROP FUNCTION ts_dist(timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP FUNCTION interval_dist(interval, interval);
+
+DROP FUNCTION int2_dist(int2, int2);
+DROP FUNCTION int4_dist(int4, int4);
+DROP FUNCTION int8_dist(int8, int8);
+DROP FUNCTION float4_dist(float4, float4);
+DROP FUNCTION float8_dist(float8, float8);
+DROP FUNCTION oid_dist(oid, oid);
+DROP FUNCTION cash_dist(money, money);
+DROP FUNCTION date_dist(date, date);
+DROP FUNCTION time_dist(time, time);
+DROP FUNCTION ts_dist(timestamp, timestamp);
+DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+DROP FUNCTION interval_dist(interval, interval);
diff --git a/contrib/btree_gist/btree_gist--1.4.sql b/contrib/btree_gist/btree_gist--1.4.sql
new file mode 100644
index 0000000..1f1327d
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.4.sql
@@ -0,0 +1,1490 @@
+/* contrib/btree_gist/btree_gist--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
+
+CREATE FUNCTION gbtreekey4_in(cstring)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey4_out(gbtreekey4)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey4 (
+ INTERNALLENGTH = 4,
+ INPUT = gbtreekey4_in,
+ OUTPUT = gbtreekey4_out
+);
+
+CREATE FUNCTION gbtreekey8_in(cstring)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey8_out(gbtreekey8)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey8 (
+ INTERNALLENGTH = 8,
+ INPUT = gbtreekey8_in,
+ OUTPUT = gbtreekey8_out
+);
+
+CREATE FUNCTION gbtreekey16_in(cstring)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey16_out(gbtreekey16)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey16 (
+ INTERNALLENGTH = 16,
+ INPUT = gbtreekey16_in,
+ OUTPUT = gbtreekey16_out
+);
+
+CREATE FUNCTION gbtreekey32_in(cstring)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey32_out(gbtreekey32)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey32 (
+ INTERNALLENGTH = 32,
+ INPUT = gbtreekey32_in,
+ OUTPUT = gbtreekey32_out
+);
+
+CREATE FUNCTION gbtreekey_var_in(cstring)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey_var (
+ INTERNALLENGTH = VARIABLE,
+ INPUT = gbtreekey_var_in,
+ OUTPUT = gbtreekey_var_out,
+ STORAGE = EXTENDED
+);
+
+
+--
+--
+--
+-- oid ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_oid_ops
+DEFAULT FOR TYPE oid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
+ FUNCTION 2 gbt_oid_union (internal, internal),
+ FUNCTION 3 gbt_oid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_oid_picksplit (internal, internal),
+ FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+-- Add operators that are new in 9.1. We do it like this, leaving them
+-- "loose" in the operator family rather than bound into the opclass, because
+-- that's the only state that can be reproduced during an upgrade from 9.0.
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
+ OPERATOR 6 <> (oid, oid) ,
+ OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
+ FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
+ -- Also add support function for index-only-scans, added in 9.5.
+ FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
+
+
+--
+--
+--
+-- int2 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_union(internal, internal)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int2_ops
+DEFAULT FOR TYPE int2 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
+ FUNCTION 2 gbt_int2_union (internal, internal),
+ FUNCTION 3 gbt_int2_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int2_picksplit (internal, internal),
+ FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
+ STORAGE gbtreekey4;
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
+ OPERATOR 6 <> (int2, int2) ,
+ OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
+ FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
+
+--
+--
+--
+-- int4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int4_ops
+DEFAULT FOR TYPE int4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
+ FUNCTION 2 gbt_int4_union (internal, internal),
+ FUNCTION 3 gbt_int4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int4_picksplit (internal, internal),
+ FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
+ OPERATOR 6 <> (int4, int4) ,
+ OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
+ FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
+
+
+--
+--
+--
+-- int8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int8_ops
+DEFAULT FOR TYPE int8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
+ FUNCTION 2 gbt_int8_union (internal, internal),
+ FUNCTION 3 gbt_int8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int8_picksplit (internal, internal),
+ FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
+ OPERATOR 6 <> (int8, int8) ,
+ OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
+ FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
+
+--
+--
+--
+-- float4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float4_ops
+DEFAULT FOR TYPE float4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
+ FUNCTION 2 gbt_float4_union (internal, internal),
+ FUNCTION 3 gbt_float4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float4_picksplit (internal, internal),
+ FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
+ OPERATOR 6 <> (float4, float4) ,
+ OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
+ FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
+
+--
+--
+--
+-- float8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float8_ops
+DEFAULT FOR TYPE float8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
+ FUNCTION 2 gbt_float8_union (internal, internal),
+ FUNCTION 3 gbt_float8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float8_picksplit (internal, internal),
+ FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
+ OPERATOR 6 <> (float8, float8) ,
+ OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
+ FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
+
+--
+--
+--
+-- timestamp ops
+--
+--
+--
+
+CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamp_ops
+DEFAULT FOR TYPE timestamp USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_ts_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
+ OPERATOR 6 <> (timestamp, timestamp) ,
+ OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
+ FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamptz_ops
+DEFAULT FOR TYPE timestamptz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_tstz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
+ OPERATOR 6 <> (timestamptz, timestamptz) ,
+ OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
+ FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
+
+--
+--
+--
+-- time ops
+--
+--
+--
+
+CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_time_ops
+DEFAULT FOR TYPE time USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_time_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
+ OPERATOR 6 <> (time, time) ,
+ OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
+ FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
+
+
+CREATE OPERATOR CLASS gist_timetz_ops
+DEFAULT FOR TYPE timetz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_timetz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
+ OPERATOR 6 <> (timetz, timetz) ;
+ -- no 'fetch' function, as the compress function is lossy.
+
+
+--
+--
+--
+-- date ops
+--
+--
+--
+
+CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_date_ops
+DEFAULT FOR TYPE date USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
+ FUNCTION 2 gbt_date_union (internal, internal),
+ FUNCTION 3 gbt_date_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_date_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_date_picksplit (internal, internal),
+ FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
+ OPERATOR 6 <> (date, date) ,
+ OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
+ FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
+
+
+--
+--
+--
+-- interval ops
+--
+--
+--
+
+CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_interval_ops
+DEFAULT FOR TYPE interval USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
+ FUNCTION 2 gbt_intv_union (internal, internal),
+ FUNCTION 3 gbt_intv_compress (internal),
+ FUNCTION 4 gbt_intv_decompress (internal),
+ FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_intv_picksplit (internal, internal),
+ FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
+ OPERATOR 6 <> (interval, interval) ,
+ OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
+ FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
+
+
+--
+--
+--
+-- cash ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cash_ops
+DEFAULT FOR TYPE money USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
+ FUNCTION 2 gbt_cash_union (internal, internal),
+ FUNCTION 3 gbt_cash_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_cash_picksplit (internal, internal),
+ FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
+ OPERATOR 6 <> (money, money) ,
+ OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
+ FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
+ FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
+
+
+--
+--
+--
+-- macaddr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr_ops
+DEFAULT FOR TYPE macaddr USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
+ FUNCTION 2 gbt_macad_union (internal, internal),
+ FUNCTION 3 gbt_macad_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
+ OPERATOR 6 <> (macaddr, macaddr) ,
+ FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
+
+
+--
+--
+--
+-- text/ bpchar ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_text_ops
+DEFAULT FOR TYPE text USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_text_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
+ OPERATOR 6 <> (text, text) ,
+ FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
+
+
+---- Create the operator class
+CREATE OPERATOR CLASS gist_bpchar_ops
+DEFAULT FOR TYPE bpchar USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_bpchar_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
+ OPERATOR 6 <> (bpchar, bpchar) ,
+ FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
+
+--
+--
+-- bytea ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bytea_ops
+DEFAULT FOR TYPE bytea USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
+ FUNCTION 2 gbt_bytea_union (internal, internal),
+ FUNCTION 3 gbt_bytea_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bytea_picksplit (internal, internal),
+ FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
+ OPERATOR 6 <> (bytea, bytea) ,
+ FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- numeric ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_numeric_ops
+DEFAULT FOR TYPE numeric USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
+ FUNCTION 2 gbt_numeric_union (internal, internal),
+ FUNCTION 3 gbt_numeric_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_numeric_picksplit (internal, internal),
+ FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
+ OPERATOR 6 <> (numeric, numeric) ,
+ FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
+
+
+--
+--
+-- bit ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bit_ops
+DEFAULT FOR TYPE bit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
+ OPERATOR 6 <> (bit, bit) ,
+ FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
+
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_vbit_ops
+DEFAULT FOR TYPE varbit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
+ OPERATOR 6 <> (varbit, varbit) ,
+ FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- inet/cidr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_inet_ops
+DEFAULT FOR TYPE inet USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cidr_ops
+DEFAULT FOR TYPE cidr USING gist
+AS
+ OPERATOR 1 < (inet, inet) ,
+ OPERATOR 2 <= (inet, inet) ,
+ OPERATOR 3 = (inet, inet) ,
+ OPERATOR 4 >= (inet, inet) ,
+ OPERATOR 5 > (inet, inet) ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+--
+--
+--
+-- uuid ops
+--
+--
+---- define the GiST support methods
+CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_uuid_ops
+DEFAULT FOR TYPE uuid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal),
+ FUNCTION 2 gbt_uuid_union (internal, internal),
+ FUNCTION 3 gbt_uuid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_uuid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_uuid_picksplit (internal, internal),
+ FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+-- These are "loose" in the opfamily for consistency with the rest of btree_gist
+ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
+ OPERATOR 6 <> (uuid, uuid) ,
+ FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ;
+
diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control
index ddbf83d..fdf0e6a 100644
--- a/contrib/btree_gist/btree_gist.control
+++ b/contrib/btree_gist/btree_gist.control
@@ -1,5 +1,5 @@
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
-default_version = '1.3'
+default_version = '1.4'
module_pathname = '$libdir/btree_gist'
relocatable = true
diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c
index 54dc1cc..c3cee90 100644
--- a/contrib/btree_gist/btree_int2.c
+++ b/contrib/btree_gist/btree_int2.c
@@ -89,28 +89,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int2_dist);
-Datum
-int2_dist(PG_FUNCTION_ARGS)
-{
- int16 a = PG_GETARG_INT16(0);
- int16 b = PG_GETARG_INT16(1);
- int16 r;
- int16 ra;
-
- r = a - b;
- ra = Abs(r);
-
- /* Overflow check. */
- if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("smallint out of range")));
-
- PG_RETURN_INT16(ra);
-}
-
-
/**************************************************
* int16 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c
index ddbcf52..e79ab3e 100644
--- a/contrib/btree_gist/btree_int4.c
+++ b/contrib/btree_gist/btree_int4.c
@@ -90,28 +90,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int4_dist);
-Datum
-int4_dist(PG_FUNCTION_ARGS)
-{
- int32 a = PG_GETARG_INT32(0);
- int32 b = PG_GETARG_INT32(1);
- int32 r;
- int32 ra;
-
- r = a - b;
- ra = Abs(r);
-
- /* Overflow check. */
- if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
-
- PG_RETURN_INT32(ra);
-}
-
-
/**************************************************
* int32 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c
index 44bf69a..89b8430 100644
--- a/contrib/btree_gist/btree_int8.c
+++ b/contrib/btree_gist/btree_int8.c
@@ -90,28 +90,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int8_dist);
-Datum
-int8_dist(PG_FUNCTION_ARGS)
-{
- int64 a = PG_GETARG_INT64(0);
- int64 b = PG_GETARG_INT64(1);
- int64 r;
- int64 ra;
-
- r = a - b;
- ra = Abs(r);
-
- /* Overflow check. */
- if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
-
- PG_RETURN_INT64(ra);
-}
-
-
/**************************************************
* int64 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c
index acccb8b..b95a963 100644
--- a/contrib/btree_gist/btree_interval.c
+++ b/contrib/btree_gist/btree_interval.c
@@ -109,32 +109,6 @@ static const gbtree_ninfo tinfo =
};
-Interval *
-abs_interval(Interval *a)
-{
- static Interval zero = {0, 0, 0};
-
- if (DatumGetBool(DirectFunctionCall2(interval_lt,
- IntervalPGetDatum(a),
- IntervalPGetDatum(&zero))))
- a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
- IntervalPGetDatum(a)));
-
- return a;
-}
-
-PG_FUNCTION_INFO_V1(interval_dist);
-Datum
-interval_dist(PG_FUNCTION_ARGS)
-{
- Datum diff = DirectFunctionCall2(interval_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
-}
-
-
/**************************************************
* interval ops
**************************************************/
diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c
index ac61a76..d389fe2 100644
--- a/contrib/btree_gist/btree_oid.c
+++ b/contrib/btree_gist/btree_oid.c
@@ -96,22 +96,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(oid_dist);
-Datum
-oid_dist(PG_FUNCTION_ARGS)
-{
- Oid a = PG_GETARG_OID(0);
- Oid b = PG_GETARG_OID(1);
- Oid res;
-
- if (a < b)
- res = b - a;
- else
- res = a - b;
- PG_RETURN_OID(res);
-}
-
-
/**************************************************
* Oid ops
**************************************************/
diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c
index 41d9959..fd95f7e 100644
--- a/contrib/btree_gist/btree_time.c
+++ b/contrib/btree_gist/btree_time.c
@@ -136,18 +136,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(time_dist);
-Datum
-time_dist(PG_FUNCTION_ARGS)
-{
- Datum diff = DirectFunctionCall2(time_mi_time,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
-}
-
-
/**************************************************
* time ops
**************************************************/
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index ab22b27..899fbf4 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -139,63 +139,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(ts_dist);
-Datum
-ts_dist(PG_FUNCTION_ARGS)
-{
- Timestamp a = PG_GETARG_TIMESTAMP(0);
- Timestamp b = PG_GETARG_TIMESTAMP(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
-#ifdef HAVE_INT64_TIMESTAMP
- p->time = PG_INT64_MAX;
-#else
- p->time = DBL_MAX;
-#endif
- PG_RETURN_INTERVAL_P(p);
- }
- else
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
-}
-
-PG_FUNCTION_INFO_V1(tstz_dist);
-Datum
-tstz_dist(PG_FUNCTION_ARGS)
-{
- TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
- TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
-#ifdef HAVE_INT64_TIMESTAMP
- p->time = PG_INT64_MAX;
-#else
- p->time = DBL_MAX;
-#endif
- PG_RETURN_INTERVAL_P(p);
- }
-
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
-}
-
-
/**************************************************
* timestamp ops
**************************************************/
diff --git a/contrib/btree_gist/btree_utils_num.h b/contrib/btree_gist/btree_utils_num.h
index a33491b..8f873a5 100644
--- a/contrib/btree_gist/btree_utils_num.h
+++ b/contrib/btree_gist/btree_utils_num.h
@@ -116,8 +116,6 @@ do { \
} while(0)
-extern Interval *abs_interval(Interval *a);
-
extern bool gbt_num_consistent(const GBT_NUMKEY_R *key, const void *query,
const StrategyNumber *strategy, bool is_leaf,
const gbtree_ninfo *tinfo);
0007-add-regression-tests-for-kNN-btree-v01.patchtext/x-patch; name=0007-add-regression-tests-for-kNN-btree-v01.patchDownload
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 755cd17..3ab1ad7 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -150,3 +150,489 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+---
+--- Test B-tree distance ordering
+---
+SET enable_bitmapscan = OFF;
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+ QUERY PLAN
+------------------------------
+ Sort
+ Sort Key: ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+ QUERY PLAN
+------------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((random <-> 1))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)))
+ Order By: (random <-> 4000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 1 | 9001
+ 1 | 8001
+ 1 | 7001
+ 1 | 6001
+ 1 | 5001
+ 1 | 4001
+ 1 | 3001
+ 1 | 2001
+ 1 | 1001
+ 1 | 1
+ 0 | 9000
+ 0 | 8000
+ 0 | 7000
+ 0 | 6000
+ 0 | 5000
+ 0 | 4000
+ 0 | 3000
+ 0 | 2000
+ 0 | 1000
+ 0 | 0
+ | 1
+ | 2
+ | 3
+(33 rows)
+
+DROP INDEX tenk3_idx;
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 1 | 1
+ 1 | 1001
+ 1 | 2001
+ 1 | 3001
+ 1 | 4001
+ 1 | 5001
+ 1 | 6001
+ 1 | 7001
+ 1 | 8001
+ 1 | 9001
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 0 | 0
+ 0 | 1000
+ 0 | 2000
+ 0 | 3000
+ 0 | 4000
+ 0 | 5000
+ 0 | 6000
+ 0 | 7000
+ 0 | 8000
+ 0 | 9000
+ | 3
+ | 2
+ | 1
+(33 rows)
+
+DROP INDEX tenk3_idx;
+DROP TABLE tenk3;
+RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 65b08c8..b0559d7 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -92,3 +92,108 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+
+---
+--- Test B-tree distance ordering
+---
+
+SET enable_bitmapscan = OFF;
+
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+
+
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+DROP TABLE tenk3;
+
+RESET enable_bitmapscan;
Sorry for the broken formatting in my previous message.
Below is a corrected version of this message.
I'd like to present a series of patches that implements k-Nearest Neighbors
(kNN) search for btree, which can be used to speed up ORDER BY distance
queries like this:
SELECT * FROM events ORDER BY date <-> '2000-01-01'::date ASC LIMIT 100;
Now only GiST supports kNN, but kNN on btree can be emulated using
contrib/btree_gist.
Scanning algorithm
==================
Algorithm is very simple: we use bidirectional B-tree index scan starting at
the point from which we measure the distance (target point). At each step,
we advance this scan in the direction that has the nearest point. But when
the target point does not fall into the scanned range, we don't even need to
use a bidirectional scan here --- we can use ordinary unidirectional scan
in the right direction.
Performance results
===================
Test database is taken from original kNN-GiST presentation (PGCon 2010).
Test query
SELECT * FROM events ORDER BY date <-> '1957-10-04'::date ASC LIMIT k;
can be optimized to the next rather complicated UNION form,
which no longer requires kNN:
WITH
t1 AS (SELECT * FROM events WHERE date >= '1957-10-04'::date
ORDER BY date ASC LIMIT k),
t2 AS (SELECT * FROM events WHERE date < '1957-10-04'::date
ORDER BY date DESC LIMIT k),
t AS (SELECT * FROM t1 UNION SELECT * FROM t2)
SELECT * FROM t ORDER BY date <-> '1957-10-04'::date ASC LIMIT k;
In each cell of this table shown query execution time in milliseconds and
the number of accessed blocks:
k | kNN-btree | kNN-GiST | Opt. query | Seq. scan
| | (btree_gist) | with UNION | with sort
--------|--------------|--------------|---------------|------------
1 | 0.041 4 | 0.079 4 | 0.060 8 | 41.1 1824
10 | 0.048 7 | 0.091 9 | 0.097 17 | 41.8 1824
100 | 0.107 47 | 0.192 52 | 0.342 104 | 42.3 1824
1000 | 0.735 573 | 0.913 650 | 2.970 1160 | 43.5 1824
10000 | 5.070 5622 | 6.240 6760 | 36.300 11031 | 54.1 1824
100000 | 49.600 51608 | 61.900 64194 | 295.100 94980 | 115.0 1824
As you can see, kNN-btree can be two times faster than kNN-GiST (btree_gist)
when k < 1000, but the number of blocks read is roughly the same.
Implementation details
======================
A brief description is given below for each of the patches:
1. Introduce amcanorderbyop() function
This patch transforms existing boolean AM property amcanorderbyop into a method
(function pointer). This is necessary because, unlike GiST, kNN for btree
supports only a one ordering operator on the first index column and we need a
different pathkey matching logic for btree (there was a corresponding comment
in match_pathkeys_to_index()). GiST-specific logic has been moved from
match_pathkeys_to_index() to gistcanorderbyop().
2. Extract substructure BTScanState from BTScanOpaque
This refactoring is necessary for bidirectional kNN-scan implementation.
Now, BTScanOpaque's substructure BTScanState containing only the fields
related to scan position is passed to some functions where the whole
BTScanOpaque was passed previously.
3. Extract get_index_column_opclass(), get_opclass_opfamily_and_input_type().
Extracted two simple common functions used in gistproperty() and
btproperty() (see the next patch).
4. Add kNN support to btree
* Added additional optional BTScanState to BTScanOpaque for
bidirectional kNN scan.
* Implemented bidirectional kNN scan.
* Implemented logic for selecting kNN strategy
* Implemented btcanorderbyop(), updated btproperty() and btvalidate()
B-tree user interface functions have not been altered because ordering
operators are used directly.
5. Add distance operators for some types
These operators for integer, float, date, time, timestamp, interval, cash and
oid types have been copied from contrib/btree_gist and added to the existing
btree opclasses as ordering operators. Their btree_gist duplicates are removed
in the next patch.
6. Remove duplicate distance operators from contrib/btree_gist.
References to their own distance operators in btree_gist opclasses are
replaced with references to the built-in operators and than duplicate
operators are dropped. But if the user is using somewhere these operators,
upgrade of btree_gist from 1.3 to 1.4 would fail.
7. Add regression tests for btree kNN.
Tests were added only after the built-in distance operators were added.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attached v02 version of patches (rebased onto HEAD).
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-introduce-amcanorderby-function-v02.patchtext/x-patch; name=0001-introduce-amcanorderby-function-v02.patchDownload
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 858798d..0b69a2a 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,7 +109,6 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BLOOM_NSTRATEGIES;
amroutine->amsupport = BLOOM_NPROC;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -141,6 +140,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index b2afdb7..22a2be7 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -83,7 +83,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -115,6 +114,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 02d920b..8c33f70 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -39,7 +39,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GINNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -71,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index c2247ad..00a0c55 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -60,7 +60,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GISTNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -92,6 +91,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amcanorderbyop = gistcanorderbyop;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index f92baed..159987e 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -19,6 +19,7 @@
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/pg_opclass.h"
+#include "optimizer/paths.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
@@ -964,3 +965,31 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+/* gistcanorderbyop() */
+Expr *
+gistcanorderbyop(IndexOptInfo *index, PathKey *pathkey, int pathkeyno,
+ Expr *orderby_clause, int *indexcol_p)
+{
+ int indexcol;
+
+ /*
+ * We allow any column of the GiST index to match each pathkey;
+ * they don't have to match left-to-right as you might expect.
+ */
+
+ for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
+ {
+ Expr *expr = match_clause_to_ordering_op(index,
+ indexcol,
+ orderby_clause,
+ pathkey->pk_opfamily);
+ if (expr)
+ {
+ *indexcol_p = indexcol;
+ return expr;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index ec8ed33..e98edd7 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -57,7 +57,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = HTMaxStrategyNumber;
amroutine->amsupport = HASHNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -89,6 +88,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 469e7ab..b4e8d44 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -89,7 +89,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BTMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = true;
amroutine->amcanmulticol = true;
@@ -121,6 +120,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 78846be..675118e 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -39,7 +39,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = SPGISTNProc;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -71,6 +70,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 5283468..21659a6 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -17,6 +17,7 @@
#include <math.h>
+#include "access/amapi.h"
#include "access/stratnum.h"
#include "access/sysattr.h"
#include "catalog/pg_am.h"
@@ -166,8 +167,6 @@ static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
List **orderby_clauses_p,
List **clause_columns_p);
-static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
- int indexcol, Expr *clause, Oid pk_opfamily);
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
EquivalenceClass *ec, EquivalenceMember *em,
void *arg);
@@ -2481,12 +2480,15 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
List *orderby_clauses = NIL;
List *clause_columns = NIL;
ListCell *lc1;
+ int pathkeyno = 1;
+ amcanorderbyop_function amcanorderbyop =
+ (amcanorderbyop_function) index->amcanorderbyop;
*orderby_clauses_p = NIL; /* set default results */
*clause_columns_p = NIL;
- /* Only indexes with the amcanorderbyop property are interesting here */
- if (!index->amcanorderbyop)
+ /* Only indexes with the amcanorderbyop function are interesting here */
+ if (!amcanorderbyop)
return;
foreach(lc1, pathkeys)
@@ -2520,42 +2522,29 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
foreach(lc2, pathkey->pk_eclass->ec_members)
{
EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
+ Expr *expr;
int indexcol;
/* No possibility of match if it references other relations */
if (!bms_equal(member->em_relids, index->rel->relids))
continue;
- /*
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, which is the sole existing AM supporting
- * amcanorderbyop. We might need different logic in future for
- * other implementations.
- */
- for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
- {
- Expr *expr;
+ expr = amcanorderbyop(index, pathkey, pathkeyno, member->em_expr,
+ &indexcol);
- expr = match_clause_to_ordering_op(index,
- indexcol,
- member->em_expr,
- pathkey->pk_opfamily);
- if (expr)
- {
- orderby_clauses = lappend(orderby_clauses, expr);
- clause_columns = lappend_int(clause_columns, indexcol);
- found = true;
- break;
- }
+ if (expr)
+ {
+ orderby_clauses = lappend(orderby_clauses, expr);
+ clause_columns = lappend_int(clause_columns, indexcol);
+ found = true;
+ break; /* don't want to look at remaining members */
}
-
- if (found) /* don't want to look at remaining members */
- break;
}
if (!found) /* fail if no match for this pathkey */
return;
+
+ pathkeyno++;
}
*orderby_clauses_p = orderby_clauses; /* success! */
@@ -2587,7 +2576,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
* If successful, return 'clause' as-is if the indexkey is on the left,
* otherwise a commuted copy of 'clause'. If no match, return NULL.
*/
-static Expr *
+Expr *
match_clause_to_ordering_op(IndexOptInfo *index,
int indexcol,
Expr *clause,
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 7836e6b..93bda0f 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -237,7 +237,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* We copy just the fields we need, not all of rd_amroutine */
amroutine = indexRelation->rd_amroutine;
- info->amcanorderbyop = amroutine->amcanorderbyop;
+ info->amcanorderbyop = (void (*)()) amroutine->amcanorderbyop;
info->amoptionalkey = amroutine->amoptionalkey;
info->amsearcharray = amroutine->amsearcharray;
info->amsearchnulls = amroutine->amsearchnulls;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index e91e41d..aae6c36 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -21,6 +21,9 @@
*/
struct PlannerInfo;
struct IndexPath;
+struct IndexOptInfo;
+struct PathKey;
+struct Expr;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
@@ -137,6 +140,13 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* does AM support ORDER BY result of an operator on indexed column? */
+typedef struct Expr *(*amcanorderbyop_function) (struct IndexOptInfo *index,
+ struct PathKey *pathkey,
+ int pathkeyno,
+ struct Expr *orderby_clause,
+ int *indexcol_p);
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -167,8 +177,6 @@ typedef struct IndexAmRoutine
uint16 amsupport;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
- /* does AM support ORDER BY result of an operator on indexed column? */
- bool amcanorderbyop;
/* does AM support backward scanning? */
bool amcanbackward;
/* does AM support UNIQUE indexes? */
@@ -208,7 +216,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
-
+ amcanorderbyop_function amcanorderbyop; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
aminitparallelscan_function aminitparallelscan; /* can be NULL */
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 60a770a..87ecbc2 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -537,6 +537,11 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
extern XLogRecPtr gistGetFakeLSN(Relation rel);
+extern struct Expr *gistcanorderbyop(struct IndexOptInfo *index,
+ struct PathKey *pathkey, int pathkeyno,
+ struct Expr *orderby_clause,
+ int *indexcol_p);
+
/* gistvacuum.c */
extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 643be54..01501c9 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -623,7 +623,6 @@ typedef struct IndexOptInfo
bool hypothetical; /* true if index doesn't really exist */
/* Remaining fields are copied from the index AM's API struct: */
- bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
@@ -631,6 +630,7 @@ typedef struct IndexOptInfo
bool amhasgetbitmap; /* does AM have amgetbitmap interface? */
/* Rather than include amapi.h here, we declare amcostestimate like this */
void (*amcostestimate) (); /* AM's cost estimator */
+ void (*amcanorderbyop) (); /* does AM support order by operator result? */
} IndexOptInfo;
/*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 81a9be7..bb7611e 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -79,6 +79,10 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
int indexcol,
List **indexcolnos,
bool *var_on_left_p);
+extern Expr *match_clause_to_ordering_op(IndexOptInfo *index,
+ int indexcol,
+ Expr *clause,
+ Oid pk_opfamily);
/*
* tidpath.h
0002-extract-structure-BTScanState-v02.patchtext/x-patch; name=0002-extract-structure-BTScanState-v02.patchDownload
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index b4e8d44..c2da15e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -299,6 +299,7 @@ bool
btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
bool res;
/* btree indexes are never lossy */
@@ -309,7 +310,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(so->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
@@ -326,7 +327,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(so->currPos))
+ if (!BTScanPosIsValid(state->currPos))
res = _bt_first(scan, dir);
else
{
@@ -344,11 +345,11 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* trying to optimize that, so we don't detect it, but instead
* just forget any excess entries.
*/
- if (so->killedItems == NULL)
- so->killedItems = (int *)
+ if (state->killedItems == NULL)
+ state->killedItems = (int *)
palloc(MaxIndexTuplesPerPage * sizeof(int));
- if (so->numKilled < MaxIndexTuplesPerPage)
- so->killedItems[so->numKilled++] = so->currPos.itemIndex;
+ if (state->numKilled < MaxIndexTuplesPerPage)
+ state->killedItems[so->state.numKilled++] = state->currPos.itemIndex;
}
/*
@@ -373,6 +374,7 @@ int64
btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
int64 ntids = 0;
ItemPointer heapTid;
@@ -405,7 +407,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* Advance to next tuple within page. This is the same as the
* easy case in _bt_next().
*/
- if (++so->currPos.itemIndex > so->currPos.lastItem)
+ if (++currPos->itemIndex > currPos->lastItem)
{
/* let _bt_next do the heavy lifting */
if (!_bt_next(scan, ForwardScanDirection))
@@ -413,7 +415,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
/* Save tuple ID, and continue scanning */
- heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
+ heapTid = &currPos->items[currPos->itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
}
@@ -441,8 +443,8 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
/* allocate private workspace */
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
- BTScanPosInvalidate(so->currPos);
- BTScanPosInvalidate(so->markPos);
+ BTScanPosInvalidate(so->state.currPos);
+ BTScanPosInvalidate(so->state.markPos);
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
else
@@ -453,15 +455,15 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
so->arrayKeys = NULL;
so->arrayContext = NULL;
- so->killedItems = NULL; /* until needed */
- so->numKilled = 0;
+ so->state.killedItems = NULL; /* until needed */
+ so->state.numKilled = 0;
/*
* We don't know yet whether the scan will be index-only, so we do not
* allocate the tuple workspace arrays until btrescan. However, we set up
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
- so->currTuples = so->markTuples = NULL;
+ so->state.currTuples = so->state.markTuples = NULL;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -470,6 +472,45 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
return scan;
}
+static void
+_bt_release_current_position(BTScanState state, Relation indexRelation,
+ bool invalidate)
+{
+ /* we aren't holding any read locks, but gotta drop the pins */
+ if (BTScanPosIsValid(state->currPos))
+ {
+ /* Before leaving current page, deal with any killed items */
+ if (state->numKilled > 0)
+ _bt_killitems(state, indexRelation);
+
+ BTScanPosUnpinIfPinned(state->currPos);
+
+ if (invalidate)
+ BTScanPosInvalidate(state->currPos);
+ }
+}
+
+static void
+_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
+{
+ /* No need to invalidate positions, if the RAM is about to be freed. */
+ _bt_release_current_position(state, scan->indexRelation, !free);
+
+ state->markItemIndex = -1;
+ BTScanPosUnpinIfPinned(state->markPos);
+
+ if (free)
+ {
+ if (state->killedItems != NULL)
+ pfree(state->killedItems);
+ if (state->currTuples != NULL)
+ pfree(state->currTuples);
+ /* markTuples should not be pfree'd (_bt_allocate_tuple_workspaces) */
+ }
+ else
+ BTScanPosInvalidate(state->markPos);
+}
+
/*
* btrescan() -- rescan an index relation
*/
@@ -478,20 +519,9 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
ScanKey orderbys, int norderbys)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
- BTScanPosInvalidate(so->markPos);
+ _bt_release_scan_state(scan, state, false);
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
@@ -509,11 +539,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
* a SIGSEGV is not possible. Yeah, this is ugly as sin, but it beats
* adding special-case treatment for name_ops elsewhere.
*/
- if (scan->xs_want_itup && so->currTuples == NULL)
- {
- so->currTuples = (char *) palloc(BLCKSZ * 2);
- so->markTuples = so->currTuples + BLCKSZ;
- }
+ if (scan->xs_want_itup && state->currTuples == NULL)
+ _bt_allocate_tuple_workspaces(state);
/*
* Reset the scan keys. Note that keys ordering stuff moved to _bt_first.
@@ -537,19 +564,7 @@ btendscan(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
-
- /* No need to invalidate positions, the RAM is about to be freed. */
+ _bt_release_scan_state(scan, &so->state, true);
/* Release storage */
if (so->keyData != NULL)
@@ -557,24 +572,15 @@ btendscan(IndexScanDesc scan)
/* so->arrayKeyData and so->arrayKeys are in arrayContext */
if (so->arrayContext != NULL)
MemoryContextDelete(so->arrayContext);
- if (so->killedItems != NULL)
- pfree(so->killedItems);
- if (so->currTuples != NULL)
- pfree(so->currTuples);
- /* so->markTuples should not be pfree'd, see btrescan */
+
pfree(so);
}
-/*
- * btmarkpos() -- save current scan position
- */
-void
-btmarkpos(IndexScanDesc scan)
+static void
+_bt_mark_current_position(BTScanState state)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
-
/* There may be an old mark with a pin (but no lock). */
- BTScanPosUnpinIfPinned(so->markPos);
+ BTScanPosUnpinIfPinned(state->markPos);
/*
* Just record the current itemIndex. If we later step to next page
@@ -582,32 +588,34 @@ btmarkpos(IndexScanDesc scan)
* the currPos struct in markPos. If (as often happens) the mark is moved
* before we leave the page, we don't have to do that work.
*/
- if (BTScanPosIsValid(so->currPos))
- so->markItemIndex = so->currPos.itemIndex;
+ if (BTScanPosIsValid(state->currPos))
+ state->markItemIndex = state->currPos.itemIndex;
else
{
- BTScanPosInvalidate(so->markPos);
- so->markItemIndex = -1;
+ BTScanPosInvalidate(state->markPos);
+ state->markItemIndex = -1;
}
-
- /* Also record the current positions of any array keys */
- if (so->numArrayKeys)
- _bt_mark_array_keys(scan);
}
/*
- * btrestrpos() -- restore scan to last saved position
+ * btmarkpos() -- save current scan position
*/
void
-btrestrpos(IndexScanDesc scan)
+btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* Restore the marked positions of any array keys */
+ _bt_mark_current_position(&so->state);
+
+ /* Also record the current positions of any array keys */
if (so->numArrayKeys)
- _bt_restore_array_keys(scan);
+ _bt_mark_array_keys(scan);
+}
- if (so->markItemIndex >= 0)
+static void
+_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
+{
+ if (state->markItemIndex >= 0)
{
/*
* The scan has never moved to a new page since the last mark. Just
@@ -616,7 +624,7 @@ btrestrpos(IndexScanDesc scan)
* NB: In this case we can't count on anything in so->markPos to be
* accurate.
*/
- so->currPos.itemIndex = so->markItemIndex;
+ state->currPos.itemIndex = state->markItemIndex;
}
else
{
@@ -626,32 +634,40 @@ btrestrpos(IndexScanDesc scan)
* locks, but if we're still holding the pin for the current position,
* we must drop it.
*/
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
+ _bt_release_current_position(state, scan->indexRelation,
+ !BTScanPosIsValid(state->markPos));
- if (BTScanPosIsValid(so->markPos))
+ if (BTScanPosIsValid(state->markPos))
{
/* bump pin on mark buffer for assignment to current buffer */
- if (BTScanPosIsPinned(so->markPos))
- IncrBufferRefCount(so->markPos.buf);
- memcpy(&so->currPos, &so->markPos,
+ if (BTScanPosIsPinned(state->markPos))
+ IncrBufferRefCount(state->markPos.buf);
+ memcpy(&state->currPos, &state->markPos,
offsetof(BTScanPosData, items[1]) +
- so->markPos.lastItem * sizeof(BTScanPosItem));
- if (so->currTuples)
- memcpy(so->currTuples, so->markTuples,
- so->markPos.nextTupleOffset);
+ state->markPos.lastItem * sizeof(BTScanPosItem));
+ if (state->currTuples)
+ memcpy(state->currTuples, state->markTuples,
+ state->markPos.nextTupleOffset);
}
- else
- BTScanPosInvalidate(so->currPos);
}
}
/*
+ * btrestrpos() -- restore scan to last saved position
+ */
+void
+btrestrpos(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
+ /* Restore the marked positions of any array keys */
+ if (so->numArrayKeys)
+ _bt_restore_array_keys(scan);
+
+ _bt_restore_marked_position(scan, &so->state);
+}
+
+/*
* Bulk deletion of all index entries pointing to a set of heap tuples.
* The set of target tuples is specified via a callback routine that tells
* whether any given heap tuple (identified by ItemPointer) is being deleted.
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index b6459d2..c041056 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -25,11 +25,11 @@
#include "utils/tqual.h"
-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir,
+static bool _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
OffsetNumber offnum);
-static void _bt_saveitem(BTScanOpaque so, int itemIndex,
+static void _bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup);
-static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
+static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
@@ -509,6 +509,58 @@ _bt_compare(Relation rel,
}
/*
+ * _bt_return_current_item() -- Prepare current scan state item for return.
+ *
+ * This function is used only in "return _bt_return_current_item();" statements
+ * and always returns true.
+ */
+static inline bool
+_bt_return_current_item(IndexScanDesc scan, BTScanState state)
+{
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+
+ scan->xs_ctup.t_self = currItem->heapTid;
+
+ if (scan->xs_want_itup)
+ scan->xs_itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+
+ return true;
+}
+
+/*
+ * _bt_load_first_page() -- Load data from the first page of the scan.
+ *
+ * Caller must have pinned and read-locked state->currPos.buf.
+ *
+ * On success exit, state->currPos is updated to contain data from the next
+ * interesting page. For success on a scan using a non-MVCC snapshot we hold
+ * a pin, but not a read lock, on that page. If we do not hold the pin, we
+ * set state->currPos.buf to InvalidBuffer. We return true to indicate success.
+ *
+ * If there are no more matching records in the given direction at all,
+ * we drop all locks and pins, set state->currPos.buf to InvalidBuffer,
+ * and return false.
+ */
+static bool
+_bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
+{
+ if (!_bt_readpage(scan, state, dir, offnum))
+ {
+ /*
+ * There's no actually-matching data on this page. Try to advance to
+ * the next page. Return false if there's no matching data at all.
+ */
+ LockBuffer(state->currPos.buf, BUFFER_LOCK_UNLOCK);
+ return _bt_steppage(scan, state, dir);
+ }
+
+ /* Drop the lock, and maybe the pin, on the current page */
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
+ return true;
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -533,6 +585,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
BTStack stack;
OffsetNumber offnum;
@@ -545,9 +598,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
int keysCount = 0;
int i;
StrategyNumber strat_total;
- BTScanPosItem *currItem;
- Assert(!BTScanPosIsValid(so->currPos));
+ Assert(!BTScanPosIsValid(*currPos));
pgstat_count_index_scan(rel);
@@ -1002,16 +1054,16 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ currPos->moreLeft = false;
+ currPos->moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ currPos->moreLeft = true;
+ currPos->moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- Assert(so->markItemIndex == -1);
+ so->state.numKilled = 0; /* just paranoia */
+ Assert(so->state.markItemIndex == -1);
/* position to the precise item on the page */
offnum = _bt_binsrch(rel, buf, keysCount, scankeys, nextkey);
@@ -1038,35 +1090,35 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
offnum = OffsetNumberPrev(offnum);
/* remember which buffer we have pinned, if any */
- Assert(!BTScanPosIsValid(so->currPos));
- so->currPos.buf = buf;
+ Assert(!BTScanPosIsValid(*currPos));
+ currPos->buf = buf;
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, offnum))
+ if (!_bt_load_first_page(scan, &so->state, dir, offnum))
+ return false;
+
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
+}
+
+/*
+ * Advance to next tuple on current page; or if there's no more,
+ * try to step to the next page with data.
+ */
+static bool
+_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
+{
+ if (ScanDirectionIsForward(dir))
{
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
+ if (++state->currPos.itemIndex <= state->currPos.lastItem)
+ return true;
}
else
{
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ if (--state->currPos.itemIndex >= state->currPos.firstItem)
+ return true;
}
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_steppage(scan, state, dir);
}
/*
@@ -1087,44 +1139,20 @@ bool
_bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- BTScanPosItem *currItem;
- /*
- * Advance to next tuple on current page; or if there's no more, try to
- * step to the next page with data.
- */
- if (ScanDirectionIsForward(dir))
- {
- if (++so->currPos.itemIndex > so->currPos.lastItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
- else
- {
- if (--so->currPos.itemIndex < so->currPos.firstItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
+ if (!_bt_next_item(scan, &so->state, dir))
+ return false;
/* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_return_current_item(scan, &so->state);
}
/*
* _bt_readpage() -- Load data from current index page into so->currPos
*
- * Caller must have pinned and read-locked so->currPos.buf; the buffer's state
- * is not changed here. Also, currPos.moreLeft and moreRight must be valid;
- * they are updated as appropriate. All other fields of so->currPos are
+ * Caller must have pinned and read-locked pos->buf; the buffer's state
+ * is not changed here. Also, pos->moreLeft and moreRight must be valid;
+ * they are updated as appropriate. All other fields of pos are
* initialized from scratch here.
*
* We scan the current page starting at offnum and moving in the indicated
@@ -1135,9 +1163,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
* Returns true if any matching items found on the page, false if none.
*/
static bool
-_bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
+_bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
@@ -1150,9 +1179,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We must have the buffer pinned and locked, but the usual macro can't be
* used here; this function is what makes it good for currPos.
*/
- Assert(BufferIsValid(so->currPos.buf));
+ Assert(BufferIsValid(pos->buf));
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
@@ -1161,30 +1190,30 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We note the buffer's block number so that we can release the pin later.
* This allows us to re-read the buffer if it is needed again for hinting.
*/
- so->currPos.currPage = BufferGetBlockNumber(so->currPos.buf);
+ pos->currPage = BufferGetBlockNumber(pos->buf);
/*
* We save the LSN of the page as we read it, so that we know whether it
* safe to apply LP_DEAD hints to the page later. This allows us to drop
* the pin for MVCC scans, which allows vacuum to avoid blocking.
*/
- so->currPos.lsn = PageGetLSN(page);
+ pos->lsn = PageGetLSN(page);
/*
* we must save the page's right-link while scanning it; this tells us
* where to step right to after we're done with these items. There is no
* corresponding need for the left-link, since splits always go right.
*/
- so->currPos.nextPage = opaque->btpo_next;
+ pos->nextPage = opaque->btpo_next;
/* initialize tuple workspace to empty */
- so->currPos.nextTupleOffset = 0;
+ pos->nextTupleOffset = 0;
/*
* Now that the current page has been made consistent, the macro should be
* good.
*/
- Assert(BTScanPosIsPinned(so->currPos));
+ Assert(BTScanPosIsPinned(*pos));
if (ScanDirectionIsForward(dir))
{
@@ -1199,13 +1228,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (itup != NULL)
{
/* tuple passes all scan key conditions, so remember it */
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
itemIndex++;
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreRight = false;
+ pos->moreRight = false;
break;
}
@@ -1213,9 +1242,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex <= MaxIndexTuplesPerPage);
- so->currPos.firstItem = 0;
- so->currPos.lastItem = itemIndex - 1;
- so->currPos.itemIndex = 0;
+ pos->firstItem = 0;
+ pos->lastItem = itemIndex - 1;
+ pos->itemIndex = 0;
}
else
{
@@ -1231,12 +1260,12 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
{
/* tuple passes all scan key conditions, so remember it */
itemIndex--;
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreLeft = false;
+ pos->moreLeft = false;
break;
}
@@ -1244,30 +1273,31 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex >= 0);
- so->currPos.firstItem = itemIndex;
- so->currPos.lastItem = MaxIndexTuplesPerPage - 1;
- so->currPos.itemIndex = MaxIndexTuplesPerPage - 1;
+ pos->firstItem = itemIndex;
+ pos->lastItem = MaxIndexTuplesPerPage - 1;
+ pos->itemIndex = MaxIndexTuplesPerPage - 1;
}
- return (so->currPos.firstItem <= so->currPos.lastItem);
+ return (pos->firstItem <= pos->lastItem);
}
/* Save an index item into so->currPos.items[itemIndex] */
static void
-_bt_saveitem(BTScanOpaque so, int itemIndex,
+_bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup)
{
- BTScanPosItem *currItem = &so->currPos.items[itemIndex];
+ BTScanPosItem *currItem = &state->currPos.items[itemIndex];
currItem->heapTid = itup->t_tid;
currItem->indexOffset = offnum;
- if (so->currTuples)
+ if (state->currTuples)
{
Size itupsz = IndexTupleSize(itup);
- currItem->tupleOffset = so->currPos.nextTupleOffset;
- memcpy(so->currTuples + so->currPos.nextTupleOffset, itup, itupsz);
- so->currPos.nextTupleOffset += MAXALIGN(itupsz);
+ currItem->tupleOffset = state->currPos.nextTupleOffset;
+ memcpy(state->currTuples + state->currPos.nextTupleOffset,
+ itup, itupsz);
+ state->currPos.nextTupleOffset += MAXALIGN(itupsz);
}
}
@@ -1287,65 +1317,63 @@ _bt_saveitem(BTScanOpaque so, int itemIndex,
* locks and pins, set so->currPos.buf to InvalidBuffer, and return FALSE.
*/
static bool
-_bt_steppage(IndexScanDesc scan, ScanDirection dir)
+_bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
- Relation rel;
+ BTScanPos currPos = &state->currPos;
+ Relation rel = scan->indexRelation;
Page page;
BTPageOpaque opaque;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(*currPos));
/* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
+ if (state->numKilled > 0)
+ _bt_killitems(state, rel);
/*
* Before we modify currPos, make a copy of the page data if there was a
* mark position that needs it.
*/
- if (so->markItemIndex >= 0)
+ if (state->markItemIndex >= 0)
{
/* bump pin on current buffer for assignment to mark buffer */
- if (BTScanPosIsPinned(so->currPos))
- IncrBufferRefCount(so->currPos.buf);
- memcpy(&so->markPos, &so->currPos,
+ if (BTScanPosIsPinned(*currPos))
+ IncrBufferRefCount(currPos->buf);
+ memcpy(&state->markPos, currPos,
offsetof(BTScanPosData, items[1]) +
- so->currPos.lastItem * sizeof(BTScanPosItem));
- if (so->markTuples)
- memcpy(so->markTuples, so->currTuples,
- so->currPos.nextTupleOffset);
- so->markPos.itemIndex = so->markItemIndex;
- so->markItemIndex = -1;
+ currPos->lastItem * sizeof(BTScanPosItem));
+ if (state->markTuples)
+ memcpy(state->markTuples, state->currTuples,
+ currPos->nextTupleOffset);
+ state->markPos.itemIndex = state->markItemIndex;
+ state->markItemIndex = -1;
}
- rel = scan->indexRelation;
-
if (ScanDirectionIsForward(dir))
{
/* Walk right to the next page with data */
/* We must rely on the previously saved nextPage link! */
- BlockNumber blkno = so->currPos.nextPage;
+ BlockNumber blkno = currPos->nextPage;
/* Remember we left a page with data */
- so->currPos.moreLeft = true;
+ currPos->moreLeft = true;
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
for (;;)
{
/* if we're at end of scan, give up */
- if (blkno == P_NONE || !so->currPos.moreRight)
+ if (blkno == P_NONE || !currPos->moreRight)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* check for interrupts while we're not holding any buffer lock */
CHECK_FOR_INTERRUPTS();
/* step right one page */
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
- page = BufferGetPage(so->currPos.buf);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* check for deleted page */
@@ -1354,19 +1382,19 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
PredicateLockPage(rel, blkno, scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreRight if we can stop */
- if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque)))
+ if (_bt_readpage(scan, state, dir, P_FIRSTDATAKEY(opaque)))
break;
}
/* nope, keep going */
blkno = opaque->btpo_next;
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
}
}
else
{
/* Remember we left a page with data */
- so->currPos.moreRight = true;
+ currPos->moreRight = true;
/*
* Walk left to the next page with data. This is much more complex
@@ -1390,29 +1418,28 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* is MVCC the page cannot move past the half-dead state to fully
* deleted.
*/
- if (BTScanPosIsPinned(so->currPos))
- LockBuffer(so->currPos.buf, BT_READ);
+ if (BTScanPosIsPinned(*currPos))
+ LockBuffer(currPos->buf, BT_READ);
else
- so->currPos.buf = _bt_getbuf(rel, so->currPos.currPage, BT_READ);
+ currPos->buf = _bt_getbuf(rel, currPos->currPage, BT_READ);
for (;;)
{
/* Done if we know there are no matching keys to the left */
- if (!so->currPos.moreLeft)
+ if (!currPos->moreLeft)
{
- _bt_relbuf(rel, so->currPos.buf);
- BTScanPosInvalidate(so->currPos);
+ _bt_relbuf(rel, currPos->buf);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* Step to next physical page */
- so->currPos.buf = _bt_walk_left(rel, so->currPos.buf,
- scan->xs_snapshot);
+ currPos->buf = _bt_walk_left(rel, currPos->buf, scan->xs_snapshot);
/* if we're physically at end of index, return failure */
- if (so->currPos.buf == InvalidBuffer)
+ if (currPos->buf == InvalidBuffer)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1421,22 +1448,22 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* it's not half-dead and contains matching tuples. Else loop back
* and do it all again.
*/
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
{
- PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
+ PredicateLockPage(rel, BufferGetBlockNumber(currPos->buf), scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreLeft if we can stop */
- if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page)))
+ if (_bt_readpage(scan, state, dir, PageGetMaxOffsetNumber(page)))
break;
}
}
}
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, currPos);
return true;
}
@@ -1661,11 +1688,11 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
Page page;
BTPageOpaque opaque;
OffsetNumber start;
- BTScanPosItem *currItem;
/*
* Scan down to the leftmost or rightmost leaf page. This is a simplified
@@ -1681,7 +1708,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* exists.
*/
PredicateLockRelation(rel, scan->xs_snapshot);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1710,46 +1737,25 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
}
/* remember which buffer we have pinned */
- so->currPos.buf = buf;
+ currPos->buf = buf;
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ currPos->moreLeft = false;
+ currPos->moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ currPos->moreLeft = true;
+ currPos->moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- so->markItemIndex = -1; /* ditto */
+ so->state.numKilled = 0; /* just paranoia */
+ so->state.markItemIndex = -1; /* ditto */
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, start))
- {
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
- }
- else
- {
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
- }
-
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
+ if (!_bt_load_first_page(scan, &so->state, dir, start))
+ return false;
- return true;
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index da0f330..ebcba7e 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1725,26 +1725,26 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
* away and the TID was re-used by a completely different heap tuple.
*/
void
-_bt_killitems(IndexScanDesc scan)
+_bt_killitems(BTScanState state, Relation indexRelation)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
OffsetNumber maxoff;
int i;
- int numKilled = so->numKilled;
+ int numKilled = state->numKilled;
bool killedsomething = false;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(state->currPos));
/*
* Always reset the scan state, so we don't look for same items on other
* pages.
*/
- so->numKilled = 0;
+ state->numKilled = 0;
- if (BTScanPosIsPinned(so->currPos))
+ if (BTScanPosIsPinned(*pos))
{
/*
* We have held the pin on this page since we read the index tuples,
@@ -1752,28 +1752,28 @@ _bt_killitems(IndexScanDesc scan)
* re-use of any TID on the page, so there is no need to check the
* LSN.
*/
- LockBuffer(so->currPos.buf, BT_READ);
+ LockBuffer(pos->buf, BT_READ);
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
}
else
{
Buffer buf;
/* Attempt to re-read the buffer, getting pin and lock. */
- buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, BT_READ);
+ buf = _bt_getbuf(indexRelation, pos->currPage, BT_READ);
/* It might not exist anymore; in which case we can't hint it. */
if (!BufferIsValid(buf))
return;
page = BufferGetPage(buf);
- if (PageGetLSN(page) == so->currPos.lsn)
- so->currPos.buf = buf;
+ if (PageGetLSN(page) == pos->lsn)
+ pos->buf = buf;
else
{
/* Modified while not pinned means hinting is not safe. */
- _bt_relbuf(scan->indexRelation, buf);
+ _bt_relbuf(indexRelation, buf);
return;
}
}
@@ -1784,12 +1784,12 @@ _bt_killitems(IndexScanDesc scan)
for (i = 0; i < numKilled; i++)
{
- int itemIndex = so->killedItems[i];
- BTScanPosItem *kitem = &so->currPos.items[itemIndex];
+ int itemIndex = state->killedItems[i];
+ BTScanPosItem *kitem = &pos->items[itemIndex];
OffsetNumber offnum = kitem->indexOffset;
- Assert(itemIndex >= so->currPos.firstItem &&
- itemIndex <= so->currPos.lastItem);
+ Assert(itemIndex >= pos->firstItem &&
+ itemIndex <= pos->lastItem);
if (offnum < minoff)
continue; /* pure paranoia */
while (offnum <= maxoff)
@@ -1817,10 +1817,10 @@ _bt_killitems(IndexScanDesc scan)
if (killedsomething)
{
opaque->btpo_flags |= BTP_HAS_GARBAGE;
- MarkBufferDirtyHint(so->currPos.buf, true);
+ MarkBufferDirtyHint(pos->buf, true);
}
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(pos->buf, BUFFER_LOCK_UNLOCK);
}
@@ -2065,3 +2065,14 @@ btproperty(Oid index_oid, int attno,
return false; /* punt to generic code */
}
}
+
+/*
+ * _bt_allocate_tuple_workspaces() -- Allocate buffers for saving index tuples
+ * in index-only scans.
+ */
+void
+_bt_allocate_tuple_workspaces(BTScanState state)
+{
+ state->currTuples = (char *) palloc(BLCKSZ * 2);
+ state->markTuples = state->currTuples + BLCKSZ;
+}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 011a72e..4124010 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -598,20 +598,8 @@ typedef struct BTArrayKeyInfo
Datum *elem_values; /* array of num_elems Datums */
} BTArrayKeyInfo;
-typedef struct BTScanOpaqueData
+typedef struct BTScanStateData
{
- /* these fields are set by _bt_preprocess_keys(): */
- bool qual_ok; /* false if qual can never be satisfied */
- int numberOfKeys; /* number of preprocessed scan keys */
- ScanKey keyData; /* array of preprocessed scan keys */
-
- /* workspace for SK_SEARCHARRAY support */
- ScanKey arrayKeyData; /* modified copy of scan->keyData */
- int numArrayKeys; /* number of equality-type array keys (-1 if
- * there are any unsatisfiable array keys) */
- BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
- MemoryContext arrayContext; /* scan-lifespan context for array data */
-
/* info about killed items if any (killedItems is NULL if never used) */
int *killedItems; /* currPos.items indexes of killed items */
int numKilled; /* number of currently stored items */
@@ -636,6 +624,23 @@ typedef struct BTScanOpaqueData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+} BTScanStateData, *BTScanState;
+
+typedef struct BTScanOpaqueData
+{
+ /* these fields are set by _bt_preprocess_keys(): */
+ bool qual_ok; /* false if qual can never be satisfied */
+ int numberOfKeys; /* number of preprocessed scan keys */
+ ScanKey keyData; /* array of preprocessed scan keys */
+
+ /* workspace for SK_SEARCHARRAY support */
+ ScanKey arrayKeyData; /* modified copy of scan->keyData */
+ int numArrayKeys; /* number of equality-type array keys (-1 if
+ * there are any unsatisfiable array keys) */
+ BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
+ MemoryContext arrayContext; /* scan-lifespan context for array data */
+
+ BTScanStateData state;
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -739,7 +744,7 @@ extern void _bt_preprocess_keys(IndexScanDesc scan);
extern IndexTuple _bt_checkkeys(IndexScanDesc scan,
Page page, OffsetNumber offnum,
ScanDirection dir, bool *continuescan);
-extern void _bt_killitems(IndexScanDesc scan);
+extern void _bt_killitems(BTScanState state, Relation indexRelation);
extern BTCycleId _bt_vacuum_cycleid(Relation rel);
extern BTCycleId _bt_start_vacuum(Relation rel);
extern void _bt_end_vacuum(Relation rel);
@@ -750,6 +755,7 @@ extern bytea *btoptions(Datum reloptions, bool validate);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
+extern void _bt_allocate_tuple_workspaces(BTScanState state);
/*
* prototypes for functions in nbtvalidate.c
0003-extract-get_index_column_opclass-and-get_opclass_opfamily_and_input_type-v02.patchtext/x-patch; name=0003-extract-get_index_column_opclass-and-get_opclass_opfamily_and_input_type-v02.patchDownload
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 159987e..986d5b8 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -24,6 +24,7 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
+#include "utils/lsyscache.h"
/*
@@ -852,12 +853,6 @@ gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull)
{
- HeapTuple tuple;
- Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
- Form_pg_opclass rd_opclass;
- Datum datum;
- bool disnull;
- oidvector *indclass;
Oid opclass,
opfamily,
opcintype;
@@ -891,49 +886,28 @@ gistproperty(Oid index_oid, int attno,
}
/* First we need to know the column's opclass. */
-
- tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
- if (!HeapTupleIsValid(tuple))
+ opclass = get_index_column_opclass(index_oid, attno);
+ if (!OidIsValid(opclass))
{
*isnull = true;
return true;
}
- rd_index = (Form_pg_index) GETSTRUCT(tuple);
-
- /* caller is supposed to guarantee this */
- Assert(attno > 0 && attno <= rd_index->indnatts);
-
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indclass, &disnull);
- Assert(!disnull);
-
- indclass = ((oidvector *) DatumGetPointer(datum));
- opclass = indclass->values[attno - 1];
-
- ReleaseSysCache(tuple);
/* Now look up the opclass family and input datatype. */
-
- tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
- if (!HeapTupleIsValid(tuple))
+ if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
{
*isnull = true;
return true;
}
- rd_opclass = (Form_pg_opclass) GETSTRUCT(tuple);
-
- opfamily = rd_opclass->opcfamily;
- opcintype = rd_opclass->opcintype;
-
- ReleaseSysCache(tuple);
/* And now we can check whether the function is provided. */
-
*res = SearchSysCacheExists4(AMPROCNUM,
ObjectIdGetDatum(opfamily),
ObjectIdGetDatum(opcintype),
ObjectIdGetDatum(opcintype),
Int16GetDatum(procno));
+ *isnull = false;
+
return true;
}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 1b04c09..1d479fe 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1050,6 +1050,32 @@ get_opclass_input_type(Oid opclass)
return result;
}
+/*
+ * get_opclass_family_and_input_type
+ *
+ * Returns the OID of the operator family the opclass belongs to,
+ * the OID of the datatype the opclass indexes
+ */
+bool
+get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
+{
+ HeapTuple tp;
+ Form_pg_opclass cla_tup;
+
+ tp = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
+ if (!HeapTupleIsValid(tp))
+ return false;
+
+ cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
+
+ *opfamily = cla_tup->opcfamily;
+ *opcintype = cla_tup->opcintype;
+
+ ReleaseSysCache(tp);
+
+ return true;
+}
+
/* ---------- OPERATOR CACHE ---------- */
/*
@@ -3061,3 +3087,45 @@ get_range_subtype(Oid rangeOid)
else
return InvalidOid;
}
+
+/* ---------- PG_INDEX CACHE ---------- */
+
+/*
+ * get_index_column_opclass
+ *
+ * Given the index OID and column number,
+ * return opclass of the index column
+ * or InvalidOid if the index was not found.
+ */
+Oid
+get_index_column_opclass(Oid index_oid, int attno)
+{
+ HeapTuple tuple;
+ Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
+ Datum datum;
+ bool isnull;
+ oidvector *indclass;
+ Oid opclass;
+
+ /* First we need to know the column's opclass. */
+
+ tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
+ if (!HeapTupleIsValid(tuple))
+ return InvalidOid;
+
+ rd_index = (Form_pg_index) GETSTRUCT(tuple);
+
+ /* caller is supposed to guarantee this */
+ Assert(attno > 0 && attno <= rd_index->indnatts);
+
+ datum = SysCacheGetAttr(INDEXRELID, tuple,
+ Anum_pg_index_indclass, &isnull);
+ Assert(!isnull);
+
+ indclass = ((oidvector *) DatumGetPointer(datum));
+ opclass = indclass->values[attno - 1];
+
+ ReleaseSysCache(tuple);
+
+ return opclass;
+}
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index b6d1fca..618c4e8 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -73,6 +73,8 @@ extern char *get_constraint_name(Oid conoid);
extern char *get_language_name(Oid langoid, bool missing_ok);
extern Oid get_opclass_family(Oid opclass);
extern Oid get_opclass_input_type(Oid opclass);
+extern bool get_opclass_opfamily_and_input_type(Oid opclass,
+ Oid *opfamily, Oid *opcintype);
extern RegProcedure get_opcode(Oid opno);
extern char *get_opname(Oid opno);
extern Oid get_op_rettype(Oid opno);
@@ -159,6 +161,7 @@ extern void free_attstatsslot(Oid atttype,
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
0004-add-kNN-support-to-btree-v02.patchtext/x-patch; name=0004-add-kNN-support-to-btree-v02.patchDownload
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 271c135..480bfd0 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -248,7 +248,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
</para>
<para>
- GiST indexes are also capable of optimizing <quote>nearest-neighbor</>
+ B-tree and GiST indexes are also capable of optimizing <quote>nearest-neighbor</>
searches, such as
<programlisting><![CDATA[
SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 333a36c..6708653 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -1171,7 +1171,7 @@ ALTER OPERATOR FAMILY integer_ops USING btree ADD
<title>Ordering Operators</title>
<para>
- Some index access methods (currently, only GiST) support the concept of
+ Some index access methods (currently, only B-tree and GiST) support the concept of
<firstterm>ordering operators</>. What we have been discussing so far
are <firstterm>search operators</>. A search operator is one for which
the index can be searched to find all rows satisfying
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
index a3f11da..764c0a9 100644
--- a/src/backend/access/nbtree/README
+++ b/src/backend/access/nbtree/README
@@ -624,6 +624,24 @@ item is irrelevant, and need not be stored at all. This arrangement
corresponds to the fact that an L&Y non-leaf page has one more pointer
than key.
+Nearest-neighbor search
+-----------------------
+
+There is a special scan strategy for nearest-neighbor (kNN) search,
+that is used in queries with ORDER BY distance clauses like this:
+SELECT * FROM tab WHERE col > const1 ORDER BY col <-> const2 LIMIT k.
+But, unlike GiST, B-tree supports only a one ordering operator on the
+first index column.
+
+At the beginning of kNN scan, we need to determine which strategy we
+will use --- a special bidirectional or a ordinary unidirectional.
+If the point from which we measure the distance falls into the scan range,
+we use bidirectional scan starting from this point, else we use simple
+unidirectional scan in the right direction. Algorithm of a bidirectional
+scan is very simple: at each step we advancing scan in that direction,
+which has the nearest point.
+
+
Notes to Operator Class Implementors
------------------------------------
@@ -660,6 +678,18 @@ procedure, of course.)
The other three operators are defined in terms of these two in the obvious way,
and must act consistently with them.
+To implement the distance ordered (nearest-neighbor) search, we only need
+to define a distance operator (usually it called <->) with a correpsonding
+operator family for distance comparison in the index's operator class.
+These operators must satisfy the following assumptions for all non-null
+values A,B,C of the datatype:
+
+ A <-> B = B <-> A symmetric law
+ if A = B, then A <-> C = B <-> C distance equivalence
+ if (A <= B and B <= C) or (A >= B and B >= C),
+ then A <-> B <= A <-> C monotonicity
+
+
For an operator family supporting multiple datatypes, the above laws must hold
when A,B,C are taken from any datatypes in the family. The transitive laws
are the trickiest to ensure, as in cross-type situations they represent
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index c2da15e..2f476df 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -23,12 +23,16 @@
#include "access/xlog.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "optimizer/paths.h"
#include "storage/indexfsm.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "tcop/tcopprot.h" /* pgrminclude ignore */
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
@@ -76,6 +80,10 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
+static Expr *btcanorderbyop(IndexOptInfo *index,
+ PathKey *pathkey, int pathkeyno,
+ Expr *expr, int *indexcol_p);
+
/*
* Btree handler function: return IndexAmRoutine with access method parameters
@@ -86,7 +94,7 @@ bthandler(PG_FUNCTION_ARGS)
{
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
- amroutine->amstrategies = BTMaxStrategyNumber;
+ amroutine->amstrategies = BtreeMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
amroutine->amcanbackward = true;
@@ -117,10 +125,10 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amcanorderbyop = btcanorderbyop;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
- amroutine->amcanorderbyop = NULL;
PG_RETURN_POINTER(amroutine);
}
@@ -304,6 +312,10 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
/* btree indexes are never lossy */
scan->xs_recheck = false;
+ scan->xs_recheckorderby = false;
+
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
/*
* If we have any array keys, initialize them during first call for a
@@ -327,7 +339,8 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(state->currPos))
+ if (!BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
res = _bt_first(scan, dir);
else
{
@@ -435,9 +448,6 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
IndexScanDesc scan;
BTScanOpaque so;
- /* no order by operators allowed */
- Assert(norderbys == 0);
-
/* get the scan */
scan = RelationGetIndexScan(rel, nkeys, norderbys);
@@ -464,6 +474,9 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
so->state.currTuples = so->state.markTuples = NULL;
+ so->knnState = NULL;
+ so->distanceTypeByVal = true;
+ so->scanDirection = NoMovementScanDirection;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -493,6 +506,8 @@ _bt_release_current_position(BTScanState state, Relation indexRelation,
static void
_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
/* No need to invalidate positions, if the RAM is about to be freed. */
_bt_release_current_position(state, scan->indexRelation, !free);
@@ -509,6 +524,14 @@ _bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
}
else
BTScanPosInvalidate(state->markPos);
+
+ if (!so->distanceTypeByVal)
+ {
+ pfree(DatumGetPointer(state->currDistance));
+ state->currDistance = PointerGetDatum(NULL);
+ pfree(DatumGetPointer(state->markDistance));
+ state->markDistance = PointerGetDatum(NULL);
+ }
}
/*
@@ -523,6 +546,13 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
_bt_release_scan_state(scan, state, false);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ so->knnState = NULL;
+ }
+
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
* not already done in a previous rescan call. To save on palloc
@@ -552,6 +582,14 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
scan->numberOfKeys * sizeof(ScanKeyData));
so->numberOfKeys = 0; /* until _bt_preprocess_keys sets it */
+ if (orderbys && scan->numberOfOrderBys > 0)
+ memmove(scan->orderByData,
+ orderbys,
+ scan->numberOfOrderBys * sizeof(ScanKeyData));
+
+ so->scanDirection = NoMovementScanDirection;
+ so->distanceTypeByVal = true;
+
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
_bt_preprocess_array_keys(scan);
}
@@ -566,6 +604,12 @@ btendscan(IndexScanDesc scan)
_bt_release_scan_state(scan, &so->state, true);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ }
+
/* Release storage */
if (so->keyData != NULL)
pfree(so->keyData);
@@ -577,7 +621,7 @@ btendscan(IndexScanDesc scan)
}
static void
-_bt_mark_current_position(BTScanState state)
+_bt_mark_current_position(BTScanOpaque so, BTScanState state)
{
/* There may be an old mark with a pin (but no lock). */
BTScanPosUnpinIfPinned(state->markPos);
@@ -595,6 +639,21 @@ _bt_mark_current_position(BTScanState state)
BTScanPosInvalidate(state->markPos);
state->markItemIndex = -1;
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->markDistance));
+
+ state->markIsNull = !BTScanPosIsValid(state->currPos) ||
+ state->currIsNull;
+
+ state->markDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->currDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -605,7 +664,13 @@ btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_mark_current_position(&so->state);
+ _bt_mark_current_position(so, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_mark_current_position(so, so->knnState);
+ so->markRightIsNearest = so->currRightIsNearest;
+ }
/* Also record the current positions of any array keys */
if (so->numArrayKeys)
@@ -615,6 +680,8 @@ btmarkpos(IndexScanDesc scan)
static void
_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
if (state->markItemIndex >= 0)
{
/*
@@ -650,6 +717,19 @@ _bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
state->markPos.nextTupleOffset);
}
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currIsNull = state->markIsNull;
+ state->currDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->markDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -665,6 +745,12 @@ btrestrpos(IndexScanDesc scan)
_bt_restore_array_keys(scan);
_bt_restore_marked_position(scan, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_restore_marked_position(scan, so->knnState);
+ so->currRightIsNearest = so->markRightIsNearest;
+ }
}
/*
@@ -1152,3 +1238,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+/*
+ * btcanorderbyop() -- Check whether KNN-search strategy is applicable to
+ * the given ORDER BY distance operator.
+ */
+static Expr *
+btcanorderbyop(IndexOptInfo *index, PathKey *pathkey, int pathkeyno,
+ Expr *expr, int *indexcol_p)
+{
+ if (pathkeyno > 1)
+ return NULL; /* only one ORDER BY clause is supported */
+
+ expr = match_clause_to_ordering_op(index,
+ 0, /* ORDER BY distance to the first
+ * index column is only supported */
+ expr,
+ pathkey->pk_opfamily);
+
+ if (expr)
+ *indexcol_p = 0;
+
+ return expr;
+}
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index c041056..97a1061 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -561,6 +561,127 @@ _bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
}
/*
+ * _bt_calc_current_dist() -- Calculate distance from the current item
+ * of the scan state to the target order-by ScanKey argument.
+ */
+static void
+_bt_calc_current_dist(IndexScanDesc scan, BTScanState state)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+ IndexTuple itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+ ScanKey scankey = &scan->orderByData[0];
+ Datum value;
+
+ value = index_getattr(itup, 1, scan->xs_itupdesc, &state->currIsNull);
+
+ if (state->currIsNull)
+ return; /* NULL distance */
+
+ value = FunctionCall2Coll(&scankey->sk_func,
+ scankey->sk_collation,
+ value,
+ scankey->sk_argument);
+
+ /* free previous distance value for by-ref types */
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currDistance = value;
+}
+
+/*
+ * _bt_compare_current_dist() -- Compare current distances of the left and right scan states.
+ *
+ * NULL distances are considered to be greater than any non-NULL distances.
+ *
+ * Returns true if right distance is lesser than left, otherwise false.
+ */
+static bool
+_bt_compare_current_dist(BTScanOpaque so, BTScanState rstate, BTScanState lstate)
+{
+ if (lstate->currIsNull)
+ return true; /* non-NULL < NULL */
+
+ if (rstate->currIsNull)
+ return false; /* NULL > non-NULL */
+
+ return DatumGetBool(FunctionCall2Coll(&so->distanceCmpProc,
+ InvalidOid, /* XXX collation for distance comparison */
+ rstate->currDistance,
+ lstate->currDistance));
+}
+
+/*
+ * _bt_init_knn_scan() -- Init additional scan state for KNN search.
+ *
+ * Caller must pin and read-lock scan->state.currPos.buf buffer.
+ *
+ * If empty result was found returned false.
+ * Otherwise prepared current item, and returned true.
+ */
+static bool
+_bt_init_knn_scan(IndexScanDesc scan, OffsetNumber offnum)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+ Buffer buf = rstate->currPos.buf;
+ bool left,
+ right;
+
+ lstate = so->knnState = (BTScanState) palloc(sizeof(BTScanStateData));
+ _bt_allocate_tuple_workspaces(lstate);
+
+ if (!scan->xs_want_itup)
+ {
+ /* We need to request index tuples for distance comparison. */
+ scan->xs_want_itup = true;
+ _bt_allocate_tuple_workspaces(rstate);
+ }
+
+ /* Bump pin and lock count before BTScanPosData copying. */
+ IncrBufferRefCount(buf);
+ LockBuffer(buf, BT_READ);
+
+ memcpy(&lstate->currPos, &rstate->currPos, sizeof(BTScanPosData));
+ lstate->currPos.moreLeft = true;
+ lstate->currPos.moreRight = false;
+
+ BTScanPosInvalidate(lstate->markPos);
+ lstate->markItemIndex = -1;
+ lstate->killedItems = NULL;
+ lstate->numKilled = 0;
+ lstate->currDistance = PointerGetDatum(NULL);
+ lstate->markDistance = PointerGetDatum(NULL);
+
+ /* Load first pages from the both scans. */
+ right = _bt_load_first_page(scan, rstate, ForwardScanDirection, offnum);
+ left = _bt_load_first_page(scan, lstate, BackwardScanDirection,
+ OffsetNumberPrev(offnum));
+
+ if (!left && !right)
+ return false; /* empty result */
+
+ if (left && right)
+ {
+ /*
+ * We have found items in both scan directions,
+ * determine nearest item to return.
+ */
+ _bt_calc_current_dist(scan, rstate);
+ _bt_calc_current_dist(scan, lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+
+ /* Reset right flag if the left item is nearer. */
+ right = so->currRightIsNearest;
+ }
+
+ /* Return current item of the selected scan direction. */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -663,7 +784,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*----------
*/
strat_total = BTEqualStrategyNumber;
- if (so->numberOfKeys > 0)
+
+ if (scan->numberOfOrderBys > 0)
+ {
+ if (_bt_process_orderings(scan, startKeys, &keysCount, notnullkeys))
+ /* use bidirectional KNN scan */
+ strat_total = BtreeKNNSearchStrategyNumber;
+
+ /* use selected KNN scan direction */
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
+ }
+
+ if (so->numberOfKeys > 0 &&
+ /* startKeys for KNN search already have been initialized */
+ strat_total != BtreeKNNSearchStrategyNumber)
{
AttrNumber curattr;
ScanKey chosen;
@@ -1003,6 +1138,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
break;
case BTGreaterEqualStrategyNumber:
+ case BtreeKNNSearchStrategyNumber:
/*
* Find first item >= scankey. (This is only used for forward
@@ -1093,16 +1229,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(!BTScanPosIsValid(*currPos));
currPos->buf = buf;
+ if (strat_total == BtreeKNNSearchStrategyNumber)
+ return _bt_init_knn_scan(scan, offnum);
+
if (!_bt_load_first_page(scan, &so->state, dir, offnum))
- return false;
+ return false; /* empty result */
/* OK, currPos->itemIndex says what to return */
return _bt_return_current_item(scan, &so->state);
}
/*
- * Advance to next tuple on current page; or if there's no more,
- * try to step to the next page with data.
+ * _bt_next_item() -- Advance to next tuple on current page;
+ * or if there's no more, try to step to the next page with data.
+ *
+ * If there are no more matching records in the given direction
*/
static bool
_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
@@ -1122,6 +1263,52 @@ _bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
}
/*
+ * _bt_next_nearest() -- Return next nearest item from bidirectional KNN scan.
+ */
+static bool
+_bt_next_nearest(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state;
+ BTScanState lstate = so->knnState;
+ bool right = BTScanPosIsValid(rstate->currPos);
+ bool left = BTScanPosIsValid(lstate->currPos);
+ bool advanceRight;
+
+ if (right && left)
+ advanceRight = so->currRightIsNearest;
+ else if (right)
+ advanceRight = true;
+ else if (left)
+ advanceRight = false;
+ else
+ return false; /* end of the scan */
+
+ *(advanceRight ? &right : &left) =
+ _bt_next_item(scan,
+ advanceRight ? rstate : lstate,
+ advanceRight ? ForwardScanDirection :
+ BackwardScanDirection);
+
+ if (!left && !right)
+ return false; /* end of the scan */
+
+ if (left && right)
+ {
+ /*
+ * If there are items in both scans we must recalculate distance
+ * in the advanced scan.
+ */
+ _bt_calc_current_dist(scan, advanceRight ? rstate : lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+ right = so->currRightIsNearest;
+ }
+
+ /* return nearest item */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_next() -- Get the next item in a scan.
*
* On entry, so->currPos describes the current page, which may be pinned
@@ -1140,6 +1327,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ if (so->knnState)
+ /* return next neareset item from KNN scan */
+ return _bt_next_nearest(scan);
+
if (!_bt_next_item(scan, &so->state, dir))
return false;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index ebcba7e..e5e855e 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -20,11 +20,13 @@
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "catalog/pg_amop.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
typedef struct BTSortArrayContext
@@ -2061,6 +2063,34 @@ btproperty(Oid index_oid, int attno,
*res = true;
return true;
+ case AMPROP_DISTANCE_ORDERABLE:
+ {
+ Oid opclass,
+ opfamily,
+ opcindtype;
+
+ /* answer only for columns, not AM or whole index */
+ if (attno == 0)
+ return false;
+
+ opclass = get_index_column_opclass(index_oid, attno);
+
+ if (!OidIsValid(opclass) ||
+ !get_opclass_opfamily_and_input_type(opclass,
+ &opfamily, &opcindtype))
+ {
+ *isnull = true;
+ return true;
+ }
+
+ *res = SearchSysCacheExists(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(opcindtype),
+ ObjectIdGetDatum(opcindtype),
+ Int16GetDatum(BtreeKNNSearchStrategyNumber));
+ return true;
+ }
+
default:
return false; /* punt to generic code */
}
@@ -2076,3 +2106,242 @@ _bt_allocate_tuple_workspaces(BTScanState state)
state->currTuples = (char *) palloc(BLCKSZ * 2);
state->markTuples = state->currTuples + BLCKSZ;
}
+
+static Oid
+_bt_get_sortfamily_for_ordering_key(Relation rel, ScanKey skey)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid sortfamily;
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(rel->rd_opfamily[skey->sk_attno - 1]),
+ ObjectIdGetDatum(rel->rd_opcintype[skey->sk_attno - 1]),
+ ObjectIdGetDatum(skey->sk_subtype),
+ Int16GetDatum(skey->sk_strategy));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ sortfamily = amop_tup->amopsortfamily;
+ ReleaseSysCache(tp);
+
+ return sortfamily;
+}
+
+static bool
+_bt_compare_row_key_with_ordering_key(ScanKey row, ScanKey ord, bool *result)
+{
+ ScanKey subkey = (ScanKey) DatumGetPointer(row->sk_argument);
+ int32 cmpresult;
+
+ Assert(subkey->sk_attno == 1);
+ Assert(subkey->sk_flags & SK_ROW_MEMBER);
+
+ if (subkey->sk_flags & SK_ISNULL)
+ return false;
+
+ /* Perform the test --- three-way comparison not bool operator */
+ cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
+ subkey->sk_collation,
+ ord->sk_argument,
+ subkey->sk_argument));
+
+ if (subkey->sk_flags & SK_BT_DESC)
+ cmpresult = -cmpresult;
+
+ /*
+ * At this point cmpresult indicates the overall result of the row
+ * comparison, and subkey points to the deciding column (or the last
+ * column if the result is "=").
+ */
+ switch (subkey->sk_strategy)
+ {
+ /* EQ and NE cases aren't allowed here */
+ case BTLessStrategyNumber:
+ *result = cmpresult < 0;
+ break;
+ case BTLessEqualStrategyNumber:
+ *result = cmpresult <= 0;
+ break;
+ case BTGreaterEqualStrategyNumber:
+ *result = cmpresult >= 0;
+ break;
+ case BTGreaterStrategyNumber:
+ *result = cmpresult > 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized RowCompareType: %d",
+ (int) subkey->sk_strategy);
+ *result = false; /* keep compiler quiet */
+ }
+
+ return true;
+}
+
+/* _bt_select_knn_search_strategy() -- Determine which KNN scan strategy to use:
+ * bidirectional or unidirectional. We are checking here if the
+ * ordering scankey argument falls into the scan range: if it falls
+ * we must use bidirectional scan, otherwise we use unidirectional.
+ *
+ * Returns BtreeKNNSearchStrategyNumber for bidirectional scan or
+ * strategy number of non-matched scankey for unidirectional.
+ */
+static StrategyNumber
+_bt_select_knn_search_strategy(IndexScanDesc scan, ScanKey ord)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey cond;
+
+ for (cond = so->keyData; cond < so->keyData + so->numberOfKeys; cond++)
+ {
+ bool result;
+
+ if (cond->sk_attno != 1)
+ break; /* only interesting in the first index attribute */
+
+ if (cond->sk_strategy == BTEqualStrategyNumber)
+ /* always use simple unidirectional scan for equals operators */
+ return BTEqualStrategyNumber;
+
+ if (cond->sk_flags & SK_ROW_HEADER)
+ {
+ if (!_bt_compare_row_key_with_ordering_key(cond, ord, &result))
+ return BTEqualStrategyNumber; /* ROW(fist_index_attr, ...) IS NULL */
+ }
+ else
+ {
+ if (!_bt_compare_scankey_args(scan, cond, ord, cond, &result))
+ elog(ERROR, "could not compare ordering key");
+ }
+
+ if (!result)
+ /*
+ * Ordering scankey argument is out of scan range,
+ * use unidirectional scan.
+ */
+ return cond->sk_strategy;
+ }
+
+ return BtreeKNNSearchStrategyNumber; /* use bidirectional scan */
+}
+
+/*
+ * _bt_init_distance_comparison() -- Init distance typlen/typbyval and its
+ * comparison procedure.
+ */
+static void
+_bt_init_distance_comparison(IndexScanDesc scan, ScanKey ord)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ RegProcedure opcode;
+ Oid sortfamily;
+ Oid opno;
+ Oid distanceType;
+
+ distanceType = get_func_rettype(ord->sk_func.fn_oid);
+
+ sortfamily = _bt_get_sortfamily_for_ordering_key(scan->indexRelation, ord);
+
+ if (!OidIsValid(sortfamily))
+ elog(ERROR, "could not find sort family for btree ordering operator");
+
+ opno = get_opfamily_member(sortfamily,
+ distanceType,
+ distanceType,
+ BTLessEqualStrategyNumber);
+
+ if (!OidIsValid(opno))
+ elog(ERROR, "could not find operator for btree distance comparison");
+
+ opcode = get_opcode(opno);
+
+ if (!RegProcedureIsValid(opcode))
+ elog(ERROR,
+ "could not find procedure for btree distance comparison operator");
+
+ fmgr_info(opcode, &so->distanceCmpProc);
+
+ get_typlenbyval(distanceType, &so->distanceTypeLen, &so->distanceTypeByVal);
+
+ if (!so->distanceTypeByVal)
+ {
+ so->state.currDistance = PointerGetDatum(NULL);
+ so->state.markDistance = PointerGetDatum(NULL);
+ }
+}
+
+/*
+ * _bt_process_orderings() -- Process ORDER BY distance scankeys and
+ * select corresponding KNN strategy.
+ *
+ * If bidirectional scan is selected then one scankey is initialized
+ * using bufKeys and placed into startKeys/keysCount, true is returned.
+ *
+ * Otherwise, so->scanDirection is set and false is returned.
+ */
+bool
+_bt_process_orderings(IndexScanDesc scan, ScanKey *startKeys, int *keysCount,
+ ScanKeyData bufKeys[])
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey ord = scan->orderByData;
+
+ if (scan->numberOfOrderBys > 1 || ord->sk_attno != 1)
+ /* it should not happen, see btcanorderbyop() */
+ elog(ERROR, "only one btree ordering operator "
+ "for the first index column is supported");
+
+ Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
+
+ switch (_bt_select_knn_search_strategy(scan, ord))
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ /*
+ * Ordering key argument is greater than all values in scan range.
+ * select backward scan direction.
+ */
+ so->scanDirection = BackwardScanDirection;
+ return false;
+
+ case BTEqualStrategyNumber:
+ /* Use default unidirectional scan direction. */
+ return false;
+
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ /*
+ * Ordering key argument is lesser than all values in scan range.
+ * select forward scan direction.
+ */
+ so->scanDirection = ForwardScanDirection;
+ return false;
+
+ case BtreeKNNSearchStrategyNumber:
+ /*
+ * Ordering key argument falls into scan range,
+ * use bidirectional scan.
+ */
+ break;
+ }
+
+ _bt_init_distance_comparison(scan, ord);
+
+ /* Init btree search key with ordering key argument. */
+ ScanKeyEntryInitialize(&bufKeys[0],
+ (scan->indexRelation->rd_indoption[ord->sk_attno - 1] <<
+ SK_BT_INDOPTION_SHIFT) |
+ SK_ORDER_BY |
+ SK_SEARCHNULL /* only for invalid procedure oid, see
+ * assert in ScanKeyEntryInitialize */,
+ ord->sk_attno,
+ BtreeKNNSearchStrategyNumber,
+ ord->sk_subtype,
+ ord->sk_collation,
+ InvalidOid,
+ ord->sk_argument);
+
+ startKeys[(*keysCount)++] = &bufKeys[0];
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 88e33f5..c56f4e8 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -22,9 +22,17 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
+#define BTRequiredOperatorSet \
+ ((1 << BTLessStrategyNumber) | \
+ (1 << BTLessEqualStrategyNumber) | \
+ (1 << BTEqualStrategyNumber) | \
+ (1 << BTGreaterEqualStrategyNumber) | \
+ (1 << BTGreaterStrategyNumber))
+
/*
* Validator for a btree opclass.
@@ -123,10 +131,11 @@ btvalidate(Oid opclassoid)
{
HeapTuple oprtup = &oprlist->members[i]->tuple;
Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ Oid op_rettype;
/* Check that only allowed strategy numbers exist */
if (oprform->amopstrategy < 1 ||
- oprform->amopstrategy > BTMaxStrategyNumber)
+ oprform->amopstrategy > BtreeMaxStrategyNumber)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -137,20 +146,29 @@ btvalidate(Oid opclassoid)
result = false;
}
- /* btree doesn't support ORDER BY operators */
- if (oprform->amoppurpose != AMOP_SEARCH ||
- OidIsValid(oprform->amopsortfamily))
+ /* btree supports ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH)
{
- ereport(INFO,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree operator family \"%s\" contains invalid ORDER BY specification for operator %s",
- opfamilyname,
- format_operator(oprform->amopopr))));
- result = false;
+ /* ... and operator result must match the claimed btree opfamily */
+ op_rettype = get_op_rettype(oprform->amopopr);
+ if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
+ {
+ ereport(INFO,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree operator family %s contains invalid ORDER BY specification for operator %s",
+ opfamilyname,
+ format_operator(oprform->amopopr))));
+ result = false;
+ }
+ }
+ else
+ {
+ /* Search operators must always return bool */
+ op_rettype = BOOLOID;
}
/* Check operator signature --- same for all btree strategies */
- if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ if (!check_amop_signature(oprform->amopopr, op_rettype,
oprform->amoplefttype,
oprform->amoprighttype))
{
@@ -189,12 +207,8 @@ btvalidate(Oid opclassoid)
* or support functions for this datatype pair. The only thing that
* is considered optional is the sortsupport function.
*/
- if (thisgroup->operatorset !=
- ((1 << BTLessStrategyNumber) |
- (1 << BTLessEqualStrategyNumber) |
- (1 << BTEqualStrategyNumber) |
- (1 << BTGreaterEqualStrategyNumber) |
- (1 << BTGreaterStrategyNumber)))
+ if ((thisgroup->operatorset & BTRequiredOperatorSet) !=
+ BTRequiredOperatorSet)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 21659a6..9762e8d 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -982,6 +982,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
* if we are only trying to build bitmap indexscans, nor if we have to
* assume the scan is unordered.
*/
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
!found_lower_saop_clause &&
has_useful_pathkeys(root, rel));
@@ -992,10 +996,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
ForwardScanDirection);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
}
- else if (index->amcanorderbyop && pathkeys_possibly_useful)
+
+ if (useful_pathkeys == NIL &&
+ index->amcanorderbyop && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -1006,12 +1010,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
else
useful_pathkeys = NIL;
}
- else
- {
- useful_pathkeys = NIL;
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
- }
/*
* 3. Check if an index-only scan is possible. If we're not building
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 4124010..030993b 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -624,6 +624,12 @@ typedef struct BTScanStateData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+
+ /* KNN-search fields: */
+ Datum currDistance; /* current distance */
+ Datum markDistance; /* marked distance */
+ bool currIsNull; /* current item is NULL */
+ bool markIsNull; /* marked item is NULL */
} BTScanStateData, *BTScanState;
typedef struct BTScanOpaqueData
@@ -640,7 +646,17 @@ typedef struct BTScanOpaqueData
BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
MemoryContext arrayContext; /* scan-lifespan context for array data */
- BTScanStateData state;
+ BTScanStateData state; /* main scan state */
+
+ /* KNN-search fields: */
+ BTScanState knnState; /* optional scan state for KNN search */
+ ScanDirection scanDirection; /* selected scan direction for
+ * unidirectional KNN scan */
+ FmgrInfo distanceCmpProc; /* distance comparison procedure */
+ int16 distanceTypeLen; /* distance typlen */
+ bool distanceTypeByVal; /* distance typebyval */
+ bool currRightIsNearest; /* current right item is nearest */
+ bool markRightIsNearest; /* marked right item is nearest */
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -756,6 +772,8 @@ extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
extern void _bt_allocate_tuple_workspaces(BTScanState state);
+extern bool _bt_process_orderings(IndexScanDesc scan,
+ ScanKey *startKeys, int *keysCount, ScanKeyData bufKeys[]);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/include/access/stratnum.h b/src/include/access/stratnum.h
index 489e5c5..0852f8a 100644
--- a/src/include/access/stratnum.h
+++ b/src/include/access/stratnum.h
@@ -32,7 +32,10 @@ typedef uint16 StrategyNumber;
#define BTGreaterEqualStrategyNumber 4
#define BTGreaterStrategyNumber 5
-#define BTMaxStrategyNumber 5
+#define BTMaxStrategyNumber 5 /* number of canonical B-tree strategies */
+
+#define BtreeKNNSearchStrategyNumber 6 /* for <-> (distance) */
+#define BtreeMaxStrategyNumber 6 /* number of extended B-tree strategies */
/*
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index b01be59..778dae3 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -347,10 +347,10 @@ ROLLBACK;
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
ERROR: access method "invalid_index_method" does not exist
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 6, must be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 0, must be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 7, must be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 0, must be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -397,11 +397,12 @@ DROP OPERATOR FAMILY alt_opf8 USING btree;
CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
-ERROR: access method "btree" does not support ordering operators
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
ALTER OPERATOR FAMILY alt_opf11 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index c9ea479..bcf9cf6 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -295,8 +295,8 @@ ROLLBACK;
-- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -340,10 +340,12 @@ CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
0005-add-distance-operators-v02.patchtext/x-patch; name=0005-add-distance-operators-v02.patchDownload
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index ac8f74f..debe1b3 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -30,6 +30,7 @@
#include "utils/numeric.h"
#include "utils/pg_locale.h"
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
/*************************************************************************
* Private routines
@@ -1157,3 +1158,23 @@ int8_cash(PG_FUNCTION_ARGS)
PG_RETURN_CASH(result);
}
+
+Datum
+cash_dist(PG_FUNCTION_ARGS)
+{
+ Cash a = PG_GETARG_CASH(0);
+ Cash b = PG_GETARG_CASH(1);
+ Cash r;
+ Cash ra;
+
+ r = a - b;
+ ra = Abs(r);
+
+ /* Overflow check. */
+ if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("money out of range")));
+
+ PG_RETURN_CASH(ra);
+}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 0a100a3..88010d5 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -565,6 +565,17 @@ date_mii(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(result);
}
+Datum
+date_dist(PG_FUNCTION_ARGS)
+{
+ /* we assume the difference can't overflow */
+ Datum diff = DirectFunctionCall2(date_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+}
+
/*
* Internal routines for promoting date to timestamp and timestamp with
* time zone
@@ -2065,6 +2076,16 @@ time_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+Datum
+time_dist(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(time_mi_time,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
+
/*****************************************************************************
* Time With Time Zone ADT
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 894f026..906e70a 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -3585,6 +3585,32 @@ width_bucket_float8(PG_FUNCTION_ARGS)
PG_RETURN_INT32(result);
}
+Datum
+float4_dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float4 r;
+
+ r = a - b;
+ CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
+
+ PG_RETURN_FLOAT4(Abs(r));
+}
+
+Datum
+float8_dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r;
+
+ r = a - b;
+ CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
/* ========== PRIVATE ROUTINES ========== */
#ifndef HAVE_CBRT
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index bd4422e..43dcf40 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -1395,3 +1395,43 @@ generate_series_step_int4(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+Datum
+int2_dist(PG_FUNCTION_ARGS)
+{
+ int16 a = PG_GETARG_INT16(0);
+ int16 b = PG_GETARG_INT16(1);
+ int16 r;
+ int16 ra;
+
+ r = a - b;
+ ra = Abs(r);
+
+ /* Overflow check. */
+ if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+
+ PG_RETURN_INT16(ra);
+}
+
+Datum
+int4_dist(PG_FUNCTION_ARGS)
+{
+ int32 a = PG_GETARG_INT32(0);
+ int32 b = PG_GETARG_INT32(1);
+ int32 r;
+ int32 ra;
+
+ r = a - b;
+ ra = Abs(r);
+
+ /* Overflow check. */
+ if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ PG_RETURN_INT32(ra);
+}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 0c6a412..77938e0 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1508,3 +1508,23 @@ generate_series_step_int8(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+Datum
+int8_dist(PG_FUNCTION_ARGS)
+{
+ int64 a = PG_GETARG_INT64(0);
+ int64 b = PG_GETARG_INT64(1);
+ int64 r;
+ int64 ra;
+
+ r = a - b;
+ ra = Abs(r);
+
+ /* Overflow check. */
+ if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+
+ PG_RETURN_INT64(ra);
+}
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index 12ef783..bab7ae7 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -455,3 +455,17 @@ oidvectorgt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+
+Datum
+oid_dist(PG_FUNCTION_ARGS)
+{
+ Oid a = PG_GETARG_OID(0);
+ Oid b = PG_GETARG_OID(1);
+ Oid res;
+
+ if (a < b)
+ res = b - a;
+ else
+ res = a - b;
+ PG_RETURN_OID(res);
+}
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index f2784da..f665177 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2922,6 +2922,62 @@ timestamp_mi(PG_FUNCTION_ARGS)
PG_RETURN_INTERVAL_P(result);
}
+Datum
+ts_dist(PG_FUNCTION_ARGS)
+{
+ Timestamp a = PG_GETARG_TIMESTAMP(0);
+ Timestamp b = PG_GETARG_TIMESTAMP(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+#ifdef HAVE_INT64_TIMESTAMP
+ p->time = PG_INT64_MAX;
+#else
+ p->time = DBL_MAX;
+#endif
+ PG_RETURN_INTERVAL_P(p);
+ }
+ else
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+
+Datum
+tstz_dist(PG_FUNCTION_ARGS)
+{
+ TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
+ TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+#ifdef HAVE_INT64_TIMESTAMP
+ p->time = PG_INT64_MAX;
+#else
+ p->time = DBL_MAX;
+#endif
+ PG_RETURN_INTERVAL_P(p);
+ }
+
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+
/*
* interval_justify_interval()
*
@@ -3725,6 +3781,29 @@ interval_avg(PG_FUNCTION_ARGS)
Float8GetDatum((double) N.time));
}
+Interval *
+abs_interval(Interval *a)
+{
+ static Interval zero = {0, 0, 0};
+
+ if (DatumGetBool(DirectFunctionCall2(interval_lt,
+ IntervalPGetDatum(a),
+ IntervalPGetDatum(&zero))))
+ a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
+ IntervalPGetDatum(a)));
+
+ return a;
+}
+
+Datum
+interval_dist(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(interval_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
/* timestamp_age()
* Calculate time difference while retaining year/month fields.
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index 0251664..44e2caf 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -105,6 +105,7 @@ DATA(insert ( 1976 21 21 2 s 522 403 0 ));
DATA(insert ( 1976 21 21 3 s 94 403 0 ));
DATA(insert ( 1976 21 21 4 s 524 403 0 ));
DATA(insert ( 1976 21 21 5 s 520 403 0 ));
+DATA(insert ( 1976 21 21 6 o 3365 403 1976 ));
/* crosstype operators int24 */
DATA(insert ( 1976 21 23 1 s 534 403 0 ));
DATA(insert ( 1976 21 23 2 s 540 403 0 ));
@@ -123,6 +124,7 @@ DATA(insert ( 1976 23 23 2 s 523 403 0 ));
DATA(insert ( 1976 23 23 3 s 96 403 0 ));
DATA(insert ( 1976 23 23 4 s 525 403 0 ));
DATA(insert ( 1976 23 23 5 s 521 403 0 ));
+DATA(insert ( 1976 23 23 6 o 3366 403 1976 ));
/* crosstype operators int42 */
DATA(insert ( 1976 23 21 1 s 535 403 0 ));
DATA(insert ( 1976 23 21 2 s 541 403 0 ));
@@ -141,6 +143,7 @@ DATA(insert ( 1976 20 20 2 s 414 403 0 ));
DATA(insert ( 1976 20 20 3 s 410 403 0 ));
DATA(insert ( 1976 20 20 4 s 415 403 0 ));
DATA(insert ( 1976 20 20 5 s 413 403 0 ));
+DATA(insert ( 1976 20 20 6 o 3367 403 1976 ));
/* crosstype operators int82 */
DATA(insert ( 1976 20 21 1 s 1870 403 0 ));
DATA(insert ( 1976 20 21 2 s 1872 403 0 ));
@@ -163,6 +166,7 @@ DATA(insert ( 1989 26 26 2 s 611 403 0 ));
DATA(insert ( 1989 26 26 3 s 607 403 0 ));
DATA(insert ( 1989 26 26 4 s 612 403 0 ));
DATA(insert ( 1989 26 26 5 s 610 403 0 ));
+DATA(insert ( 1989 26 26 6 o 3368 403 1989 ));
/*
* btree tid_ops
@@ -194,6 +198,7 @@ DATA(insert ( 1970 700 700 2 s 624 403 0 ));
DATA(insert ( 1970 700 700 3 s 620 403 0 ));
DATA(insert ( 1970 700 700 4 s 625 403 0 ));
DATA(insert ( 1970 700 700 5 s 623 403 0 ));
+DATA(insert ( 1970 700 700 6 o 3369 403 1970 ));
/* crosstype operators float48 */
DATA(insert ( 1970 700 701 1 s 1122 403 0 ));
DATA(insert ( 1970 700 701 2 s 1124 403 0 ));
@@ -206,6 +211,7 @@ DATA(insert ( 1970 701 701 2 s 673 403 0 ));
DATA(insert ( 1970 701 701 3 s 670 403 0 ));
DATA(insert ( 1970 701 701 4 s 675 403 0 ));
DATA(insert ( 1970 701 701 5 s 674 403 0 ));
+DATA(insert ( 1970 701 701 6 o 3370 403 1970 ));
/* crosstype operators float84 */
DATA(insert ( 1970 701 700 1 s 1132 403 0 ));
DATA(insert ( 1970 701 700 2 s 1134 403 0 ));
@@ -283,6 +289,7 @@ DATA(insert ( 434 1082 1082 2 s 1096 403 0 ));
DATA(insert ( 434 1082 1082 3 s 1093 403 0 ));
DATA(insert ( 434 1082 1082 4 s 1098 403 0 ));
DATA(insert ( 434 1082 1082 5 s 1097 403 0 ));
+DATA(insert ( 434 1082 1082 6 o 3372 403 1976 ));
/* crosstype operators vs timestamp */
DATA(insert ( 434 1082 1114 1 s 2345 403 0 ));
DATA(insert ( 434 1082 1114 2 s 2346 403 0 ));
@@ -301,6 +308,7 @@ DATA(insert ( 434 1114 1114 2 s 2063 403 0 ));
DATA(insert ( 434 1114 1114 3 s 2060 403 0 ));
DATA(insert ( 434 1114 1114 4 s 2065 403 0 ));
DATA(insert ( 434 1114 1114 5 s 2064 403 0 ));
+DATA(insert ( 434 1114 1114 6 o 3374 403 1982 ));
/* crosstype operators vs date */
DATA(insert ( 434 1114 1082 1 s 2371 403 0 ));
DATA(insert ( 434 1114 1082 2 s 2372 403 0 ));
@@ -319,6 +327,7 @@ DATA(insert ( 434 1184 1184 2 s 1323 403 0 ));
DATA(insert ( 434 1184 1184 3 s 1320 403 0 ));
DATA(insert ( 434 1184 1184 4 s 1325 403 0 ));
DATA(insert ( 434 1184 1184 5 s 1324 403 0 ));
+DATA(insert ( 434 1184 1184 6 o 3375 403 1982 ));
/* crosstype operators vs date */
DATA(insert ( 434 1184 1082 1 s 2384 403 0 ));
DATA(insert ( 434 1184 1082 2 s 2385 403 0 ));
@@ -341,6 +350,7 @@ DATA(insert ( 1996 1083 1083 2 s 1111 403 0 ));
DATA(insert ( 1996 1083 1083 3 s 1108 403 0 ));
DATA(insert ( 1996 1083 1083 4 s 1113 403 0 ));
DATA(insert ( 1996 1083 1083 5 s 1112 403 0 ));
+DATA(insert ( 1996 1083 1083 6 o 3373 403 1982 ));
/*
* btree timetz_ops
@@ -361,6 +371,7 @@ DATA(insert ( 1982 1186 1186 2 s 1333 403 0 ));
DATA(insert ( 1982 1186 1186 3 s 1330 403 0 ));
DATA(insert ( 1982 1186 1186 4 s 1335 403 0 ));
DATA(insert ( 1982 1186 1186 5 s 1334 403 0 ));
+DATA(insert ( 1982 1186 1186 6 o 3376 403 1982 ));
/*
* btree macaddr
@@ -451,6 +462,7 @@ DATA(insert ( 2099 790 790 2 s 904 403 0 ));
DATA(insert ( 2099 790 790 3 s 900 403 0 ));
DATA(insert ( 2099 790 790 4 s 905 403 0 ));
DATA(insert ( 2099 790 790 5 s 903 403 0 ));
+DATA(insert ( 2099 790 790 6 o 3371 403 2099 ));
/*
* btree reltime_ops
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 45feb69..dca1706 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1624,6 +1624,32 @@ DESCR("greater than or equal");
DATA(insert OID = 3228 ( "-" PGNSP PGUID b f f 3220 3220 1700 0 0 pg_lsn_mi - - ));
DESCR("minus");
+/* distance operators */
+DATA(insert OID = 3365 ( "<->" PGNSP PGUID b f f 21 21 21 3365 0 int2_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3366 ( "<->" PGNSP PGUID b f f 23 23 23 3366 0 int4_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3367 ( "<->" PGNSP PGUID b f f 20 20 20 3367 0 int8_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3368 ( "<->" PGNSP PGUID b f f 26 26 26 3368 0 oid_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3369 ( "<->" PGNSP PGUID b f f 700 700 700 3369 0 float4_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3370 ( "<->" PGNSP PGUID b f f 701 701 701 3370 0 float8_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3371 ( "<->" PGNSP PGUID b f f 790 790 790 3371 0 cash_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3372 ( "<->" PGNSP PGUID b f f 1082 1082 23 3372 0 date_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3373 ( "<->" PGNSP PGUID b f f 1083 1083 1186 3373 0 time_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3374 ( "<->" PGNSP PGUID b f f 1114 1114 1186 3374 0 ts_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3375 ( "<->" PGNSP PGUID b f f 1184 1184 1186 3375 0 tstz_dist - - ));
+DESCR("distance between");
+DATA(insert OID = 3376 ( "<->" PGNSP PGUID b f f 1186 1186 1186 3376 0 interval_dist - - ));
+DESCR("distance between");
+
/* enum operators */
DATA(insert OID = 3516 ( "=" PGNSP PGUID b t t 3500 3500 16 3516 3517 enum_eq eqsel eqjoinsel ));
DESCR("equal");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 05652e8..ca418fe 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5361,6 +5361,21 @@ DESCR("pg_controldata init state information as a function");
DATA(insert OID = 3445 ( pg_import_system_collations PGNSP PGUID 12 100 0 0 0 f f f f t f v r 2 0 2278 "16 4089" _null_ _null_ "{if_not_exists,schema}" _null_ _null_ pg_import_system_collations _null_ _null_ _null_ ));
DESCR("import collations from operating system");
+/* distance functions */
+DATA(insert OID = 3353 ( int2_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 21 "21 21" _null_ _null_ _null_ _null_ _null_ int2_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3354 ( int4_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 23 "23 23" _null_ _null_ _null_ _null_ _null_ int4_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3355 ( int8_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "20 20" _null_ _null_ _null_ _null_ _null_ int8_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3356 ( oid_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 26 "26 26" _null_ _null_ _null_ _null_ _null_ oid_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3357 ( float4_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 700 "700 700" _null_ _null_ _null_ _null_ _null_ float4_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3358 ( float8_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 701 "701 701" _null_ _null_ _null_ _null_ _null_ float8_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3359 ( cash_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 790 "790 790" _null_ _null_ _null_ _null_ _null_ cash_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3360 ( date_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 23 "1082 1082" _null_ _null_ _null_ _null_ _null_ date_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3361 ( time_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1186 "1083 1083" _null_ _null_ _null_ _null_ _null_ time_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3362 ( ts_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1186 "1114 1114" _null_ _null_ _null_ _null_ _null_ ts_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3363 ( tstz_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1186 "1184 1184" _null_ _null_ _null_ _null_ _null_ tstz_dist _null_ _null_ _null_ ));
+DATA(insert OID = 3364 ( interval_dist PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1186 "1186 1186" _null_ _null_ _null_ _null_ _null_ interval_dist _null_ _null_ _null_ ));
+
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index f0e7798..4b4d02d 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -346,4 +346,6 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
+extern Interval *abs_interval(Interval *a);
+
#endif /* DATETIME_H */
diff --git a/src/test/regress/expected/amutils.out b/src/test/regress/expected/amutils.out
index 74f7c9f..55296b7 100644
--- a/src/test/regress/expected/amutils.out
+++ b/src/test/regress/expected/amutils.out
@@ -24,7 +24,7 @@ select prop,
nulls_first | | | f
nulls_last | | | t
orderable | | | t
- distance_orderable | | | f
+ distance_orderable | | | t
returnable | | | t
search_array | | | t
search_nulls | | | t
@@ -97,7 +97,7 @@ select prop,
nulls_first | f | f | f | f | f | f
nulls_last | t | f | f | f | f | f
orderable | t | f | f | f | f | f
- distance_orderable | f | f | t | f | f | f
+ distance_orderable | t | f | t | f | f | f
returnable | t | f | f | t | f | f
search_array | t | f | f | f | f | f
search_nulls | t | f | t | t | f | t
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 0bcec13..fb44ae8 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1721,6 +1721,7 @@ ORDER BY 1, 2, 3;
403 | 5 | *>
403 | 5 | >
403 | 5 | ~>~
+ 403 | 6 | <->
405 | 1 | =
783 | 1 | <<
783 | 1 | @@
@@ -1828,7 +1829,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
0006-remove-distance-operators-from-btree_gist-v02.patchtext/x-patch; name=0006-remove-distance-operators-from-btree_gist-v02.patchDownload
diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile
index d36f517..c650581 100644
--- a/contrib/btree_gist/Makefile
+++ b/contrib/btree_gist/Makefile
@@ -10,7 +10,8 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
EXTENSION = btree_gist
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
- btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql
+ btree_gist--1.1--1.2.sql btree_gist--1.2--1.3.sql \
+ btree_gist--1.3--1.4.sql btree_gist--1.4.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c
index aa14735..6ebeda1 100644
--- a/contrib/btree_gist/btree_cash.c
+++ b/contrib/btree_gist/btree_cash.c
@@ -90,27 +90,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(cash_dist);
-Datum
-cash_dist(PG_FUNCTION_ARGS)
-{
- Cash a = PG_GETARG_CASH(0);
- Cash b = PG_GETARG_CASH(1);
- Cash r;
- Cash ra;
-
- r = a - b;
- ra = Abs(r);
-
- /* Overflow check. */
- if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("money out of range")));
-
- PG_RETURN_CASH(ra);
-}
-
/**************************************************
* Cash ops
**************************************************/
diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c
index 56031d4..96d8821 100644
--- a/contrib/btree_gist/btree_date.c
+++ b/contrib/btree_gist/btree_date.c
@@ -109,19 +109,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(date_dist);
-Datum
-date_dist(PG_FUNCTION_ARGS)
-{
- /* we assume the difference can't overflow */
- Datum diff = DirectFunctionCall2(date_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
-}
-
-
/**************************************************
* date ops
**************************************************/
diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c
index 13dc4a5..c798b42 100644
--- a/contrib/btree_gist/btree_float4.c
+++ b/contrib/btree_gist/btree_float4.c
@@ -89,21 +89,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(float4_dist);
-Datum
-float4_dist(PG_FUNCTION_ARGS)
-{
- float4 a = PG_GETARG_FLOAT4(0);
- float4 b = PG_GETARG_FLOAT4(1);
- float4 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT4(Abs(r));
-}
-
-
/**************************************************
* float4 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c
index c3a2415..c332c2d 100644
--- a/contrib/btree_gist/btree_float8.c
+++ b/contrib/btree_gist/btree_float8.c
@@ -96,21 +96,6 @@ static const gbtree_ninfo tinfo =
gbt_float8_dist
};
-
-PG_FUNCTION_INFO_V1(float8_dist);
-Datum
-float8_dist(PG_FUNCTION_ARGS)
-{
- float8 a = PG_GETARG_FLOAT8(0);
- float8 b = PG_GETARG_FLOAT8(1);
- float8 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT8(Abs(r));
-}
-
/**************************************************
* float8 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_gist--1.2.sql b/contrib/btree_gist/btree_gist--1.2.sql
deleted file mode 100644
index 1efe753..0000000
--- a/contrib/btree_gist/btree_gist--1.2.sql
+++ /dev/null
@@ -1,1570 +0,0 @@
-/* contrib/btree_gist/btree_gist--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
-
-CREATE FUNCTION gbtreekey4_in(cstring)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey4_out(gbtreekey4)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey4 (
- INTERNALLENGTH = 4,
- INPUT = gbtreekey4_in,
- OUTPUT = gbtreekey4_out
-);
-
-CREATE FUNCTION gbtreekey8_in(cstring)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey8_out(gbtreekey8)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey8 (
- INTERNALLENGTH = 8,
- INPUT = gbtreekey8_in,
- OUTPUT = gbtreekey8_out
-);
-
-CREATE FUNCTION gbtreekey16_in(cstring)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey16_out(gbtreekey16)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey16 (
- INTERNALLENGTH = 16,
- INPUT = gbtreekey16_in,
- OUTPUT = gbtreekey16_out
-);
-
-CREATE FUNCTION gbtreekey32_in(cstring)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey32_out(gbtreekey32)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey32 (
- INTERNALLENGTH = 32,
- INPUT = gbtreekey32_in,
- OUTPUT = gbtreekey32_out
-);
-
-CREATE FUNCTION gbtreekey_var_in(cstring)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey_var (
- INTERNALLENGTH = VARIABLE,
- INPUT = gbtreekey_var_in,
- OUTPUT = gbtreekey_var_out,
- STORAGE = EXTENDED
-);
-
---distance operators
-
-CREATE FUNCTION cash_dist(money, money)
-RETURNS money
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = money,
- RIGHTARG = money,
- PROCEDURE = cash_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION date_dist(date, date)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = date,
- RIGHTARG = date,
- PROCEDURE = date_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float4_dist(float4, float4)
-RETURNS float4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float4,
- RIGHTARG = float4,
- PROCEDURE = float4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float8_dist(float8, float8)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float8,
- RIGHTARG = float8,
- PROCEDURE = float8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int2_dist(int2, int2)
-RETURNS int2
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int2,
- RIGHTARG = int2,
- PROCEDURE = int2_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int4_dist(int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int4,
- RIGHTARG = int4,
- PROCEDURE = int4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int8_dist(int8, int8)
-RETURNS int8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int8,
- RIGHTARG = int8,
- PROCEDURE = int8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION interval_dist(interval, interval)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = interval,
- RIGHTARG = interval,
- PROCEDURE = interval_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION oid_dist(oid, oid)
-RETURNS oid
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = oid,
- RIGHTARG = oid,
- PROCEDURE = oid_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION time_dist(time, time)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = time,
- RIGHTARG = time,
- PROCEDURE = time_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION ts_dist(timestamp, timestamp)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamp,
- RIGHTARG = timestamp,
- PROCEDURE = ts_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION tstz_dist(timestamptz, timestamptz)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamptz,
- RIGHTARG = timestamptz,
- PROCEDURE = tstz_dist,
- COMMUTATOR = '<->'
-);
-
-
---
---
---
--- oid ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_oid_ops
-DEFAULT FOR TYPE oid USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
- FUNCTION 2 gbt_oid_union (internal, internal),
- FUNCTION 3 gbt_oid_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
- FUNCTION 6 gbt_oid_picksplit (internal, internal),
- FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
--- Add operators that are new in 9.1. We do it like this, leaving them
--- "loose" in the operator family rather than bound into the opclass, because
--- that's the only state that can be reproduced during an upgrade from 9.0.
-ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
- OPERATOR 6 <> (oid, oid) ,
- OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
- FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
- -- Also add support function for index-only-scans, added in 9.5.
- FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
-
-
---
---
---
--- int2 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_union(internal, internal)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int2_ops
-DEFAULT FOR TYPE int2 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
- FUNCTION 2 gbt_int2_union (internal, internal),
- FUNCTION 3 gbt_int2_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int2_picksplit (internal, internal),
- FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
- STORAGE gbtreekey4;
-
-ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
- OPERATOR 6 <> (int2, int2) ,
- OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
- FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
-
---
---
---
--- int4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int4_ops
-DEFAULT FOR TYPE int4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
- FUNCTION 2 gbt_int4_union (internal, internal),
- FUNCTION 3 gbt_int4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int4_picksplit (internal, internal),
- FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
- OPERATOR 6 <> (int4, int4) ,
- OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
- FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
-
-
---
---
---
--- int8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int8_ops
-DEFAULT FOR TYPE int8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
- FUNCTION 2 gbt_int8_union (internal, internal),
- FUNCTION 3 gbt_int8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int8_picksplit (internal, internal),
- FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
- OPERATOR 6 <> (int8, int8) ,
- OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
- FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
-
---
---
---
--- float4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float4_ops
-DEFAULT FOR TYPE float4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
- FUNCTION 2 gbt_float4_union (internal, internal),
- FUNCTION 3 gbt_float4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float4_picksplit (internal, internal),
- FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
- OPERATOR 6 <> (float4, float4) ,
- OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
- FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
-
---
---
---
--- float8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float8_ops
-DEFAULT FOR TYPE float8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
- FUNCTION 2 gbt_float8_union (internal, internal),
- FUNCTION 3 gbt_float8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float8_picksplit (internal, internal),
- FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
- OPERATOR 6 <> (float8, float8) ,
- OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
- FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
-
---
---
---
--- timestamp ops
---
---
---
-
-CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamp_ops
-DEFAULT FOR TYPE timestamp USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_ts_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
- OPERATOR 6 <> (timestamp, timestamp) ,
- OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
- FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamptz_ops
-DEFAULT FOR TYPE timestamptz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_tstz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
- OPERATOR 6 <> (timestamptz, timestamptz) ,
- OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
- FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
-
---
---
---
--- time ops
---
---
---
-
-CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_time_ops
-DEFAULT FOR TYPE time USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_time_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
- OPERATOR 6 <> (time, time) ,
- OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
- FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
-
-
-CREATE OPERATOR CLASS gist_timetz_ops
-DEFAULT FOR TYPE timetz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_timetz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
- OPERATOR 6 <> (timetz, timetz) ;
- -- no 'fetch' function, as the compress function is lossy.
-
-
---
---
---
--- date ops
---
---
---
-
-CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_date_ops
-DEFAULT FOR TYPE date USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
- FUNCTION 2 gbt_date_union (internal, internal),
- FUNCTION 3 gbt_date_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_date_penalty (internal, internal, internal),
- FUNCTION 6 gbt_date_picksplit (internal, internal),
- FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
- OPERATOR 6 <> (date, date) ,
- OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
- FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
-
-
---
---
---
--- interval ops
---
---
---
-
-CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_union(internal, internal)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_interval_ops
-DEFAULT FOR TYPE interval USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
- FUNCTION 2 gbt_intv_union (internal, internal),
- FUNCTION 3 gbt_intv_compress (internal),
- FUNCTION 4 gbt_intv_decompress (internal),
- FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
- FUNCTION 6 gbt_intv_picksplit (internal, internal),
- FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
- STORAGE gbtreekey32;
-
-ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
- OPERATOR 6 <> (interval, interval) ,
- OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
- FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
-
-
---
---
---
--- cash ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cash_ops
-DEFAULT FOR TYPE money USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
- FUNCTION 2 gbt_cash_union (internal, internal),
- FUNCTION 3 gbt_cash_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
- FUNCTION 6 gbt_cash_picksplit (internal, internal),
- FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
- OPERATOR 6 <> (money, money) ,
- OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
- FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
- FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
-
-
---
---
---
--- macaddr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_macaddr_ops
-DEFAULT FOR TYPE macaddr USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
- FUNCTION 2 gbt_macad_union (internal, internal),
- FUNCTION 3 gbt_macad_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
- FUNCTION 6 gbt_macad_picksplit (internal, internal),
- FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
- OPERATOR 6 <> (macaddr, macaddr) ,
- FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
-
-
---
---
---
--- text/ bpchar ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_text_ops
-DEFAULT FOR TYPE text USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_text_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
- OPERATOR 6 <> (text, text) ,
- FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
-
-
----- Create the operator class
-CREATE OPERATOR CLASS gist_bpchar_ops
-DEFAULT FOR TYPE bpchar USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_bpchar_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
- OPERATOR 6 <> (bpchar, bpchar) ,
- FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
-
---
---
--- bytea ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bytea_ops
-DEFAULT FOR TYPE bytea USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
- FUNCTION 2 gbt_bytea_union (internal, internal),
- FUNCTION 3 gbt_bytea_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bytea_picksplit (internal, internal),
- FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
- OPERATOR 6 <> (bytea, bytea) ,
- FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
-
-
---
---
---
--- numeric ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_numeric_ops
-DEFAULT FOR TYPE numeric USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
- FUNCTION 2 gbt_numeric_union (internal, internal),
- FUNCTION 3 gbt_numeric_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
- FUNCTION 6 gbt_numeric_picksplit (internal, internal),
- FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
- OPERATOR 6 <> (numeric, numeric) ,
- FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
-
-
---
---
--- bit ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bit_ops
-DEFAULT FOR TYPE bit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
- OPERATOR 6 <> (bit, bit) ,
- FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
-
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_vbit_ops
-DEFAULT FOR TYPE varbit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
- OPERATOR 6 <> (varbit, varbit) ,
- FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
-
-
---
---
---
--- inet/cidr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_inet_ops
-DEFAULT FOR TYPE inet USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cidr_ops
-DEFAULT FOR TYPE cidr USING gist
-AS
- OPERATOR 1 < (inet, inet) ,
- OPERATOR 2 <= (inet, inet) ,
- OPERATOR 3 = (inet, inet) ,
- OPERATOR 4 >= (inet, inet) ,
- OPERATOR 5 > (inet, inet) ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
diff --git a/contrib/btree_gist/btree_gist--1.3--1.4.sql b/contrib/btree_gist/btree_gist--1.3--1.4.sql
new file mode 100644
index 0000000..4062aed
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.3--1.4.sql
@@ -0,0 +1,150 @@
+/* contrib/btree_gist/btree_gist--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.4'" to load this file. \quit
+
+-- update references to distance operators in pg_amop and pg_depend
+
+WITH
+btree_ops AS (
+ SELECT
+ amoplefttype, amoprighttype, amopopr
+ FROM
+ pg_amop
+ JOIN pg_am ON pg_am.oid = amopmethod
+ JOIN pg_opfamily ON pg_opfamily.oid = amopfamily
+ JOIN pg_namespace ON pg_namespace.oid = opfnamespace
+ WHERE
+ nspname = 'pg_catalog'
+ AND opfname IN (
+ 'integer_ops',
+ 'oid_ops',
+ 'money_ops',
+ 'float_ops',
+ 'datetime_ops',
+ 'time_ops',
+ 'interval_ops'
+ )
+ AND amname = 'btree'
+ AND amoppurpose = 'o'
+ AND amopstrategy = 6
+ ),
+gist_ops AS (
+ SELECT
+ pg_amop.oid AS oid, amoplefttype, amoprighttype, amopopr
+ FROM
+ pg_amop
+ JOIN pg_am ON amopmethod = pg_am.oid
+ JOIN pg_opfamily ON amopfamily = pg_opfamily.oid
+ JOIN pg_namespace ON pg_namespace.oid = opfnamespace
+ WHERE
+ nspname = current_schema()
+ AND opfname IN (
+ 'gist_oid_ops',
+ 'gist_int2_ops',
+ 'gist_int4_ops',
+ 'gist_int8_ops',
+ 'gist_float4_ops',
+ 'gist_float8_ops',
+ 'gist_timestamp_ops',
+ 'gist_timestamptz_ops',
+ 'gist_time_ops',
+ 'gist_date_ops',
+ 'gist_interval_ops',
+ 'gist_cash_ops'
+ )
+ AND amname = 'gist'
+ AND amoppurpose = 'o'
+ AND amopstrategy = 15
+ ),
+depend_update_data(gist_amop, gist_amopopr, btree_amopopr) AS (
+ SELECT
+ gist_ops.oid, gist_ops.amopopr, btree_ops.amopopr
+ FROM
+ btree_ops JOIN gist_ops USING (amoplefttype, amoprighttype)
+),
+amop_update_data AS (
+ UPDATE
+ pg_depend
+ SET
+ refobjid = btree_amopopr
+ FROM
+ depend_update_data
+ WHERE
+ objid = gist_amop AND refobjid = gist_amopopr
+ RETURNING
+ depend_update_data.*
+)
+UPDATE
+ pg_amop
+SET
+ amopopr = btree_amopopr
+FROM
+ amop_update_data
+WHERE
+ pg_amop.oid = gist_amop;
+
+-- disable implicit pg_catalog search
+
+DO
+$$
+BEGIN
+ EXECUTE 'SET LOCAL search_path TO ' || current_schema() || ', pg_catalog';
+END
+$$;
+
+-- drop distance operators
+
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int2, int2);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int4, int4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int8, int8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float4, float4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float8, float8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (oid, oid);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (money, money);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (date, date);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (time, time);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (interval, interval);
+
+DROP OPERATOR <-> (int2, int2);
+DROP OPERATOR <-> (int4, int4);
+DROP OPERATOR <-> (int8, int8);
+DROP OPERATOR <-> (float4, float4);
+DROP OPERATOR <-> (float8, float8);
+DROP OPERATOR <-> (oid, oid);
+DROP OPERATOR <-> (money, money);
+DROP OPERATOR <-> (date, date);
+DROP OPERATOR <-> (time, time);
+DROP OPERATOR <-> (timestamp, timestamp);
+DROP OPERATOR <-> (timestamptz, timestamptz);
+DROP OPERATOR <-> (interval, interval);
+
+-- drop distance functions
+
+ALTER EXTENSION btree_gist DROP FUNCTION int2_dist(int2, int2);
+ALTER EXTENSION btree_gist DROP FUNCTION int4_dist(int4, int4);
+ALTER EXTENSION btree_gist DROP FUNCTION int8_dist(int8, int8);
+ALTER EXTENSION btree_gist DROP FUNCTION float4_dist(float4, float4);
+ALTER EXTENSION btree_gist DROP FUNCTION float8_dist(float8, float8);
+ALTER EXTENSION btree_gist DROP FUNCTION oid_dist(oid, oid);
+ALTER EXTENSION btree_gist DROP FUNCTION cash_dist(money, money);
+ALTER EXTENSION btree_gist DROP FUNCTION date_dist(date, date);
+ALTER EXTENSION btree_gist DROP FUNCTION time_dist(time, time);
+ALTER EXTENSION btree_gist DROP FUNCTION ts_dist(timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP FUNCTION interval_dist(interval, interval);
+
+DROP FUNCTION int2_dist(int2, int2);
+DROP FUNCTION int4_dist(int4, int4);
+DROP FUNCTION int8_dist(int8, int8);
+DROP FUNCTION float4_dist(float4, float4);
+DROP FUNCTION float8_dist(float8, float8);
+DROP FUNCTION oid_dist(oid, oid);
+DROP FUNCTION cash_dist(money, money);
+DROP FUNCTION date_dist(date, date);
+DROP FUNCTION time_dist(time, time);
+DROP FUNCTION ts_dist(timestamp, timestamp);
+DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+DROP FUNCTION interval_dist(interval, interval);
diff --git a/contrib/btree_gist/btree_gist--1.4.sql b/contrib/btree_gist/btree_gist--1.4.sql
new file mode 100644
index 0000000..1f1327d
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.4.sql
@@ -0,0 +1,1490 @@
+/* contrib/btree_gist/btree_gist--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
+
+CREATE FUNCTION gbtreekey4_in(cstring)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey4_out(gbtreekey4)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey4 (
+ INTERNALLENGTH = 4,
+ INPUT = gbtreekey4_in,
+ OUTPUT = gbtreekey4_out
+);
+
+CREATE FUNCTION gbtreekey8_in(cstring)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey8_out(gbtreekey8)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey8 (
+ INTERNALLENGTH = 8,
+ INPUT = gbtreekey8_in,
+ OUTPUT = gbtreekey8_out
+);
+
+CREATE FUNCTION gbtreekey16_in(cstring)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey16_out(gbtreekey16)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey16 (
+ INTERNALLENGTH = 16,
+ INPUT = gbtreekey16_in,
+ OUTPUT = gbtreekey16_out
+);
+
+CREATE FUNCTION gbtreekey32_in(cstring)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey32_out(gbtreekey32)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey32 (
+ INTERNALLENGTH = 32,
+ INPUT = gbtreekey32_in,
+ OUTPUT = gbtreekey32_out
+);
+
+CREATE FUNCTION gbtreekey_var_in(cstring)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey_var (
+ INTERNALLENGTH = VARIABLE,
+ INPUT = gbtreekey_var_in,
+ OUTPUT = gbtreekey_var_out,
+ STORAGE = EXTENDED
+);
+
+
+--
+--
+--
+-- oid ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_oid_ops
+DEFAULT FOR TYPE oid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
+ FUNCTION 2 gbt_oid_union (internal, internal),
+ FUNCTION 3 gbt_oid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_oid_picksplit (internal, internal),
+ FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+-- Add operators that are new in 9.1. We do it like this, leaving them
+-- "loose" in the operator family rather than bound into the opclass, because
+-- that's the only state that can be reproduced during an upgrade from 9.0.
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
+ OPERATOR 6 <> (oid, oid) ,
+ OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
+ FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
+ -- Also add support function for index-only-scans, added in 9.5.
+ FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
+
+
+--
+--
+--
+-- int2 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_union(internal, internal)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int2_ops
+DEFAULT FOR TYPE int2 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
+ FUNCTION 2 gbt_int2_union (internal, internal),
+ FUNCTION 3 gbt_int2_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int2_picksplit (internal, internal),
+ FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
+ STORAGE gbtreekey4;
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
+ OPERATOR 6 <> (int2, int2) ,
+ OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
+ FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
+
+--
+--
+--
+-- int4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int4_ops
+DEFAULT FOR TYPE int4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
+ FUNCTION 2 gbt_int4_union (internal, internal),
+ FUNCTION 3 gbt_int4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int4_picksplit (internal, internal),
+ FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
+ OPERATOR 6 <> (int4, int4) ,
+ OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
+ FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
+
+
+--
+--
+--
+-- int8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int8_ops
+DEFAULT FOR TYPE int8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
+ FUNCTION 2 gbt_int8_union (internal, internal),
+ FUNCTION 3 gbt_int8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int8_picksplit (internal, internal),
+ FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
+ OPERATOR 6 <> (int8, int8) ,
+ OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
+ FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
+
+--
+--
+--
+-- float4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float4_ops
+DEFAULT FOR TYPE float4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
+ FUNCTION 2 gbt_float4_union (internal, internal),
+ FUNCTION 3 gbt_float4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float4_picksplit (internal, internal),
+ FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
+ OPERATOR 6 <> (float4, float4) ,
+ OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
+ FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
+
+--
+--
+--
+-- float8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float8_ops
+DEFAULT FOR TYPE float8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
+ FUNCTION 2 gbt_float8_union (internal, internal),
+ FUNCTION 3 gbt_float8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float8_picksplit (internal, internal),
+ FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
+ OPERATOR 6 <> (float8, float8) ,
+ OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
+ FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
+
+--
+--
+--
+-- timestamp ops
+--
+--
+--
+
+CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamp_ops
+DEFAULT FOR TYPE timestamp USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_ts_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
+ OPERATOR 6 <> (timestamp, timestamp) ,
+ OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
+ FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamptz_ops
+DEFAULT FOR TYPE timestamptz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_tstz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
+ OPERATOR 6 <> (timestamptz, timestamptz) ,
+ OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
+ FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
+
+--
+--
+--
+-- time ops
+--
+--
+--
+
+CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_time_ops
+DEFAULT FOR TYPE time USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_time_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
+ OPERATOR 6 <> (time, time) ,
+ OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
+ FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
+
+
+CREATE OPERATOR CLASS gist_timetz_ops
+DEFAULT FOR TYPE timetz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_timetz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
+ OPERATOR 6 <> (timetz, timetz) ;
+ -- no 'fetch' function, as the compress function is lossy.
+
+
+--
+--
+--
+-- date ops
+--
+--
+--
+
+CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_date_ops
+DEFAULT FOR TYPE date USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
+ FUNCTION 2 gbt_date_union (internal, internal),
+ FUNCTION 3 gbt_date_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_date_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_date_picksplit (internal, internal),
+ FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
+ OPERATOR 6 <> (date, date) ,
+ OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
+ FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
+
+
+--
+--
+--
+-- interval ops
+--
+--
+--
+
+CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_interval_ops
+DEFAULT FOR TYPE interval USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
+ FUNCTION 2 gbt_intv_union (internal, internal),
+ FUNCTION 3 gbt_intv_compress (internal),
+ FUNCTION 4 gbt_intv_decompress (internal),
+ FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_intv_picksplit (internal, internal),
+ FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
+ OPERATOR 6 <> (interval, interval) ,
+ OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
+ FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
+
+
+--
+--
+--
+-- cash ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cash_ops
+DEFAULT FOR TYPE money USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
+ FUNCTION 2 gbt_cash_union (internal, internal),
+ FUNCTION 3 gbt_cash_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_cash_picksplit (internal, internal),
+ FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
+ OPERATOR 6 <> (money, money) ,
+ OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
+ FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
+ FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
+
+
+--
+--
+--
+-- macaddr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr_ops
+DEFAULT FOR TYPE macaddr USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
+ FUNCTION 2 gbt_macad_union (internal, internal),
+ FUNCTION 3 gbt_macad_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
+ OPERATOR 6 <> (macaddr, macaddr) ,
+ FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
+
+
+--
+--
+--
+-- text/ bpchar ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_text_ops
+DEFAULT FOR TYPE text USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_text_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
+ OPERATOR 6 <> (text, text) ,
+ FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
+
+
+---- Create the operator class
+CREATE OPERATOR CLASS gist_bpchar_ops
+DEFAULT FOR TYPE bpchar USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_bpchar_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
+ OPERATOR 6 <> (bpchar, bpchar) ,
+ FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
+
+--
+--
+-- bytea ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bytea_ops
+DEFAULT FOR TYPE bytea USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
+ FUNCTION 2 gbt_bytea_union (internal, internal),
+ FUNCTION 3 gbt_bytea_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bytea_picksplit (internal, internal),
+ FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
+ OPERATOR 6 <> (bytea, bytea) ,
+ FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- numeric ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_numeric_ops
+DEFAULT FOR TYPE numeric USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
+ FUNCTION 2 gbt_numeric_union (internal, internal),
+ FUNCTION 3 gbt_numeric_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_numeric_picksplit (internal, internal),
+ FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
+ OPERATOR 6 <> (numeric, numeric) ,
+ FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
+
+
+--
+--
+-- bit ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bit_ops
+DEFAULT FOR TYPE bit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
+ OPERATOR 6 <> (bit, bit) ,
+ FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
+
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_vbit_ops
+DEFAULT FOR TYPE varbit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
+ OPERATOR 6 <> (varbit, varbit) ,
+ FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- inet/cidr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_inet_ops
+DEFAULT FOR TYPE inet USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cidr_ops
+DEFAULT FOR TYPE cidr USING gist
+AS
+ OPERATOR 1 < (inet, inet) ,
+ OPERATOR 2 <= (inet, inet) ,
+ OPERATOR 3 = (inet, inet) ,
+ OPERATOR 4 >= (inet, inet) ,
+ OPERATOR 5 > (inet, inet) ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+--
+--
+--
+-- uuid ops
+--
+--
+---- define the GiST support methods
+CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_uuid_ops
+DEFAULT FOR TYPE uuid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal),
+ FUNCTION 2 gbt_uuid_union (internal, internal),
+ FUNCTION 3 gbt_uuid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_uuid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_uuid_picksplit (internal, internal),
+ FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+-- These are "loose" in the opfamily for consistency with the rest of btree_gist
+ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
+ OPERATOR 6 <> (uuid, uuid) ,
+ FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ;
+
diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control
index ddbf83d..fdf0e6a 100644
--- a/contrib/btree_gist/btree_gist.control
+++ b/contrib/btree_gist/btree_gist.control
@@ -1,5 +1,5 @@
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
-default_version = '1.3'
+default_version = '1.4'
module_pathname = '$libdir/btree_gist'
relocatable = true
diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c
index 54dc1cc..c3cee90 100644
--- a/contrib/btree_gist/btree_int2.c
+++ b/contrib/btree_gist/btree_int2.c
@@ -89,28 +89,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int2_dist);
-Datum
-int2_dist(PG_FUNCTION_ARGS)
-{
- int16 a = PG_GETARG_INT16(0);
- int16 b = PG_GETARG_INT16(1);
- int16 r;
- int16 ra;
-
- r = a - b;
- ra = Abs(r);
-
- /* Overflow check. */
- if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("smallint out of range")));
-
- PG_RETURN_INT16(ra);
-}
-
-
/**************************************************
* int16 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c
index ddbcf52..e79ab3e 100644
--- a/contrib/btree_gist/btree_int4.c
+++ b/contrib/btree_gist/btree_int4.c
@@ -90,28 +90,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int4_dist);
-Datum
-int4_dist(PG_FUNCTION_ARGS)
-{
- int32 a = PG_GETARG_INT32(0);
- int32 b = PG_GETARG_INT32(1);
- int32 r;
- int32 ra;
-
- r = a - b;
- ra = Abs(r);
-
- /* Overflow check. */
- if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
-
- PG_RETURN_INT32(ra);
-}
-
-
/**************************************************
* int32 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c
index 44bf69a..89b8430 100644
--- a/contrib/btree_gist/btree_int8.c
+++ b/contrib/btree_gist/btree_int8.c
@@ -90,28 +90,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int8_dist);
-Datum
-int8_dist(PG_FUNCTION_ARGS)
-{
- int64 a = PG_GETARG_INT64(0);
- int64 b = PG_GETARG_INT64(1);
- int64 r;
- int64 ra;
-
- r = a - b;
- ra = Abs(r);
-
- /* Overflow check. */
- if (ra < 0 || (!SAMESIGN(a, b) && !SAMESIGN(r, a)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
-
- PG_RETURN_INT64(ra);
-}
-
-
/**************************************************
* int64 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c
index e5cd0a2..af48540 100644
--- a/contrib/btree_gist/btree_interval.c
+++ b/contrib/btree_gist/btree_interval.c
@@ -110,32 +110,6 @@ static const gbtree_ninfo tinfo =
};
-Interval *
-abs_interval(Interval *a)
-{
- static Interval zero = {0, 0, 0};
-
- if (DatumGetBool(DirectFunctionCall2(interval_lt,
- IntervalPGetDatum(a),
- IntervalPGetDatum(&zero))))
- a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
- IntervalPGetDatum(a)));
-
- return a;
-}
-
-PG_FUNCTION_INFO_V1(interval_dist);
-Datum
-interval_dist(PG_FUNCTION_ARGS)
-{
- Datum diff = DirectFunctionCall2(interval_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
-}
-
-
/**************************************************
* interval ops
**************************************************/
diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c
index ac61a76..d389fe2 100644
--- a/contrib/btree_gist/btree_oid.c
+++ b/contrib/btree_gist/btree_oid.c
@@ -96,22 +96,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(oid_dist);
-Datum
-oid_dist(PG_FUNCTION_ARGS)
-{
- Oid a = PG_GETARG_OID(0);
- Oid b = PG_GETARG_OID(1);
- Oid res;
-
- if (a < b)
- res = b - a;
- else
- res = a - b;
- PG_RETURN_OID(res);
-}
-
-
/**************************************************
* Oid ops
**************************************************/
diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c
index 27f30bc..c515168 100644
--- a/contrib/btree_gist/btree_time.c
+++ b/contrib/btree_gist/btree_time.c
@@ -137,18 +137,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(time_dist);
-Datum
-time_dist(PG_FUNCTION_ARGS)
-{
- Datum diff = DirectFunctionCall2(time_mi_time,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
-}
-
-
/**************************************************
* time ops
**************************************************/
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index ab22b27..899fbf4 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -139,63 +139,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(ts_dist);
-Datum
-ts_dist(PG_FUNCTION_ARGS)
-{
- Timestamp a = PG_GETARG_TIMESTAMP(0);
- Timestamp b = PG_GETARG_TIMESTAMP(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
-#ifdef HAVE_INT64_TIMESTAMP
- p->time = PG_INT64_MAX;
-#else
- p->time = DBL_MAX;
-#endif
- PG_RETURN_INTERVAL_P(p);
- }
- else
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
-}
-
-PG_FUNCTION_INFO_V1(tstz_dist);
-Datum
-tstz_dist(PG_FUNCTION_ARGS)
-{
- TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
- TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
-#ifdef HAVE_INT64_TIMESTAMP
- p->time = PG_INT64_MAX;
-#else
- p->time = DBL_MAX;
-#endif
- PG_RETURN_INTERVAL_P(p);
- }
-
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
-}
-
-
/**************************************************
* timestamp ops
**************************************************/
diff --git a/contrib/btree_gist/btree_utils_num.h b/contrib/btree_gist/btree_utils_num.h
index a33491b..8f873a5 100644
--- a/contrib/btree_gist/btree_utils_num.h
+++ b/contrib/btree_gist/btree_utils_num.h
@@ -116,8 +116,6 @@ do { \
} while(0)
-extern Interval *abs_interval(Interval *a);
-
extern bool gbt_num_consistent(const GBT_NUMKEY_R *key, const void *query,
const StrategyNumber *strategy, bool is_leaf,
const gbtree_ninfo *tinfo);
0007-add-regression-tests-for-kNN-btree-v02.patchtext/x-patch; name=0007-add-regression-tests-for-kNN-btree-v02.patchDownload
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 755cd17..3ab1ad7 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -150,3 +150,489 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+---
+--- Test B-tree distance ordering
+---
+SET enable_bitmapscan = OFF;
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+ QUERY PLAN
+------------------------------
+ Sort
+ Sort Key: ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+ QUERY PLAN
+------------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((random <-> 1))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)))
+ Order By: (random <-> 4000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 1 | 9001
+ 1 | 8001
+ 1 | 7001
+ 1 | 6001
+ 1 | 5001
+ 1 | 4001
+ 1 | 3001
+ 1 | 2001
+ 1 | 1001
+ 1 | 1
+ 0 | 9000
+ 0 | 8000
+ 0 | 7000
+ 0 | 6000
+ 0 | 5000
+ 0 | 4000
+ 0 | 3000
+ 0 | 2000
+ 0 | 1000
+ 0 | 0
+ | 1
+ | 2
+ | 3
+(33 rows)
+
+DROP INDEX tenk3_idx;
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 1 | 1
+ 1 | 1001
+ 1 | 2001
+ 1 | 3001
+ 1 | 4001
+ 1 | 5001
+ 1 | 6001
+ 1 | 7001
+ 1 | 8001
+ 1 | 9001
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 0 | 0
+ 0 | 1000
+ 0 | 2000
+ 0 | 3000
+ 0 | 4000
+ 0 | 5000
+ 0 | 6000
+ 0 | 7000
+ 0 | 8000
+ 0 | 9000
+ | 3
+ | 2
+ | 1
+(33 rows)
+
+DROP INDEX tenk3_idx;
+DROP TABLE tenk3;
+RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 65b08c8..b0559d7 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -92,3 +92,108 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+
+---
+--- Test B-tree distance ordering
+---
+
+SET enable_bitmapscan = OFF;
+
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+
+
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+DROP TABLE tenk3;
+
+RESET enable_bitmapscan;
Hi, Nikita!
I have assigned as a reviewer for this patchset. I took a fist look to
these patches.
At first, I'd like to notice that it's very cool that you picked up this
work. I frequently hear people complains about lack of this feature.
k | kNN-btree | kNN-GiST | Opt. query | Seq. scan
| | (btree_gist) | with UNION | with sort
--------|--------------|--------------|---------------|------------
1 | 0.041 4 | 0.079 4 | 0.060 8 | 41.1 1824
10 | 0.048 7 | 0.091 9 | 0.097 17 | 41.8 1824
100 | 0.107 47 | 0.192 52 | 0.342 104 | 42.3 1824
1000 | 0.735 573 | 0.913 650 | 2.970 1160 | 43.5 1824
10000 | 5.070 5622 | 6.240 6760 | 36.300 11031 | 54.1 1824
100000 | 49.600 51608 | 61.900 64194 | 295.100 94980 | 115.0 1824
These results looks quite expected. KNN-btree uses about half of blocks in
comparison with UNION query, and it's more than twice faster. In
comparison with kNN-GiST there is still some win.
1. Introduce amcanorderbyop() function
This patch transforms existing boolean AM property amcanorderbyop into a
method
(function pointer). This is necessary because, unlike GiST, kNN for btree
supports only a one ordering operator on the first index column and we
need a
different pathkey matching logic for btree (there was a corresponding
comment
in match_pathkeys_to_index()). GiST-specific logic has been moved from
match_pathkeys_to_index() to gistcanorderbyop().
I'm not very excited about this design of amcanorderbyop callback.
Introducing new callback from index access method to the planner should
imply quite good flexibility to the future. In this particular signature
of callback I see no potential future use-cases than your implementation
for btree. We could just add amcanorderbyonlyfisrtop property and that
would give us same level of flexibility I think.
With existing index types, we could cover much more orderings that we
currently do. Some of possible cases:
1) "ORDER BY col" for btree_gist, SP-GiST text_ops
2) "ORDER BY col1, col2 <-> const" for btree_gist
3) "ORDER BY col1, col2 <-> const" for btree
I understand that #3 is quite hard task and I don't ask you to implement it
now. But it would be nice if some day we decide to add #3, we wouldn't
have to change IndexAmRoutine definition.
My idea is that we need more general redesign of specifying ordering which
index can produce. Ideally, we should replace amcanorder, amcanbackward
and amcanorderbyop with single callback. Such callback should take a list
of pathkeys and return number of leading pathkeys index could satisfy (with
corresponding information for index scan). I'm not sure that other hackers
would agree with such design, but I'm very convinced that we need something
of this level of extendability. Otherwise we would have to hack our
planner <-> index_access_method interface each time we decide to cover
another index produced ordering.
6. Remove duplicate distance operators from contrib/btree_gist.
References to their own distance operators in btree_gist opclasses are
replaced with references to the built-in operators and than duplicate
operators are dropped. But if the user is using somewhere these operators,
upgrade of btree_gist from 1.3 to 1.4 would fail.
The query in "btree_gist--1.3--1.4.sql" which directly touches system
catalogue to update opfamilies looks too hackery. I think we shouldn't use
such queries until we have no other choice.
In this particular case we can update opfamilies using legal mechanism
"ALTER OPERATOR FAMILY name USING index_method ADD/DROP ... " (note that
operator name could be schema-qualified if needed). This way wouldn't be
that brief, but it is much more correct.
Also this like catch my eyes.
info->amcanorderbyop = (void (*)()) amroutine->amcanorderbyop;
It's not necessary to use cast here. For instance, we don't use cast
for amcostestimate.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On Thu, Feb 16, 2017 at 8:05 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
My idea is that we need more general redesign of specifying ordering which
index can produce. Ideally, we should replace amcanorder, amcanbackward and
amcanorderbyop with single callback. Such callback should take a list of
pathkeys and return number of leading pathkeys index could satisfy (with
corresponding information for index scan). I'm not sure that other hackers
would agree with such design, but I'm very convinced that we need something
of this level of extendability. Otherwise we would have to hack our planner
<-> index_access_method interface each time we decide to cover another index
produced ordering.
Yeah. I'm not sure if that's exactly the right idea. But it seems
like we need something.
info->amcanorderbyop = (void (*)()) amroutine->amcanorderbyop;
It's not necessary to use cast here. For instance, we don't use cast for
amcostestimate.
In fact, it's bad to use the cast here, because if in future the
signature of one of amroutine->amcanorderbyop is changed and
info->amcanorderbyop is not changed to match, then the cast will
prevent a compiler warning, but at runtime you may crash.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
On Thu, Feb 16, 2017 at 8:05 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:My idea is that we need more general redesign of specifying ordering which
index can produce. Ideally, we should replace amcanorder, amcanbackward and
amcanorderbyop with single callback. Such callback should take a list of
pathkeys and return number of leading pathkeys index could satisfy (with
corresponding information for index scan). I'm not sure that other hackers
would agree with such design, but I'm very convinced that we need something
of this level of extendability. Otherwise we would have to hack our planner
<-> index_access_method interface each time we decide to cover another index
produced ordering.
Yeah. I'm not sure if that's exactly the right idea. But it seems
like we need something.
That's definitely not exactly the right idea, because using it would
require the core planner to play twenty-questions trying to guess which
pathkeys the index can satisfy. ("Can you satisfy some prefix of this
pathkey list? How about that one?") It could be sensible to have a
callback that's called once per index and hands back a list of pathkey
lists that represent interesting orders the index could produce, which
could be informed by looking aside at the PlannerInfo contents to see
what is likely to be relevant to the query.
But even so, I'm not convinced that that is a better design or more
maintainable than the current approach. I fear that it will lead to
duplicating substantial amounts of code and knowledge into each index AM,
which is not an improvement; and if anything, that increases the risk of
breaking every index AM anytime you want to introduce some fundamentally
new capability in the area. Now that it's actually practical to have
out-of-core index AMs, that's a bigger concern than it might once have
been.
Also see the discussion that led up to commit ed0097e4f. Users objected
the last time we tried to make index capabilities opaque at the SQL level,
so they're not going to like a design that tries to hide that information
even from the core C code.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Feb 16, 2017 at 10:59 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Thu, Feb 16, 2017 at 8:05 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:My idea is that we need more general redesign of specifying ordering which
index can produce. Ideally, we should replace amcanorder, amcanbackward and
amcanorderbyop with single callback. Such callback should take a list of
pathkeys and return number of leading pathkeys index could satisfy (with
corresponding information for index scan). I'm not sure that other hackers
would agree with such design, but I'm very convinced that we need something
of this level of extendability. Otherwise we would have to hack our planner
<-> index_access_method interface each time we decide to cover another index
produced ordering.Yeah. I'm not sure if that's exactly the right idea. But it seems
like we need something.That's definitely not exactly the right idea, because using it would
require the core planner to play twenty-questions trying to guess which
pathkeys the index can satisfy. ("Can you satisfy some prefix of this
pathkey list? How about that one?") It could be sensible to have a
callback that's called once per index and hands back a list of pathkey
lists that represent interesting orders the index could produce, which
could be informed by looking aside at the PlannerInfo contents to see
what is likely to be relevant to the query.But even so, I'm not convinced that that is a better design or more
maintainable than the current approach. I fear that it will lead to
duplicating substantial amounts of code and knowledge into each index AM,
which is not an improvement; and if anything, that increases the risk of
breaking every index AM anytime you want to introduce some fundamentally
new capability in the area. Now that it's actually practical to have
out-of-core index AMs, that's a bigger concern than it might once have
been.
Yeah, that's all true. But I think Alexander is right that just
adding amcandoblah flags ad infinitum doesn't feel good either. The
interface isn't really arm's-length if every new thing somebody wants
to do something new requires another flag.
Also see the discussion that led up to commit ed0097e4f. Users objected
the last time we tried to make index capabilities opaque at the SQL level,
so they're not going to like a design that tries to hide that information
even from the core C code.
Discoverability is definitely important, but first we have to figure
out how we're going to make it work, and then we can work out how to
let users see how it works.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi Alexander,
On 2/16/17 11:20 AM, Robert Haas wrote:
On Thu, Feb 16, 2017 at 10:59 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Thu, Feb 16, 2017 at 8:05 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:My idea is that we need more general redesign of specifying ordering which
index can produce. Ideally, we should replace amcanorder, amcanbackward and
amcanorderbyop with single callback. Such callback should take a list of
pathkeys and return number of leading pathkeys index could satisfy (with
corresponding information for index scan). I'm not sure that other hackers
would agree with such design, but I'm very convinced that we need something
of this level of extendability. Otherwise we would have to hack our planner
<-> index_access_method interface each time we decide to cover another index
produced ordering.Yeah. I'm not sure if that's exactly the right idea. But it seems
like we need something.That's definitely not exactly the right idea, because using it would
require the core planner to play twenty-questions trying to guess which
pathkeys the index can satisfy. ("Can you satisfy some prefix of this
pathkey list? How about that one?") It could be sensible to have a
callback that's called once per index and hands back a list of pathkey
lists that represent interesting orders the index could produce, which
could be informed by looking aside at the PlannerInfo contents to see
what is likely to be relevant to the query.But even so, I'm not convinced that that is a better design or more
maintainable than the current approach. I fear that it will lead to
duplicating substantial amounts of code and knowledge into each index AM,
which is not an improvement; and if anything, that increases the risk of
breaking every index AM anytime you want to introduce some fundamentally
new capability in the area. Now that it's actually practical to have
out-of-core index AMs, that's a bigger concern than it might once have
been.Yeah, that's all true. But I think Alexander is right that just
adding amcandoblah flags ad infinitum doesn't feel good either. The
interface isn't really arm's-length if every new thing somebody wants
to do something new requires another flag.Also see the discussion that led up to commit ed0097e4f. Users objected
the last time we tried to make index capabilities opaque at the SQL level,
so they're not going to like a design that tries to hide that information
even from the core C code.Discoverability is definitely important, but first we have to figure
out how we're going to make it work, and then we can work out how to
let users see how it works.
Reading through this thread I'm concerned that this appears to be a big
change making its first appearance in the last CF. There is also the
need for a new patch and a general consensus of how to proceed.
I recommend moving this patch to 2017-07 or marking it RWF.
Thanks,
--
-David
david@pgmasters.net
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Mar 2, 2017 at 5:57 PM, David Steele <david@pgmasters.net> wrote:
Hi Alexander,
On 2/16/17 11:20 AM, Robert Haas wrote:
On Thu, Feb 16, 2017 at 10:59 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Thu, Feb 16, 2017 at 8:05 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:My idea is that we need more general redesign of specifying ordering
which
index can produce. Ideally, we should replace amcanorder,
amcanbackward and
amcanorderbyop with single callback. Such callback should take a
list of
pathkeys and return number of leading pathkeys index could satisfy
(with
corresponding information for index scan). I'm not sure that other
hackers
would agree with such design, but I'm very convinced that we need
something
of this level of extendability. Otherwise we would have to hack our
planner
<-> index_access_method interface each time we decide to cover
another index
produced ordering.
Yeah. I'm not sure if that's exactly the right idea. But it seems
like we need something.That's definitely not exactly the right idea, because using it would
require the core planner to play twenty-questions trying to guess which
pathkeys the index can satisfy. ("Can you satisfy some prefix of this
pathkey list? How about that one?") It could be sensible to have a
callback that's called once per index and hands back a list of pathkey
lists that represent interesting orders the index could produce, which
could be informed by looking aside at the PlannerInfo contents to see
what is likely to be relevant to the query.But even so, I'm not convinced that that is a better design or more
maintainable than the current approach. I fear that it will lead to
duplicating substantial amounts of code and knowledge into each indexAM,
which is not an improvement; and if anything, that increases the risk of
breaking every index AM anytime you want to introduce some fundamentally
new capability in the area. Now that it's actually practical to have
out-of-core index AMs, that's a bigger concern than it might once have
been.Yeah, that's all true. But I think Alexander is right that just
adding amcandoblah flags ad infinitum doesn't feel good either. The
interface isn't really arm's-length if every new thing somebody wants
to do something new requires another flag.Also see the discussion that led up to commit ed0097e4f. Users objected
the last time we tried to make index capabilities opaque at the SQLlevel,
so they're not going to like a design that tries to hide that
information
even from the core C code.
Discoverability is definitely important, but first we have to figure
out how we're going to make it work, and then we can work out how to
let users see how it works.Reading through this thread I'm concerned that this appears to be a big
change making its first appearance in the last CF. There is also the
need for a new patch and a general consensus of how to proceed.
Yes, refactoring of amcanorder/amcanorderbyop should be very thoughtful.
I recommend moving this patch to 2017-07 or marking it RWF.
I agree. Done.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attached 3rd version of the patches rebased onto the current master.
Changes from the previous version:
- Added support of INCLUDE columns to get_index_column_opclass() (1st patch).
- Added parallel kNN scan support.
- amcanorderbyop() was transformed into ammatchorderby() which takes a List of
PathKeys and checks each of them with new function match_orderbyop_pathkey()
extracted from match_pathkeys_to_index(). I think that this design can be
used in the future to support a mix of ordinary and order-by-op PathKeys,
but I am not sure.
On 09.03.2017 15:00, Alexander Korotkov wrote:
On Thu, Mar 2, 2017 at 5:57 PM, David Steele <david@pgmasters.net
<mailto:david@pgmasters.net>> wrote:Hi Alexander,
On 2/16/17 11:20 AM, Robert Haas wrote:
On Thu, Feb 16, 2017 at 10:59 AM, Tom Lane <tgl@sss.pgh.pa.us
<mailto:tgl@sss.pgh.pa.us>> wrote:
Robert Haas <robertmhaas@gmail.com
<mailto:robertmhaas@gmail.com>> writes:
On Thu, Feb 16, 2017 at 8:05 AM, Alexander Korotkov
<a.korotkov@postgrespro.ru <mailto:a.korotkov@postgrespro.ru>>wrote:
My idea is that we need more general redesign of specifying
ordering which
index can produce. Ideally, we should replace amcanorder,
amcanbackward and
amcanorderbyop with single callback. Such callback should
take a list of
pathkeys and return number of leading pathkeys index could
satisfy (with
corresponding information for index scan). I'm not sure that
other hackers
would agree with such design, but I'm very convinced that we
need something
of this level of extendability. Otherwise we would have to
hack our planner
<-> index_access_method interface each time we decide to
cover another index
produced ordering.
Yeah. I'm not sure if that's exactly the right idea. But it
seems
like we need something.
That's definitely not exactly the right idea, because using it
would
require the core planner to play twenty-questions trying to
guess which
pathkeys the index can satisfy. ("Can you satisfy some prefix
of this
pathkey list? How about that one?") It could be sensible to
have a
callback that's called once per index and hands back a list of
pathkey
lists that represent interesting orders the index could
produce, which
could be informed by looking aside at the PlannerInfo contents
to see
what is likely to be relevant to the query.
But even so, I'm not convinced that that is a better design or more
maintainable than the current approach. I fear that it willlead to
duplicating substantial amounts of code and knowledge into each
index AM,
which is not an improvement; and if anything, that increases
the risk of
breaking every index AM anytime you want to introduce some
fundamentally
new capability in the area. Now that it's actually practical
to have
out-of-core index AMs, that's a bigger concern than it might
once have
been.
Yeah, that's all true. But I think Alexander is right that just
adding amcandoblah flags ad infinitum doesn't feel good either. The
interface isn't really arm's-length if every new thing somebodywants
to do something new requires another flag.
Also see the discussion that led up to commit ed0097e4f. Users
objected
the last time we tried to make index capabilities opaque at the
SQL level,
so they're not going to like a design that tries to hide that
information
even from the core C code.
Discoverability is definitely important, but first we have to figure
out how we're going to make it work, and then we can work out how to
let users see how it works.Reading through this thread I'm concerned that this appears to be
a big
change making its first appearance in the last CF. There is also the
need for a new patch and a general consensus of how to proceed.Yes, refactoring of amcanorder/amcanorderbyop should be very thoughtful.
I recommend moving this patch to 2017-07 or marking it RWF.
I agree. Done.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
<http://www.postgrespro.com/>
The Russian Postgres Company
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-Fix-get_index_column_opclass-v03.patchtext/x-patch; name=0001-Fix-get_index_column_opclass-v03.patchDownload
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 0c116b3..a488c72 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3147,9 +3147,6 @@ get_index_column_opclass(Oid index_oid, int attno)
{
HeapTuple tuple;
Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
- Datum datum;
- bool isnull;
- oidvector *indclass;
Oid opclass;
/* First we need to know the column's opclass. */
@@ -3163,12 +3160,22 @@ get_index_column_opclass(Oid index_oid, int attno)
/* caller is supposed to guarantee this */
Assert(attno > 0 && attno <= rd_index->indnatts);
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indclass, &isnull);
- Assert(!isnull);
+ if (attno >= 1 && attno <= rd_index->indnkeyatts)
+ {
+ oidvector *indclass;
+ bool isnull;
+ Datum datum = SysCacheGetAttr(INDEXRELID, tuple,
+ Anum_pg_index_indclass,
+ &isnull);
+ Assert(!isnull);
- indclass = ((oidvector *) DatumGetPointer(datum));
- opclass = indclass->values[attno - 1];
+ indclass = ((oidvector *) DatumGetPointer(datum));
+ opclass = indclass->values[attno - 1];
+ }
+ else
+ {
+ opclass = InvalidOid;
+ }
ReleaseSysCache(tuple);
0002-Introduce-ammatchorderby-function-v03.patchtext/x-patch; name=0002-Introduce-ammatchorderby-function-v03.patchDownload
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6b2b9e3..71636ae 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,7 +109,6 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BLOOM_NSTRATEGIES;
amroutine->amsupport = BLOOM_NPROC;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -143,6 +142,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index e95fbbc..ffb6de0 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -86,7 +86,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -120,6 +119,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 0a32182..c142d09 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -41,7 +41,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GINNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -75,6 +74,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8a42eff..f60bb45 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,7 @@
#include "access/gistscan.h"
#include "catalog/pg_collation.h"
#include "miscadmin.h"
+#include "optimizer/paths.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "nodes/execnodes.h"
@@ -63,7 +64,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GISTNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -97,6 +97,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 0002df3..39f7f45 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -59,7 +59,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = HTMaxStrategyNumber;
amroutine->amsupport = HASHNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -93,6 +92,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index e8725fb..3e47c37 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -110,7 +110,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BTMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = true;
amroutine->amcanmulticol = true;
@@ -144,6 +143,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 9919e6f..a843650 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -32,10 +32,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-extern Expr *spgcanorderbyop(IndexOptInfo *index,
- PathKey *pathkey, int pathkeyno,
- Expr *orderby_clause, int *indexcol_p);
-
/*
* SP-GiST handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -48,7 +44,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = SPGISTNProc;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -82,6 +77,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 3b5c90e..6c763ef 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -1083,7 +1083,7 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
*/
IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
- if (!amroutine->amcanorderbyop)
+ if (!amroutine->ammatchorderby)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("access method \"%s\" does not support ordering operators",
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 6285a21..b7262e3 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -199,7 +199,7 @@ IndexNextWithReorder(IndexScanState *node)
* with just Asserting here because the system will not try to run the
* plan backwards if ExecSupportsBackwardScan() says it won't work.
* Currently, that is guaranteed because no index AMs support both
- * amcanorderbyop and amcanbackward; if any ever do,
+ * ammatchorderby and amcanbackward; if any ever do,
* ExecSupportsBackwardScan() will need to consider indexorderbys
* explicitly.
*/
@@ -1145,7 +1145,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* 5. NullTest ("indexkey IS NULL/IS NOT NULL"). We just fill in the
* ScanKey properly.
*
- * This code is also used to prepare ORDER BY expressions for amcanorderbyop
+ * This code is also used to prepare ORDER BY expressions for ammatchorderby
* indexes. The behavior is exactly the same, except that we have to look up
* the operator differently. Note that only cases 1 and 2 are currently
* possible for ORDER BY.
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index f295558..e043664 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -17,6 +17,7 @@
#include <math.h>
+#include "access/amapi.h"
#include "access/stratnum.h"
#include "access/sysattr.h"
#include "catalog/pg_am.h"
@@ -154,6 +155,10 @@ static void match_clause_to_index(IndexOptInfo *index,
static bool match_clause_to_indexcol(IndexOptInfo *index,
int indexcol,
RestrictInfo *rinfo);
+static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
+ int indexcol,
+ Expr *clause,
+ Oid pk_opfamily);
static bool is_indexable_operator(Oid expr_op, Oid opfamily,
bool indexkey_on_left);
static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
@@ -164,8 +169,6 @@ static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
List **orderby_clauses_p,
List **clause_columns_p);
-static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
- int indexcol, Expr *clause, Oid pk_opfamily);
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
EquivalenceClass *ec, EquivalenceMember *em,
void *arg);
@@ -998,7 +1001,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
orderbyclauses = NIL;
orderbyclausecols = NIL;
}
- else if (index->amcanorderbyop && pathkeys_possibly_useful)
+ else if (index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -2545,6 +2548,99 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
return false;
}
+Expr *
+match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey, int *indexcol_p)
+{
+ ListCell *lc;
+
+ /* Pathkey must request default sort order for the target opfamily */
+ if (pathkey->pk_strategy != BTLessStrategyNumber ||
+ pathkey->pk_nulls_first)
+ return NULL;
+
+ /* If eclass is volatile, no hope of using an indexscan */
+ if (pathkey->pk_eclass->ec_has_volatile)
+ return NULL;
+
+ /*
+ * Try to match eclass member expression(s) to index. Note that child
+ * EC members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could
+ * be considered to match more than one pathkey list, which is OK
+ * here. See also get_eclass_for_sort_expr.)
+ */
+ foreach(lc, pathkey->pk_eclass->ec_members)
+ {
+ EquivalenceMember *member = castNode(EquivalenceMember, lfirst(lc));
+ int indexcol;
+ int indexcol_min;
+ int indexcol_max;
+
+ /* No possibility of match if it references other relations */
+ if (!bms_equal(member->em_relids, index->rel->relids))
+ continue;
+
+ /* If *indexcol_p is non-negative then try to match only to it */
+ if (*indexcol_p >= 0)
+ {
+ indexcol_min = *indexcol_p;
+ indexcol_max = *indexcol_p + 1;
+ }
+ else /* try to match all columns */
+ {
+ indexcol_min = 0;
+ indexcol_max = index->ncolumns;
+ }
+
+ /*
+ * We allow any column of the GiST index to match each pathkey;
+ * they don't have to match left-to-right as you might expect.
+ */
+ for (indexcol = indexcol_min; indexcol < indexcol_max; indexcol++)
+ {
+ Expr *expr = match_clause_to_ordering_op(index,
+ indexcol,
+ member->em_expr,
+ pathkey->pk_opfamily);
+ if (expr)
+ {
+ *indexcol_p = indexcol;
+ return expr; /* don't want to look at remaining members */
+ }
+ }
+ }
+
+ return NULL;
+}
+
+bool
+match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p)
+{
+ ListCell *lc;
+
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = castNode(PathKey, lfirst(lc));
+ Expr *expr;
+ int indexcol = -1; /* match all index columns */
+
+ expr = match_orderbyop_pathkey(index, pathkey, &indexcol);
+
+ /*
+ * Note: for any failure to match, we just return NIL immediately.
+ * There is no value in matching just some of the pathkeys.
+ */
+ if (!expr)
+ return false;
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *clause_columns_p = lappend_int(*clause_columns_p, indexcol);
+ }
+
+ return true; /* success */
+}
/****************************************************************************
* ---- ROUTINES TO CHECK ORDERING OPERATORS ----
@@ -2570,86 +2666,24 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
{
List *orderby_clauses = NIL;
List *clause_columns = NIL;
- ListCell *lc1;
+ ammatchorderby_function ammatchorderby =
+ (ammatchorderby_function) index->ammatchorderby;
- *orderby_clauses_p = NIL; /* set default results */
- *clause_columns_p = NIL;
-
- /* Only indexes with the amcanorderbyop property are interesting here */
- if (!index->amcanorderbyop)
- return;
-
- foreach(lc1, pathkeys)
+ /* Only indexes with the ammatchorderby function are interesting here */
+ if (ammatchorderby &&
+ ammatchorderby(index, pathkeys, &orderby_clauses, &clause_columns))
{
- PathKey *pathkey = (PathKey *) lfirst(lc1);
- bool found = false;
- ListCell *lc2;
-
- /*
- * Note: for any failure to match, we just return NIL immediately.
- * There is no value in matching just some of the pathkeys.
- */
-
- /* Pathkey must request default sort order for the target opfamily */
- if (pathkey->pk_strategy != BTLessStrategyNumber ||
- pathkey->pk_nulls_first)
- return;
+ Assert(list_length(pathkeys) == list_length(orderby_clauses));
+ Assert(list_length(pathkeys) == list_length(clause_columns));
- /* If eclass is volatile, no hope of using an indexscan */
- if (pathkey->pk_eclass->ec_has_volatile)
- return;
-
- /*
- * Try to match eclass member expression(s) to index. Note that child
- * EC members are considered, but only when they belong to the target
- * relation. (Unlike regular members, the same expression could be a
- * child member of more than one EC. Therefore, the same index could
- * be considered to match more than one pathkey list, which is OK
- * here. See also get_eclass_for_sort_expr.)
- */
- foreach(lc2, pathkey->pk_eclass->ec_members)
- {
- EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
- int indexcol;
-
- /* No possibility of match if it references other relations */
- if (!bms_equal(member->em_relids, index->rel->relids))
- continue;
-
- /*
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, which is the sole existing AM supporting
- * amcanorderbyop. We might need different logic in future for
- * other implementations.
- */
- for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
- {
- Expr *expr;
-
- expr = match_clause_to_ordering_op(index,
- indexcol,
- member->em_expr,
- pathkey->pk_opfamily);
- if (expr)
- {
- orderby_clauses = lappend(orderby_clauses, expr);
- clause_columns = lappend_int(clause_columns, indexcol);
- found = true;
- break;
- }
- }
-
- if (found) /* don't want to look at remaining members */
- break;
- }
-
- if (!found) /* fail if no match for this pathkey */
- return;
+ *orderby_clauses_p = orderby_clauses; /* success! */
+ *clause_columns_p = clause_columns;
+ }
+ else
+ {
+ *orderby_clauses_p = NIL; /* set default results */
+ *clause_columns_p = NIL;
}
-
- *orderby_clauses_p = orderby_clauses; /* success! */
- *clause_columns_p = clause_columns;
}
/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8369e3a..6c26d26 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -266,7 +266,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* We copy just the fields we need, not all of rd_amroutine */
amroutine = indexRelation->rd_amroutine;
- info->amcanorderbyop = amroutine->amcanorderbyop;
+ info->ammatchorderby = amroutine->ammatchorderby;
info->amoptionalkey = amroutine->amoptionalkey;
info->amsearcharray = amroutine->amsearcharray;
info->amsearchnulls = amroutine->amsearchnulls;
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index dc04148..02879e3 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -298,7 +298,7 @@ indexam_property(FunctionCallInfo fcinfo,
* a nonkey column, and null otherwise (meaning we don't
* know).
*/
- if (!iskey || !routine->amcanorderbyop)
+ if (!iskey || !routine->ammatchorderby)
{
res = false;
isnull = false;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 14526a6..a293b38 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -21,6 +21,9 @@
*/
struct PlannerInfo;
struct IndexPath;
+struct IndexOptInfo;
+struct PathKey;
+struct Expr;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
@@ -140,6 +143,12 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* does AM support ORDER BY result of an operator on indexed column? */
+typedef bool (*ammatchorderby_function) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List **orderby_clauses_p,
+ List **clause_columns_p);
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -170,8 +179,6 @@ typedef struct IndexAmRoutine
uint16 amsupport;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
- /* does AM support ORDER BY result of an operator on indexed column? */
- bool amcanorderbyop;
/* does AM support backward scanning? */
bool amcanbackward;
/* does AM support UNIQUE indexes? */
@@ -221,7 +228,8 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
-
+ ammatchorderby_function ammatchorderby; /* can be NULL */
+
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
aminitparallelscan_function aminitparallelscan; /* can be NULL */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd..5bbfc5a 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -395,7 +395,7 @@ typedef struct SampleScan
* indexorderbyops is a list of the OIDs of the operators used to sort the
* ORDER BY expressions. This is used together with indexorderbyorig to
* recheck ordering at run time. (Note that indexorderby, indexorderbyorig,
- * and indexorderbyops are used for amcanorderbyop cases, not amcanorder.)
+ * and indexorderbyops are used for ammatchorderby cases, not amcanorder.)
*
* indexorderdir specifies the scan ordering, for indexscans on amcanorder
* indexes (for other indexes it should be "don't care").
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index adb4265..75ee8d6 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -805,7 +805,6 @@ typedef struct IndexOptInfo
bool hypothetical; /* true if index doesn't really exist */
/* Remaining fields are copied from the index AM's API struct: */
- bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
@@ -814,6 +813,11 @@ typedef struct IndexOptInfo
bool amcanparallel; /* does AM support parallel scan? */
/* Rather than include amapi.h here, we declare amcostestimate like this */
void (*amcostestimate) (); /* AM's cost estimator */
+ /* AM order-by match function */
+ bool (*ammatchorderby) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List **orderby_clauses_p,
+ List **clause_columns_p);
} IndexOptInfo;
/*
@@ -1137,7 +1141,7 @@ typedef struct Path
* (The order of multiple quals for the same index column is unspecified.)
*
* 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
- * been found to be usable as ordering operators for an amcanorderbyop index.
+ * been found to be usable as ordering operators for an ammatchorderby index.
* The list must match the path's pathkeys, ie, one expression per pathkey
* in the same order. These are not RestrictInfos, just bare expressions,
* since they generally won't yield booleans. Also, unlike the case for
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index cafde30..21677eb 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -87,6 +87,10 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
int indexcol,
List **indexcolnos,
bool *var_on_left_p);
+extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
+ int *indexcol_p);
+extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p);
/*
* tidpath.h
0003-Extract-structure-BTScanState-v03.patchtext/x-patch; name=0003-Extract-structure-BTScanState-v03.patchDownload
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 3e47c37..55c7833 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -214,6 +214,7 @@ bool
btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
bool res;
/* btree indexes are never lossy */
@@ -224,7 +225,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(so->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
@@ -241,7 +242,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(so->currPos))
+ if (!BTScanPosIsValid(state->currPos))
res = _bt_first(scan, dir);
else
{
@@ -259,11 +260,11 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* trying to optimize that, so we don't detect it, but instead
* just forget any excess entries.
*/
- if (so->killedItems == NULL)
- so->killedItems = (int *)
+ if (state->killedItems == NULL)
+ state->killedItems = (int *)
palloc(MaxIndexTuplesPerPage * sizeof(int));
- if (so->numKilled < MaxIndexTuplesPerPage)
- so->killedItems[so->numKilled++] = so->currPos.itemIndex;
+ if (state->numKilled < MaxIndexTuplesPerPage)
+ state->killedItems[so->state.numKilled++] = state->currPos.itemIndex;
}
/*
@@ -288,6 +289,7 @@ int64
btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
int64 ntids = 0;
ItemPointer heapTid;
@@ -320,7 +322,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* Advance to next tuple within page. This is the same as the
* easy case in _bt_next().
*/
- if (++so->currPos.itemIndex > so->currPos.lastItem)
+ if (++currPos->itemIndex > currPos->lastItem)
{
/* let _bt_next do the heavy lifting */
if (!_bt_next(scan, ForwardScanDirection))
@@ -328,7 +330,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
/* Save tuple ID, and continue scanning */
- heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
+ heapTid = &currPos->items[currPos->itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
}
@@ -356,8 +358,8 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
/* allocate private workspace */
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
- BTScanPosInvalidate(so->currPos);
- BTScanPosInvalidate(so->markPos);
+ BTScanPosInvalidate(so->state.currPos);
+ BTScanPosInvalidate(so->state.markPos);
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
else
@@ -368,15 +370,15 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
so->arrayKeys = NULL;
so->arrayContext = NULL;
- so->killedItems = NULL; /* until needed */
- so->numKilled = 0;
+ so->state.killedItems = NULL; /* until needed */
+ so->state.numKilled = 0;
/*
* We don't know yet whether the scan will be index-only, so we do not
* allocate the tuple workspace arrays until btrescan. However, we set up
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
- so->currTuples = so->markTuples = NULL;
+ so->state.currTuples = so->state.markTuples = NULL;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -385,6 +387,45 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
return scan;
}
+static void
+_bt_release_current_position(BTScanState state, Relation indexRelation,
+ bool invalidate)
+{
+ /* we aren't holding any read locks, but gotta drop the pins */
+ if (BTScanPosIsValid(state->currPos))
+ {
+ /* Before leaving current page, deal with any killed items */
+ if (state->numKilled > 0)
+ _bt_killitems(state, indexRelation);
+
+ BTScanPosUnpinIfPinned(state->currPos);
+
+ if (invalidate)
+ BTScanPosInvalidate(state->currPos);
+ }
+}
+
+static void
+_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
+{
+ /* No need to invalidate positions, if the RAM is about to be freed. */
+ _bt_release_current_position(state, scan->indexRelation, !free);
+
+ state->markItemIndex = -1;
+ BTScanPosUnpinIfPinned(state->markPos);
+
+ if (free)
+ {
+ if (state->killedItems != NULL)
+ pfree(state->killedItems);
+ if (state->currTuples != NULL)
+ pfree(state->currTuples);
+ /* markTuples should not be pfree'd (_bt_allocate_tuple_workspaces) */
+ }
+ else
+ BTScanPosInvalidate(state->markPos);
+}
+
/*
* btrescan() -- rescan an index relation
*/
@@ -393,21 +434,11 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
ScanKey orderbys, int norderbys)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
- }
+ _bt_release_scan_state(scan, state, false);
- so->markItemIndex = -1;
- so->arrayKeyCount = 0;
- BTScanPosUnpinIfPinned(so->markPos);
- BTScanPosInvalidate(so->markPos);
+ so->arrayKeyCount = 0; /* FIXME in _bt_release_scan_state */
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
@@ -425,11 +456,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
* a SIGSEGV is not possible. Yeah, this is ugly as sin, but it beats
* adding special-case treatment for name_ops elsewhere.
*/
- if (scan->xs_want_itup && so->currTuples == NULL)
- {
- so->currTuples = (char *) palloc(BLCKSZ * 2);
- so->markTuples = so->currTuples + BLCKSZ;
- }
+ if (scan->xs_want_itup && state->currTuples == NULL)
+ _bt_allocate_tuple_workspaces(state);
/*
* Reset the scan keys. Note that keys ordering stuff moved to _bt_first.
@@ -453,19 +481,7 @@ btendscan(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
-
- /* No need to invalidate positions, the RAM is about to be freed. */
+ _bt_release_scan_state(scan, &so->state, true);
/* Release storage */
if (so->keyData != NULL)
@@ -473,24 +489,15 @@ btendscan(IndexScanDesc scan)
/* so->arrayKeyData and so->arrayKeys are in arrayContext */
if (so->arrayContext != NULL)
MemoryContextDelete(so->arrayContext);
- if (so->killedItems != NULL)
- pfree(so->killedItems);
- if (so->currTuples != NULL)
- pfree(so->currTuples);
- /* so->markTuples should not be pfree'd, see btrescan */
+
pfree(so);
}
-/*
- * btmarkpos() -- save current scan position
- */
-void
-btmarkpos(IndexScanDesc scan)
+static void
+_bt_mark_current_position(BTScanState state)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
-
/* There may be an old mark with a pin (but no lock). */
- BTScanPosUnpinIfPinned(so->markPos);
+ BTScanPosUnpinIfPinned(state->markPos);
/*
* Just record the current itemIndex. If we later step to next page
@@ -498,32 +505,34 @@ btmarkpos(IndexScanDesc scan)
* the currPos struct in markPos. If (as often happens) the mark is moved
* before we leave the page, we don't have to do that work.
*/
- if (BTScanPosIsValid(so->currPos))
- so->markItemIndex = so->currPos.itemIndex;
+ if (BTScanPosIsValid(state->currPos))
+ state->markItemIndex = state->currPos.itemIndex;
else
{
- BTScanPosInvalidate(so->markPos);
- so->markItemIndex = -1;
+ BTScanPosInvalidate(state->markPos);
+ state->markItemIndex = -1;
}
-
- /* Also record the current positions of any array keys */
- if (so->numArrayKeys)
- _bt_mark_array_keys(scan);
}
/*
- * btrestrpos() -- restore scan to last saved position
+ * btmarkpos() -- save current scan position
*/
void
-btrestrpos(IndexScanDesc scan)
+btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* Restore the marked positions of any array keys */
+ _bt_mark_current_position(&so->state);
+
+ /* Also record the current positions of any array keys */
if (so->numArrayKeys)
- _bt_restore_array_keys(scan);
+ _bt_mark_array_keys(scan);
+}
- if (so->markItemIndex >= 0)
+static void
+_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
+{
+ if (state->markItemIndex >= 0)
{
/*
* The scan has never moved to a new page since the last mark. Just
@@ -532,7 +541,7 @@ btrestrpos(IndexScanDesc scan)
* NB: In this case we can't count on anything in so->markPos to be
* accurate.
*/
- so->currPos.itemIndex = so->markItemIndex;
+ state->currPos.itemIndex = state->markItemIndex;
}
else
{
@@ -542,28 +551,21 @@ btrestrpos(IndexScanDesc scan)
* locks, but if we're still holding the pin for the current position,
* we must drop it.
*/
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
+ _bt_release_current_position(state, scan->indexRelation,
+ !BTScanPosIsValid(state->markPos));
- if (BTScanPosIsValid(so->markPos))
+ if (BTScanPosIsValid(state->markPos))
{
/* bump pin on mark buffer for assignment to current buffer */
- if (BTScanPosIsPinned(so->markPos))
- IncrBufferRefCount(so->markPos.buf);
- memcpy(&so->currPos, &so->markPos,
+ if (BTScanPosIsPinned(state->markPos))
+ IncrBufferRefCount(state->markPos.buf);
+ memcpy(&state->currPos, &state->markPos,
offsetof(BTScanPosData, items[1]) +
- so->markPos.lastItem * sizeof(BTScanPosItem));
- if (so->currTuples)
- memcpy(so->currTuples, so->markTuples,
- so->markPos.nextTupleOffset);
+ state->markPos.lastItem * sizeof(BTScanPosItem));
+ if (state->currTuples)
+ memcpy(state->currTuples, state->markTuples,
+ state->markPos.nextTupleOffset);
}
- else
- BTScanPosInvalidate(so->currPos);
}
}
@@ -779,9 +781,10 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
}
/*
- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
- * btbulkdelete() wasn't called.
- */
+- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
+- * btbulkdelete() wasn't called.
++ * btrestrpos() -- restore scan to last saved position
+ */
static bool
_bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
{
@@ -844,6 +847,21 @@ _bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
}
/*
+ * btrestrpos() -- restore scan to last saved position
+ */
+void
+btrestrpos(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
+ /* Restore the marked positions of any array keys */
+ if (so->numArrayKeys)
+ _bt_restore_array_keys(scan);
+
+ _bt_restore_marked_position(scan, &so->state);
+}
+
+/*
* Bulk deletion of all index entries pointing to a set of heap tuples.
* The set of target tuples is specified via a callback routine that tells
* whether any given heap tuple (identified by ItemPointer) is being deleted.
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index d3700bd..2b63e0c 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -25,18 +25,19 @@
#include "utils/tqual.h"
-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir,
+static bool _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
OffsetNumber offnum);
-static void _bt_saveitem(BTScanOpaque so, int itemIndex,
+static void _bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup);
-static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
-static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir);
+static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
+static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
-static inline void _bt_initialize_more_data(BTScanOpaque so, ScanDirection dir);
+static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
/*
@@ -545,6 +546,58 @@ _bt_compare(Relation rel,
}
/*
+ * _bt_return_current_item() -- Prepare current scan state item for return.
+ *
+ * This function is used only in "return _bt_return_current_item();" statements
+ * and always returns true.
+ */
+static inline bool
+_bt_return_current_item(IndexScanDesc scan, BTScanState state)
+{
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+
+ scan->xs_ctup.t_self = currItem->heapTid;
+
+ if (scan->xs_want_itup)
+ scan->xs_itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+
+ return true;
+}
+
+/*
+ * _bt_load_first_page() -- Load data from the first page of the scan.
+ *
+ * Caller must have pinned and read-locked state->currPos.buf.
+ *
+ * On success exit, state->currPos is updated to contain data from the next
+ * interesting page. For success on a scan using a non-MVCC snapshot we hold
+ * a pin, but not a read lock, on that page. If we do not hold the pin, we
+ * set state->currPos.buf to InvalidBuffer. We return true to indicate success.
+ *
+ * If there are no more matching records in the given direction at all,
+ * we drop all locks and pins, set state->currPos.buf to InvalidBuffer,
+ * and return false.
+ */
+static bool
+_bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
+{
+ if (!_bt_readpage(scan, state, dir, offnum))
+ {
+ /*
+ * There's no actually-matching data on this page. Try to advance to
+ * the next page. Return false if there's no matching data at all.
+ */
+ LockBuffer(state->currPos.buf, BUFFER_LOCK_UNLOCK);
+ return _bt_steppage(scan, state, dir);
+ }
+
+ /* Drop the lock, and maybe the pin, on the current page */
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
+ return true;
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -569,6 +622,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
BTStack stack;
OffsetNumber offnum;
@@ -582,10 +636,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
int i;
bool status = true;
StrategyNumber strat_total;
- BTScanPosItem *currItem;
BlockNumber blkno;
- Assert(!BTScanPosIsValid(so->currPos));
+ Assert(!BTScanPosIsValid(*currPos));
pgstat_count_index_scan(rel);
@@ -1076,7 +1129,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* their scan
*/
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1084,7 +1137,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
PredicateLockPage(rel, BufferGetBlockNumber(buf),
scan->xs_snapshot);
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
/* position to the precise item on the page */
offnum = _bt_binsrch(rel, buf, keysCount, scankeys, nextkey);
@@ -1111,36 +1164,36 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
offnum = OffsetNumberPrev(offnum);
/* remember which buffer we have pinned, if any */
- Assert(!BTScanPosIsValid(so->currPos));
- so->currPos.buf = buf;
+ Assert(!BTScanPosIsValid(*currPos));
+ currPos->buf = buf;
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, offnum))
+ if (!_bt_load_first_page(scan, &so->state, dir, offnum))
+ return false;
+
+readcomplete:
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
+}
+
+/*
+ * Advance to next tuple on current page; or if there's no more,
+ * try to step to the next page with data.
+ */
+static bool
+_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
+{
+ if (ScanDirectionIsForward(dir))
{
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
+ if (++state->currPos.itemIndex <= state->currPos.lastItem)
+ return true;
}
else
{
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ if (--state->currPos.itemIndex >= state->currPos.firstItem)
+ return true;
}
-readcomplete:
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_steppage(scan, state, dir);
}
/*
@@ -1161,44 +1214,20 @@ bool
_bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- BTScanPosItem *currItem;
- /*
- * Advance to next tuple on current page; or if there's no more, try to
- * step to the next page with data.
- */
- if (ScanDirectionIsForward(dir))
- {
- if (++so->currPos.itemIndex > so->currPos.lastItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
- else
- {
- if (--so->currPos.itemIndex < so->currPos.firstItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
+ if (!_bt_next_item(scan, &so->state, dir))
+ return false;
/* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_return_current_item(scan, &so->state);
}
/*
* _bt_readpage() -- Load data from current index page into so->currPos
*
- * Caller must have pinned and read-locked so->currPos.buf; the buffer's state
- * is not changed here. Also, currPos.moreLeft and moreRight must be valid;
- * they are updated as appropriate. All other fields of so->currPos are
+ * Caller must have pinned and read-locked pos->buf; the buffer's state
+ * is not changed here. Also, pos->moreLeft and moreRight must be valid;
+ * they are updated as appropriate. All other fields of pos are
* initialized from scratch here.
*
* We scan the current page starting at offnum and moving in the indicated
@@ -1213,9 +1242,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
* Returns true if any matching items found on the page, false if none.
*/
static bool
-_bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
+_bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
@@ -1228,9 +1258,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We must have the buffer pinned and locked, but the usual macro can't be
* used here; this function is what makes it good for currPos.
*/
- Assert(BufferIsValid(so->currPos.buf));
+ Assert(BufferIsValid(pos->buf));
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* allow next page be processed by parallel worker */
@@ -1239,7 +1269,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (ScanDirectionIsForward(dir))
_bt_parallel_release(scan, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1249,30 +1279,30 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We note the buffer's block number so that we can release the pin later.
* This allows us to re-read the buffer if it is needed again for hinting.
*/
- so->currPos.currPage = BufferGetBlockNumber(so->currPos.buf);
+ pos->currPage = BufferGetBlockNumber(pos->buf);
/*
* We save the LSN of the page as we read it, so that we know whether it
* safe to apply LP_DEAD hints to the page later. This allows us to drop
* the pin for MVCC scans, which allows vacuum to avoid blocking.
*/
- so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf);
+ pos->lsn = BufferGetLSNAtomic(pos->buf);
/*
* we must save the page's right-link while scanning it; this tells us
* where to step right to after we're done with these items. There is no
* corresponding need for the left-link, since splits always go right.
*/
- so->currPos.nextPage = opaque->btpo_next;
+ pos->nextPage = opaque->btpo_next;
/* initialize tuple workspace to empty */
- so->currPos.nextTupleOffset = 0;
+ pos->nextTupleOffset = 0;
/*
* Now that the current page has been made consistent, the macro should be
* good.
*/
- Assert(BTScanPosIsPinned(so->currPos));
+ Assert(BTScanPosIsPinned(*pos));
if (ScanDirectionIsForward(dir))
{
@@ -1287,13 +1317,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (itup != NULL)
{
/* tuple passes all scan key conditions, so remember it */
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
itemIndex++;
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreRight = false;
+ pos->moreRight = false;
break;
}
@@ -1301,9 +1331,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex <= MaxIndexTuplesPerPage);
- so->currPos.firstItem = 0;
- so->currPos.lastItem = itemIndex - 1;
- so->currPos.itemIndex = 0;
+ pos->firstItem = 0;
+ pos->lastItem = itemIndex - 1;
+ pos->itemIndex = 0;
}
else
{
@@ -1319,12 +1349,12 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
{
/* tuple passes all scan key conditions, so remember it */
itemIndex--;
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreLeft = false;
+ pos->moreLeft = false;
break;
}
@@ -1332,30 +1362,31 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex >= 0);
- so->currPos.firstItem = itemIndex;
- so->currPos.lastItem = MaxIndexTuplesPerPage - 1;
- so->currPos.itemIndex = MaxIndexTuplesPerPage - 1;
+ pos->firstItem = itemIndex;
+ pos->lastItem = MaxIndexTuplesPerPage - 1;
+ pos->itemIndex = MaxIndexTuplesPerPage - 1;
}
- return (so->currPos.firstItem <= so->currPos.lastItem);
+ return (pos->firstItem <= pos->lastItem);
}
/* Save an index item into so->currPos.items[itemIndex] */
static void
-_bt_saveitem(BTScanOpaque so, int itemIndex,
+_bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup)
{
- BTScanPosItem *currItem = &so->currPos.items[itemIndex];
+ BTScanPosItem *currItem = &state->currPos.items[itemIndex];
currItem->heapTid = itup->t_tid;
currItem->indexOffset = offnum;
- if (so->currTuples)
+ if (state->currTuples)
{
Size itupsz = IndexTupleSize(itup);
- currItem->tupleOffset = so->currPos.nextTupleOffset;
- memcpy(so->currTuples + so->currPos.nextTupleOffset, itup, itupsz);
- so->currPos.nextTupleOffset += MAXALIGN(itupsz);
+ currItem->tupleOffset = state->currPos.nextTupleOffset;
+ memcpy(state->currTuples + state->currPos.nextTupleOffset,
+ itup, itupsz);
+ state->currPos.nextTupleOffset += MAXALIGN(itupsz);
}
}
@@ -1371,35 +1402,36 @@ _bt_saveitem(BTScanOpaque so, int itemIndex,
* to InvalidBuffer. We return true to indicate success.
*/
static bool
-_bt_steppage(IndexScanDesc scan, ScanDirection dir)
+_bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
+ Relation rel = scan->indexRelation;
BlockNumber blkno = InvalidBlockNumber;
bool status = true;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(*currPos));
/* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
+ if (state->numKilled > 0)
+ _bt_killitems(state, rel);
/*
* Before we modify currPos, make a copy of the page data if there was a
* mark position that needs it.
*/
- if (so->markItemIndex >= 0)
+ if (state->markItemIndex >= 0)
{
/* bump pin on current buffer for assignment to mark buffer */
- if (BTScanPosIsPinned(so->currPos))
- IncrBufferRefCount(so->currPos.buf);
- memcpy(&so->markPos, &so->currPos,
+ if (BTScanPosIsPinned(*currPos))
+ IncrBufferRefCount(currPos->buf);
+ memcpy(&state->markPos, currPos,
offsetof(BTScanPosData, items[1]) +
- so->currPos.lastItem * sizeof(BTScanPosItem));
- if (so->markTuples)
- memcpy(so->markTuples, so->currTuples,
- so->currPos.nextTupleOffset);
- so->markPos.itemIndex = so->markItemIndex;
- so->markItemIndex = -1;
+ currPos->lastItem * sizeof(BTScanPosItem));
+ if (state->markTuples)
+ memcpy(state->markTuples, state->currTuples,
+ currPos->nextTupleOffset);
+ state->markPos.itemIndex = state->markItemIndex;
+ state->markItemIndex = -1;
}
if (ScanDirectionIsForward(dir))
@@ -1415,27 +1447,27 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
if (!status)
{
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so use the previously-saved nextPage link. */
- blkno = so->currPos.nextPage;
+ blkno = currPos->nextPage;
}
/* Remember we left a page with data */
- so->currPos.moreLeft = true;
+ currPos->moreLeft = true;
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
}
else
{
/* Remember we left a page with data */
- so->currPos.moreRight = true;
+ currPos->moreRight = true;
if (scan->parallel_scan != NULL)
{
@@ -1444,25 +1476,25 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* ended already, bail out.
*/
status = _bt_parallel_seize(scan, &blkno);
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so just use our own notion of the current page */
- blkno = so->currPos.currPage;
+ blkno = currPos->currPage;
}
}
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, currPos);
return true;
}
@@ -1478,9 +1510,10 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* locks and pins, set so->currPos.buf to InvalidBuffer, and return false.
*/
static bool
-_bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
Relation rel;
Page page;
BTPageOpaque opaque;
@@ -1496,17 +1529,17 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* if we're at end of scan, give up and mark parallel scan as
* done, so that all the workers can finish their scan
*/
- if (blkno == P_NONE || !so->currPos.moreRight)
+ if (blkno == P_NONE || !currPos->moreRight)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* check for interrupts while we're not holding any buffer lock */
CHECK_FOR_INTERRUPTS();
/* step right one page */
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
- page = BufferGetPage(so->currPos.buf);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* check for deleted page */
@@ -1515,7 +1548,7 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
PredicateLockPage(rel, blkno, scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreRight if we can stop */
- if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque)))
+ if (_bt_readpage(scan, state, dir, P_FIRSTDATAKEY(opaque)))
break;
}
else if (scan->parallel_scan != NULL)
@@ -1527,18 +1560,18 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
blkno = opaque->btpo_next;
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
}
}
}
@@ -1548,10 +1581,10 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* Should only happen in parallel cases, when some other backend
* advanced the scan.
*/
- if (so->currPos.currPage != blkno)
+ if (currPos->currPage != blkno)
{
- BTScanPosUnpinIfPinned(so->currPos);
- so->currPos.currPage = blkno;
+ BTScanPosUnpinIfPinned(*currPos);
+ currPos->currPage = blkno;
}
/*
@@ -1576,31 +1609,30 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* is MVCC the page cannot move past the half-dead state to fully
* deleted.
*/
- if (BTScanPosIsPinned(so->currPos))
- LockBuffer(so->currPos.buf, BT_READ);
+ if (BTScanPosIsPinned(*currPos))
+ LockBuffer(currPos->buf, BT_READ);
else
- so->currPos.buf = _bt_getbuf(rel, so->currPos.currPage, BT_READ);
+ currPos->buf = _bt_getbuf(rel, currPos->currPage, BT_READ);
for (;;)
{
/* Done if we know there are no matching keys to the left */
- if (!so->currPos.moreLeft)
+ if (!currPos->moreLeft)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* Step to next physical page */
- so->currPos.buf = _bt_walk_left(rel, so->currPos.buf,
- scan->xs_snapshot);
+ currPos->buf = _bt_walk_left(rel, currPos->buf, scan->xs_snapshot);
/* if we're physically at end of index, return failure */
- if (so->currPos.buf == InvalidBuffer)
+ if (currPos->buf == InvalidBuffer)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1609,21 +1641,21 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* it's not half-dead and contains matching tuples. Else loop back
* and do it all again.
*/
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
{
- PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
+ PredicateLockPage(rel, BufferGetBlockNumber(currPos->buf), scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreLeft if we can stop */
- if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page)))
+ if (_bt_readpage(scan, state, dir, PageGetMaxOffsetNumber(page)))
break;
}
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1634,14 +1666,14 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
}
}
}
@@ -1660,13 +1692,13 @@ _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, &so->state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
return true;
}
@@ -1891,11 +1923,11 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
Page page;
BTPageOpaque opaque;
OffsetNumber start;
- BTScanPosItem *currItem;
/*
* Scan down to the leftmost or rightmost leaf page. This is a simplified
@@ -1911,7 +1943,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* exists.
*/
PredicateLockRelation(rel, scan->xs_snapshot);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1940,36 +1972,15 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
}
/* remember which buffer we have pinned */
- so->currPos.buf = buf;
+ currPos->buf = buf;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, start))
- {
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
- }
- else
- {
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
- }
-
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
+ if (!_bt_load_first_page(scan, &so->state, dir, start))
+ return false;
- return true;
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
}
/*
@@ -1977,19 +1988,19 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* for scan direction
*/
static inline void
-_bt_initialize_more_data(BTScanOpaque so, ScanDirection dir)
+_bt_initialize_more_data(BTScanState state, ScanDirection dir)
{
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ state->currPos.moreLeft = false;
+ state->currPos.moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ state->currPos.moreLeft = true;
+ state->currPos.moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- so->markItemIndex = -1; /* ditto */
+ state->numKilled = 0; /* just paranoia */
+ state->markItemIndex = -1; /* ditto */
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 4528e87..9bf453c 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1741,26 +1741,26 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
* away and the TID was re-used by a completely different heap tuple.
*/
void
-_bt_killitems(IndexScanDesc scan)
+_bt_killitems(BTScanState state, Relation indexRelation)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
OffsetNumber maxoff;
int i;
- int numKilled = so->numKilled;
+ int numKilled = state->numKilled;
bool killedsomething = false;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(state->currPos));
/*
* Always reset the scan state, so we don't look for same items on other
* pages.
*/
- so->numKilled = 0;
+ state->numKilled = 0;
- if (BTScanPosIsPinned(so->currPos))
+ if (BTScanPosIsPinned(*pos))
{
/*
* We have held the pin on this page since we read the index tuples,
@@ -1768,44 +1768,42 @@ _bt_killitems(IndexScanDesc scan)
* re-use of any TID on the page, so there is no need to check the
* LSN.
*/
- LockBuffer(so->currPos.buf, BT_READ);
-
- page = BufferGetPage(so->currPos.buf);
+ LockBuffer(pos->buf, BT_READ);
}
else
{
Buffer buf;
/* Attempt to re-read the buffer, getting pin and lock. */
- buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, BT_READ);
+ buf = _bt_getbuf(indexRelation, pos->currPage, BT_READ);
/* It might not exist anymore; in which case we can't hint it. */
if (!BufferIsValid(buf))
return;
- page = BufferGetPage(buf);
- if (BufferGetLSNAtomic(buf) == so->currPos.lsn)
- so->currPos.buf = buf;
+ if (BufferGetLSNAtomic(buf) == pos->lsn)
+ pos->buf = buf;
else
{
/* Modified while not pinned means hinting is not safe. */
- _bt_relbuf(scan->indexRelation, buf);
+ _bt_relbuf(indexRelation, buf);
return;
}
}
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
for (i = 0; i < numKilled; i++)
{
- int itemIndex = so->killedItems[i];
- BTScanPosItem *kitem = &so->currPos.items[itemIndex];
+ int itemIndex = state->killedItems[i];
+ BTScanPosItem *kitem = &pos->items[itemIndex];
OffsetNumber offnum = kitem->indexOffset;
- Assert(itemIndex >= so->currPos.firstItem &&
- itemIndex <= so->currPos.lastItem);
+ Assert(itemIndex >= pos->firstItem &&
+ itemIndex <= pos->lastItem);
if (offnum < minoff)
continue; /* pure paranoia */
while (offnum <= maxoff)
@@ -1833,10 +1831,10 @@ _bt_killitems(IndexScanDesc scan)
if (killedsomething)
{
opaque->btpo_flags |= BTP_HAS_GARBAGE;
- MarkBufferDirtyHint(so->currPos.buf, true);
+ MarkBufferDirtyHint(pos->buf, true);
}
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(pos->buf, BUFFER_LOCK_UNLOCK);
}
@@ -2214,3 +2212,14 @@ _bt_check_natts(Relation rel, Page page, OffsetNumber offnum)
}
}
+
+/*
+ * _bt_allocate_tuple_workspaces() -- Allocate buffers for saving index tuples
+ * in index-only scans.
+ */
+void
+_bt_allocate_tuple_workspaces(BTScanState state)
+{
+ state->currTuples = (char *) palloc(BLCKSZ * 2);
+ state->markTuples = state->currTuples + BLCKSZ;
+}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 04ecb4c..388b311 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -434,22 +434,8 @@ typedef struct BTArrayKeyInfo
Datum *elem_values; /* array of num_elems Datums */
} BTArrayKeyInfo;
-typedef struct BTScanOpaqueData
+typedef struct BTScanStateData
{
- /* these fields are set by _bt_preprocess_keys(): */
- bool qual_ok; /* false if qual can never be satisfied */
- int numberOfKeys; /* number of preprocessed scan keys */
- ScanKey keyData; /* array of preprocessed scan keys */
-
- /* workspace for SK_SEARCHARRAY support */
- ScanKey arrayKeyData; /* modified copy of scan->keyData */
- int numArrayKeys; /* number of equality-type array keys (-1 if
- * there are any unsatisfiable array keys) */
- int arrayKeyCount; /* count indicating number of array scan keys
- * processed */
- BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
- MemoryContext arrayContext; /* scan-lifespan context for array data */
-
/* info about killed items if any (killedItems is NULL if never used) */
int *killedItems; /* currPos.items indexes of killed items */
int numKilled; /* number of currently stored items */
@@ -474,6 +460,25 @@ typedef struct BTScanOpaqueData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+} BTScanStateData, *BTScanState;
+
+typedef struct BTScanOpaqueData
+{
+ /* these fields are set by _bt_preprocess_keys(): */
+ bool qual_ok; /* false if qual can never be satisfied */
+ int numberOfKeys; /* number of preprocessed scan keys */
+ ScanKey keyData; /* array of preprocessed scan keys */
+
+ /* workspace for SK_SEARCHARRAY support */
+ ScanKey arrayKeyData; /* modified copy of scan->keyData */
+ int numArrayKeys; /* number of equality-type array keys (-1 if
+ * there are any unsatisfiable array keys) */
+ int arrayKeyCount; /* count indicating number of array scan keys
+ * processed */
+ BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
+ MemoryContext arrayContext; /* scan-lifespan context for array data */
+
+ BTScanStateData state;
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -590,7 +595,7 @@ extern void _bt_preprocess_keys(IndexScanDesc scan);
extern IndexTuple _bt_checkkeys(IndexScanDesc scan,
Page page, OffsetNumber offnum,
ScanDirection dir, bool *continuescan);
-extern void _bt_killitems(IndexScanDesc scan);
+extern void _bt_killitems(BTScanState state, Relation indexRelation);
extern BTCycleId _bt_vacuum_cycleid(Relation rel);
extern BTCycleId _bt_start_vacuum(Relation rel);
extern void _bt_end_vacuum(Relation rel);
@@ -603,6 +608,7 @@ extern bool btproperty(Oid index_oid, int attno,
bool *res, bool *isnull);
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
+extern void _bt_allocate_tuple_workspaces(BTScanState state);
/*
* prototypes for functions in nbtvalidate.c
0004-Add-kNN-support-to-btree-v03.patchtext/x-patch; name=0004-Add-kNN-support-to-btree-v03.patchDownload
diff --git a/doc/src/sgml/btree.sgml b/doc/src/sgml/btree.sgml
index 8bd0bad..ed9625e 100644
--- a/doc/src/sgml/btree.sgml
+++ b/doc/src/sgml/btree.sgml
@@ -200,6 +200,20 @@
planner relies on them for optimization purposes.
</para>
+ <para>
+ FIXME!!!
+ To implement the distance ordered (nearest-neighbor) search, we only need
+ to define a distance operator (usually it called <->) with a correpsonding
+ operator family for distance comparison in the index's operator class.
+ These operators must satisfy the following assumptions for all non-null
+ values A,B,C of the datatype:
+
+ A <-> B = B <-> A symmetric law
+ if A = B, then A <-> C = B <-> C distance equivalence
+ if (A <= B and B <= C) or (A >= B and B >= C),
+ then A <-> B <= A <-> C monotonicity
+ </para>
+
</sect1>
<sect1 id="btree-support-funcs">
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index df7d16f..9015557 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -175,6 +175,17 @@ CREATE INDEX test1_id_index ON test1 (id);
</para>
<para>
+ B-tree indexes are also capable of optimizing <quote>nearest-neighbor</>
+ searches, such as
+<programlisting><![CDATA[
+SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
+]]>
+</programlisting>
+ which finds the ten events closest to a given target date. The ability
+ to do this is again dependent on the particular operator class being used.
+ </para>
+
+ <para>
<indexterm>
<primary>index</primary>
<secondary>hash</secondary>
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..93094bc 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -1242,7 +1242,8 @@ SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING)
<title>Ordering Operators</title>
<para>
- Some index access methods (currently, only GiST and SP-GiST) support the concept of
+ Some index access methods (currently, only B-tree, GiST and SP-GiST)
+ support the concept of
<firstterm>ordering operators</firstterm>. What we have been discussing so far
are <firstterm>search operators</firstterm>. A search operator is one for which
the index can be searched to find all rows satisfying
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
index 3680e69..3f7e1b1 100644
--- a/src/backend/access/nbtree/README
+++ b/src/backend/access/nbtree/README
@@ -659,3 +659,20 @@ routines must treat it accordingly. The actual key stored in the
item is irrelevant, and need not be stored at all. This arrangement
corresponds to the fact that an L&Y non-leaf page has one more pointer
than key.
+
+Nearest-neighbor search
+-----------------------
+
+There is a special scan strategy for nearest-neighbor (kNN) search,
+that is used in queries with ORDER BY distance clauses like this:
+SELECT * FROM tab WHERE col > const1 ORDER BY col <-> const2 LIMIT k.
+But, unlike GiST, B-tree supports only a one ordering operator on the
+first index column.
+
+At the beginning of kNN scan, we need to determine which strategy we
+will use --- a special bidirectional or a ordinary unidirectional.
+If the point from which we measure the distance falls into the scan range,
+we use bidirectional scan starting from this point, else we use simple
+unidirectional scan in the right direction. Algorithm of a bidirectional
+scan is very simple: at each step we advancing scan in that direction,
+which has the nearest point.
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 55c7833..9270a85 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -25,6 +25,9 @@
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "optimizer/paths.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "storage/condition_variable.h"
@@ -33,6 +36,7 @@
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
@@ -79,6 +83,7 @@ typedef enum
typedef struct BTParallelScanDescData
{
BlockNumber btps_scanPage; /* latest or next page to be scanned */
+ BlockNumber btps_knnScanPage; /* secondary latest or next page to be scanned */
BTPS_State btps_pageStatus; /* indicates whether next page is
* available for scan. see above for
* possible states of parallel scan. */
@@ -97,6 +102,9 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
+static bool btmatchorderby(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p);
+
/*
* Btree handler function: return IndexAmRoutine with access method parameters
@@ -107,7 +115,7 @@ bthandler(PG_FUNCTION_ARGS)
{
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
- amroutine->amstrategies = BTMaxStrategyNumber;
+ amroutine->amstrategies = BtreeMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
amroutine->amcanbackward = true;
@@ -143,7 +151,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
- amroutine->ammatchorderby = NULL;
+ amroutine->ammatchorderby = btmatchorderby;
PG_RETURN_POINTER(amroutine);
}
@@ -215,23 +223,30 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTScanState state = &so->state;
+ ScanDirection arraydir =
+ scan->numberOfOrderBys > 0 ? ForwardScanDirection : dir;
bool res;
/* btree indexes are never lossy */
scan->xs_recheck = false;
+ scan->xs_recheckorderby = false;
+
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
/*
* If we have any array keys, initialize them during first call for a
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
return false;
- _bt_start_array_keys(scan, dir);
+ _bt_start_array_keys(scan, arraydir);
}
/* This loop handles advancing to the next array elements, if any */
@@ -242,7 +257,8 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(state->currPos))
+ if (!BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
res = _bt_first(scan, dir);
else
{
@@ -277,7 +293,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
if (res)
break;
/* ... otherwise see if we have more array keys to deal with */
- } while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
+ } while (so->numArrayKeys && _bt_advance_array_keys(scan, arraydir));
return res;
}
@@ -350,9 +366,6 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
IndexScanDesc scan;
BTScanOpaque so;
- /* no order by operators allowed */
- Assert(norderbys == 0);
-
/* get the scan */
scan = RelationGetIndexScan(rel, nkeys, norderbys);
@@ -379,6 +392,9 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
so->state.currTuples = so->state.markTuples = NULL;
+ so->knnState = NULL;
+ so->distanceTypeByVal = true;
+ so->scanDirection = NoMovementScanDirection;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -408,6 +424,8 @@ _bt_release_current_position(BTScanState state, Relation indexRelation,
static void
_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
/* No need to invalidate positions, if the RAM is about to be freed. */
_bt_release_current_position(state, scan->indexRelation, !free);
@@ -424,6 +442,17 @@ _bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
}
else
BTScanPosInvalidate(state->markPos);
+
+ if (!so->distanceTypeByVal)
+ {
+ if (DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+ state->currDistance = PointerGetDatum(NULL);
+
+ if (DatumGetPointer(state->markDistance))
+ pfree(DatumGetPointer(state->markDistance));
+ state->markDistance = PointerGetDatum(NULL);
+ }
}
/*
@@ -438,6 +467,13 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
_bt_release_scan_state(scan, state, false);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ so->knnState = NULL;
+ }
+
so->arrayKeyCount = 0; /* FIXME in _bt_release_scan_state */
/*
@@ -469,6 +505,14 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
scan->numberOfKeys * sizeof(ScanKeyData));
so->numberOfKeys = 0; /* until _bt_preprocess_keys sets it */
+ if (orderbys && scan->numberOfOrderBys > 0)
+ memmove(scan->orderByData,
+ orderbys,
+ scan->numberOfOrderBys * sizeof(ScanKeyData));
+
+ so->scanDirection = NoMovementScanDirection;
+ so->distanceTypeByVal = true;
+
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
_bt_preprocess_array_keys(scan);
}
@@ -483,6 +527,12 @@ btendscan(IndexScanDesc scan)
_bt_release_scan_state(scan, &so->state, true);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ }
+
/* Release storage */
if (so->keyData != NULL)
pfree(so->keyData);
@@ -494,7 +544,7 @@ btendscan(IndexScanDesc scan)
}
static void
-_bt_mark_current_position(BTScanState state)
+_bt_mark_current_position(BTScanOpaque so, BTScanState state)
{
/* There may be an old mark with a pin (but no lock). */
BTScanPosUnpinIfPinned(state->markPos);
@@ -512,6 +562,21 @@ _bt_mark_current_position(BTScanState state)
BTScanPosInvalidate(state->markPos);
state->markItemIndex = -1;
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->markDistance));
+
+ state->markIsNull = !BTScanPosIsValid(state->currPos) ||
+ state->currIsNull;
+
+ state->markDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->currDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -522,7 +587,13 @@ btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_mark_current_position(&so->state);
+ _bt_mark_current_position(so, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_mark_current_position(so, so->knnState);
+ so->markRightIsNearest = so->currRightIsNearest;
+ }
/* Also record the current positions of any array keys */
if (so->numArrayKeys)
@@ -532,6 +603,8 @@ btmarkpos(IndexScanDesc scan)
static void
_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
if (state->markItemIndex >= 0)
{
/*
@@ -567,6 +640,19 @@ _bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
state->markPos.nextTupleOffset);
}
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currIsNull = state->markIsNull;
+ state->currDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->markDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -588,6 +674,7 @@ btinitparallelscan(void *target)
SpinLockInit(&bt_target->btps_mutex);
bt_target->btps_scanPage = InvalidBlockNumber;
+ bt_target->btps_knnScanPage = InvalidBlockNumber;
bt_target->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
bt_target->btps_arrayKeyCount = 0;
ConditionVariableInit(&bt_target->btps_cv);
@@ -614,6 +701,7 @@ btparallelrescan(IndexScanDesc scan)
*/
SpinLockAcquire(&btscan->btps_mutex);
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount = 0;
SpinLockRelease(&btscan->btps_mutex);
@@ -638,7 +726,7 @@ btparallelrescan(IndexScanDesc scan)
* Callers should ignore the value of pageno if the return value is false.
*/
bool
-_bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
+_bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTPS_State pageStatus;
@@ -646,12 +734,17 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
bool status = true;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
*pageno = P_NONE;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ scanPage = state == &so->state
+ ? &btscan->btps_scanPage
+ : &btscan->btps_knnScanPage;
+
while (1)
{
SpinLockAcquire(&btscan->btps_mutex);
@@ -677,7 +770,7 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* of advancing it to a new page!
*/
btscan->btps_pageStatus = BTPARALLEL_ADVANCING;
- *pageno = btscan->btps_scanPage;
+ *pageno = *scanPage;
exit_loop = true;
}
SpinLockRelease(&btscan->btps_mutex);
@@ -696,19 +789,42 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* can now begin advancing the scan.
*/
void
-_bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
+_bt_parallel_release(IndexScanDesc scan, BTScanState state,
+ BlockNumber scan_page)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
+ bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
SpinLockAcquire(&btscan->btps_mutex);
- btscan->btps_scanPage = scan_page;
- btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ *scanPage = scan_page;
+ /* switch to idle state only if both KNN pages are initialized */
+ if (!knnScan || *otherScanPage != InvalidBlockNumber)
+ {
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ status_changed = true;
+ }
SpinLockRelease(&btscan->btps_mutex);
- ConditionVariableSignal(&btscan->btps_cv);
+
+ if (status_changed)
+ ConditionVariableSignal(&btscan->btps_cv);
}
/*
@@ -719,12 +835,15 @@ _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
* advance to the next page.
*/
void
-_bt_parallel_done(IndexScanDesc scan)
+_bt_parallel_done(IndexScanDesc scan, BTScanState state)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
/* Do nothing, for non-parallel scans */
if (parallel_scan == NULL)
@@ -733,18 +852,41 @@ _bt_parallel_done(IndexScanDesc scan)
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
+
/*
* Mark the parallel scan as done for this combination of scan keys,
* unless some other process already did so. See also
* _bt_advance_array_keys.
*/
SpinLockAcquire(&btscan->btps_mutex);
- if (so->arrayKeyCount >= btscan->btps_arrayKeyCount &&
- btscan->btps_pageStatus != BTPARALLEL_DONE)
+
+ Assert(btscan->btps_pageStatus == BTPARALLEL_ADVANCING);
+
+ if (so->arrayKeyCount >= btscan->btps_arrayKeyCount)
{
- btscan->btps_pageStatus = BTPARALLEL_DONE;
+ *scanPage = P_NONE;
status_changed = true;
+
+ /* switch to "done" state only if both KNN scans are done */
+ if (!knnScan || *otherScanPage == P_NONE)
+ btscan->btps_pageStatus = BTPARALLEL_DONE;
+ /* else switch to "idle" state only if both KNN scans are initialized */
+ else if (*otherScanPage != InvalidBlockNumber)
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ else
+ status_changed = false;
}
+
SpinLockRelease(&btscan->btps_mutex);
/* wake up all the workers associated with this parallel scan */
@@ -774,6 +916,7 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
if (btscan->btps_pageStatus == BTPARALLEL_DONE)
{
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount++;
}
@@ -859,6 +1002,12 @@ btrestrpos(IndexScanDesc scan)
_bt_restore_array_keys(scan);
_bt_restore_marked_position(scan, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_restore_marked_position(scan, so->knnState);
+ so->currRightIsNearest = so->markRightIsNearest;
+ }
}
/*
@@ -1394,3 +1543,30 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+/*
+ * btmatchorderby() -- Check whether KNN-search strategy is applicable to
+ * the given ORDER BY distance operator.
+ */
+static bool
+btmatchorderby(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p)
+{
+ Expr *expr;
+ /* ORDER BY distance to the first index column is only supported */
+ int indexcol = 0;
+
+ if (list_length(pathkeys) != 1)
+ return false; /* only one ORDER BY clause is supported */
+
+ expr = match_orderbyop_pathkey(index, castNode(PathKey, linitial(pathkeys)),
+ &indexcol);
+
+ if (!expr)
+ return false;
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *clause_columns_p = lappend_int(*clause_columns_p, 0);
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 2b63e0c..6b58dd52 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -32,12 +32,14 @@ static void _bt_saveitem(BTScanState state, int itemIndex,
static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
BlockNumber blkno, ScanDirection dir);
-static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
- ScanDirection dir);
+static bool _bt_parallel_readpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
+static BTScanState _bt_alloc_knn_scan(IndexScanDesc scan);
+static bool _bt_start_knn_scan(IndexScanDesc scan, bool left, bool right);
/*
@@ -598,6 +600,156 @@ _bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
}
/*
+ * _bt_calc_current_dist() -- Calculate distance from the current item
+ * of the scan state to the target order-by ScanKey argument.
+ */
+static void
+_bt_calc_current_dist(IndexScanDesc scan, BTScanState state)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+ IndexTuple itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+ ScanKey scankey = &scan->orderByData[0];
+ Datum value;
+
+ value = index_getattr(itup, 1, scan->xs_itupdesc, &state->currIsNull);
+
+ if (state->currIsNull)
+ return; /* NULL distance */
+
+ value = FunctionCall2Coll(&scankey->sk_func,
+ scankey->sk_collation,
+ value,
+ scankey->sk_argument);
+
+ /* free previous distance value for by-ref types */
+ if (!so->distanceTypeByVal && DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currDistance = value;
+}
+
+/*
+ * _bt_compare_current_dist() -- Compare current distances of the left and right scan states.
+ *
+ * NULL distances are considered to be greater than any non-NULL distances.
+ *
+ * Returns true if right distance is lesser than left, otherwise false.
+ */
+static bool
+_bt_compare_current_dist(BTScanOpaque so, BTScanState rstate, BTScanState lstate)
+{
+ if (lstate->currIsNull)
+ return true; /* non-NULL < NULL */
+
+ if (rstate->currIsNull)
+ return false; /* NULL > non-NULL */
+
+ return DatumGetBool(FunctionCall2Coll(&so->distanceCmpProc,
+ InvalidOid, /* XXX collation for distance comparison */
+ rstate->currDistance,
+ lstate->currDistance));
+}
+
+/*
+ * _bt_alloc_knn_scan() -- Allocate additional backward scan state for KNN.
+ */
+static BTScanState
+_bt_alloc_knn_scan(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState lstate = (BTScanState) palloc(sizeof(BTScanStateData));
+
+ _bt_allocate_tuple_workspaces(lstate);
+
+ if (!scan->xs_want_itup)
+ {
+ /* We need to request index tuples for distance comparison. */
+ scan->xs_want_itup = true;
+ _bt_allocate_tuple_workspaces(&so->state);
+ }
+
+ BTScanPosInvalidate(lstate->currPos);
+ lstate->currPos.moreLeft = false;
+ lstate->currPos.moreRight = false;
+ BTScanPosInvalidate(lstate->markPos);
+ lstate->markItemIndex = -1;
+ lstate->killedItems = NULL;
+ lstate->numKilled = 0;
+ lstate->currDistance = PointerGetDatum(NULL);
+ lstate->markDistance = PointerGetDatum(NULL);
+
+ return so->knnState = lstate;
+}
+
+static bool
+_bt_start_knn_scan(IndexScanDesc scan, bool left, bool right)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+
+ if (!left && !right)
+ return false; /* empty result */
+
+ rstate = &so->state;
+ lstate = so->knnState;
+
+ if (left && right)
+ {
+ /*
+ * We have found items in both scan directions,
+ * determine nearest item to return.
+ */
+ _bt_calc_current_dist(scan, rstate);
+ _bt_calc_current_dist(scan, lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+
+ /* Reset right flag if the left item is nearer. */
+ right = so->currRightIsNearest;
+ }
+
+ /* Return current item of the selected scan direction. */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
+ * _bt_init_knn_scan() -- Init additional scan state for KNN search.
+ *
+ * Caller must pin and read-lock scan->state.currPos.buf buffer.
+ *
+ * If empty result was found returned false.
+ * Otherwise prepared current item, and returned true.
+ */
+static bool
+_bt_init_knn_scan(IndexScanDesc scan, OffsetNumber offnum)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+ Buffer buf = rstate->currPos.buf;
+ bool left,
+ right;
+
+ lstate = _bt_alloc_knn_scan(scan);
+
+ /* Bump pin and lock count before BTScanPosData copying. */
+ IncrBufferRefCount(buf);
+ LockBuffer(buf, BT_READ);
+
+ memcpy(&lstate->currPos, &rstate->currPos, sizeof(BTScanPosData));
+ lstate->currPos.moreLeft = true;
+ lstate->currPos.moreRight = false;
+
+ /* Load first pages from the both scans. */
+ right = _bt_load_first_page(scan, rstate, ForwardScanDirection, offnum);
+ left = _bt_load_first_page(scan, lstate, BackwardScanDirection,
+ OffsetNumberPrev(offnum));
+
+ return _bt_start_knn_scan(scan, left, right);
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -655,6 +807,19 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!so->qual_ok)
return false;
+ strat_total = BTEqualStrategyNumber;
+
+ if (scan->numberOfOrderBys > 0)
+ {
+ if (_bt_process_orderings(scan, startKeys, &keysCount, notnullkeys))
+ /* use bidirectional KNN scan */
+ strat_total = BtreeKNNSearchStrategyNumber;
+
+ /* use selected KNN scan direction */
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
+ }
+
/*
* For parallel scans, get the starting page from shared state. If the
* scan has not started, proceed to find out first leaf page in the usual
@@ -663,19 +828,50 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, &so->state, &blkno);
if (!status)
return false;
- else if (blkno == P_NONE)
- {
- _bt_parallel_done(scan);
- return false;
- }
else if (blkno != InvalidBlockNumber)
{
- if (!_bt_parallel_readpage(scan, blkno, dir))
- return false;
- goto readcomplete;
+ bool knn = strat_total == BtreeKNNSearchStrategyNumber;
+ bool right;
+ bool left;
+
+ if (knn)
+ _bt_alloc_knn_scan(scan);
+
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, &so->state);
+ right = false;
+ }
+ else
+ right = _bt_parallel_readpage(scan, &so->state, blkno,
+ knn ? ForwardScanDirection : dir);
+
+ if (!knn)
+ return right && _bt_return_current_item(scan, &so->state);
+
+ /* seize additional backward KNN scan */
+ left = _bt_parallel_seize(scan, so->knnState, &blkno);
+
+ if (left)
+ {
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, so->knnState);
+ left = false;
+ }
+ else
+ {
+ /* backward scan should be already initialized */
+ Assert(blkno != InvalidBlockNumber);
+ left = _bt_parallel_readpage(scan, so->knnState, blkno,
+ BackwardScanDirection);
+ }
+ }
+
+ return _bt_start_knn_scan(scan, left, right);
}
}
@@ -725,8 +921,10 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* storing their addresses into the local startKeys[] array.
*----------
*/
- strat_total = BTEqualStrategyNumber;
- if (so->numberOfKeys > 0)
+
+ if (so->numberOfKeys > 0 &&
+ /* startKeys for KNN search already have been initialized */
+ strat_total != BtreeKNNSearchStrategyNumber)
{
AttrNumber curattr;
ScanKey chosen;
@@ -866,7 +1064,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!match)
{
/* No match, so mark (parallel) scan finished */
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
}
return match;
@@ -901,7 +1099,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(subkey->sk_flags & SK_ROW_MEMBER);
if (subkey->sk_flags & SK_ISNULL)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
return false;
}
memcpy(scankeys + i, subkey, sizeof(ScanKeyData));
@@ -1081,6 +1279,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
break;
case BTGreaterEqualStrategyNumber:
+ case BtreeKNNSearchStrategyNumber:
/*
* Find first item >= scankey. (This is only used for forward
@@ -1128,7 +1327,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* mark parallel scan as done, so that all the workers can finish
* their scan
*/
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
BTScanPosInvalidate(*currPos);
return false;
@@ -1167,17 +1366,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(!BTScanPosIsValid(*currPos));
currPos->buf = buf;
+ if (strat_total == BtreeKNNSearchStrategyNumber)
+ return _bt_init_knn_scan(scan, offnum);
+
if (!_bt_load_first_page(scan, &so->state, dir, offnum))
- return false;
+ return false; /* empty result */
-readcomplete:
/* OK, currPos->itemIndex says what to return */
return _bt_return_current_item(scan, &so->state);
}
/*
- * Advance to next tuple on current page; or if there's no more,
- * try to step to the next page with data.
+ * _bt_next_item() -- Advance to next tuple on current page;
+ * or if there's no more, try to step to the next page with data.
+ *
+ * If there are no more matching records in the given direction
*/
static bool
_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
@@ -1197,6 +1400,51 @@ _bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
}
/*
+ * _bt_next_nearest() -- Return next nearest item from bidirectional KNN scan.
+ */
+static bool
+_bt_next_nearest(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state;
+ BTScanState lstate = so->knnState;
+ bool right = BTScanPosIsValid(rstate->currPos);
+ bool left = BTScanPosIsValid(lstate->currPos);
+ bool advanceRight;
+
+ if (right && left)
+ advanceRight = so->currRightIsNearest;
+ else if (right)
+ advanceRight = true;
+ else if (left)
+ advanceRight = false;
+ else
+ return false; /* end of the scan */
+
+ if (advanceRight)
+ right = _bt_next_item(scan, rstate, ForwardScanDirection);
+ else
+ left = _bt_next_item(scan, lstate, BackwardScanDirection);
+
+ if (!left && !right)
+ return false; /* end of the scan */
+
+ if (left && right)
+ {
+ /*
+ * If there are items in both scans we must recalculate distance
+ * in the advanced scan.
+ */
+ _bt_calc_current_dist(scan, advanceRight ? rstate : lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+ right = so->currRightIsNearest;
+ }
+
+ /* return nearest item */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_next() -- Get the next item in a scan.
*
* On entry, so->currPos describes the current page, which may be pinned
@@ -1215,6 +1463,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ if (so->knnState)
+ /* return next neareset item from KNN scan */
+ return _bt_next_nearest(scan);
+
if (!_bt_next_item(scan, &so->state, dir))
return false;
@@ -1267,9 +1519,9 @@ _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
if (scan->parallel_scan)
{
if (ScanDirectionIsForward(dir))
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1443,7 +1695,7 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the next block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
/* release the previous buffer, if pinned */
@@ -1475,13 +1727,19 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the current block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
BTScanPosInvalidate(*currPos);
return false;
}
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, state);
+ BTScanPosInvalidate(*currPos);
+ return false;
+ }
}
else
{
@@ -1531,7 +1789,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
*/
if (blkno == P_NONE || !currPos->moreRight)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1554,14 +1812,14 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
}
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1620,7 +1878,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (!currPos->moreLeft)
{
_bt_relbuf(rel, currPos->buf);
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1631,7 +1889,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
/* if we're physically at end of index, return failure */
if (currPos->buf == InvalidBuffer)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1655,7 +1913,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1667,7 +1925,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1688,17 +1946,16 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
* indicate success.
*/
static bool
-_bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_parallel_readpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ _bt_initialize_more_data(state, dir);
- _bt_initialize_more_data(&so->state, dir);
-
- if (!_bt_readnextpage(scan, &so->state, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
return true;
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 9bf453c..cd81490 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -20,16 +20,21 @@
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "catalog/pg_amop.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
typedef struct BTSortArrayContext
{
FmgrInfo flinfo;
+ FmgrInfo distflinfo;
+ FmgrInfo distcmpflinfo;
+ ScanKey distkey;
Oid collation;
bool reverse;
} BTSortArrayContext;
@@ -49,6 +54,9 @@ static void _bt_mark_scankey_required(ScanKey skey);
static bool _bt_check_rowcompare(ScanKey skey,
IndexTuple tuple, TupleDesc tupdesc,
ScanDirection dir, bool *continuescan);
+static void _bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval);
+
/*
@@ -445,6 +453,7 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
{
Relation rel = scan->indexRelation;
Oid elemtype;
+ Oid opfamily;
RegProcedure cmp_proc;
BTSortArrayContext cxt;
int last_non_dup;
@@ -462,6 +471,53 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
if (elemtype == InvalidOid)
elemtype = rel->rd_opcintype[skey->sk_attno - 1];
+ opfamily = rel->rd_opfamily[skey->sk_attno - 1];
+
+ if (scan->numberOfOrderBys <= 0)
+ {
+ cxt.distkey = NULL;
+ cxt.reverse = reverse;
+ }
+ else
+ {
+ /* Init procedures for distance calculation and comparison. */
+ ScanKey distkey = &scan->orderByData[0];
+ ScanKeyData distkey2;
+ Oid disttype = distkey->sk_subtype;
+ Oid distopr;
+ RegProcedure distproc;
+
+ if (!OidIsValid(disttype))
+ disttype = rel->rd_opcintype[skey->sk_attno - 1];
+
+ /* Lookup distance operator in index column's operator family. */
+ distopr = get_opfamily_member(opfamily,
+ elemtype,
+ disttype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(distopr))
+ elog(ERROR, "missing operator (%u,%u) for strategy %d in opfamily %u",
+ elemtype, disttype, BtreeKNNSearchStrategyNumber, opfamily);
+
+ distproc = get_opcode(distopr);
+
+ if (!RegProcedureIsValid(distproc))
+ elog(ERROR, "missing code for operator %u", distopr);
+
+ fmgr_info(distproc, &cxt.distflinfo);
+
+ distkey2 = *distkey;
+ fmgr_info_copy(&distkey2.sk_func, &cxt.distflinfo, CurrentMemoryContext);
+ distkey2.sk_subtype = disttype;
+
+ _bt_get_distance_cmp_proc(&distkey2, opfamily, elemtype,
+ &cxt.distcmpflinfo, NULL, NULL);
+
+ cxt.distkey = distkey;
+ cxt.reverse = false; /* supported only ascending ordering */
+ }
+
/*
* Look up the appropriate comparison function in the opfamily.
*
@@ -470,19 +526,17 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
* non-cross-type support functions for any datatype that it supports at
* all.
*/
- cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
+ cmp_proc = get_opfamily_proc(opfamily,
elemtype,
elemtype,
BTORDER_PROC);
if (!RegProcedureIsValid(cmp_proc))
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
- BTORDER_PROC, elemtype, elemtype,
- rel->rd_opfamily[skey->sk_attno - 1]);
+ BTORDER_PROC, elemtype, elemtype, opfamily);
/* Sort the array elements */
fmgr_info(cmp_proc, &cxt.flinfo);
cxt.collation = skey->sk_collation;
- cxt.reverse = reverse;
qsort_arg((void *) elems, nelems, sizeof(Datum),
_bt_compare_array_elements, (void *) &cxt);
@@ -514,6 +568,23 @@ _bt_compare_array_elements(const void *a, const void *b, void *arg)
BTSortArrayContext *cxt = (BTSortArrayContext *) arg;
int32 compare;
+ if (cxt->distkey)
+ {
+ Datum dista = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ da,
+ cxt->distkey->sk_argument);
+ Datum distb = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ db,
+ cxt->distkey->sk_argument);
+ bool cmp = DatumGetBool(FunctionCall2Coll(&cxt->distcmpflinfo,
+ cxt->collation,
+ dista,
+ distb));
+ return cmp ? -1 : 1;
+ }
+
compare = DatumGetInt32(FunctionCall2Coll(&cxt->flinfo,
cxt->collation,
da, db));
@@ -2075,6 +2146,39 @@ btproperty(Oid index_oid, int attno,
*res = true;
return true;
+ case AMPROP_DISTANCE_ORDERABLE:
+ {
+ Oid opclass,
+ opfamily,
+ opcindtype;
+
+ /* answer only for columns, not AM or whole index */
+ if (attno == 0)
+ return false;
+
+ opclass = get_index_column_opclass(index_oid, attno);
+
+ if (!OidIsValid(opclass))
+ {
+ *res = false; /* non-key attribute */
+ return true;
+ }
+
+ if (!get_opclass_opfamily_and_input_type(opclass,
+ &opfamily, &opcindtype))
+ {
+ *isnull = true;
+ return true;
+ }
+
+ *res = SearchSysCacheExists(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(opcindtype),
+ ObjectIdGetDatum(opcindtype),
+ Int16GetDatum(BtreeKNNSearchStrategyNumber));
+ return true;
+ }
+
default:
return false; /* punt to generic code */
}
@@ -2223,3 +2327,264 @@ _bt_allocate_tuple_workspaces(BTScanState state)
state->currTuples = (char *) palloc(BLCKSZ * 2);
state->markTuples = state->currTuples + BLCKSZ;
}
+
+static bool
+_bt_compare_row_key_with_ordering_key(ScanKey row, ScanKey ord, bool *result)
+{
+ ScanKey subkey = (ScanKey) DatumGetPointer(row->sk_argument);
+ int32 cmpresult;
+
+ Assert(subkey->sk_attno == 1);
+ Assert(subkey->sk_flags & SK_ROW_MEMBER);
+
+ if (subkey->sk_flags & SK_ISNULL)
+ return false;
+
+ /* Perform the test --- three-way comparison not bool operator */
+ cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
+ subkey->sk_collation,
+ ord->sk_argument,
+ subkey->sk_argument));
+
+ if (subkey->sk_flags & SK_BT_DESC)
+ cmpresult = -cmpresult;
+
+ /*
+ * At this point cmpresult indicates the overall result of the row
+ * comparison, and subkey points to the deciding column (or the last
+ * column if the result is "=").
+ */
+ switch (subkey->sk_strategy)
+ {
+ /* EQ and NE cases aren't allowed here */
+ case BTLessStrategyNumber:
+ *result = cmpresult < 0;
+ break;
+ case BTLessEqualStrategyNumber:
+ *result = cmpresult <= 0;
+ break;
+ case BTGreaterEqualStrategyNumber:
+ *result = cmpresult >= 0;
+ break;
+ case BTGreaterStrategyNumber:
+ *result = cmpresult > 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized RowCompareType: %d",
+ (int) subkey->sk_strategy);
+ *result = false; /* keep compiler quiet */
+ }
+
+ return true;
+}
+
+/* _bt_select_knn_search_strategy() -- Determine which KNN scan strategy to use:
+ * bidirectional or unidirectional. We are checking here if the
+ * ordering scankey argument falls into the scan range: if it falls
+ * we must use bidirectional scan, otherwise we use unidirectional.
+ *
+ * Returns BtreeKNNSearchStrategyNumber for bidirectional scan or
+ * strategy number of non-matched scankey for unidirectional.
+ */
+static StrategyNumber
+_bt_select_knn_search_strategy(IndexScanDesc scan, ScanKey ord)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey cond;
+
+ for (cond = so->keyData; cond < so->keyData + so->numberOfKeys; cond++)
+ {
+ bool result;
+
+ if (cond->sk_attno != 1)
+ break; /* only interesting in the first index attribute */
+
+ if (cond->sk_strategy == BTEqualStrategyNumber)
+ /* always use simple unidirectional scan for equals operators */
+ return BTEqualStrategyNumber;
+
+ if (cond->sk_flags & SK_ROW_HEADER)
+ {
+ if (!_bt_compare_row_key_with_ordering_key(cond, ord, &result))
+ return BTEqualStrategyNumber; /* ROW(fist_index_attr, ...) IS NULL */
+ }
+ else
+ {
+ if (!_bt_compare_scankey_args(scan, cond, ord, cond, &result))
+ elog(ERROR, "could not compare ordering key");
+ }
+
+ if (!result)
+ /*
+ * Ordering scankey argument is out of scan range,
+ * use unidirectional scan.
+ */
+ return cond->sk_strategy;
+ }
+
+ return BtreeKNNSearchStrategyNumber; /* use bidirectional scan */
+}
+
+static Oid
+_bt_get_sortfamily_for_opfamily_op(Oid opfamily, Oid lefttype, Oid righttype,
+ StrategyNumber strategy)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid sortfamily;
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(lefttype),
+ ObjectIdGetDatum(righttype),
+ Int16GetDatum(strategy));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ sortfamily = amop_tup->amopsortfamily;
+ ReleaseSysCache(tp);
+
+ return sortfamily;
+}
+
+/*
+ * _bt_get_distance_cmp_proc() -- Init procedure for comparsion of distances
+ * between "leftargtype" and "distkey".
+ */
+static void
+_bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval)
+{
+ RegProcedure opcode;
+ Oid sortfamily;
+ Oid opno;
+ Oid distanceType;
+
+ distanceType = get_func_rettype(distkey->sk_func.fn_oid);
+
+ sortfamily = _bt_get_sortfamily_for_opfamily_op(opfamily, leftargtype,
+ distkey->sk_subtype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(sortfamily))
+ elog(ERROR, "could not find sort family for btree ordering operator");
+
+ opno = get_opfamily_member(sortfamily,
+ distanceType,
+ distanceType,
+ BTLessEqualStrategyNumber);
+
+ if (!OidIsValid(opno))
+ elog(ERROR, "could not find operator for btree distance comparison");
+
+ opcode = get_opcode(opno);
+
+ if (!RegProcedureIsValid(opcode))
+ elog(ERROR,
+ "could not find procedure for btree distance comparison operator");
+
+ fmgr_info(opcode, finfo);
+
+ if (typlen)
+ get_typlenbyval(distanceType, typlen, typbyval);
+}
+
+/*
+ * _bt_init_distance_comparison() -- Init distance typlen/typbyval and its
+ * comparison procedure.
+ */
+static void
+_bt_init_distance_comparison(IndexScanDesc scan, ScanKey ord)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ Relation rel = scan->indexRelation;
+
+ _bt_get_distance_cmp_proc(ord,
+ rel->rd_opfamily[ord->sk_attno - 1],
+ rel->rd_opcintype[ord->sk_attno - 1],
+ &so->distanceCmpProc,
+ &so->distanceTypeLen,
+ &so->distanceTypeByVal);
+
+ if (!so->distanceTypeByVal)
+ {
+ so->state.currDistance = PointerGetDatum(NULL);
+ so->state.markDistance = PointerGetDatum(NULL);
+ }
+}
+
+/*
+ * _bt_process_orderings() -- Process ORDER BY distance scankeys and
+ * select corresponding KNN strategy.
+ *
+ * If bidirectional scan is selected then one scankey is initialized
+ * using bufKeys and placed into startKeys/keysCount, true is returned.
+ *
+ * Otherwise, so->scanDirection is set and false is returned.
+ */
+bool
+_bt_process_orderings(IndexScanDesc scan, ScanKey *startKeys, int *keysCount,
+ ScanKeyData bufKeys[])
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey ord = scan->orderByData;
+
+ if (scan->numberOfOrderBys > 1 || ord->sk_attno != 1)
+ /* it should not happen, see btmatchorderby() */
+ elog(ERROR, "only one btree ordering operator "
+ "for the first index column is supported");
+
+ Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
+
+ switch (_bt_select_knn_search_strategy(scan, ord))
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ /*
+ * Ordering key argument is greater than all values in scan range.
+ * select backward scan direction.
+ */
+ so->scanDirection = BackwardScanDirection;
+ return false;
+
+ case BTEqualStrategyNumber:
+ /* Use default unidirectional scan direction. */
+ return false;
+
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ /*
+ * Ordering key argument is lesser than all values in scan range.
+ * select forward scan direction.
+ */
+ so->scanDirection = ForwardScanDirection;
+ return false;
+
+ case BtreeKNNSearchStrategyNumber:
+ /*
+ * Ordering key argument falls into scan range,
+ * use bidirectional scan.
+ */
+ break;
+ }
+
+ _bt_init_distance_comparison(scan, ord);
+
+ /* Init btree search key with ordering key argument. */
+ ScanKeyEntryInitialize(&bufKeys[0],
+ (scan->indexRelation->rd_indoption[ord->sk_attno - 1] <<
+ SK_BT_INDOPTION_SHIFT) |
+ SK_ORDER_BY |
+ SK_SEARCHNULL /* only for invalid procedure oid, see
+ * assert in ScanKeyEntryInitialize */,
+ ord->sk_attno,
+ BtreeKNNSearchStrategyNumber,
+ ord->sk_subtype,
+ ord->sk_collation,
+ InvalidOid,
+ ord->sk_argument);
+
+ startKeys[(*keysCount)++] = &bufKeys[0];
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index f24091c..be4e843 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -22,9 +22,17 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
+#define BTRequiredOperatorSet \
+ ((1 << BTLessStrategyNumber) | \
+ (1 << BTLessEqualStrategyNumber) | \
+ (1 << BTEqualStrategyNumber) | \
+ (1 << BTGreaterEqualStrategyNumber) | \
+ (1 << BTGreaterStrategyNumber))
+
/*
* Validator for a btree opclass.
@@ -132,10 +140,11 @@ btvalidate(Oid opclassoid)
{
HeapTuple oprtup = &oprlist->members[i]->tuple;
Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ Oid op_rettype;
/* Check that only allowed strategy numbers exist */
if (oprform->amopstrategy < 1 ||
- oprform->amopstrategy > BTMaxStrategyNumber)
+ oprform->amopstrategy > BtreeMaxStrategyNumber)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -146,20 +155,29 @@ btvalidate(Oid opclassoid)
result = false;
}
- /* btree doesn't support ORDER BY operators */
- if (oprform->amoppurpose != AMOP_SEARCH ||
- OidIsValid(oprform->amopsortfamily))
+ /* btree supports ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH)
{
- ereport(INFO,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
- opfamilyname, "btree",
- format_operator(oprform->amopopr))));
- result = false;
+ /* ... and operator result must match the claimed btree opfamily */
+ op_rettype = get_op_rettype(oprform->amopopr);
+ if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
+ {
+ ereport(INFO,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
+ opfamilyname, "btree",
+ format_operator(oprform->amopopr))));
+ result = false;
+ }
+ }
+ else
+ {
+ /* Search operators must always return bool */
+ op_rettype = BOOLOID;
}
/* Check operator signature --- same for all btree strategies */
- if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ if (!check_amop_signature(oprform->amopopr, op_rettype,
oprform->amoplefttype,
oprform->amoprighttype))
{
@@ -214,12 +232,8 @@ btvalidate(Oid opclassoid)
* or support functions for this datatype pair. The only things
* considered optional are the sortsupport and in_range functions.
*/
- if (thisgroup->operatorset !=
- ((1 << BTLessStrategyNumber) |
- (1 << BTLessEqualStrategyNumber) |
- (1 << BTEqualStrategyNumber) |
- (1 << BTGreaterEqualStrategyNumber) |
- (1 << BTGreaterStrategyNumber)))
+ if ((thisgroup->operatorset & BTRequiredOperatorSet) !=
+ BTRequiredOperatorSet)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index e043664..7c9b97b 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -988,6 +988,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
* if we are only trying to build bitmap indexscans, nor if we have to
* assume the scan is unordered.
*/
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
!found_lower_saop_clause &&
has_useful_pathkeys(root, rel));
@@ -998,10 +1002,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
ForwardScanDirection);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
}
- else if (index->ammatchorderby && pathkeys_possibly_useful)
+
+ if (useful_pathkeys == NIL &&
+ index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -1012,12 +1016,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
else
useful_pathkeys = NIL;
}
- else
- {
- useful_pathkeys = NIL;
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
- }
/*
* 3. Check if an index-only scan is possible. If we're not building
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 388b311..9c8a384 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -460,6 +460,12 @@ typedef struct BTScanStateData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+
+ /* KNN-search fields: */
+ Datum currDistance; /* current distance */
+ Datum markDistance; /* marked distance */
+ bool currIsNull; /* current item is NULL */
+ bool markIsNull; /* marked item is NULL */
} BTScanStateData, *BTScanState;
typedef struct BTScanOpaqueData
@@ -478,7 +484,17 @@ typedef struct BTScanOpaqueData
BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
MemoryContext arrayContext; /* scan-lifespan context for array data */
- BTScanStateData state;
+ BTScanStateData state; /* main scan state */
+
+ /* KNN-search fields: */
+ BTScanState knnState; /* optional scan state for KNN search */
+ ScanDirection scanDirection; /* selected scan direction for
+ * unidirectional KNN scan */
+ FmgrInfo distanceCmpProc; /* distance comparison procedure */
+ int16 distanceTypeLen; /* distance typlen */
+ bool distanceTypeByVal; /* distance typebyval */
+ bool currRightIsNearest; /* current right item is nearest */
+ bool markRightIsNearest; /* marked right item is nearest */
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -524,11 +540,12 @@ extern bool btcanreturn(Relation index, int attno);
/*
* prototypes for internal functions in nbtree.c
*/
-extern bool _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno);
-extern void _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page);
-extern void _bt_parallel_done(IndexScanDesc scan);
+extern bool _bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno);
+extern void _bt_parallel_release(IndexScanDesc scan, BTScanState state, BlockNumber scan_page);
+extern void _bt_parallel_done(IndexScanDesc scan, BTScanState state);
extern void _bt_parallel_advance_array_keys(IndexScanDesc scan);
+
/*
* prototypes for functions in nbtinsert.c
*/
@@ -609,6 +626,8 @@ extern bool btproperty(Oid index_oid, int attno,
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
extern void _bt_allocate_tuple_workspaces(BTScanState state);
+extern bool _bt_process_orderings(IndexScanDesc scan,
+ ScanKey *startKeys, int *keysCount, ScanKeyData bufKeys[]);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/include/access/stratnum.h b/src/include/access/stratnum.h
index 0db11a1..f44b41a 100644
--- a/src/include/access/stratnum.h
+++ b/src/include/access/stratnum.h
@@ -32,7 +32,10 @@ typedef uint16 StrategyNumber;
#define BTGreaterEqualStrategyNumber 4
#define BTGreaterStrategyNumber 5
-#define BTMaxStrategyNumber 5
+#define BTMaxStrategyNumber 5 /* number of canonical B-tree strategies */
+
+#define BtreeKNNSearchStrategyNumber 6 /* for <-> (distance) */
+#define BtreeMaxStrategyNumber 6 /* number of extended B-tree strategies */
/*
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 6faa9d7..c75ef39 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -347,10 +347,10 @@ ROLLBACK;
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
ERROR: access method "invalid_index_method" does not exist
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 6, must be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 0, must be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 7, must be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 0, must be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -397,11 +397,12 @@ DROP OPERATOR FAMILY alt_opf8 USING btree;
CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
-ERROR: access method "btree" does not support ordering operators
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
ALTER OPERATOR FAMILY alt_opf11 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 84fd900..73e6e206 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -295,8 +295,8 @@ ROLLBACK;
-- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -340,10 +340,12 @@ CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
0005-Add-btree-distance-operators-v03.patchtext/x-patch; name=0005-Add-btree-distance-operators-v03.patchDownload
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 9015557..a72f45a 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -183,6 +183,12 @@ SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
</programlisting>
which finds the ten events closest to a given target date. The ability
to do this is again dependent on the particular operator class being used.
+ Built-in B-tree operator classes support distance ordering for data types
+ <type>int2</>, <type>int4</>, <type>int8</>,
+ <type>float4</>, <type>float8</>, <type>numeric</>,
+ <type>timestamp with time zone</>, <type>timestamp without time zone</>,
+ <type>time with time zone</>, <type>time without time zone</>,
+ <type>date</>, <type>interval</>, <type>oid</>, <type>money</>.
</para>
<para>
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index c787dd3..3c07ced 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -30,6 +30,7 @@
#include "utils/numeric.h"
#include "utils/pg_locale.h"
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
/*************************************************************************
* Private routines
@@ -1157,3 +1158,22 @@ int8_cash(PG_FUNCTION_ARGS)
PG_RETURN_CASH(result);
}
+
+Datum
+cash_dist(PG_FUNCTION_ARGS)
+{
+ Cash a = PG_GETARG_CASH(0);
+ Cash b = PG_GETARG_CASH(1);
+ Cash r;
+ Cash ra;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("money out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_CASH(ra);
+}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 87146a2..789d277 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -563,6 +563,17 @@ date_mii(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(result);
}
+Datum
+date_dist(PG_FUNCTION_ARGS)
+{
+ /* we assume the difference can't overflow */
+ Datum diff = DirectFunctionCall2(date_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+}
+
/*
* Internal routines for promoting date to timestamp and timestamp with
* time zone
@@ -760,6 +771,29 @@ date_cmp_timestamp(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
+ Timestamp dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
date_eq_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
@@ -844,6 +878,30 @@ date_cmp_timestamptz(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
+
+
+Datum
timestamp_eq_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
@@ -928,6 +986,29 @@ timestamp_cmp_date(PG_FUNCTION_ARGS)
}
Datum
+timestamp_dist_date(PG_FUNCTION_ARGS)
+{
+ Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ Timestamp dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
timestamptz_eq_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
@@ -1039,6 +1120,28 @@ in_range_date_interval(PG_FUNCTION_ARGS)
BoolGetDatum(less));
}
+Datum
+timestamptz_dist_date(PG_FUNCTION_ARGS)
+{
+ TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ TimestampTz dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
/* Add an interval to a date, giving a new date.
* Must handle both positive and negative intervals.
@@ -1996,6 +2099,16 @@ time_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+Datum
+time_dist(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(time_mi_time,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
+
/*****************************************************************************
* Time With Time Zone ADT
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index df35557..24078b3 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -3416,6 +3416,47 @@ width_bucket_float8(PG_FUNCTION_ARGS)
PG_RETURN_INT32(result);
}
+Datum
+float4_dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float4 r = float4_mi(a, b);
+
+ PG_RETURN_FLOAT4(Abs(r));
+}
+
+Datum
+float8_dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+
+Datum
+float48_dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+Datum
+float84_dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
/* ========== PRIVATE ROUTINES ========== */
#ifndef HAVE_CBRT
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 8149dc1..9335746 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -1427,3 +1427,55 @@ generate_series_step_int4(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+Datum
+int2_dist(PG_FUNCTION_ARGS)
+{
+ int16 a = PG_GETARG_INT16(0);
+ int16 b = PG_GETARG_INT16(1);
+ int16 r;
+ int16 ra;
+
+ if (pg_sub_s16_overflow(a, b, &r) ||
+ r == PG_INT16_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_INT16(ra);
+}
+
+static int32
+int44_dist(int32 a, int32 b)
+{
+ int32 r;
+
+ if (pg_sub_s32_overflow(a, b, &r) ||
+ r == PG_INT32_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ return Abs(r);
+}
+
+
+Datum
+int4_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int24_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT16(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int42_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT16(1)));
+}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 3c595e8..a114319 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1357,3 +1357,47 @@ generate_series_step_int8(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+static int64
+int88_dist(int64 a, int64 b)
+{
+ int64 r;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+
+ return Abs(r);
+}
+
+Datum
+int8_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int82_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT16(1)));
+}
+
+Datum
+int84_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT32(1)));
+}
+
+Datum
+int28_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT16(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int48_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT32(0), PG_GETARG_INT64(1)));
+}
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index b0670e0..536507b 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -469,3 +469,17 @@ oidvectorgt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+
+Datum
+oid_dist(PG_FUNCTION_ARGS)
+{
+ Oid a = PG_GETARG_OID(0);
+ Oid b = PG_GETARG_OID(1);
+ Oid res;
+
+ if (a < b)
+ res = b - a;
+ else
+ res = a - b;
+ PG_RETURN_OID(res);
+}
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 284e14d..e9a4a0c 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2649,6 +2649,86 @@ timestamp_mi(PG_FUNCTION_ARGS)
PG_RETURN_INTERVAL_P(result);
}
+Datum
+timestamp_dist(PG_FUNCTION_ARGS)
+{
+ Timestamp a = PG_GETARG_TIMESTAMP(0);
+ Timestamp b = PG_GETARG_TIMESTAMP(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+ p->time = PG_INT64_MAX;
+ PG_RETURN_INTERVAL_P(p);
+ }
+ else
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+Interval *
+timestamp_dist_internal(Timestamp a, Timestamp b)
+{
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ return r;
+ }
+
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ TimestampGetDatum(a),
+ TimestampGetDatum(b)));
+
+ return abs_interval(r);
+}
+
+Datum
+timestamptz_dist(PG_FUNCTION_ARGS)
+{
+ TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
+ TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(a, b));
+}
+
+Datum
+timestamp_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ Timestamp ts1 = PG_GETARG_TIMESTAMP(0);
+ TimestampTz tstz2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz tstz1;
+
+ tstz1 = timestamp2timestamptz(ts1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+Datum
+timestamptz_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ TimestampTz tstz1 = PG_GETARG_TIMESTAMPTZ(0);
+ Timestamp ts2 = PG_GETARG_TIMESTAMP(1);
+ TimestampTz tstz2;
+
+ tstz2 = timestamp2timestamptz(ts2);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+
/*
* interval_justify_interval()
*
@@ -3518,6 +3598,29 @@ interval_avg(PG_FUNCTION_ARGS)
Float8GetDatum((double) N.time));
}
+Interval *
+abs_interval(Interval *a)
+{
+ static Interval zero = {0, 0, 0};
+
+ if (DatumGetBool(DirectFunctionCall2(interval_lt,
+ IntervalPGetDatum(a),
+ IntervalPGetDatum(&zero))))
+ a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
+ IntervalPGetDatum(a)));
+
+ return a;
+}
+
+Datum
+interval_dist(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(interval_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
/* timestamp_age()
* Calculate time difference while retaining year/month fields.
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index a1e57d7..3fe49a6 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -30,6 +30,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int24
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -47,6 +51,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int28
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -64,6 +72,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int4
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -81,6 +93,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int42
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -98,6 +114,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int48
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -115,6 +135,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int8
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -132,6 +156,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int82
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -149,6 +177,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int84
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -166,6 +198,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# btree oid_ops
@@ -179,6 +215,10 @@
amopstrategy => '4', amopopr => '>=(oid,oid)', amopmethod => 'btree' },
{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid', amoprighttype => 'oid',
amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'btree' },
+{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(oid,oid)', amopmethod => 'btree',
+ amopsortfamily => 'btree/oid_ops' },
# btree tid_ops
@@ -229,6 +269,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float48
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
@@ -246,6 +290,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# default operators float8
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -263,6 +311,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float84
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -280,6 +332,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# btree char_ops
@@ -407,6 +463,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -424,6 +484,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(date,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -441,6 +505,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(date,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -458,6 +526,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamp,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -475,6 +547,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -492,6 +568,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamp,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -509,6 +589,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -526,6 +610,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'date', amopstrategy => '5',
amopopr => '>(timestamptz,date)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -543,6 +631,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamptz,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree time_ops
@@ -561,6 +653,10 @@
{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(time,time)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree timetz_ops
@@ -597,6 +693,10 @@
{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'btree' },
+{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(interval,interval)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree macaddr
@@ -772,6 +872,10 @@
{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
amoprighttype => 'money', amopstrategy => '5', amopopr => '>(money,money)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
+ amoprighttype => 'money', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(money,money)', amopmethod => 'btree',
+ amopsortfamily => 'btree/money_ops' },
# btree reltime_ops
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index d9b6bad..6be94f3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -2932,6 +2932,114 @@
oprname => '-', oprleft => 'pg_lsn', oprright => 'pg_lsn',
oprresult => 'numeric', oprcode => 'pg_lsn_mi' },
+# distance operators
+{ oid => '4217', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int2', oprresult => 'int2', oprcom => '<->(int2,int2)',
+ oprcode => 'int2_dist'},
+{ oid => '4218', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int4)',
+ oprcode => 'int4_dist'},
+{ oid => '4219', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int8)',
+ oprcode => 'int8_dist'},
+{ oid => '4220', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'oid',
+ oprright => 'oid', oprresult => 'oid', oprcom => '<->(oid,oid)',
+ oprcode => 'oid_dist'},
+{ oid => '4221', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float4', oprresult => 'float4', oprcom => '<->(float4,float4)',
+ oprcode => 'float4_dist'},
+{ oid => '4222', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float8)',
+ oprcode => 'float8_dist'},
+{ oid => '4223', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'money',
+ oprright => 'money', oprresult => 'money', oprcom => '<->(money,money)',
+ oprcode => 'cash_dist'},
+{ oid => '4224', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'date', oprresult => 'int4', oprcom => '<->(date,date)',
+ oprcode => 'date_dist'},
+{ oid => '4225', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'time',
+ oprright => 'time', oprresult => 'interval', oprcom => '<->(time,time)',
+ oprcode => 'time_dist'},
+{ oid => '4226', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamp)',
+ oprcode => 'timestamp_dist'},
+{ oid => '4227', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamptz)',
+ oprcode => 'timestamptz_dist'},
+{ oid => '4228', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'interval',
+ oprright => 'interval', oprresult => 'interval', oprcom => '<->(interval,interval)',
+ oprcode => 'interval_dist'},
+
+# cross-type distance operators
+{ oid => '4229', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int2)',
+ oprcode => 'int24_dist'},
+{ oid => '4230', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int2', oprresult => 'int4', oprcom => '<->(int2,int4)',
+ oprcode => 'int42_dist'},
+{ oid => '4231', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int2)',
+ oprcode => 'int28_dist'},
+{ oid => '4232', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int2', oprresult => 'int8', oprcom => '<->(int2,int8)',
+ oprcode => 'int82_dist'},
+{ oid => '4233', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int4)',
+ oprcode => 'int48_dist'},
+{ oid => '4234', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int4', oprresult => 'int8', oprcom => '<->(int4,int8)',
+ oprcode => 'int84_dist'},
+{ oid => '4235', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float4)',
+ oprcode => 'float48_dist'},
+{ oid => '4236', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float4', oprresult => 'float8', oprcom => '<->(float4,float8)',
+ oprcode => 'float84_dist'},
+{ oid => '4237', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,date)',
+ oprcode => 'date_dist_timestamp'},
+{ oid => '4238', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamp)',
+ oprcode => 'timestamp_dist_date'},
+{ oid => '4239', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,date)',
+ oprcode => 'date_dist_timestamptz'},
+{ oid => '4240', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamptz)',
+ oprcode => 'timestamptz_dist_date'},
+{ oid => '4241', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamp)',
+ oprcode => 'timestamp_dist_timestamptz'},
+{ oid => '4242', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamptz)',
+ oprcode => 'timestamptz_dist_timestamp'},
+
# enum operators
{ oid => '3516', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anyenum',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 8e4145f..ed3a50f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10206,4 +10206,86 @@
proisstrict => 'f', prorettype => 'bool', proargtypes => 'oid int4 int4 any',
proargmodes => '{i,i,i,v}', prosrc => 'satisfies_hash_partition' },
+# distance functions
+{ oid => '4243',
+ proname => 'int2_dist', prorettype => 'int2',
+ proargtypes => 'int2 int2', prosrc => 'int2_dist' },
+{ oid => '4244',
+ proname => 'int4_dist', prorettype => 'int4',
+ proargtypes => 'int4 int4', prosrc => 'int4_dist' },
+{ oid => '4245',
+ proname => 'int8_dist', prorettype => 'int8',
+ proargtypes => 'int8 int8', prosrc => 'int8_dist' },
+{ oid => '4246',
+ proname => 'oid_dist', prorettype => 'oid',
+ proargtypes => 'oid oid', prosrc => 'oid_dist' },
+{ oid => '4247',
+ proname => 'float4_dist', prorettype => 'float4',
+ proargtypes => 'float4 float4', prosrc => 'float4_dist' },
+{ oid => '4248',
+ proname => 'float8_dist', prorettype => 'float8',
+ proargtypes => 'float8 float8', prosrc => 'float8_dist' },
+{ oid => '4249',
+ proname => 'cash_dist', prorettype => 'money',
+ proargtypes => 'money money', prosrc => 'cash_dist' },
+{ oid => '4250',
+ proname => 'date_dist', prorettype => 'int4',
+ proargtypes => 'date date', prosrc => 'date_dist' },
+{ oid => '4251',
+ proname => 'time_dist', prorettype => 'interval',
+ proargtypes => 'time time', prosrc => 'time_dist' },
+{ oid => '4252',
+ proname => 'timestamp_dist', prorettype => 'interval',
+ proargtypes => 'timestamp timestamp', prosrc => 'timestamp_dist' },
+{ oid => '4253',
+ proname => 'timestamptz_dist', prorettype => 'interval',
+ proargtypes => 'timestamptz timestamptz', prosrc => 'timestamptz_dist' },
+{ oid => '4254',
+ proname => 'interval_dist', prorettype => 'interval',
+ proargtypes => 'interval interval', prosrc => 'interval_dist' },
+
+# cross-type distance functions
+{ oid => '4255',
+ proname => 'int24_dist', prorettype => 'int4',
+ proargtypes => 'int2 int4', prosrc => 'int24_dist' },
+{ oid => '4256',
+ proname => 'int28_dist', prorettype => 'int8',
+ proargtypes => 'int2 int8', prosrc => 'int28_dist' },
+{ oid => '4257',
+ proname => 'int42_dist', prorettype => 'int4',
+ proargtypes => 'int4 int2', prosrc => 'int42_dist' },
+{ oid => '4258',
+ proname => 'int48_dist', prorettype => 'int8',
+ proargtypes => 'int4 int8', prosrc => 'int48_dist' },
+{ oid => '4259',
+ proname => 'int82_dist', prorettype => 'int8',
+ proargtypes => 'int8 int2', prosrc => 'int82_dist' },
+{ oid => '4260',
+ proname => 'int84_dist', prorettype => 'int8',
+ proargtypes => 'int8 int4', prosrc => 'int84_dist' },
+{ oid => '4261',
+ proname => 'float48_dist', prorettype => 'float8',
+ proargtypes => 'float4 float8', prosrc => 'float48_dist' },
+{ oid => '4262',
+ proname => 'float84_dist', prorettype => 'float8',
+ proargtypes => 'float8 float4', prosrc => 'float84_dist' },
+{ oid => '4263',
+ proname => 'date_dist_timestamp', prorettype => 'interval',
+ proargtypes => 'date timestamp', prosrc => 'date_dist_timestamp' },
+{ oid => '4264',
+ proname => 'date_dist_timestamptz', prorettype => 'interval',
+ proargtypes => 'date timestamptz', prosrc => 'date_dist_timestamptz' },
+{ oid => '4265',
+ proname => 'timestamp_dist_date', prorettype => 'interval',
+ proargtypes => 'timestamp date', prosrc => 'timestamp_dist_date' },
+{ oid => '4266',
+ proname => 'timestamp_dist_timestamptz', prorettype => 'interval',
+ proargtypes => 'timestamp timestamptz', prosrc => 'timestamp_dist_timestamptz' },
+{ oid => '4267',
+ proname => 'timestamptz_dist_date', prorettype => 'interval',
+ proargtypes => 'timestamptz date', prosrc => 'timestamptz_dist_date' },
+{ oid => '4268',
+ proname => 'timestamptz_dist_timestamp', prorettype => 'interval',
+ proargtypes => 'timestamptz timestamp', prosrc => 'timestamptz_dist_timestamp' },
+
]
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index d66582b..9a79369 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -338,4 +338,6 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
+extern Interval *abs_interval(Interval *a);
+
#endif /* DATETIME_H */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index 2b3b357..1fa56e5 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -93,9 +93,11 @@ extern Timestamp SetEpochTimestamp(void);
extern void GetEpochTime(struct pg_tm *tm);
extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
+extern Interval *timestamp_dist_internal(Timestamp a, Timestamp b);
-/* timestamp comparison works for timestamptz also */
+/* timestamp comparison and distance works for timestamptz also */
#define timestamptz_cmp_internal(dt1,dt2) timestamp_cmp_internal(dt1, dt2)
+#define timestamptz_dist_internal(dt1,dt2) timestamp_dist_internal(dt1, dt2)
extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday);
diff --git a/src/test/regress/expected/amutils.out b/src/test/regress/expected/amutils.out
index 4570a39..630dc6b 100644
--- a/src/test/regress/expected/amutils.out
+++ b/src/test/regress/expected/amutils.out
@@ -24,7 +24,7 @@ select prop,
nulls_first | | | f
nulls_last | | | t
orderable | | | t
- distance_orderable | | | f
+ distance_orderable | | | t
returnable | | | t
search_array | | | t
search_nulls | | | t
@@ -100,7 +100,7 @@ select prop,
nulls_first | f | f | f | f | f | f | f
nulls_last | t | f | f | f | f | f | f
orderable | t | f | f | f | f | f | f
- distance_orderable | f | f | t | f | t | f | f
+ distance_orderable | t | f | t | f | t | f | f
returnable | t | f | f | t | t | f | f
search_array | t | f | f | f | f | f | f
search_nulls | t | f | t | t | t | f | t
@@ -231,7 +231,7 @@ select col, prop, pg_index_column_has_property(o, col, prop)
1 | desc | f
1 | nulls_first | f
1 | nulls_last | t
- 1 | distance_orderable | f
+ 1 | distance_orderable | t
1 | returnable | t
1 | bogus |
2 | orderable | f
diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out
index 1bcc946..b9b819c 100644
--- a/src/test/regress/expected/date.out
+++ b/src/test/regress/expected/date.out
@@ -1477,3 +1477,64 @@ select make_time(10, 55, 100.1);
ERROR: time field value out of range: 10:55:100.1
select make_time(24, 0, 2.1);
ERROR: time field value out of range: 24:00:2.1
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+----------
+ | 16006
+ | 15941
+ | 1802
+ | 1801
+ | 1800
+ | 1799
+ | 1436
+ | 1435
+ | 1434
+ | 308
+ | 307
+ | 306
+ | 13578
+ | 13944
+ | 14311
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16006 days 1 hour 23 mins 45 secs
+ | @ 15941 days 1 hour 23 mins 45 secs
+ | @ 1802 days 1 hour 23 mins 45 secs
+ | @ 1801 days 1 hour 23 mins 45 secs
+ | @ 1800 days 1 hour 23 mins 45 secs
+ | @ 1799 days 1 hour 23 mins 45 secs
+ | @ 1436 days 1 hour 23 mins 45 secs
+ | @ 1435 days 1 hour 23 mins 45 secs
+ | @ 1434 days 1 hour 23 mins 45 secs
+ | @ 308 days 1 hour 23 mins 45 secs
+ | @ 307 days 1 hour 23 mins 45 secs
+ | @ 306 days 1 hour 23 mins 45 secs
+ | @ 13577 days 22 hours 36 mins 15 secs
+ | @ 13943 days 22 hours 36 mins 15 secs
+ | @ 14310 days 22 hours 36 mins 15 secs
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16005 days 14 hours 23 mins 45 secs
+ | @ 15940 days 14 hours 23 mins 45 secs
+ | @ 1801 days 14 hours 23 mins 45 secs
+ | @ 1800 days 14 hours 23 mins 45 secs
+ | @ 1799 days 14 hours 23 mins 45 secs
+ | @ 1798 days 14 hours 23 mins 45 secs
+ | @ 1435 days 14 hours 23 mins 45 secs
+ | @ 1434 days 14 hours 23 mins 45 secs
+ | @ 1433 days 14 hours 23 mins 45 secs
+ | @ 307 days 14 hours 23 mins 45 secs
+ | @ 306 days 14 hours 23 mins 45 secs
+ | @ 305 days 15 hours 23 mins 45 secs
+ | @ 13578 days 8 hours 36 mins 15 secs
+ | @ 13944 days 8 hours 36 mins 15 secs
+ | @ 14311 days 8 hours 36 mins 15 secs
+(15 rows)
+
diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out
index fd46a4a..51f82ea 100644
--- a/src/test/regress/expected/float4.out
+++ b/src/test/regress/expected/float4.out
@@ -257,3 +257,23 @@ SELECT '' AS five, * FROM FLOAT4_TBL;
| -1.23457e-20
(5 rows)
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+--------------+-------------
+ | 0 | 1004.3
+ | -34.84 | 1039.14
+ | -1004.3 | 2008.6
+ | -1.23457e+20 | 1.23457e+20
+ | -1.23457e-20 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+--------------+----------------------
+ | 0 | 1004.3
+ | -34.84 | 1039.14000015259
+ | -1004.3 | 2008.59998779297
+ | -1.23457e+20 | 1.23456789557014e+20
+ | -1.23457e-20 | 1004.3
+(5 rows)
+
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index b05831d..ff3dff6 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -404,6 +404,27 @@ SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
| 1.2345678901234e-200 | 2.3112042409018e-67
(5 rows)
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.3
+ | 1004.3 | 0
+ | -34.84 | 1039.14
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.29998779297
+ | 1004.3 | 1.22070312045253e-05
+ | -34.84 | 1039.13998779297
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.29998779297
+(5 rows)
+
SELECT '' AS five, * FROM FLOAT8_TBL;
five | f1
------+----------------------
diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out
index 8c255b9..0edc57e 100644
--- a/src/test/regress/expected/int2.out
+++ b/src/test/regress/expected/int2.out
@@ -242,6 +242,39 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
| -32767 | -16383
(5 rows)
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+ERROR: smallint out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+ four | f1 | x
+------+-------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
-- corner cases
SELECT (-1::int2<<15)::text;
text
diff --git a/src/test/regress/expected/int4.out b/src/test/regress/expected/int4.out
index bda7a8d..3735dbc 100644
--- a/src/test/regress/expected/int4.out
+++ b/src/test/regress/expected/int4.out
@@ -247,6 +247,38 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
| -2147483647 | -1073741823
(5 rows)
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+ERROR: integer out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+ five | f1 | x
+------+-------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+ | -2147483647 | 2147483649
+(5 rows)
+
--
-- more complex expressions
--
diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out
index 35e3b3f..8940e81 100644
--- a/src/test/regress/expected/int8.out
+++ b/src/test/regress/expected/int8.out
@@ -432,6 +432,37 @@ SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 A
4567890123457035 | -4567890123456543 | 1123700970370370094 | 0
(5 rows)
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
SELECT q2, abs(q2) FROM INT8_TBL;
q2 | abs
-------------------+------------------
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index f88f345..cb95adf 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -207,6 +207,21 @@ SELECT '' AS fortyfive, r1.*, r2.*
| 34 years | 6 years
(45 rows)
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+ ten | ?column?
+-----+----------------------------
+ | 2 days 02:59:00
+ | 2 days -02:00:00
+ | 8 days -03:00:00
+ | 34 years -2 days -03:00:00
+ | 3 mons -2 days -03:00:00
+ | 2 days 03:00:14
+ | 1 day 00:56:56
+ | 6 years -2 days -03:00:00
+ | 5 mons -2 days -03:00:00
+ | 5 mons -2 days +09:00:00
+(10 rows)
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out
index ab86595..fb2a489 100644
--- a/src/test/regress/expected/money.out
+++ b/src/test/regress/expected/money.out
@@ -123,6 +123,12 @@ SELECT m / 2::float4 FROM money_data;
$61.50
(1 row)
+SELECT m <-> '$123.45' FROM money_data;
+ ?column?
+----------
+ $0.45
+(1 row)
+
-- All true
SELECT m = '$123.00' FROM money_data;
?column?
diff --git a/src/test/regress/expected/oid.out b/src/test/regress/expected/oid.out
index 1eab9cc..5339a48 100644
--- a/src/test/regress/expected/oid.out
+++ b/src/test/regress/expected/oid.out
@@ -119,4 +119,17 @@ SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
| 99999999
(3 rows)
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+ eight | f1 | ?column?
+-------+------------+------------
+ | 1234 | 1111
+ | 1235 | 1112
+ | 987 | 864
+ | 4294966256 | 4294966133
+ | 99999999 | 99999876
+ | 5 | 118
+ | 10 | 113
+ | 15 | 108
+(8 rows)
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 7bcc03b..15ed87a 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1816,6 +1816,7 @@ ORDER BY 1, 2, 3;
403 | 5 | *>
403 | 5 | >
403 | 5 | ~>~
+ 403 | 6 | <->
405 | 1 | =
783 | 1 | <<
783 | 1 | @@
@@ -1925,7 +1926,7 @@ ORDER BY 1, 2, 3;
4000 | 26 | >>
4000 | 27 | >>=
4000 | 28 | ^@
-(123 rows)
+(124 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/expected/time.out b/src/test/regress/expected/time.out
index 8e0afe6..ee74faa 100644
--- a/src/test/regress/expected/time.out
+++ b/src/test/regress/expected/time.out
@@ -86,3 +86,19 @@ ERROR: operator is not unique: time without time zone + time without time zone
LINE 1: SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
+ Ten | Distance
+-------------+-------------------------------
+ 00:00:00 | @ 1 hour 23 mins 45 secs
+ 01:00:00 | @ 23 mins 45 secs
+ 02:03:00 | @ 39 mins 15 secs
+ 11:59:00 | @ 10 hours 35 mins 15 secs
+ 12:00:00 | @ 10 hours 36 mins 15 secs
+ 12:01:00 | @ 10 hours 37 mins 15 secs
+ 23:59:00 | @ 22 hours 35 mins 15 secs
+ 23:59:59.99 | @ 22 hours 36 mins 14.99 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+(10 rows)
+
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index 4a2fabd..dcb4205 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -1604,3 +1604,214 @@ SELECT make_timestamp(2014,12,28,6,30,45.887);
Sun Dec 28 06:30:45.887 2014
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 231 days 18 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 10 hours 45 mins 58 secs
+ | @ 324 days 11 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 21 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 5 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 1 hour 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 231 days 16 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 12 hours 9 mins 43 secs
+ | @ 324 days 13 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 324 days 23 hours 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 6 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 14 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 1 hour 9 mins 43 secs
+ | @ 324 days 2 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 12 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(63 rows)
+
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 2340f30..4018fc8 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -2526,3 +2526,217 @@ select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
Tue Jan 17 16:00:00 2017 PST
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 8 hours
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 7 hours 27 mins 59 secs
+ | @ 231 days 17 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 19 hours 45 mins 58 secs
+ | @ 324 days 21 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 22 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 9 hours 27 mins 59 secs
+ | @ 1303 days 10 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 9 hours 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 8 hours 51 mins 44 secs
+ | @ 231 days 15 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 21 hours 9 mins 43 secs
+ | @ 324 days 23 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 325 days 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 10 hours 51 mins 44 secs
+ | @ 1303 days 11 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 22 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 10 hours 9 mins 43 secs
+ | @ 324 days 12 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 13 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1452 days 23 hours 51 mins 44 secs
+ | @ 1303 days 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(64 rows)
+
diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql
index 22f80f2..24be476 100644
--- a/src/test/regress/sql/date.sql
+++ b/src/test/regress/sql/date.sql
@@ -346,3 +346,8 @@ select make_date(2013, 13, 1);
select make_date(2013, 11, -1);
select make_time(10, 55, 100.1);
select make_time(24, 0, 2.1);
+
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql
index 3b363f9..da4af1a 100644
--- a/src/test/regress/sql/float4.sql
+++ b/src/test/regress/sql/float4.sql
@@ -81,3 +81,6 @@ UPDATE FLOAT4_TBL
WHERE FLOAT4_TBL.f1 > '0.0';
SELECT '' AS five, * FROM FLOAT4_TBL;
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index eeebddd..c770237 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -125,6 +125,9 @@ SELECT ||/ float8 '27' AS three;
SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
SELECT '' AS five, * FROM FLOAT8_TBL;
diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql
index 7dbafb6..16dd5d8 100644
--- a/src/test/regress/sql/int2.sql
+++ b/src/test/regress/sql/int2.sql
@@ -84,6 +84,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+
-- corner cases
SELECT (-1::int2<<15)::text;
SELECT ((-1::int2<<15)+1::int2)::text;
diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql
index f014cb2..cff32946 100644
--- a/src/test/regress/sql/int4.sql
+++ b/src/test/regress/sql/int4.sql
@@ -93,6 +93,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT4_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+
--
-- more complex expressions
--
diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql
index e890452..d7f5bde 100644
--- a/src/test/regress/sql/int8.sql
+++ b/src/test/regress/sql/int8.sql
@@ -89,6 +89,11 @@ SELECT q1 + 42::int2 AS "8plus2", q1 - 42::int2 AS "8minus2", q1 * 42::int2 AS "
-- int2 op int8
SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 AS "2mul8", 246::int2 / q1 AS "2div8" FROM INT8_TBL;
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+
SELECT q2, abs(q2) FROM INT8_TBL;
SELECT min(q1), min(q2) FROM INT8_TBL;
SELECT max(q1), max(q2) FROM INT8_TBL;
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index bc5537d..d51c866 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -59,6 +59,8 @@ SELECT '' AS fortyfive, r1.*, r2.*
WHERE r1.f1 > r2.f1
ORDER BY r1.f1, r2.f1;
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql
index 37b9ecc..8428d59 100644
--- a/src/test/regress/sql/money.sql
+++ b/src/test/regress/sql/money.sql
@@ -25,6 +25,7 @@ SELECT m / 2::float8 FROM money_data;
SELECT m * 2::float4 FROM money_data;
SELECT 2::float4 * m FROM money_data;
SELECT m / 2::float4 FROM money_data;
+SELECT m <-> '$123.45' FROM money_data;
-- All true
SELECT m = '$123.00' FROM money_data;
diff --git a/src/test/regress/sql/oid.sql b/src/test/regress/sql/oid.sql
index 4a09689..9f54f92 100644
--- a/src/test/regress/sql/oid.sql
+++ b/src/test/regress/sql/oid.sql
@@ -40,4 +40,6 @@ SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 >= '1234';
SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/sql/time.sql b/src/test/regress/sql/time.sql
index 99a1562..31f0330 100644
--- a/src/test/regress/sql/time.sql
+++ b/src/test/regress/sql/time.sql
@@ -40,3 +40,6 @@ SELECT f1 AS "Eight" FROM TIME_TBL WHERE f1 >= '00:00';
-- where we do mixed-type arithmetic. - thomas 2000-12-02
SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
+
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index b7957cb..5d023dd 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -230,3 +230,11 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
-- timestamp numeric fields constructor
SELECT make_timestamp(2014,12,28,6,30,45.887);
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index f17d153..92c3388 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -460,3 +460,11 @@ insert into tmptz values ('2017-01-18 00:00+00');
explain (costs off)
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
0006-Remove-distance-operators-from-btree_gist-v03.patchtext/x-patch; name=0006-Remove-distance-operators-from-btree_gist-v03.patchDownload
diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile
index af65120..46ab241 100644
--- a/contrib/btree_gist/Makefile
+++ b/contrib/btree_gist/Makefile
@@ -11,8 +11,9 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
EXTENSION = btree_gist
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
- btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \
- btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql
+ btree_gist--1.1--1.2.sql btree_gist--1.2--1.3.sql \
+ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \
+ btree_gist--1.5--1.6.sql btree_gist--1.6.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c
index 894d0a2..86e319d 100644
--- a/contrib/btree_gist/btree_cash.c
+++ b/contrib/btree_gist/btree_cash.c
@@ -91,26 +91,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(cash_dist);
-Datum
-cash_dist(PG_FUNCTION_ARGS)
-{
- Cash a = PG_GETARG_CASH(0);
- Cash b = PG_GETARG_CASH(1);
- Cash r;
- Cash ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("money out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_CASH(ra);
-}
-
/**************************************************
* Cash ops
**************************************************/
diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c
index 992ce57..05a4909 100644
--- a/contrib/btree_gist/btree_date.c
+++ b/contrib/btree_gist/btree_date.c
@@ -109,19 +109,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(date_dist);
-Datum
-date_dist(PG_FUNCTION_ARGS)
-{
- /* we assume the difference can't overflow */
- Datum diff = DirectFunctionCall2(date_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
-}
-
-
/**************************************************
* date ops
**************************************************/
diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c
index 6b20f44..26fa79b 100644
--- a/contrib/btree_gist/btree_float4.c
+++ b/contrib/btree_gist/btree_float4.c
@@ -89,21 +89,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(float4_dist);
-Datum
-float4_dist(PG_FUNCTION_ARGS)
-{
- float4 a = PG_GETARG_FLOAT4(0);
- float4 b = PG_GETARG_FLOAT4(1);
- float4 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT4(Abs(r));
-}
-
-
/**************************************************
* float4 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c
index ee114cb..63e5597 100644
--- a/contrib/btree_gist/btree_float8.c
+++ b/contrib/btree_gist/btree_float8.c
@@ -96,21 +96,6 @@ static const gbtree_ninfo tinfo =
gbt_float8_dist
};
-
-PG_FUNCTION_INFO_V1(float8_dist);
-Datum
-float8_dist(PG_FUNCTION_ARGS)
-{
- float8 a = PG_GETARG_FLOAT8(0);
- float8 b = PG_GETARG_FLOAT8(1);
- float8 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT8(Abs(r));
-}
-
/**************************************************
* float8 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_gist--1.2.sql b/contrib/btree_gist/btree_gist--1.2.sql
deleted file mode 100644
index 1efe753..0000000
--- a/contrib/btree_gist/btree_gist--1.2.sql
+++ /dev/null
@@ -1,1570 +0,0 @@
-/* contrib/btree_gist/btree_gist--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
-
-CREATE FUNCTION gbtreekey4_in(cstring)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey4_out(gbtreekey4)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey4 (
- INTERNALLENGTH = 4,
- INPUT = gbtreekey4_in,
- OUTPUT = gbtreekey4_out
-);
-
-CREATE FUNCTION gbtreekey8_in(cstring)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey8_out(gbtreekey8)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey8 (
- INTERNALLENGTH = 8,
- INPUT = gbtreekey8_in,
- OUTPUT = gbtreekey8_out
-);
-
-CREATE FUNCTION gbtreekey16_in(cstring)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey16_out(gbtreekey16)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey16 (
- INTERNALLENGTH = 16,
- INPUT = gbtreekey16_in,
- OUTPUT = gbtreekey16_out
-);
-
-CREATE FUNCTION gbtreekey32_in(cstring)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey32_out(gbtreekey32)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey32 (
- INTERNALLENGTH = 32,
- INPUT = gbtreekey32_in,
- OUTPUT = gbtreekey32_out
-);
-
-CREATE FUNCTION gbtreekey_var_in(cstring)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey_var (
- INTERNALLENGTH = VARIABLE,
- INPUT = gbtreekey_var_in,
- OUTPUT = gbtreekey_var_out,
- STORAGE = EXTENDED
-);
-
---distance operators
-
-CREATE FUNCTION cash_dist(money, money)
-RETURNS money
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = money,
- RIGHTARG = money,
- PROCEDURE = cash_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION date_dist(date, date)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = date,
- RIGHTARG = date,
- PROCEDURE = date_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float4_dist(float4, float4)
-RETURNS float4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float4,
- RIGHTARG = float4,
- PROCEDURE = float4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float8_dist(float8, float8)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float8,
- RIGHTARG = float8,
- PROCEDURE = float8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int2_dist(int2, int2)
-RETURNS int2
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int2,
- RIGHTARG = int2,
- PROCEDURE = int2_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int4_dist(int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int4,
- RIGHTARG = int4,
- PROCEDURE = int4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int8_dist(int8, int8)
-RETURNS int8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int8,
- RIGHTARG = int8,
- PROCEDURE = int8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION interval_dist(interval, interval)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = interval,
- RIGHTARG = interval,
- PROCEDURE = interval_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION oid_dist(oid, oid)
-RETURNS oid
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = oid,
- RIGHTARG = oid,
- PROCEDURE = oid_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION time_dist(time, time)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = time,
- RIGHTARG = time,
- PROCEDURE = time_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION ts_dist(timestamp, timestamp)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamp,
- RIGHTARG = timestamp,
- PROCEDURE = ts_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION tstz_dist(timestamptz, timestamptz)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamptz,
- RIGHTARG = timestamptz,
- PROCEDURE = tstz_dist,
- COMMUTATOR = '<->'
-);
-
-
---
---
---
--- oid ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_oid_ops
-DEFAULT FOR TYPE oid USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
- FUNCTION 2 gbt_oid_union (internal, internal),
- FUNCTION 3 gbt_oid_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
- FUNCTION 6 gbt_oid_picksplit (internal, internal),
- FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
--- Add operators that are new in 9.1. We do it like this, leaving them
--- "loose" in the operator family rather than bound into the opclass, because
--- that's the only state that can be reproduced during an upgrade from 9.0.
-ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
- OPERATOR 6 <> (oid, oid) ,
- OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
- FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
- -- Also add support function for index-only-scans, added in 9.5.
- FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
-
-
---
---
---
--- int2 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_union(internal, internal)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int2_ops
-DEFAULT FOR TYPE int2 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
- FUNCTION 2 gbt_int2_union (internal, internal),
- FUNCTION 3 gbt_int2_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int2_picksplit (internal, internal),
- FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
- STORAGE gbtreekey4;
-
-ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
- OPERATOR 6 <> (int2, int2) ,
- OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
- FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
-
---
---
---
--- int4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int4_ops
-DEFAULT FOR TYPE int4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
- FUNCTION 2 gbt_int4_union (internal, internal),
- FUNCTION 3 gbt_int4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int4_picksplit (internal, internal),
- FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
- OPERATOR 6 <> (int4, int4) ,
- OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
- FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
-
-
---
---
---
--- int8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int8_ops
-DEFAULT FOR TYPE int8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
- FUNCTION 2 gbt_int8_union (internal, internal),
- FUNCTION 3 gbt_int8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int8_picksplit (internal, internal),
- FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
- OPERATOR 6 <> (int8, int8) ,
- OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
- FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
-
---
---
---
--- float4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float4_ops
-DEFAULT FOR TYPE float4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
- FUNCTION 2 gbt_float4_union (internal, internal),
- FUNCTION 3 gbt_float4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float4_picksplit (internal, internal),
- FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
- OPERATOR 6 <> (float4, float4) ,
- OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
- FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
-
---
---
---
--- float8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float8_ops
-DEFAULT FOR TYPE float8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
- FUNCTION 2 gbt_float8_union (internal, internal),
- FUNCTION 3 gbt_float8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float8_picksplit (internal, internal),
- FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
- OPERATOR 6 <> (float8, float8) ,
- OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
- FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
-
---
---
---
--- timestamp ops
---
---
---
-
-CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamp_ops
-DEFAULT FOR TYPE timestamp USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_ts_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
- OPERATOR 6 <> (timestamp, timestamp) ,
- OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
- FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamptz_ops
-DEFAULT FOR TYPE timestamptz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_tstz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
- OPERATOR 6 <> (timestamptz, timestamptz) ,
- OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
- FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
-
---
---
---
--- time ops
---
---
---
-
-CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_time_ops
-DEFAULT FOR TYPE time USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_time_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
- OPERATOR 6 <> (time, time) ,
- OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
- FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
-
-
-CREATE OPERATOR CLASS gist_timetz_ops
-DEFAULT FOR TYPE timetz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_timetz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
- OPERATOR 6 <> (timetz, timetz) ;
- -- no 'fetch' function, as the compress function is lossy.
-
-
---
---
---
--- date ops
---
---
---
-
-CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_date_ops
-DEFAULT FOR TYPE date USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
- FUNCTION 2 gbt_date_union (internal, internal),
- FUNCTION 3 gbt_date_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_date_penalty (internal, internal, internal),
- FUNCTION 6 gbt_date_picksplit (internal, internal),
- FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
- OPERATOR 6 <> (date, date) ,
- OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
- FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
-
-
---
---
---
--- interval ops
---
---
---
-
-CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_union(internal, internal)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_interval_ops
-DEFAULT FOR TYPE interval USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
- FUNCTION 2 gbt_intv_union (internal, internal),
- FUNCTION 3 gbt_intv_compress (internal),
- FUNCTION 4 gbt_intv_decompress (internal),
- FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
- FUNCTION 6 gbt_intv_picksplit (internal, internal),
- FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
- STORAGE gbtreekey32;
-
-ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
- OPERATOR 6 <> (interval, interval) ,
- OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
- FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
-
-
---
---
---
--- cash ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cash_ops
-DEFAULT FOR TYPE money USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
- FUNCTION 2 gbt_cash_union (internal, internal),
- FUNCTION 3 gbt_cash_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
- FUNCTION 6 gbt_cash_picksplit (internal, internal),
- FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
- OPERATOR 6 <> (money, money) ,
- OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
- FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
- FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
-
-
---
---
---
--- macaddr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_macaddr_ops
-DEFAULT FOR TYPE macaddr USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
- FUNCTION 2 gbt_macad_union (internal, internal),
- FUNCTION 3 gbt_macad_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
- FUNCTION 6 gbt_macad_picksplit (internal, internal),
- FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
- OPERATOR 6 <> (macaddr, macaddr) ,
- FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
-
-
---
---
---
--- text/ bpchar ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_text_ops
-DEFAULT FOR TYPE text USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_text_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
- OPERATOR 6 <> (text, text) ,
- FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
-
-
----- Create the operator class
-CREATE OPERATOR CLASS gist_bpchar_ops
-DEFAULT FOR TYPE bpchar USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_bpchar_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
- OPERATOR 6 <> (bpchar, bpchar) ,
- FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
-
---
---
--- bytea ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bytea_ops
-DEFAULT FOR TYPE bytea USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
- FUNCTION 2 gbt_bytea_union (internal, internal),
- FUNCTION 3 gbt_bytea_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bytea_picksplit (internal, internal),
- FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
- OPERATOR 6 <> (bytea, bytea) ,
- FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
-
-
---
---
---
--- numeric ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_numeric_ops
-DEFAULT FOR TYPE numeric USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
- FUNCTION 2 gbt_numeric_union (internal, internal),
- FUNCTION 3 gbt_numeric_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
- FUNCTION 6 gbt_numeric_picksplit (internal, internal),
- FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
- OPERATOR 6 <> (numeric, numeric) ,
- FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
-
-
---
---
--- bit ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bit_ops
-DEFAULT FOR TYPE bit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
- OPERATOR 6 <> (bit, bit) ,
- FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
-
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_vbit_ops
-DEFAULT FOR TYPE varbit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
- OPERATOR 6 <> (varbit, varbit) ,
- FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
-
-
---
---
---
--- inet/cidr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_inet_ops
-DEFAULT FOR TYPE inet USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cidr_ops
-DEFAULT FOR TYPE cidr USING gist
-AS
- OPERATOR 1 < (inet, inet) ,
- OPERATOR 2 <= (inet, inet) ,
- OPERATOR 3 = (inet, inet) ,
- OPERATOR 4 >= (inet, inet) ,
- OPERATOR 5 > (inet, inet) ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
diff --git a/contrib/btree_gist/btree_gist--1.5--1.6.sql b/contrib/btree_gist/btree_gist--1.5--1.6.sql
new file mode 100644
index 0000000..fcd10f5
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.5--1.6.sql
@@ -0,0 +1,150 @@
+/* contrib/btree_gist/btree_gist--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.6'" to load this file. \quit
+
+-- update references to distance operators in pg_amop and pg_depend
+
+WITH
+btree_ops AS (
+ SELECT
+ amoplefttype, amoprighttype, amopopr
+ FROM
+ pg_amop
+ JOIN pg_am ON pg_am.oid = amopmethod
+ JOIN pg_opfamily ON pg_opfamily.oid = amopfamily
+ JOIN pg_namespace ON pg_namespace.oid = opfnamespace
+ WHERE
+ nspname = 'pg_catalog'
+ AND opfname IN (
+ 'integer_ops',
+ 'oid_ops',
+ 'money_ops',
+ 'float_ops',
+ 'datetime_ops',
+ 'time_ops',
+ 'interval_ops'
+ )
+ AND amname = 'btree'
+ AND amoppurpose = 'o'
+ AND amopstrategy = 6
+ ),
+gist_ops AS (
+ SELECT
+ pg_amop.oid AS oid, amoplefttype, amoprighttype, amopopr
+ FROM
+ pg_amop
+ JOIN pg_am ON amopmethod = pg_am.oid
+ JOIN pg_opfamily ON amopfamily = pg_opfamily.oid
+ JOIN pg_namespace ON pg_namespace.oid = opfnamespace
+ WHERE
+ nspname = current_schema()
+ AND opfname IN (
+ 'gist_oid_ops',
+ 'gist_int2_ops',
+ 'gist_int4_ops',
+ 'gist_int8_ops',
+ 'gist_float4_ops',
+ 'gist_float8_ops',
+ 'gist_timestamp_ops',
+ 'gist_timestamptz_ops',
+ 'gist_time_ops',
+ 'gist_date_ops',
+ 'gist_interval_ops',
+ 'gist_cash_ops'
+ )
+ AND amname = 'gist'
+ AND amoppurpose = 'o'
+ AND amopstrategy = 15
+ ),
+depend_update_data(gist_amop, gist_amopopr, btree_amopopr) AS (
+ SELECT
+ gist_ops.oid, gist_ops.amopopr, btree_ops.amopopr
+ FROM
+ btree_ops JOIN gist_ops USING (amoplefttype, amoprighttype)
+),
+amop_update_data AS (
+ UPDATE
+ pg_depend
+ SET
+ refobjid = btree_amopopr
+ FROM
+ depend_update_data
+ WHERE
+ objid = gist_amop AND refobjid = gist_amopopr
+ RETURNING
+ depend_update_data.*
+)
+UPDATE
+ pg_amop
+SET
+ amopopr = btree_amopopr
+FROM
+ amop_update_data
+WHERE
+ pg_amop.oid = gist_amop;
+
+-- disable implicit pg_catalog search
+
+DO
+$$
+BEGIN
+ EXECUTE 'SET LOCAL search_path TO ' || current_schema() || ', pg_catalog';
+END
+$$;
+
+-- drop distance operators
+
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int2, int2);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int4, int4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int8, int8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float4, float4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float8, float8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (oid, oid);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (money, money);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (date, date);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (time, time);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (interval, interval);
+
+DROP OPERATOR <-> (int2, int2);
+DROP OPERATOR <-> (int4, int4);
+DROP OPERATOR <-> (int8, int8);
+DROP OPERATOR <-> (float4, float4);
+DROP OPERATOR <-> (float8, float8);
+DROP OPERATOR <-> (oid, oid);
+DROP OPERATOR <-> (money, money);
+DROP OPERATOR <-> (date, date);
+DROP OPERATOR <-> (time, time);
+DROP OPERATOR <-> (timestamp, timestamp);
+DROP OPERATOR <-> (timestamptz, timestamptz);
+DROP OPERATOR <-> (interval, interval);
+
+-- drop distance functions
+
+ALTER EXTENSION btree_gist DROP FUNCTION int2_dist(int2, int2);
+ALTER EXTENSION btree_gist DROP FUNCTION int4_dist(int4, int4);
+ALTER EXTENSION btree_gist DROP FUNCTION int8_dist(int8, int8);
+ALTER EXTENSION btree_gist DROP FUNCTION float4_dist(float4, float4);
+ALTER EXTENSION btree_gist DROP FUNCTION float8_dist(float8, float8);
+ALTER EXTENSION btree_gist DROP FUNCTION oid_dist(oid, oid);
+ALTER EXTENSION btree_gist DROP FUNCTION cash_dist(money, money);
+ALTER EXTENSION btree_gist DROP FUNCTION date_dist(date, date);
+ALTER EXTENSION btree_gist DROP FUNCTION time_dist(time, time);
+ALTER EXTENSION btree_gist DROP FUNCTION ts_dist(timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP FUNCTION interval_dist(interval, interval);
+
+DROP FUNCTION int2_dist(int2, int2);
+DROP FUNCTION int4_dist(int4, int4);
+DROP FUNCTION int8_dist(int8, int8);
+DROP FUNCTION float4_dist(float4, float4);
+DROP FUNCTION float8_dist(float8, float8);
+DROP FUNCTION oid_dist(oid, oid);
+DROP FUNCTION cash_dist(money, money);
+DROP FUNCTION date_dist(date, date);
+DROP FUNCTION time_dist(time, time);
+DROP FUNCTION ts_dist(timestamp, timestamp);
+DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+DROP FUNCTION interval_dist(interval, interval);
diff --git a/contrib/btree_gist/btree_gist--1.6.sql b/contrib/btree_gist/btree_gist--1.6.sql
new file mode 100644
index 0000000..8ff8eb5
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.6.sql
@@ -0,0 +1,1615 @@
+/* contrib/btree_gist/btree_gist--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
+
+CREATE FUNCTION gbtreekey4_in(cstring)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey4_out(gbtreekey4)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey4 (
+ INTERNALLENGTH = 4,
+ INPUT = gbtreekey4_in,
+ OUTPUT = gbtreekey4_out
+);
+
+CREATE FUNCTION gbtreekey8_in(cstring)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey8_out(gbtreekey8)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey8 (
+ INTERNALLENGTH = 8,
+ INPUT = gbtreekey8_in,
+ OUTPUT = gbtreekey8_out
+);
+
+CREATE FUNCTION gbtreekey16_in(cstring)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey16_out(gbtreekey16)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey16 (
+ INTERNALLENGTH = 16,
+ INPUT = gbtreekey16_in,
+ OUTPUT = gbtreekey16_out
+);
+
+CREATE FUNCTION gbtreekey32_in(cstring)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey32_out(gbtreekey32)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey32 (
+ INTERNALLENGTH = 32,
+ INPUT = gbtreekey32_in,
+ OUTPUT = gbtreekey32_out
+);
+
+CREATE FUNCTION gbtreekey_var_in(cstring)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey_var (
+ INTERNALLENGTH = VARIABLE,
+ INPUT = gbtreekey_var_in,
+ OUTPUT = gbtreekey_var_out,
+ STORAGE = EXTENDED
+);
+
+
+--
+--
+--
+-- oid ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_oid_ops
+DEFAULT FOR TYPE oid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
+ FUNCTION 2 gbt_oid_union (internal, internal),
+ FUNCTION 3 gbt_oid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_oid_picksplit (internal, internal),
+ FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+-- Add operators that are new in 9.1. We do it like this, leaving them
+-- "loose" in the operator family rather than bound into the opclass, because
+-- that's the only state that can be reproduced during an upgrade from 9.0.
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
+ OPERATOR 6 <> (oid, oid) ,
+ OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
+ FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
+ -- Also add support function for index-only-scans, added in 9.5.
+ FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
+
+
+--
+--
+--
+-- int2 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_union(internal, internal)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int2_ops
+DEFAULT FOR TYPE int2 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
+ FUNCTION 2 gbt_int2_union (internal, internal),
+ FUNCTION 3 gbt_int2_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int2_picksplit (internal, internal),
+ FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
+ STORAGE gbtreekey4;
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
+ OPERATOR 6 <> (int2, int2) ,
+ OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
+ FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
+
+--
+--
+--
+-- int4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int4_ops
+DEFAULT FOR TYPE int4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
+ FUNCTION 2 gbt_int4_union (internal, internal),
+ FUNCTION 3 gbt_int4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int4_picksplit (internal, internal),
+ FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
+ OPERATOR 6 <> (int4, int4) ,
+ OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
+ FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
+
+
+--
+--
+--
+-- int8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int8_ops
+DEFAULT FOR TYPE int8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
+ FUNCTION 2 gbt_int8_union (internal, internal),
+ FUNCTION 3 gbt_int8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int8_picksplit (internal, internal),
+ FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
+ OPERATOR 6 <> (int8, int8) ,
+ OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
+ FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
+
+--
+--
+--
+-- float4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float4_ops
+DEFAULT FOR TYPE float4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
+ FUNCTION 2 gbt_float4_union (internal, internal),
+ FUNCTION 3 gbt_float4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float4_picksplit (internal, internal),
+ FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
+ OPERATOR 6 <> (float4, float4) ,
+ OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
+ FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
+
+--
+--
+--
+-- float8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float8_ops
+DEFAULT FOR TYPE float8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
+ FUNCTION 2 gbt_float8_union (internal, internal),
+ FUNCTION 3 gbt_float8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float8_picksplit (internal, internal),
+ FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
+ OPERATOR 6 <> (float8, float8) ,
+ OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
+ FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
+
+--
+--
+--
+-- timestamp ops
+--
+--
+--
+
+CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamp_ops
+DEFAULT FOR TYPE timestamp USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_ts_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
+ OPERATOR 6 <> (timestamp, timestamp) ,
+ OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
+ FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamptz_ops
+DEFAULT FOR TYPE timestamptz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_tstz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
+ OPERATOR 6 <> (timestamptz, timestamptz) ,
+ OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
+ FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
+
+--
+--
+--
+-- time ops
+--
+--
+--
+
+CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_time_ops
+DEFAULT FOR TYPE time USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_time_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
+ OPERATOR 6 <> (time, time) ,
+ OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
+ FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
+
+
+CREATE OPERATOR CLASS gist_timetz_ops
+DEFAULT FOR TYPE timetz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_timetz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
+ OPERATOR 6 <> (timetz, timetz) ;
+ -- no 'fetch' function, as the compress function is lossy.
+
+
+--
+--
+--
+-- date ops
+--
+--
+--
+
+CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_date_ops
+DEFAULT FOR TYPE date USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
+ FUNCTION 2 gbt_date_union (internal, internal),
+ FUNCTION 3 gbt_date_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_date_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_date_picksplit (internal, internal),
+ FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
+ OPERATOR 6 <> (date, date) ,
+ OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
+ FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
+
+
+--
+--
+--
+-- interval ops
+--
+--
+--
+
+CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_interval_ops
+DEFAULT FOR TYPE interval USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
+ FUNCTION 2 gbt_intv_union (internal, internal),
+ FUNCTION 3 gbt_intv_compress (internal),
+ FUNCTION 4 gbt_intv_decompress (internal),
+ FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_intv_picksplit (internal, internal),
+ FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
+ OPERATOR 6 <> (interval, interval) ,
+ OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
+ FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
+
+
+--
+--
+--
+-- cash ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cash_ops
+DEFAULT FOR TYPE money USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
+ FUNCTION 2 gbt_cash_union (internal, internal),
+ FUNCTION 3 gbt_cash_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_cash_picksplit (internal, internal),
+ FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
+ OPERATOR 6 <> (money, money) ,
+ OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
+ FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
+ FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
+
+
+--
+--
+--
+-- macaddr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr_ops
+DEFAULT FOR TYPE macaddr USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
+ FUNCTION 2 gbt_macad_union (internal, internal),
+ FUNCTION 3 gbt_macad_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
+ OPERATOR 6 <> (macaddr, macaddr) ,
+ FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
+
+
+--
+--
+--
+-- text/ bpchar ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_text_ops
+DEFAULT FOR TYPE text USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_text_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
+ OPERATOR 6 <> (text, text) ,
+ FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
+
+
+---- Create the operator class
+CREATE OPERATOR CLASS gist_bpchar_ops
+DEFAULT FOR TYPE bpchar USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_bpchar_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
+ OPERATOR 6 <> (bpchar, bpchar) ,
+ FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
+
+--
+--
+-- bytea ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bytea_ops
+DEFAULT FOR TYPE bytea USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
+ FUNCTION 2 gbt_bytea_union (internal, internal),
+ FUNCTION 3 gbt_bytea_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bytea_picksplit (internal, internal),
+ FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
+ OPERATOR 6 <> (bytea, bytea) ,
+ FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- numeric ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_numeric_ops
+DEFAULT FOR TYPE numeric USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
+ FUNCTION 2 gbt_numeric_union (internal, internal),
+ FUNCTION 3 gbt_numeric_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_numeric_picksplit (internal, internal),
+ FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
+ OPERATOR 6 <> (numeric, numeric) ,
+ FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
+
+
+--
+--
+-- bit ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bit_ops
+DEFAULT FOR TYPE bit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
+ OPERATOR 6 <> (bit, bit) ,
+ FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
+
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_vbit_ops
+DEFAULT FOR TYPE varbit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
+ OPERATOR 6 <> (varbit, varbit) ,
+ FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- inet/cidr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_inet_ops
+DEFAULT FOR TYPE inet USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cidr_ops
+DEFAULT FOR TYPE cidr USING gist
+AS
+ OPERATOR 1 < (inet, inet) ,
+ OPERATOR 2 <= (inet, inet) ,
+ OPERATOR 3 = (inet, inet) ,
+ OPERATOR 4 >= (inet, inet) ,
+ OPERATOR 5 > (inet, inet) ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+--
+--
+--
+-- uuid ops
+--
+--
+---- define the GiST support methods
+CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_uuid_ops
+DEFAULT FOR TYPE uuid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal),
+ FUNCTION 2 gbt_uuid_union (internal, internal),
+ FUNCTION 3 gbt_uuid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_uuid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_uuid_picksplit (internal, internal),
+ FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+-- These are "loose" in the opfamily for consistency with the rest of btree_gist
+ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
+ OPERATOR 6 <> (uuid, uuid) ,
+ FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ;
+
+
+-- Add support for indexing macaddr8 columns
+
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad8_consistent(internal,macaddr8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr8_ops
+DEFAULT FOR TYPE macaddr8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad8_consistent (internal, macaddr8, int2, oid, internal),
+ FUNCTION 2 gbt_macad8_union (internal, internal),
+ FUNCTION 3 gbt_macad8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad8_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD
+ OPERATOR 6 <> (macaddr8, macaddr8) ,
+ FUNCTION 9 (macaddr8, macaddr8) gbt_macad8_fetch (internal);
+
+--
+--
+--
+-- enum ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_enum_consistent(internal,anyenum,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_enum_ops
+DEFAULT FOR TYPE anyenum USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_enum_consistent (internal, anyenum, int2, oid, internal),
+ FUNCTION 2 gbt_enum_union (internal, internal),
+ FUNCTION 3 gbt_enum_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_enum_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_enum_picksplit (internal, internal),
+ FUNCTION 7 gbt_enum_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD
+ OPERATOR 6 <> (anyenum, anyenum) ,
+ FUNCTION 9 (anyenum, anyenum) gbt_enum_fetch (internal) ;
diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control
index 81c8509..9ced3bc 100644
--- a/contrib/btree_gist/btree_gist.control
+++ b/contrib/btree_gist/btree_gist.control
@@ -1,5 +1,5 @@
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
-default_version = '1.5'
+default_version = '1.6'
module_pathname = '$libdir/btree_gist'
relocatable = true
diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c
index 7674e2d..3c402c0 100644
--- a/contrib/btree_gist/btree_int2.c
+++ b/contrib/btree_gist/btree_int2.c
@@ -90,26 +90,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int2_dist);
-Datum
-int2_dist(PG_FUNCTION_ARGS)
-{
- int16 a = PG_GETARG_INT16(0);
- int16 b = PG_GETARG_INT16(1);
- int16 r;
- int16 ra;
-
- if (pg_sub_s16_overflow(a, b, &r) ||
- r == PG_INT16_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("smallint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT16(ra);
-}
-
/**************************************************
* int16 ops
diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c
index 80005ab..a4f1968 100644
--- a/contrib/btree_gist/btree_int4.c
+++ b/contrib/btree_gist/btree_int4.c
@@ -91,27 +91,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int4_dist);
-Datum
-int4_dist(PG_FUNCTION_ARGS)
-{
- int32 a = PG_GETARG_INT32(0);
- int32 b = PG_GETARG_INT32(1);
- int32 r;
- int32 ra;
-
- if (pg_sub_s32_overflow(a, b, &r) ||
- r == PG_INT32_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT32(ra);
-}
-
-
/**************************************************
* int32 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c
index b0fd3e1..efec64c 100644
--- a/contrib/btree_gist/btree_int8.c
+++ b/contrib/btree_gist/btree_int8.c
@@ -91,27 +91,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int8_dist);
-Datum
-int8_dist(PG_FUNCTION_ARGS)
-{
- int64 a = PG_GETARG_INT64(0);
- int64 b = PG_GETARG_INT64(1);
- int64 r;
- int64 ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT64(ra);
-}
-
-
/**************************************************
* int64 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c
index 3a527a7..b244fa4 100644
--- a/contrib/btree_gist/btree_interval.c
+++ b/contrib/btree_gist/btree_interval.c
@@ -110,32 +110,6 @@ static const gbtree_ninfo tinfo =
};
-Interval *
-abs_interval(Interval *a)
-{
- static Interval zero = {0, 0, 0};
-
- if (DatumGetBool(DirectFunctionCall2(interval_lt,
- IntervalPGetDatum(a),
- IntervalPGetDatum(&zero))))
- a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
- IntervalPGetDatum(a)));
-
- return a;
-}
-
-PG_FUNCTION_INFO_V1(interval_dist);
-Datum
-interval_dist(PG_FUNCTION_ARGS)
-{
- Datum diff = DirectFunctionCall2(interval_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
-}
-
-
/**************************************************
* interval ops
**************************************************/
diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c
index 00e7019..3a0bd73 100644
--- a/contrib/btree_gist/btree_oid.c
+++ b/contrib/btree_gist/btree_oid.c
@@ -96,22 +96,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(oid_dist);
-Datum
-oid_dist(PG_FUNCTION_ARGS)
-{
- Oid a = PG_GETARG_OID(0);
- Oid b = PG_GETARG_OID(1);
- Oid res;
-
- if (a < b)
- res = b - a;
- else
- res = a - b;
- PG_RETURN_OID(res);
-}
-
-
/**************************************************
* Oid ops
**************************************************/
diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c
index 90cf655..b6e7e4a 100644
--- a/contrib/btree_gist/btree_time.c
+++ b/contrib/btree_gist/btree_time.c
@@ -137,18 +137,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(time_dist);
-Datum
-time_dist(PG_FUNCTION_ARGS)
-{
- Datum diff = DirectFunctionCall2(time_mi_time,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
-}
-
-
/**************************************************
* time ops
**************************************************/
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index 49d1849..b0271a6 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -142,55 +142,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(ts_dist);
-Datum
-ts_dist(PG_FUNCTION_ARGS)
-{
- Timestamp a = PG_GETARG_TIMESTAMP(0);
- Timestamp b = PG_GETARG_TIMESTAMP(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
- else
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
-}
-
-PG_FUNCTION_INFO_V1(tstz_dist);
-Datum
-tstz_dist(PG_FUNCTION_ARGS)
-{
- TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
- TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
-
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
-}
-
-
/**************************************************
* timestamp ops
**************************************************/
diff --git a/contrib/btree_gist/btree_utils_num.h b/contrib/btree_gist/btree_utils_num.h
index d7945f8..794d92b 100644
--- a/contrib/btree_gist/btree_utils_num.h
+++ b/contrib/btree_gist/btree_utils_num.h
@@ -107,8 +107,6 @@ do { \
} while(0)
-extern Interval *abs_interval(Interval *a);
-
extern bool gbt_num_consistent(const GBT_NUMKEY_R *key, const void *query,
const StrategyNumber *strategy, bool is_leaf,
const gbtree_ninfo *tinfo, FmgrInfo *flinfo);
0007-Add-regression-tests-for-kNN-btree-v03.patchtext/x-patch; name=0007-Add-regression-tests-for-kNN-btree-v03.patchDownload
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0bd48dc..a8ed9da 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -179,3 +179,782 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+---
+--- Test B-tree distance ordering
+---
+SET enable_bitmapscan = OFF;
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+ QUERY PLAN
+------------------------------
+ Sort
+ Sort Key: ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+ QUERY PLAN
+------------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((random <-> 1))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)))
+ Order By: (random <-> 4000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)) AND (random = ANY ('{1809552,1919087,2321799,2648497,3000193,3013326,4157193,4488889,5257716,5593978,NULL}'::integer[])))
+ Order By: (random <-> 3000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ seqno | random
+-------+---------
+ 5380 | 3000193
+ 6262 | 3013326
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 6448 | 4157193
+ 210 | 1809552
+ 4408 | 4488889
+ 6320 | 5257716
+ 1836 | 5593978
+(10 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+-- test parallel KNN scan
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+RESET enable_indexscan;
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Gather
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i > 8000000)
+(4 rows)
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+-----------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t2.n = t1.n)
+ Join Filter: (t1.i <> t2.i)
+ CTE bt_knn_test1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Order By: (i <-> 4000003)
+ -> Seq Scan on bt_knn_test2 t2
+ -> Hash
+ -> CTE Scan on bt_knn_test1 t1
+(12 rows)
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+-----------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t2.n = t1.n)
+ Join Filter: (t1.i <> t2.i)
+ CTE t1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i = ANY ('{3,4,7,8,2}'::integer[]))
+ Order By: (i <-> 4)
+ CTE t2
+ -> Nested Loop
+ -> Function Scan on generate_series i
+ -> Function Scan on generate_series j
+ -> CTE Scan on t2
+ -> Hash
+ -> CTE Scan on t1
+(17 rows)
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+ROLLBACK;
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ QUERY PLAN
+-----------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000))
+ Order By: (thousand <-> 998)
+(3 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 1 | 9001
+ 1 | 8001
+ 1 | 7001
+ 1 | 6001
+ 1 | 5001
+ 1 | 4001
+ 1 | 3001
+ 1 | 2001
+ 1 | 1001
+ 1 | 1
+ 0 | 9000
+ 0 | 8000
+ 0 | 7000
+ 0 | 6000
+ 0 | 5000
+ 0 | 4000
+ 0 | 3000
+ 0 | 2000
+ 0 | 1000
+ 0 | 0
+ | 1
+ | 2
+ | 3
+(33 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: ((thousand > 100) AND (thousand < 800) AND (thousand = ANY ('{0,123,234,345,456,678,901,NULL}'::smallint[])))
+ Order By: (thousand <-> '300'::bigint)
+(3 rows)
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ thousand | tenthous
+----------+----------
+ 345 | 345
+ 345 | 1345
+ 345 | 2345
+ 345 | 3345
+ 345 | 4345
+ 345 | 5345
+ 345 | 6345
+ 345 | 7345
+ 345 | 8345
+ 345 | 9345
+ 234 | 234
+ 234 | 1234
+ 234 | 2234
+ 234 | 3234
+ 234 | 4234
+ 234 | 5234
+ 234 | 6234
+ 234 | 7234
+ 234 | 8234
+ 234 | 9234
+ 456 | 456
+ 456 | 1456
+ 456 | 2456
+ 456 | 3456
+ 456 | 4456
+ 456 | 5456
+ 456 | 6456
+ 456 | 7456
+ 456 | 8456
+ 456 | 9456
+ 123 | 123
+ 123 | 1123
+ 123 | 2123
+ 123 | 3123
+ 123 | 4123
+ 123 | 5123
+ 123 | 6123
+ 123 | 7123
+ 123 | 8123
+ 123 | 9123
+ 678 | 678
+ 678 | 1678
+ 678 | 2678
+ 678 | 3678
+ 678 | 4678
+ 678 | 5678
+ 678 | 6678
+ 678 | 7678
+ 678 | 8678
+ 678 | 9678
+(50 rows)
+
+DROP INDEX tenk3_idx;
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 1 | 1
+ 1 | 1001
+ 1 | 2001
+ 1 | 3001
+ 1 | 4001
+ 1 | 5001
+ 1 | 6001
+ 1 | 7001
+ 1 | 8001
+ 1 | 9001
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 0 | 0
+ 0 | 1000
+ 0 | 2000
+ 0 | 3000
+ 0 | 4000
+ 0 | 5000
+ 0 | 6000
+ 0 | 7000
+ 0 | 8000
+ 0 | 9000
+ | 3
+ | 2
+ | 1
+(33 rows)
+
+DROP INDEX tenk3_idx;
+DROP TABLE tenk3;
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+-------------------
+ Wed May 03 00:00:00 2017 | @ 2 days
+ Wed May 03 01:00:00 2017 | @ 2 days 1 hour
+ Wed May 03 02:00:00 2017 | @ 2 days 2 hours
+ Wed May 03 03:00:00 2017 | @ 2 days 3 hours
+ Wed May 03 04:00:00 2017 | @ 2 days 4 hours
+ Wed May 03 05:00:00 2017 | @ 2 days 5 hours
+ Wed May 03 06:00:00 2017 | @ 2 days 6 hours
+ Wed May 03 07:00:00 2017 | @ 2 days 7 hours
+ Wed May 03 08:00:00 2017 | @ 2 days 8 hours
+ Wed May 03 09:00:00 2017 | @ 2 days 9 hours
+ Wed May 03 10:00:00 2017 | @ 2 days 10 hours
+ Wed May 03 11:00:00 2017 | @ 2 days 11 hours
+ Wed May 03 12:00:00 2017 | @ 2 days 12 hours
+ Wed May 03 13:00:00 2017 | @ 2 days 13 hours
+ Wed May 03 14:00:00 2017 | @ 2 days 14 hours
+ Wed May 03 15:00:00 2017 | @ 2 days 15 hours
+ Wed May 03 16:00:00 2017 | @ 2 days 16 hours
+ Wed May 03 17:00:00 2017 | @ 2 days 17 hours
+ Wed May 03 18:00:00 2017 | @ 2 days 18 hours
+ Wed May 03 19:00:00 2017 | @ 2 days 19 hours
+(20 rows)
+
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+------------
+ Mon Jan 01 00:00:00 2018 | @ 0
+ Mon Jan 01 01:00:00 2018 | @ 1 hour
+ Sun Dec 31 23:00:00 2017 | @ 1 hour
+ Mon Jan 01 02:00:00 2018 | @ 2 hours
+ Sun Dec 31 22:00:00 2017 | @ 2 hours
+ Mon Jan 01 03:00:00 2018 | @ 3 hours
+ Sun Dec 31 21:00:00 2017 | @ 3 hours
+ Mon Jan 01 04:00:00 2018 | @ 4 hours
+ Sun Dec 31 20:00:00 2017 | @ 4 hours
+ Mon Jan 01 05:00:00 2018 | @ 5 hours
+ Sun Dec 31 19:00:00 2017 | @ 5 hours
+ Mon Jan 01 06:00:00 2018 | @ 6 hours
+ Sun Dec 31 18:00:00 2017 | @ 6 hours
+ Mon Jan 01 07:00:00 2018 | @ 7 hours
+ Sun Dec 31 17:00:00 2017 | @ 7 hours
+ Mon Jan 01 08:00:00 2018 | @ 8 hours
+ Sun Dec 31 16:00:00 2017 | @ 8 hours
+ Mon Jan 01 09:00:00 2018 | @ 9 hours
+ Sun Dec 31 15:00:00 2017 | @ 9 hours
+ Mon Jan 01 10:00:00 2018 | @ 10 hours
+(20 rows)
+
+DROP TABLE knn_btree_ts;
+RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 21171f7..307f2f5 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -111,3 +111,235 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
-- Simple ALTER INDEX
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+---
+--- Test B-tree distance ordering
+---
+
+SET enable_bitmapscan = OFF;
+
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+-- test parallel KNN scan
+
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+
+RESET enable_indexscan;
+
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+
+ROLLBACK;
+
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+
+
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+DROP INDEX tenk3_idx;
+
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+DROP TABLE tenk3;
+
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+
+DROP TABLE knn_btree_ts;
+
+RESET enable_bitmapscan;
On Wed, Sep 26, 2018 at 5:41 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
Attached 3rd version of the patches rebased onto the current master.
Changes from the previous version:
- Added support of INCLUDE columns to get_index_column_opclass() (1st patch).
- Added parallel kNN scan support.
- amcanorderbyop() was transformed into ammatchorderby() which takes a List of
PathKeys and checks each of them with new function match_orderbyop_pathkey()
extracted from match_pathkeys_to_index(). I think that this design can be
used in the future to support a mix of ordinary and order-by-op PathKeys,
but I am not sure.
Hi,
Unfortunately, the patch has some conflicts, could you rebase it? In the
meantime I'll move it to the next CF, hoping to have more reviewers for this
item.
On 29.11.2018 18:24, Dmitry Dolgov wrote:
On Wed, Sep 26, 2018 at 5:41 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
Attached 3rd version of the patches rebased onto the current master.
Changes from the previous version:
- Added support of INCLUDE columns to get_index_column_opclass() (1st patch).
- Added parallel kNN scan support.
- amcanorderbyop() was transformed into ammatchorderby() which takes a List of
PathKeys and checks each of them with new function match_orderbyop_pathkey()
extracted from match_pathkeys_to_index(). I think that this design can be
used in the future to support a mix of ordinary and order-by-op PathKeys,
but I am not sure.Hi,
Unfortunately, the patch has some conflicts, could you rebase it? In the
meantime I'll move it to the next CF, hoping to have more reviewers for this
item.
Attached 4th version of the patches rebased onto the current master.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-Fix-get_index_column_opclass-v04.patchtext/x-patch; name=0001-Fix-get_index_column_opclass-v04.patchDownload
From f26de4505d217a40bd4dd88cb4de71c82bd40ab0 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 30 Nov 2018 14:56:54 +0300
Subject: [PATCH 1/7] Fix get_index_column_opclass
---
src/backend/utils/cache/lsyscache.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 7a263cc..0ae7639 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3112,9 +3112,6 @@ get_index_column_opclass(Oid index_oid, int attno)
{
HeapTuple tuple;
Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
- Datum datum;
- bool isnull;
- oidvector *indclass;
Oid opclass;
/* First we need to know the column's opclass. */
@@ -3128,12 +3125,22 @@ get_index_column_opclass(Oid index_oid, int attno)
/* caller is supposed to guarantee this */
Assert(attno > 0 && attno <= rd_index->indnatts);
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indclass, &isnull);
- Assert(!isnull);
+ if (attno >= 1 && attno <= rd_index->indnkeyatts)
+ {
+ oidvector *indclass;
+ bool isnull;
+ Datum datum = SysCacheGetAttr(INDEXRELID, tuple,
+ Anum_pg_index_indclass,
+ &isnull);
+ Assert(!isnull);
- indclass = ((oidvector *) DatumGetPointer(datum));
- opclass = indclass->values[attno - 1];
+ indclass = ((oidvector *) DatumGetPointer(datum));
+ opclass = indclass->values[attno - 1];
+ }
+ else
+ {
+ opclass = InvalidOid;
+ }
ReleaseSysCache(tuple);
--
2.7.4
0002-Introduce-ammatchorderby-function-v04.patchtext/x-patch; name=0002-Introduce-ammatchorderby-function-v04.patchDownload
From 0f6e09819203e7b5d7fea224a7173b9fde2014b6 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 30 Nov 2018 14:56:54 +0300
Subject: [PATCH 2/7] Introduce ammatchorderby function
---
contrib/bloom/blutils.c | 2 +-
src/backend/access/brin/brin.c | 2 +-
src/backend/access/gin/ginutil.c | 2 +-
src/backend/access/gist/gist.c | 3 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/nbtree/nbtree.c | 2 +-
src/backend/access/spgist/spgutils.c | 6 +-
src/backend/commands/opclasscmds.c | 2 +-
src/backend/executor/nodeIndexscan.c | 4 +-
src/backend/optimizer/path/indxpath.c | 192 ++++++++++++++++++++--------------
src/backend/optimizer/util/plancat.c | 2 +-
src/backend/utils/adt/amutils.c | 2 +-
src/include/access/amapi.h | 14 ++-
src/include/nodes/plannodes.h | 2 +-
src/include/nodes/relation.h | 8 +-
src/include/optimizer/paths.h | 4 +
16 files changed, 148 insertions(+), 101 deletions(-)
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6b2b9e3..71636ae 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,7 +109,6 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BLOOM_NSTRATEGIES;
amroutine->amsupport = BLOOM_NPROC;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -143,6 +142,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index e95fbbc..ffb6de0 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -86,7 +86,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -120,6 +119,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index d7696a1..0fe76d6 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -41,7 +41,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GINNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -75,6 +74,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8a42eff..f60bb45 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,7 @@
#include "access/gistscan.h"
#include "catalog/pg_collation.h"
#include "miscadmin.h"
+#include "optimizer/paths.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "nodes/execnodes.h"
@@ -63,7 +64,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GISTNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -97,6 +97,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 0002df3..39f7f45 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -59,7 +59,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = HTMaxStrategyNumber;
amroutine->amsupport = HASHNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -93,6 +92,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index e8725fb..3e47c37 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -110,7 +110,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BTMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = true;
amroutine->amcanmulticol = true;
@@ -144,6 +143,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 9919e6f..a843650 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -32,10 +32,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-extern Expr *spgcanorderbyop(IndexOptInfo *index,
- PathKey *pathkey, int pathkeyno,
- Expr *orderby_clause, int *indexcol_p);
-
/*
* SP-GiST handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -48,7 +44,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = SPGISTNProc;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -82,6 +77,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 93ef3bd..cd1c3f8 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -1098,7 +1098,7 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
*/
IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
- if (!amroutine->amcanorderbyop)
+ if (!amroutine->ammatchorderby)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("access method \"%s\" does not support ordering operators",
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index f209173..909d4d6 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -199,7 +199,7 @@ IndexNextWithReorder(IndexScanState *node)
* with just Asserting here because the system will not try to run the
* plan backwards if ExecSupportsBackwardScan() says it won't work.
* Currently, that is guaranteed because no index AMs support both
- * amcanorderbyop and amcanbackward; if any ever do,
+ * ammatchorderby and amcanbackward; if any ever do,
* ExecSupportsBackwardScan() will need to consider indexorderbys
* explicitly.
*/
@@ -1149,7 +1149,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* 5. NullTest ("indexkey IS NULL/IS NOT NULL"). We just fill in the
* ScanKey properly.
*
- * This code is also used to prepare ORDER BY expressions for amcanorderbyop
+ * This code is also used to prepare ORDER BY expressions for ammatchorderby
* indexes. The behavior is exactly the same, except that we have to look up
* the operator differently. Note that only cases 1 and 2 are currently
* possible for ORDER BY.
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 5f46415..2c21eae 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -17,6 +17,7 @@
#include <math.h>
+#include "access/amapi.h"
#include "access/stratnum.h"
#include "access/sysattr.h"
#include "catalog/pg_am.h"
@@ -155,6 +156,10 @@ static void match_clause_to_index(IndexOptInfo *index,
static bool match_clause_to_indexcol(IndexOptInfo *index,
int indexcol,
RestrictInfo *rinfo);
+static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
+ int indexcol,
+ Expr *clause,
+ Oid pk_opfamily);
static bool is_indexable_operator(Oid expr_op, Oid opfamily,
bool indexkey_on_left);
static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
@@ -165,8 +170,6 @@ static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
List **orderby_clauses_p,
List **clause_columns_p);
-static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
- int indexcol, Expr *clause, Oid pk_opfamily);
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
EquivalenceClass *ec, EquivalenceMember *em,
void *arg);
@@ -999,7 +1002,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
orderbyclauses = NIL;
orderbyclausecols = NIL;
}
- else if (index->amcanorderbyop && pathkeys_possibly_useful)
+ else if (index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -2575,6 +2578,99 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
return false;
}
+Expr *
+match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey, int *indexcol_p)
+{
+ ListCell *lc;
+
+ /* Pathkey must request default sort order for the target opfamily */
+ if (pathkey->pk_strategy != BTLessStrategyNumber ||
+ pathkey->pk_nulls_first)
+ return NULL;
+
+ /* If eclass is volatile, no hope of using an indexscan */
+ if (pathkey->pk_eclass->ec_has_volatile)
+ return NULL;
+
+ /*
+ * Try to match eclass member expression(s) to index. Note that child
+ * EC members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could
+ * be considered to match more than one pathkey list, which is OK
+ * here. See also get_eclass_for_sort_expr.)
+ */
+ foreach(lc, pathkey->pk_eclass->ec_members)
+ {
+ EquivalenceMember *member = castNode(EquivalenceMember, lfirst(lc));
+ int indexcol;
+ int indexcol_min;
+ int indexcol_max;
+
+ /* No possibility of match if it references other relations */
+ if (!bms_equal(member->em_relids, index->rel->relids))
+ continue;
+
+ /* If *indexcol_p is non-negative then try to match only to it */
+ if (*indexcol_p >= 0)
+ {
+ indexcol_min = *indexcol_p;
+ indexcol_max = *indexcol_p + 1;
+ }
+ else /* try to match all columns */
+ {
+ indexcol_min = 0;
+ indexcol_max = index->ncolumns;
+ }
+
+ /*
+ * We allow any column of the GiST index to match each pathkey;
+ * they don't have to match left-to-right as you might expect.
+ */
+ for (indexcol = indexcol_min; indexcol < indexcol_max; indexcol++)
+ {
+ Expr *expr = match_clause_to_ordering_op(index,
+ indexcol,
+ member->em_expr,
+ pathkey->pk_opfamily);
+ if (expr)
+ {
+ *indexcol_p = indexcol;
+ return expr; /* don't want to look at remaining members */
+ }
+ }
+ }
+
+ return NULL;
+}
+
+bool
+match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p)
+{
+ ListCell *lc;
+
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = castNode(PathKey, lfirst(lc));
+ Expr *expr;
+ int indexcol = -1; /* match all index columns */
+
+ expr = match_orderbyop_pathkey(index, pathkey, &indexcol);
+
+ /*
+ * Note: for any failure to match, we just return NIL immediately.
+ * There is no value in matching just some of the pathkeys.
+ */
+ if (!expr)
+ return false;
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *clause_columns_p = lappend_int(*clause_columns_p, indexcol);
+ }
+
+ return true; /* success */
+}
/****************************************************************************
* ---- ROUTINES TO CHECK ORDERING OPERATORS ----
@@ -2600,86 +2696,24 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
{
List *orderby_clauses = NIL;
List *clause_columns = NIL;
- ListCell *lc1;
+ ammatchorderby_function ammatchorderby =
+ (ammatchorderby_function) index->ammatchorderby;
- *orderby_clauses_p = NIL; /* set default results */
- *clause_columns_p = NIL;
-
- /* Only indexes with the amcanorderbyop property are interesting here */
- if (!index->amcanorderbyop)
- return;
-
- foreach(lc1, pathkeys)
+ /* Only indexes with the ammatchorderby function are interesting here */
+ if (ammatchorderby &&
+ ammatchorderby(index, pathkeys, &orderby_clauses, &clause_columns))
{
- PathKey *pathkey = (PathKey *) lfirst(lc1);
- bool found = false;
- ListCell *lc2;
-
- /*
- * Note: for any failure to match, we just return NIL immediately.
- * There is no value in matching just some of the pathkeys.
- */
-
- /* Pathkey must request default sort order for the target opfamily */
- if (pathkey->pk_strategy != BTLessStrategyNumber ||
- pathkey->pk_nulls_first)
- return;
+ Assert(list_length(pathkeys) == list_length(orderby_clauses));
+ Assert(list_length(pathkeys) == list_length(clause_columns));
- /* If eclass is volatile, no hope of using an indexscan */
- if (pathkey->pk_eclass->ec_has_volatile)
- return;
-
- /*
- * Try to match eclass member expression(s) to index. Note that child
- * EC members are considered, but only when they belong to the target
- * relation. (Unlike regular members, the same expression could be a
- * child member of more than one EC. Therefore, the same index could
- * be considered to match more than one pathkey list, which is OK
- * here. See also get_eclass_for_sort_expr.)
- */
- foreach(lc2, pathkey->pk_eclass->ec_members)
- {
- EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
- int indexcol;
-
- /* No possibility of match if it references other relations */
- if (!bms_equal(member->em_relids, index->rel->relids))
- continue;
-
- /*
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, which is the sole existing AM supporting
- * amcanorderbyop. We might need different logic in future for
- * other implementations.
- */
- for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
- {
- Expr *expr;
-
- expr = match_clause_to_ordering_op(index,
- indexcol,
- member->em_expr,
- pathkey->pk_opfamily);
- if (expr)
- {
- orderby_clauses = lappend(orderby_clauses, expr);
- clause_columns = lappend_int(clause_columns, indexcol);
- found = true;
- break;
- }
- }
-
- if (found) /* don't want to look at remaining members */
- break;
- }
-
- if (!found) /* fail if no match for this pathkey */
- return;
+ *orderby_clauses_p = orderby_clauses; /* success! */
+ *clause_columns_p = clause_columns;
+ }
+ else
+ {
+ *orderby_clauses_p = NIL; /* set default results */
+ *clause_columns_p = NIL;
}
-
- *orderby_clauses_p = orderby_clauses; /* success! */
- *clause_columns_p = clause_columns;
}
/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index a570ac0..bf3912d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -265,7 +265,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* We copy just the fields we need, not all of rd_amroutine */
amroutine = indexRelation->rd_amroutine;
- info->amcanorderbyop = amroutine->amcanorderbyop;
+ info->ammatchorderby = amroutine->ammatchorderby;
info->amoptionalkey = amroutine->amoptionalkey;
info->amsearcharray = amroutine->amsearcharray;
info->amsearchnulls = amroutine->amsearchnulls;
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index dc04148..02879e3 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -298,7 +298,7 @@ indexam_property(FunctionCallInfo fcinfo,
* a nonkey column, and null otherwise (meaning we don't
* know).
*/
- if (!iskey || !routine->amcanorderbyop)
+ if (!iskey || !routine->ammatchorderby)
{
res = false;
isnull = false;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 14526a6..a293b38 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -21,6 +21,9 @@
*/
struct PlannerInfo;
struct IndexPath;
+struct IndexOptInfo;
+struct PathKey;
+struct Expr;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
@@ -140,6 +143,12 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* does AM support ORDER BY result of an operator on indexed column? */
+typedef bool (*ammatchorderby_function) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List **orderby_clauses_p,
+ List **clause_columns_p);
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -170,8 +179,6 @@ typedef struct IndexAmRoutine
uint16 amsupport;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
- /* does AM support ORDER BY result of an operator on indexed column? */
- bool amcanorderbyop;
/* does AM support backward scanning? */
bool amcanbackward;
/* does AM support UNIQUE indexes? */
@@ -221,7 +228,8 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
-
+ ammatchorderby_function ammatchorderby; /* can be NULL */
+
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
aminitparallelscan_function aminitparallelscan; /* can be NULL */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index f116bc2..ad9530e 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -388,7 +388,7 @@ typedef struct SampleScan
* indexorderbyops is a list of the OIDs of the operators used to sort the
* ORDER BY expressions. This is used together with indexorderbyorig to
* recheck ordering at run time. (Note that indexorderby, indexorderbyorig,
- * and indexorderbyops are used for amcanorderbyop cases, not amcanorder.)
+ * and indexorderbyops are used for ammatchorderby cases, not amcanorder.)
*
* indexorderdir specifies the scan ordering, for indexscans on amcanorder
* indexes (for other indexes it should be "don't care").
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6fd2420..0f6f07c 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -804,7 +804,6 @@ typedef struct IndexOptInfo
bool hypothetical; /* true if index doesn't really exist */
/* Remaining fields are copied from the index AM's API struct: */
- bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
@@ -813,6 +812,11 @@ typedef struct IndexOptInfo
bool amcanparallel; /* does AM support parallel scan? */
/* Rather than include amapi.h here, we declare amcostestimate like this */
void (*amcostestimate) (); /* AM's cost estimator */
+ /* AM order-by match function */
+ bool (*ammatchorderby) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List **orderby_clauses_p,
+ List **clause_columns_p);
} IndexOptInfo;
/*
@@ -1136,7 +1140,7 @@ typedef struct Path
* (The order of multiple quals for the same index column is unspecified.)
*
* 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
- * been found to be usable as ordering operators for an amcanorderbyop index.
+ * been found to be usable as ordering operators for an ammatchorderby index.
* The list must match the path's pathkeys, ie, one expression per pathkey
* in the same order. These are not RestrictInfos, just bare expressions,
* since they generally won't yield booleans. Also, unlike the case for
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index cafde30..21677eb 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -87,6 +87,10 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
int indexcol,
List **indexcolnos,
bool *var_on_left_p);
+extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
+ int *indexcol_p);
+extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p);
/*
* tidpath.h
--
2.7.4
0003-Extract-structure-BTScanState-v04.patchtext/x-patch; name=0003-Extract-structure-BTScanState-v04.patchDownload
From 46c11340c1e697eafca07a4452c94e0a63d6c3fd Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 30 Nov 2018 14:56:54 +0300
Subject: [PATCH 3/7] Extract structure BTScanState
---
src/backend/access/nbtree/nbtree.c | 200 ++++++++++--------
src/backend/access/nbtree/nbtsearch.c | 379 +++++++++++++++++-----------------
src/backend/access/nbtree/nbtutils.c | 49 +++--
src/include/access/nbtree.h | 38 ++--
4 files changed, 355 insertions(+), 311 deletions(-)
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 3e47c37..55c7833 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -214,6 +214,7 @@ bool
btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
bool res;
/* btree indexes are never lossy */
@@ -224,7 +225,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(so->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
@@ -241,7 +242,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(so->currPos))
+ if (!BTScanPosIsValid(state->currPos))
res = _bt_first(scan, dir);
else
{
@@ -259,11 +260,11 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* trying to optimize that, so we don't detect it, but instead
* just forget any excess entries.
*/
- if (so->killedItems == NULL)
- so->killedItems = (int *)
+ if (state->killedItems == NULL)
+ state->killedItems = (int *)
palloc(MaxIndexTuplesPerPage * sizeof(int));
- if (so->numKilled < MaxIndexTuplesPerPage)
- so->killedItems[so->numKilled++] = so->currPos.itemIndex;
+ if (state->numKilled < MaxIndexTuplesPerPage)
+ state->killedItems[so->state.numKilled++] = state->currPos.itemIndex;
}
/*
@@ -288,6 +289,7 @@ int64
btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
int64 ntids = 0;
ItemPointer heapTid;
@@ -320,7 +322,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* Advance to next tuple within page. This is the same as the
* easy case in _bt_next().
*/
- if (++so->currPos.itemIndex > so->currPos.lastItem)
+ if (++currPos->itemIndex > currPos->lastItem)
{
/* let _bt_next do the heavy lifting */
if (!_bt_next(scan, ForwardScanDirection))
@@ -328,7 +330,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
/* Save tuple ID, and continue scanning */
- heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
+ heapTid = &currPos->items[currPos->itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
}
@@ -356,8 +358,8 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
/* allocate private workspace */
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
- BTScanPosInvalidate(so->currPos);
- BTScanPosInvalidate(so->markPos);
+ BTScanPosInvalidate(so->state.currPos);
+ BTScanPosInvalidate(so->state.markPos);
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
else
@@ -368,15 +370,15 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
so->arrayKeys = NULL;
so->arrayContext = NULL;
- so->killedItems = NULL; /* until needed */
- so->numKilled = 0;
+ so->state.killedItems = NULL; /* until needed */
+ so->state.numKilled = 0;
/*
* We don't know yet whether the scan will be index-only, so we do not
* allocate the tuple workspace arrays until btrescan. However, we set up
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
- so->currTuples = so->markTuples = NULL;
+ so->state.currTuples = so->state.markTuples = NULL;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -385,6 +387,45 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
return scan;
}
+static void
+_bt_release_current_position(BTScanState state, Relation indexRelation,
+ bool invalidate)
+{
+ /* we aren't holding any read locks, but gotta drop the pins */
+ if (BTScanPosIsValid(state->currPos))
+ {
+ /* Before leaving current page, deal with any killed items */
+ if (state->numKilled > 0)
+ _bt_killitems(state, indexRelation);
+
+ BTScanPosUnpinIfPinned(state->currPos);
+
+ if (invalidate)
+ BTScanPosInvalidate(state->currPos);
+ }
+}
+
+static void
+_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
+{
+ /* No need to invalidate positions, if the RAM is about to be freed. */
+ _bt_release_current_position(state, scan->indexRelation, !free);
+
+ state->markItemIndex = -1;
+ BTScanPosUnpinIfPinned(state->markPos);
+
+ if (free)
+ {
+ if (state->killedItems != NULL)
+ pfree(state->killedItems);
+ if (state->currTuples != NULL)
+ pfree(state->currTuples);
+ /* markTuples should not be pfree'd (_bt_allocate_tuple_workspaces) */
+ }
+ else
+ BTScanPosInvalidate(state->markPos);
+}
+
/*
* btrescan() -- rescan an index relation
*/
@@ -393,21 +434,11 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
ScanKey orderbys, int norderbys)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
- }
+ _bt_release_scan_state(scan, state, false);
- so->markItemIndex = -1;
- so->arrayKeyCount = 0;
- BTScanPosUnpinIfPinned(so->markPos);
- BTScanPosInvalidate(so->markPos);
+ so->arrayKeyCount = 0; /* FIXME in _bt_release_scan_state */
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
@@ -425,11 +456,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
* a SIGSEGV is not possible. Yeah, this is ugly as sin, but it beats
* adding special-case treatment for name_ops elsewhere.
*/
- if (scan->xs_want_itup && so->currTuples == NULL)
- {
- so->currTuples = (char *) palloc(BLCKSZ * 2);
- so->markTuples = so->currTuples + BLCKSZ;
- }
+ if (scan->xs_want_itup && state->currTuples == NULL)
+ _bt_allocate_tuple_workspaces(state);
/*
* Reset the scan keys. Note that keys ordering stuff moved to _bt_first.
@@ -453,19 +481,7 @@ btendscan(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
-
- /* No need to invalidate positions, the RAM is about to be freed. */
+ _bt_release_scan_state(scan, &so->state, true);
/* Release storage */
if (so->keyData != NULL)
@@ -473,24 +489,15 @@ btendscan(IndexScanDesc scan)
/* so->arrayKeyData and so->arrayKeys are in arrayContext */
if (so->arrayContext != NULL)
MemoryContextDelete(so->arrayContext);
- if (so->killedItems != NULL)
- pfree(so->killedItems);
- if (so->currTuples != NULL)
- pfree(so->currTuples);
- /* so->markTuples should not be pfree'd, see btrescan */
+
pfree(so);
}
-/*
- * btmarkpos() -- save current scan position
- */
-void
-btmarkpos(IndexScanDesc scan)
+static void
+_bt_mark_current_position(BTScanState state)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
-
/* There may be an old mark with a pin (but no lock). */
- BTScanPosUnpinIfPinned(so->markPos);
+ BTScanPosUnpinIfPinned(state->markPos);
/*
* Just record the current itemIndex. If we later step to next page
@@ -498,32 +505,34 @@ btmarkpos(IndexScanDesc scan)
* the currPos struct in markPos. If (as often happens) the mark is moved
* before we leave the page, we don't have to do that work.
*/
- if (BTScanPosIsValid(so->currPos))
- so->markItemIndex = so->currPos.itemIndex;
+ if (BTScanPosIsValid(state->currPos))
+ state->markItemIndex = state->currPos.itemIndex;
else
{
- BTScanPosInvalidate(so->markPos);
- so->markItemIndex = -1;
+ BTScanPosInvalidate(state->markPos);
+ state->markItemIndex = -1;
}
-
- /* Also record the current positions of any array keys */
- if (so->numArrayKeys)
- _bt_mark_array_keys(scan);
}
/*
- * btrestrpos() -- restore scan to last saved position
+ * btmarkpos() -- save current scan position
*/
void
-btrestrpos(IndexScanDesc scan)
+btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* Restore the marked positions of any array keys */
+ _bt_mark_current_position(&so->state);
+
+ /* Also record the current positions of any array keys */
if (so->numArrayKeys)
- _bt_restore_array_keys(scan);
+ _bt_mark_array_keys(scan);
+}
- if (so->markItemIndex >= 0)
+static void
+_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
+{
+ if (state->markItemIndex >= 0)
{
/*
* The scan has never moved to a new page since the last mark. Just
@@ -532,7 +541,7 @@ btrestrpos(IndexScanDesc scan)
* NB: In this case we can't count on anything in so->markPos to be
* accurate.
*/
- so->currPos.itemIndex = so->markItemIndex;
+ state->currPos.itemIndex = state->markItemIndex;
}
else
{
@@ -542,28 +551,21 @@ btrestrpos(IndexScanDesc scan)
* locks, but if we're still holding the pin for the current position,
* we must drop it.
*/
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
+ _bt_release_current_position(state, scan->indexRelation,
+ !BTScanPosIsValid(state->markPos));
- if (BTScanPosIsValid(so->markPos))
+ if (BTScanPosIsValid(state->markPos))
{
/* bump pin on mark buffer for assignment to current buffer */
- if (BTScanPosIsPinned(so->markPos))
- IncrBufferRefCount(so->markPos.buf);
- memcpy(&so->currPos, &so->markPos,
+ if (BTScanPosIsPinned(state->markPos))
+ IncrBufferRefCount(state->markPos.buf);
+ memcpy(&state->currPos, &state->markPos,
offsetof(BTScanPosData, items[1]) +
- so->markPos.lastItem * sizeof(BTScanPosItem));
- if (so->currTuples)
- memcpy(so->currTuples, so->markTuples,
- so->markPos.nextTupleOffset);
+ state->markPos.lastItem * sizeof(BTScanPosItem));
+ if (state->currTuples)
+ memcpy(state->currTuples, state->markTuples,
+ state->markPos.nextTupleOffset);
}
- else
- BTScanPosInvalidate(so->currPos);
}
}
@@ -779,9 +781,10 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
}
/*
- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
- * btbulkdelete() wasn't called.
- */
+- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
+- * btbulkdelete() wasn't called.
++ * btrestrpos() -- restore scan to last saved position
+ */
static bool
_bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
{
@@ -844,6 +847,21 @@ _bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
}
/*
+ * btrestrpos() -- restore scan to last saved position
+ */
+void
+btrestrpos(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
+ /* Restore the marked positions of any array keys */
+ if (so->numArrayKeys)
+ _bt_restore_array_keys(scan);
+
+ _bt_restore_marked_position(scan, &so->state);
+}
+
+/*
* Bulk deletion of all index entries pointing to a set of heap tuples.
* The set of target tuples is specified via a callback routine that tells
* whether any given heap tuple (identified by ItemPointer) is being deleted.
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 16223d0..8c9df32 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -25,18 +25,19 @@
#include "utils/tqual.h"
-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir,
+static bool _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
OffsetNumber offnum);
-static void _bt_saveitem(BTScanOpaque so, int itemIndex,
+static void _bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup);
-static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
-static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir);
+static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
+static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
-static inline void _bt_initialize_more_data(BTScanOpaque so, ScanDirection dir);
+static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
/*
@@ -545,6 +546,58 @@ _bt_compare(Relation rel,
}
/*
+ * _bt_return_current_item() -- Prepare current scan state item for return.
+ *
+ * This function is used only in "return _bt_return_current_item();" statements
+ * and always returns true.
+ */
+static inline bool
+_bt_return_current_item(IndexScanDesc scan, BTScanState state)
+{
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+
+ scan->xs_ctup.t_self = currItem->heapTid;
+
+ if (scan->xs_want_itup)
+ scan->xs_itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+
+ return true;
+}
+
+/*
+ * _bt_load_first_page() -- Load data from the first page of the scan.
+ *
+ * Caller must have pinned and read-locked state->currPos.buf.
+ *
+ * On success exit, state->currPos is updated to contain data from the next
+ * interesting page. For success on a scan using a non-MVCC snapshot we hold
+ * a pin, but not a read lock, on that page. If we do not hold the pin, we
+ * set state->currPos.buf to InvalidBuffer. We return true to indicate success.
+ *
+ * If there are no more matching records in the given direction at all,
+ * we drop all locks and pins, set state->currPos.buf to InvalidBuffer,
+ * and return false.
+ */
+static bool
+_bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
+{
+ if (!_bt_readpage(scan, state, dir, offnum))
+ {
+ /*
+ * There's no actually-matching data on this page. Try to advance to
+ * the next page. Return false if there's no matching data at all.
+ */
+ LockBuffer(state->currPos.buf, BUFFER_LOCK_UNLOCK);
+ return _bt_steppage(scan, state, dir);
+ }
+
+ /* Drop the lock, and maybe the pin, on the current page */
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
+ return true;
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -569,6 +622,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
BTStack stack;
OffsetNumber offnum;
@@ -582,10 +636,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
int i;
bool status = true;
StrategyNumber strat_total;
- BTScanPosItem *currItem;
BlockNumber blkno;
- Assert(!BTScanPosIsValid(so->currPos));
+ Assert(!BTScanPosIsValid(*currPos));
pgstat_count_index_scan(rel);
@@ -1076,7 +1129,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* their scan
*/
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1084,7 +1137,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
PredicateLockPage(rel, BufferGetBlockNumber(buf),
scan->xs_snapshot);
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
/* position to the precise item on the page */
offnum = _bt_binsrch(rel, buf, keysCount, scankeys, nextkey);
@@ -1111,36 +1164,36 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
offnum = OffsetNumberPrev(offnum);
/* remember which buffer we have pinned, if any */
- Assert(!BTScanPosIsValid(so->currPos));
- so->currPos.buf = buf;
+ Assert(!BTScanPosIsValid(*currPos));
+ currPos->buf = buf;
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, offnum))
+ if (!_bt_load_first_page(scan, &so->state, dir, offnum))
+ return false;
+
+readcomplete:
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
+}
+
+/*
+ * Advance to next tuple on current page; or if there's no more,
+ * try to step to the next page with data.
+ */
+static bool
+_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
+{
+ if (ScanDirectionIsForward(dir))
{
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
+ if (++state->currPos.itemIndex <= state->currPos.lastItem)
+ return true;
}
else
{
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ if (--state->currPos.itemIndex >= state->currPos.firstItem)
+ return true;
}
-readcomplete:
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_steppage(scan, state, dir);
}
/*
@@ -1161,44 +1214,20 @@ bool
_bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- BTScanPosItem *currItem;
- /*
- * Advance to next tuple on current page; or if there's no more, try to
- * step to the next page with data.
- */
- if (ScanDirectionIsForward(dir))
- {
- if (++so->currPos.itemIndex > so->currPos.lastItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
- else
- {
- if (--so->currPos.itemIndex < so->currPos.firstItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
+ if (!_bt_next_item(scan, &so->state, dir))
+ return false;
/* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_return_current_item(scan, &so->state);
}
/*
* _bt_readpage() -- Load data from current index page into so->currPos
*
- * Caller must have pinned and read-locked so->currPos.buf; the buffer's state
- * is not changed here. Also, currPos.moreLeft and moreRight must be valid;
- * they are updated as appropriate. All other fields of so->currPos are
+ * Caller must have pinned and read-locked pos->buf; the buffer's state
+ * is not changed here. Also, pos->moreLeft and moreRight must be valid;
+ * they are updated as appropriate. All other fields of pos are
* initialized from scratch here.
*
* We scan the current page starting at offnum and moving in the indicated
@@ -1213,9 +1242,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
* Returns true if any matching items found on the page, false if none.
*/
static bool
-_bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
+_bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
@@ -1228,9 +1258,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We must have the buffer pinned and locked, but the usual macro can't be
* used here; this function is what makes it good for currPos.
*/
- Assert(BufferIsValid(so->currPos.buf));
+ Assert(BufferIsValid(pos->buf));
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* allow next page be processed by parallel worker */
@@ -1239,7 +1269,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (ScanDirectionIsForward(dir))
_bt_parallel_release(scan, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1249,30 +1279,30 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We note the buffer's block number so that we can release the pin later.
* This allows us to re-read the buffer if it is needed again for hinting.
*/
- so->currPos.currPage = BufferGetBlockNumber(so->currPos.buf);
+ pos->currPage = BufferGetBlockNumber(pos->buf);
/*
* We save the LSN of the page as we read it, so that we know whether it
* safe to apply LP_DEAD hints to the page later. This allows us to drop
* the pin for MVCC scans, which allows vacuum to avoid blocking.
*/
- so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf);
+ pos->lsn = BufferGetLSNAtomic(pos->buf);
/*
* we must save the page's right-link while scanning it; this tells us
* where to step right to after we're done with these items. There is no
* corresponding need for the left-link, since splits always go right.
*/
- so->currPos.nextPage = opaque->btpo_next;
+ pos->nextPage = opaque->btpo_next;
/* initialize tuple workspace to empty */
- so->currPos.nextTupleOffset = 0;
+ pos->nextTupleOffset = 0;
/*
* Now that the current page has been made consistent, the macro should be
* good.
*/
- Assert(BTScanPosIsPinned(so->currPos));
+ Assert(BTScanPosIsPinned(*pos));
if (ScanDirectionIsForward(dir))
{
@@ -1287,13 +1317,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (itup != NULL)
{
/* tuple passes all scan key conditions, so remember it */
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
itemIndex++;
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreRight = false;
+ pos->moreRight = false;
break;
}
@@ -1301,9 +1331,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex <= MaxIndexTuplesPerPage);
- so->currPos.firstItem = 0;
- so->currPos.lastItem = itemIndex - 1;
- so->currPos.itemIndex = 0;
+ pos->firstItem = 0;
+ pos->lastItem = itemIndex - 1;
+ pos->itemIndex = 0;
}
else
{
@@ -1319,12 +1349,12 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
{
/* tuple passes all scan key conditions, so remember it */
itemIndex--;
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreLeft = false;
+ pos->moreLeft = false;
break;
}
@@ -1332,30 +1362,31 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex >= 0);
- so->currPos.firstItem = itemIndex;
- so->currPos.lastItem = MaxIndexTuplesPerPage - 1;
- so->currPos.itemIndex = MaxIndexTuplesPerPage - 1;
+ pos->firstItem = itemIndex;
+ pos->lastItem = MaxIndexTuplesPerPage - 1;
+ pos->itemIndex = MaxIndexTuplesPerPage - 1;
}
- return (so->currPos.firstItem <= so->currPos.lastItem);
+ return (pos->firstItem <= pos->lastItem);
}
/* Save an index item into so->currPos.items[itemIndex] */
static void
-_bt_saveitem(BTScanOpaque so, int itemIndex,
+_bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup)
{
- BTScanPosItem *currItem = &so->currPos.items[itemIndex];
+ BTScanPosItem *currItem = &state->currPos.items[itemIndex];
currItem->heapTid = itup->t_tid;
currItem->indexOffset = offnum;
- if (so->currTuples)
+ if (state->currTuples)
{
Size itupsz = IndexTupleSize(itup);
- currItem->tupleOffset = so->currPos.nextTupleOffset;
- memcpy(so->currTuples + so->currPos.nextTupleOffset, itup, itupsz);
- so->currPos.nextTupleOffset += MAXALIGN(itupsz);
+ currItem->tupleOffset = state->currPos.nextTupleOffset;
+ memcpy(state->currTuples + state->currPos.nextTupleOffset,
+ itup, itupsz);
+ state->currPos.nextTupleOffset += MAXALIGN(itupsz);
}
}
@@ -1371,35 +1402,36 @@ _bt_saveitem(BTScanOpaque so, int itemIndex,
* to InvalidBuffer. We return true to indicate success.
*/
static bool
-_bt_steppage(IndexScanDesc scan, ScanDirection dir)
+_bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
+ Relation rel = scan->indexRelation;
BlockNumber blkno = InvalidBlockNumber;
bool status = true;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(*currPos));
/* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
+ if (state->numKilled > 0)
+ _bt_killitems(state, rel);
/*
* Before we modify currPos, make a copy of the page data if there was a
* mark position that needs it.
*/
- if (so->markItemIndex >= 0)
+ if (state->markItemIndex >= 0)
{
/* bump pin on current buffer for assignment to mark buffer */
- if (BTScanPosIsPinned(so->currPos))
- IncrBufferRefCount(so->currPos.buf);
- memcpy(&so->markPos, &so->currPos,
+ if (BTScanPosIsPinned(*currPos))
+ IncrBufferRefCount(currPos->buf);
+ memcpy(&state->markPos, currPos,
offsetof(BTScanPosData, items[1]) +
- so->currPos.lastItem * sizeof(BTScanPosItem));
- if (so->markTuples)
- memcpy(so->markTuples, so->currTuples,
- so->currPos.nextTupleOffset);
- so->markPos.itemIndex = so->markItemIndex;
- so->markItemIndex = -1;
+ currPos->lastItem * sizeof(BTScanPosItem));
+ if (state->markTuples)
+ memcpy(state->markTuples, state->currTuples,
+ currPos->nextTupleOffset);
+ state->markPos.itemIndex = state->markItemIndex;
+ state->markItemIndex = -1;
}
if (ScanDirectionIsForward(dir))
@@ -1415,27 +1447,27 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
if (!status)
{
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so use the previously-saved nextPage link. */
- blkno = so->currPos.nextPage;
+ blkno = currPos->nextPage;
}
/* Remember we left a page with data */
- so->currPos.moreLeft = true;
+ currPos->moreLeft = true;
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
}
else
{
/* Remember we left a page with data */
- so->currPos.moreRight = true;
+ currPos->moreRight = true;
if (scan->parallel_scan != NULL)
{
@@ -1444,25 +1476,25 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* ended already, bail out.
*/
status = _bt_parallel_seize(scan, &blkno);
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so just use our own notion of the current page */
- blkno = so->currPos.currPage;
+ blkno = currPos->currPage;
}
}
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, currPos);
return true;
}
@@ -1478,9 +1510,10 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* locks and pins, set so->currPos.buf to InvalidBuffer, and return false.
*/
static bool
-_bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
Relation rel;
Page page;
BTPageOpaque opaque;
@@ -1496,17 +1529,17 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* if we're at end of scan, give up and mark parallel scan as
* done, so that all the workers can finish their scan
*/
- if (blkno == P_NONE || !so->currPos.moreRight)
+ if (blkno == P_NONE || !currPos->moreRight)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* check for interrupts while we're not holding any buffer lock */
CHECK_FOR_INTERRUPTS();
/* step right one page */
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
- page = BufferGetPage(so->currPos.buf);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* check for deleted page */
@@ -1515,7 +1548,7 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
PredicateLockPage(rel, blkno, scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreRight if we can stop */
- if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque)))
+ if (_bt_readpage(scan, state, dir, P_FIRSTDATAKEY(opaque)))
break;
}
else if (scan->parallel_scan != NULL)
@@ -1527,18 +1560,18 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
blkno = opaque->btpo_next;
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
}
}
}
@@ -1548,10 +1581,10 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* Should only happen in parallel cases, when some other backend
* advanced the scan.
*/
- if (so->currPos.currPage != blkno)
+ if (currPos->currPage != blkno)
{
- BTScanPosUnpinIfPinned(so->currPos);
- so->currPos.currPage = blkno;
+ BTScanPosUnpinIfPinned(*currPos);
+ currPos->currPage = blkno;
}
/*
@@ -1576,31 +1609,30 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* is MVCC the page cannot move past the half-dead state to fully
* deleted.
*/
- if (BTScanPosIsPinned(so->currPos))
- LockBuffer(so->currPos.buf, BT_READ);
+ if (BTScanPosIsPinned(*currPos))
+ LockBuffer(currPos->buf, BT_READ);
else
- so->currPos.buf = _bt_getbuf(rel, so->currPos.currPage, BT_READ);
+ currPos->buf = _bt_getbuf(rel, currPos->currPage, BT_READ);
for (;;)
{
/* Done if we know there are no matching keys to the left */
- if (!so->currPos.moreLeft)
+ if (!currPos->moreLeft)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* Step to next physical page */
- so->currPos.buf = _bt_walk_left(rel, so->currPos.buf,
- scan->xs_snapshot);
+ currPos->buf = _bt_walk_left(rel, currPos->buf, scan->xs_snapshot);
/* if we're physically at end of index, return failure */
- if (so->currPos.buf == InvalidBuffer)
+ if (currPos->buf == InvalidBuffer)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1609,21 +1641,21 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* it's not half-dead and contains matching tuples. Else loop back
* and do it all again.
*/
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
{
- PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
+ PredicateLockPage(rel, BufferGetBlockNumber(currPos->buf), scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreLeft if we can stop */
- if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page)))
+ if (_bt_readpage(scan, state, dir, PageGetMaxOffsetNumber(page)))
break;
}
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1634,14 +1666,14 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
}
}
}
@@ -1660,13 +1692,13 @@ _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, &so->state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
return true;
}
@@ -1891,11 +1923,11 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
Page page;
BTPageOpaque opaque;
OffsetNumber start;
- BTScanPosItem *currItem;
/*
* Scan down to the leftmost or rightmost leaf page. This is a simplified
@@ -1911,7 +1943,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* exists.
*/
PredicateLockRelation(rel, scan->xs_snapshot);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1940,36 +1972,15 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
}
/* remember which buffer we have pinned */
- so->currPos.buf = buf;
+ currPos->buf = buf;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, start))
- {
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
- }
- else
- {
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
- }
-
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
+ if (!_bt_load_first_page(scan, &so->state, dir, start))
+ return false;
- return true;
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
}
/*
@@ -1977,19 +1988,19 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* for scan direction
*/
static inline void
-_bt_initialize_more_data(BTScanOpaque so, ScanDirection dir)
+_bt_initialize_more_data(BTScanState state, ScanDirection dir)
{
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ state->currPos.moreLeft = false;
+ state->currPos.moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ state->currPos.moreLeft = true;
+ state->currPos.moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- so->markItemIndex = -1; /* ditto */
+ state->numKilled = 0; /* just paranoia */
+ state->markItemIndex = -1; /* ditto */
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 205457e..619f96ce 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1741,26 +1741,26 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
* away and the TID was re-used by a completely different heap tuple.
*/
void
-_bt_killitems(IndexScanDesc scan)
+_bt_killitems(BTScanState state, Relation indexRelation)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
OffsetNumber maxoff;
int i;
- int numKilled = so->numKilled;
+ int numKilled = state->numKilled;
bool killedsomething = false;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(state->currPos));
/*
* Always reset the scan state, so we don't look for same items on other
* pages.
*/
- so->numKilled = 0;
+ state->numKilled = 0;
- if (BTScanPosIsPinned(so->currPos))
+ if (BTScanPosIsPinned(*pos))
{
/*
* We have held the pin on this page since we read the index tuples,
@@ -1768,44 +1768,42 @@ _bt_killitems(IndexScanDesc scan)
* re-use of any TID on the page, so there is no need to check the
* LSN.
*/
- LockBuffer(so->currPos.buf, BT_READ);
-
- page = BufferGetPage(so->currPos.buf);
+ LockBuffer(pos->buf, BT_READ);
}
else
{
Buffer buf;
/* Attempt to re-read the buffer, getting pin and lock. */
- buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, BT_READ);
+ buf = _bt_getbuf(indexRelation, pos->currPage, BT_READ);
/* It might not exist anymore; in which case we can't hint it. */
if (!BufferIsValid(buf))
return;
- page = BufferGetPage(buf);
- if (BufferGetLSNAtomic(buf) == so->currPos.lsn)
- so->currPos.buf = buf;
+ if (BufferGetLSNAtomic(buf) == pos->lsn)
+ pos->buf = buf;
else
{
/* Modified while not pinned means hinting is not safe. */
- _bt_relbuf(scan->indexRelation, buf);
+ _bt_relbuf(indexRelation, buf);
return;
}
}
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
for (i = 0; i < numKilled; i++)
{
- int itemIndex = so->killedItems[i];
- BTScanPosItem *kitem = &so->currPos.items[itemIndex];
+ int itemIndex = state->killedItems[i];
+ BTScanPosItem *kitem = &pos->items[itemIndex];
OffsetNumber offnum = kitem->indexOffset;
- Assert(itemIndex >= so->currPos.firstItem &&
- itemIndex <= so->currPos.lastItem);
+ Assert(itemIndex >= pos->firstItem &&
+ itemIndex <= pos->lastItem);
if (offnum < minoff)
continue; /* pure paranoia */
while (offnum <= maxoff)
@@ -1833,10 +1831,10 @@ _bt_killitems(IndexScanDesc scan)
if (killedsomething)
{
opaque->btpo_flags |= BTP_HAS_GARBAGE;
- MarkBufferDirtyHint(so->currPos.buf, true);
+ MarkBufferDirtyHint(pos->buf, true);
}
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(pos->buf, BUFFER_LOCK_UNLOCK);
}
@@ -2214,3 +2212,14 @@ _bt_check_natts(Relation rel, Page page, OffsetNumber offnum)
}
}
+
+/*
+ * _bt_allocate_tuple_workspaces() -- Allocate buffers for saving index tuples
+ * in index-only scans.
+ */
+void
+_bt_allocate_tuple_workspaces(BTScanState state)
+{
+ state->currTuples = (char *) palloc(BLCKSZ * 2);
+ state->markTuples = state->currTuples + BLCKSZ;
+}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index ea495f1..1bfee58 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -433,22 +433,8 @@ typedef struct BTArrayKeyInfo
Datum *elem_values; /* array of num_elems Datums */
} BTArrayKeyInfo;
-typedef struct BTScanOpaqueData
+typedef struct BTScanStateData
{
- /* these fields are set by _bt_preprocess_keys(): */
- bool qual_ok; /* false if qual can never be satisfied */
- int numberOfKeys; /* number of preprocessed scan keys */
- ScanKey keyData; /* array of preprocessed scan keys */
-
- /* workspace for SK_SEARCHARRAY support */
- ScanKey arrayKeyData; /* modified copy of scan->keyData */
- int numArrayKeys; /* number of equality-type array keys (-1 if
- * there are any unsatisfiable array keys) */
- int arrayKeyCount; /* count indicating number of array scan keys
- * processed */
- BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
- MemoryContext arrayContext; /* scan-lifespan context for array data */
-
/* info about killed items if any (killedItems is NULL if never used) */
int *killedItems; /* currPos.items indexes of killed items */
int numKilled; /* number of currently stored items */
@@ -473,6 +459,25 @@ typedef struct BTScanOpaqueData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+} BTScanStateData, *BTScanState;
+
+typedef struct BTScanOpaqueData
+{
+ /* these fields are set by _bt_preprocess_keys(): */
+ bool qual_ok; /* false if qual can never be satisfied */
+ int numberOfKeys; /* number of preprocessed scan keys */
+ ScanKey keyData; /* array of preprocessed scan keys */
+
+ /* workspace for SK_SEARCHARRAY support */
+ ScanKey arrayKeyData; /* modified copy of scan->keyData */
+ int numArrayKeys; /* number of equality-type array keys (-1 if
+ * there are any unsatisfiable array keys) */
+ int arrayKeyCount; /* count indicating number of array scan keys
+ * processed */
+ BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
+ MemoryContext arrayContext; /* scan-lifespan context for array data */
+
+ BTScanStateData state;
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -589,7 +594,7 @@ extern void _bt_preprocess_keys(IndexScanDesc scan);
extern IndexTuple _bt_checkkeys(IndexScanDesc scan,
Page page, OffsetNumber offnum,
ScanDirection dir, bool *continuescan);
-extern void _bt_killitems(IndexScanDesc scan);
+extern void _bt_killitems(BTScanState state, Relation indexRelation);
extern BTCycleId _bt_vacuum_cycleid(Relation rel);
extern BTCycleId _bt_start_vacuum(Relation rel);
extern void _bt_end_vacuum(Relation rel);
@@ -602,6 +607,7 @@ extern bool btproperty(Oid index_oid, int attno,
bool *res, bool *isnull);
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
+extern void _bt_allocate_tuple_workspaces(BTScanState state);
/*
* prototypes for functions in nbtvalidate.c
--
2.7.4
0004-Add-kNN-support-to-btree-v04.patchtext/x-patch; name=0004-Add-kNN-support-to-btree-v04.patchDownload
From 096a442eefaf3227da92880aad841eae02285d34 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 30 Nov 2018 14:56:54 +0300
Subject: [PATCH 4/7] Add kNN support to btree
---
doc/src/sgml/btree.sgml | 14 ++
doc/src/sgml/indices.sgml | 11 +
doc/src/sgml/xindex.sgml | 3 +-
src/backend/access/nbtree/README | 17 ++
src/backend/access/nbtree/nbtree.c | 218 ++++++++++++++--
src/backend/access/nbtree/nbtsearch.c | 331 +++++++++++++++++++++---
src/backend/access/nbtree/nbtutils.c | 373 +++++++++++++++++++++++++++-
src/backend/access/nbtree/nbtvalidate.c | 48 ++--
src/backend/optimizer/path/indxpath.c | 16 +-
src/include/access/nbtree.h | 27 +-
src/include/access/stratnum.h | 5 +-
src/test/regress/expected/alter_generic.out | 13 +-
src/test/regress/sql/alter_generic.sql | 8 +-
13 files changed, 981 insertions(+), 103 deletions(-)
diff --git a/doc/src/sgml/btree.sgml b/doc/src/sgml/btree.sgml
index c16825e..e508615 100644
--- a/doc/src/sgml/btree.sgml
+++ b/doc/src/sgml/btree.sgml
@@ -200,6 +200,20 @@
planner relies on them for optimization purposes.
</para>
+ <para>
+ FIXME!!!
+ To implement the distance ordered (nearest-neighbor) search, we only need
+ to define a distance operator (usually it called <->) with a correpsonding
+ operator family for distance comparison in the index's operator class.
+ These operators must satisfy the following assumptions for all non-null
+ values A,B,C of the datatype:
+
+ A <-> B = B <-> A symmetric law
+ if A = B, then A <-> C = B <-> C distance equivalence
+ if (A <= B and B <= C) or (A >= B and B >= C),
+ then A <-> B <= A <-> C monotonicity
+ </para>
+
</sect1>
<sect1 id="btree-support-funcs">
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 46f427b..caec484 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -175,6 +175,17 @@ CREATE INDEX test1_id_index ON test1 (id);
</para>
<para>
+ B-tree indexes are also capable of optimizing <quote>nearest-neighbor</>
+ searches, such as
+<programlisting><![CDATA[
+SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
+]]>
+</programlisting>
+ which finds the ten events closest to a given target date. The ability
+ to do this is again dependent on the particular operator class being used.
+ </para>
+
+ <para>
<indexterm>
<primary>index</primary>
<secondary>hash</secondary>
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..93094bc 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -1242,7 +1242,8 @@ SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING)
<title>Ordering Operators</title>
<para>
- Some index access methods (currently, only GiST and SP-GiST) support the concept of
+ Some index access methods (currently, only B-tree, GiST and SP-GiST)
+ support the concept of
<firstterm>ordering operators</firstterm>. What we have been discussing so far
are <firstterm>search operators</firstterm>. A search operator is one for which
the index can be searched to find all rows satisfying
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
index 3680e69..3f7e1b1 100644
--- a/src/backend/access/nbtree/README
+++ b/src/backend/access/nbtree/README
@@ -659,3 +659,20 @@ routines must treat it accordingly. The actual key stored in the
item is irrelevant, and need not be stored at all. This arrangement
corresponds to the fact that an L&Y non-leaf page has one more pointer
than key.
+
+Nearest-neighbor search
+-----------------------
+
+There is a special scan strategy for nearest-neighbor (kNN) search,
+that is used in queries with ORDER BY distance clauses like this:
+SELECT * FROM tab WHERE col > const1 ORDER BY col <-> const2 LIMIT k.
+But, unlike GiST, B-tree supports only a one ordering operator on the
+first index column.
+
+At the beginning of kNN scan, we need to determine which strategy we
+will use --- a special bidirectional or a ordinary unidirectional.
+If the point from which we measure the distance falls into the scan range,
+we use bidirectional scan starting from this point, else we use simple
+unidirectional scan in the right direction. Algorithm of a bidirectional
+scan is very simple: at each step we advancing scan in that direction,
+which has the nearest point.
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 55c7833..9270a85 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -25,6 +25,9 @@
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "optimizer/paths.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "storage/condition_variable.h"
@@ -33,6 +36,7 @@
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
@@ -79,6 +83,7 @@ typedef enum
typedef struct BTParallelScanDescData
{
BlockNumber btps_scanPage; /* latest or next page to be scanned */
+ BlockNumber btps_knnScanPage; /* secondary latest or next page to be scanned */
BTPS_State btps_pageStatus; /* indicates whether next page is
* available for scan. see above for
* possible states of parallel scan. */
@@ -97,6 +102,9 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
+static bool btmatchorderby(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p);
+
/*
* Btree handler function: return IndexAmRoutine with access method parameters
@@ -107,7 +115,7 @@ bthandler(PG_FUNCTION_ARGS)
{
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
- amroutine->amstrategies = BTMaxStrategyNumber;
+ amroutine->amstrategies = BtreeMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
amroutine->amcanbackward = true;
@@ -143,7 +151,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
- amroutine->ammatchorderby = NULL;
+ amroutine->ammatchorderby = btmatchorderby;
PG_RETURN_POINTER(amroutine);
}
@@ -215,23 +223,30 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTScanState state = &so->state;
+ ScanDirection arraydir =
+ scan->numberOfOrderBys > 0 ? ForwardScanDirection : dir;
bool res;
/* btree indexes are never lossy */
scan->xs_recheck = false;
+ scan->xs_recheckorderby = false;
+
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
/*
* If we have any array keys, initialize them during first call for a
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
return false;
- _bt_start_array_keys(scan, dir);
+ _bt_start_array_keys(scan, arraydir);
}
/* This loop handles advancing to the next array elements, if any */
@@ -242,7 +257,8 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(state->currPos))
+ if (!BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
res = _bt_first(scan, dir);
else
{
@@ -277,7 +293,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
if (res)
break;
/* ... otherwise see if we have more array keys to deal with */
- } while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
+ } while (so->numArrayKeys && _bt_advance_array_keys(scan, arraydir));
return res;
}
@@ -350,9 +366,6 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
IndexScanDesc scan;
BTScanOpaque so;
- /* no order by operators allowed */
- Assert(norderbys == 0);
-
/* get the scan */
scan = RelationGetIndexScan(rel, nkeys, norderbys);
@@ -379,6 +392,9 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
so->state.currTuples = so->state.markTuples = NULL;
+ so->knnState = NULL;
+ so->distanceTypeByVal = true;
+ so->scanDirection = NoMovementScanDirection;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -408,6 +424,8 @@ _bt_release_current_position(BTScanState state, Relation indexRelation,
static void
_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
/* No need to invalidate positions, if the RAM is about to be freed. */
_bt_release_current_position(state, scan->indexRelation, !free);
@@ -424,6 +442,17 @@ _bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
}
else
BTScanPosInvalidate(state->markPos);
+
+ if (!so->distanceTypeByVal)
+ {
+ if (DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+ state->currDistance = PointerGetDatum(NULL);
+
+ if (DatumGetPointer(state->markDistance))
+ pfree(DatumGetPointer(state->markDistance));
+ state->markDistance = PointerGetDatum(NULL);
+ }
}
/*
@@ -438,6 +467,13 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
_bt_release_scan_state(scan, state, false);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ so->knnState = NULL;
+ }
+
so->arrayKeyCount = 0; /* FIXME in _bt_release_scan_state */
/*
@@ -469,6 +505,14 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
scan->numberOfKeys * sizeof(ScanKeyData));
so->numberOfKeys = 0; /* until _bt_preprocess_keys sets it */
+ if (orderbys && scan->numberOfOrderBys > 0)
+ memmove(scan->orderByData,
+ orderbys,
+ scan->numberOfOrderBys * sizeof(ScanKeyData));
+
+ so->scanDirection = NoMovementScanDirection;
+ so->distanceTypeByVal = true;
+
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
_bt_preprocess_array_keys(scan);
}
@@ -483,6 +527,12 @@ btendscan(IndexScanDesc scan)
_bt_release_scan_state(scan, &so->state, true);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ }
+
/* Release storage */
if (so->keyData != NULL)
pfree(so->keyData);
@@ -494,7 +544,7 @@ btendscan(IndexScanDesc scan)
}
static void
-_bt_mark_current_position(BTScanState state)
+_bt_mark_current_position(BTScanOpaque so, BTScanState state)
{
/* There may be an old mark with a pin (but no lock). */
BTScanPosUnpinIfPinned(state->markPos);
@@ -512,6 +562,21 @@ _bt_mark_current_position(BTScanState state)
BTScanPosInvalidate(state->markPos);
state->markItemIndex = -1;
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->markDistance));
+
+ state->markIsNull = !BTScanPosIsValid(state->currPos) ||
+ state->currIsNull;
+
+ state->markDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->currDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -522,7 +587,13 @@ btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_mark_current_position(&so->state);
+ _bt_mark_current_position(so, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_mark_current_position(so, so->knnState);
+ so->markRightIsNearest = so->currRightIsNearest;
+ }
/* Also record the current positions of any array keys */
if (so->numArrayKeys)
@@ -532,6 +603,8 @@ btmarkpos(IndexScanDesc scan)
static void
_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
if (state->markItemIndex >= 0)
{
/*
@@ -567,6 +640,19 @@ _bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
state->markPos.nextTupleOffset);
}
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currIsNull = state->markIsNull;
+ state->currDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->markDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -588,6 +674,7 @@ btinitparallelscan(void *target)
SpinLockInit(&bt_target->btps_mutex);
bt_target->btps_scanPage = InvalidBlockNumber;
+ bt_target->btps_knnScanPage = InvalidBlockNumber;
bt_target->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
bt_target->btps_arrayKeyCount = 0;
ConditionVariableInit(&bt_target->btps_cv);
@@ -614,6 +701,7 @@ btparallelrescan(IndexScanDesc scan)
*/
SpinLockAcquire(&btscan->btps_mutex);
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount = 0;
SpinLockRelease(&btscan->btps_mutex);
@@ -638,7 +726,7 @@ btparallelrescan(IndexScanDesc scan)
* Callers should ignore the value of pageno if the return value is false.
*/
bool
-_bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
+_bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTPS_State pageStatus;
@@ -646,12 +734,17 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
bool status = true;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
*pageno = P_NONE;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ scanPage = state == &so->state
+ ? &btscan->btps_scanPage
+ : &btscan->btps_knnScanPage;
+
while (1)
{
SpinLockAcquire(&btscan->btps_mutex);
@@ -677,7 +770,7 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* of advancing it to a new page!
*/
btscan->btps_pageStatus = BTPARALLEL_ADVANCING;
- *pageno = btscan->btps_scanPage;
+ *pageno = *scanPage;
exit_loop = true;
}
SpinLockRelease(&btscan->btps_mutex);
@@ -696,19 +789,42 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* can now begin advancing the scan.
*/
void
-_bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
+_bt_parallel_release(IndexScanDesc scan, BTScanState state,
+ BlockNumber scan_page)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
+ bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
SpinLockAcquire(&btscan->btps_mutex);
- btscan->btps_scanPage = scan_page;
- btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ *scanPage = scan_page;
+ /* switch to idle state only if both KNN pages are initialized */
+ if (!knnScan || *otherScanPage != InvalidBlockNumber)
+ {
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ status_changed = true;
+ }
SpinLockRelease(&btscan->btps_mutex);
- ConditionVariableSignal(&btscan->btps_cv);
+
+ if (status_changed)
+ ConditionVariableSignal(&btscan->btps_cv);
}
/*
@@ -719,12 +835,15 @@ _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
* advance to the next page.
*/
void
-_bt_parallel_done(IndexScanDesc scan)
+_bt_parallel_done(IndexScanDesc scan, BTScanState state)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
/* Do nothing, for non-parallel scans */
if (parallel_scan == NULL)
@@ -733,18 +852,41 @@ _bt_parallel_done(IndexScanDesc scan)
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
+
/*
* Mark the parallel scan as done for this combination of scan keys,
* unless some other process already did so. See also
* _bt_advance_array_keys.
*/
SpinLockAcquire(&btscan->btps_mutex);
- if (so->arrayKeyCount >= btscan->btps_arrayKeyCount &&
- btscan->btps_pageStatus != BTPARALLEL_DONE)
+
+ Assert(btscan->btps_pageStatus == BTPARALLEL_ADVANCING);
+
+ if (so->arrayKeyCount >= btscan->btps_arrayKeyCount)
{
- btscan->btps_pageStatus = BTPARALLEL_DONE;
+ *scanPage = P_NONE;
status_changed = true;
+
+ /* switch to "done" state only if both KNN scans are done */
+ if (!knnScan || *otherScanPage == P_NONE)
+ btscan->btps_pageStatus = BTPARALLEL_DONE;
+ /* else switch to "idle" state only if both KNN scans are initialized */
+ else if (*otherScanPage != InvalidBlockNumber)
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ else
+ status_changed = false;
}
+
SpinLockRelease(&btscan->btps_mutex);
/* wake up all the workers associated with this parallel scan */
@@ -774,6 +916,7 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
if (btscan->btps_pageStatus == BTPARALLEL_DONE)
{
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount++;
}
@@ -859,6 +1002,12 @@ btrestrpos(IndexScanDesc scan)
_bt_restore_array_keys(scan);
_bt_restore_marked_position(scan, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_restore_marked_position(scan, so->knnState);
+ so->currRightIsNearest = so->markRightIsNearest;
+ }
}
/*
@@ -1394,3 +1543,30 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+/*
+ * btmatchorderby() -- Check whether KNN-search strategy is applicable to
+ * the given ORDER BY distance operator.
+ */
+static bool
+btmatchorderby(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p)
+{
+ Expr *expr;
+ /* ORDER BY distance to the first index column is only supported */
+ int indexcol = 0;
+
+ if (list_length(pathkeys) != 1)
+ return false; /* only one ORDER BY clause is supported */
+
+ expr = match_orderbyop_pathkey(index, castNode(PathKey, linitial(pathkeys)),
+ &indexcol);
+
+ if (!expr)
+ return false;
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *clause_columns_p = lappend_int(*clause_columns_p, 0);
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 8c9df32..36f8886 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -32,12 +32,14 @@ static void _bt_saveitem(BTScanState state, int itemIndex,
static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
BlockNumber blkno, ScanDirection dir);
-static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
- ScanDirection dir);
+static bool _bt_parallel_readpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
+static BTScanState _bt_alloc_knn_scan(IndexScanDesc scan);
+static bool _bt_start_knn_scan(IndexScanDesc scan, bool left, bool right);
/*
@@ -598,6 +600,156 @@ _bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
}
/*
+ * _bt_calc_current_dist() -- Calculate distance from the current item
+ * of the scan state to the target order-by ScanKey argument.
+ */
+static void
+_bt_calc_current_dist(IndexScanDesc scan, BTScanState state)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+ IndexTuple itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+ ScanKey scankey = &scan->orderByData[0];
+ Datum value;
+
+ value = index_getattr(itup, 1, scan->xs_itupdesc, &state->currIsNull);
+
+ if (state->currIsNull)
+ return; /* NULL distance */
+
+ value = FunctionCall2Coll(&scankey->sk_func,
+ scankey->sk_collation,
+ value,
+ scankey->sk_argument);
+
+ /* free previous distance value for by-ref types */
+ if (!so->distanceTypeByVal && DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currDistance = value;
+}
+
+/*
+ * _bt_compare_current_dist() -- Compare current distances of the left and right scan states.
+ *
+ * NULL distances are considered to be greater than any non-NULL distances.
+ *
+ * Returns true if right distance is lesser than left, otherwise false.
+ */
+static bool
+_bt_compare_current_dist(BTScanOpaque so, BTScanState rstate, BTScanState lstate)
+{
+ if (lstate->currIsNull)
+ return true; /* non-NULL < NULL */
+
+ if (rstate->currIsNull)
+ return false; /* NULL > non-NULL */
+
+ return DatumGetBool(FunctionCall2Coll(&so->distanceCmpProc,
+ InvalidOid, /* XXX collation for distance comparison */
+ rstate->currDistance,
+ lstate->currDistance));
+}
+
+/*
+ * _bt_alloc_knn_scan() -- Allocate additional backward scan state for KNN.
+ */
+static BTScanState
+_bt_alloc_knn_scan(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState lstate = (BTScanState) palloc(sizeof(BTScanStateData));
+
+ _bt_allocate_tuple_workspaces(lstate);
+
+ if (!scan->xs_want_itup)
+ {
+ /* We need to request index tuples for distance comparison. */
+ scan->xs_want_itup = true;
+ _bt_allocate_tuple_workspaces(&so->state);
+ }
+
+ BTScanPosInvalidate(lstate->currPos);
+ lstate->currPos.moreLeft = false;
+ lstate->currPos.moreRight = false;
+ BTScanPosInvalidate(lstate->markPos);
+ lstate->markItemIndex = -1;
+ lstate->killedItems = NULL;
+ lstate->numKilled = 0;
+ lstate->currDistance = PointerGetDatum(NULL);
+ lstate->markDistance = PointerGetDatum(NULL);
+
+ return so->knnState = lstate;
+}
+
+static bool
+_bt_start_knn_scan(IndexScanDesc scan, bool left, bool right)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+
+ if (!left && !right)
+ return false; /* empty result */
+
+ rstate = &so->state;
+ lstate = so->knnState;
+
+ if (left && right)
+ {
+ /*
+ * We have found items in both scan directions,
+ * determine nearest item to return.
+ */
+ _bt_calc_current_dist(scan, rstate);
+ _bt_calc_current_dist(scan, lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+
+ /* Reset right flag if the left item is nearer. */
+ right = so->currRightIsNearest;
+ }
+
+ /* Return current item of the selected scan direction. */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
+ * _bt_init_knn_scan() -- Init additional scan state for KNN search.
+ *
+ * Caller must pin and read-lock scan->state.currPos.buf buffer.
+ *
+ * If empty result was found returned false.
+ * Otherwise prepared current item, and returned true.
+ */
+static bool
+_bt_init_knn_scan(IndexScanDesc scan, OffsetNumber offnum)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+ Buffer buf = rstate->currPos.buf;
+ bool left,
+ right;
+
+ lstate = _bt_alloc_knn_scan(scan);
+
+ /* Bump pin and lock count before BTScanPosData copying. */
+ IncrBufferRefCount(buf);
+ LockBuffer(buf, BT_READ);
+
+ memcpy(&lstate->currPos, &rstate->currPos, sizeof(BTScanPosData));
+ lstate->currPos.moreLeft = true;
+ lstate->currPos.moreRight = false;
+
+ /* Load first pages from the both scans. */
+ right = _bt_load_first_page(scan, rstate, ForwardScanDirection, offnum);
+ left = _bt_load_first_page(scan, lstate, BackwardScanDirection,
+ OffsetNumberPrev(offnum));
+
+ return _bt_start_knn_scan(scan, left, right);
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -655,6 +807,19 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!so->qual_ok)
return false;
+ strat_total = BTEqualStrategyNumber;
+
+ if (scan->numberOfOrderBys > 0)
+ {
+ if (_bt_process_orderings(scan, startKeys, &keysCount, notnullkeys))
+ /* use bidirectional KNN scan */
+ strat_total = BtreeKNNSearchStrategyNumber;
+
+ /* use selected KNN scan direction */
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
+ }
+
/*
* For parallel scans, get the starting page from shared state. If the
* scan has not started, proceed to find out first leaf page in the usual
@@ -663,19 +828,50 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, &so->state, &blkno);
if (!status)
return false;
- else if (blkno == P_NONE)
- {
- _bt_parallel_done(scan);
- return false;
- }
else if (blkno != InvalidBlockNumber)
{
- if (!_bt_parallel_readpage(scan, blkno, dir))
- return false;
- goto readcomplete;
+ bool knn = strat_total == BtreeKNNSearchStrategyNumber;
+ bool right;
+ bool left;
+
+ if (knn)
+ _bt_alloc_knn_scan(scan);
+
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, &so->state);
+ right = false;
+ }
+ else
+ right = _bt_parallel_readpage(scan, &so->state, blkno,
+ knn ? ForwardScanDirection : dir);
+
+ if (!knn)
+ return right && _bt_return_current_item(scan, &so->state);
+
+ /* seize additional backward KNN scan */
+ left = _bt_parallel_seize(scan, so->knnState, &blkno);
+
+ if (left)
+ {
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, so->knnState);
+ left = false;
+ }
+ else
+ {
+ /* backward scan should be already initialized */
+ Assert(blkno != InvalidBlockNumber);
+ left = _bt_parallel_readpage(scan, so->knnState, blkno,
+ BackwardScanDirection);
+ }
+ }
+
+ return _bt_start_knn_scan(scan, left, right);
}
}
@@ -725,8 +921,10 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* storing their addresses into the local startKeys[] array.
*----------
*/
- strat_total = BTEqualStrategyNumber;
- if (so->numberOfKeys > 0)
+
+ if (so->numberOfKeys > 0 &&
+ /* startKeys for KNN search already have been initialized */
+ strat_total != BtreeKNNSearchStrategyNumber)
{
AttrNumber curattr;
ScanKey chosen;
@@ -866,7 +1064,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!match)
{
/* No match, so mark (parallel) scan finished */
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
}
return match;
@@ -901,7 +1099,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(subkey->sk_flags & SK_ROW_MEMBER);
if (subkey->sk_flags & SK_ISNULL)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
return false;
}
memcpy(scankeys + i, subkey, sizeof(ScanKeyData));
@@ -1081,6 +1279,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
break;
case BTGreaterEqualStrategyNumber:
+ case BtreeKNNSearchStrategyNumber:
/*
* Find first item >= scankey. (This is only used for forward
@@ -1128,7 +1327,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* mark parallel scan as done, so that all the workers can finish
* their scan
*/
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
BTScanPosInvalidate(*currPos);
return false;
@@ -1167,17 +1366,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(!BTScanPosIsValid(*currPos));
currPos->buf = buf;
+ if (strat_total == BtreeKNNSearchStrategyNumber)
+ return _bt_init_knn_scan(scan, offnum);
+
if (!_bt_load_first_page(scan, &so->state, dir, offnum))
- return false;
+ return false; /* empty result */
-readcomplete:
/* OK, currPos->itemIndex says what to return */
return _bt_return_current_item(scan, &so->state);
}
/*
- * Advance to next tuple on current page; or if there's no more,
- * try to step to the next page with data.
+ * _bt_next_item() -- Advance to next tuple on current page;
+ * or if there's no more, try to step to the next page with data.
+ *
+ * If there are no more matching records in the given direction
*/
static bool
_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
@@ -1197,6 +1400,51 @@ _bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
}
/*
+ * _bt_next_nearest() -- Return next nearest item from bidirectional KNN scan.
+ */
+static bool
+_bt_next_nearest(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state;
+ BTScanState lstate = so->knnState;
+ bool right = BTScanPosIsValid(rstate->currPos);
+ bool left = BTScanPosIsValid(lstate->currPos);
+ bool advanceRight;
+
+ if (right && left)
+ advanceRight = so->currRightIsNearest;
+ else if (right)
+ advanceRight = true;
+ else if (left)
+ advanceRight = false;
+ else
+ return false; /* end of the scan */
+
+ if (advanceRight)
+ right = _bt_next_item(scan, rstate, ForwardScanDirection);
+ else
+ left = _bt_next_item(scan, lstate, BackwardScanDirection);
+
+ if (!left && !right)
+ return false; /* end of the scan */
+
+ if (left && right)
+ {
+ /*
+ * If there are items in both scans we must recalculate distance
+ * in the advanced scan.
+ */
+ _bt_calc_current_dist(scan, advanceRight ? rstate : lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+ right = so->currRightIsNearest;
+ }
+
+ /* return nearest item */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_next() -- Get the next item in a scan.
*
* On entry, so->currPos describes the current page, which may be pinned
@@ -1215,6 +1463,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ if (so->knnState)
+ /* return next neareset item from KNN scan */
+ return _bt_next_nearest(scan);
+
if (!_bt_next_item(scan, &so->state, dir))
return false;
@@ -1267,9 +1519,9 @@ _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
if (scan->parallel_scan)
{
if (ScanDirectionIsForward(dir))
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1443,7 +1695,7 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the next block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
/* release the previous buffer, if pinned */
@@ -1475,13 +1727,19 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the current block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
BTScanPosInvalidate(*currPos);
return false;
}
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, state);
+ BTScanPosInvalidate(*currPos);
+ return false;
+ }
}
else
{
@@ -1531,7 +1789,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
*/
if (blkno == P_NONE || !currPos->moreRight)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1554,14 +1812,14 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
}
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1620,7 +1878,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (!currPos->moreLeft)
{
_bt_relbuf(rel, currPos->buf);
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1631,7 +1889,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
/* if we're physically at end of index, return failure */
if (currPos->buf == InvalidBuffer)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1655,7 +1913,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1667,7 +1925,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1688,17 +1946,16 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
* indicate success.
*/
static bool
-_bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_parallel_readpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ _bt_initialize_more_data(state, dir);
- _bt_initialize_more_data(&so->state, dir);
-
- if (!_bt_readnextpage(scan, &so->state, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
return true;
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 619f96ce..3e615c2 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -20,16 +20,21 @@
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "catalog/pg_amop.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
typedef struct BTSortArrayContext
{
FmgrInfo flinfo;
+ FmgrInfo distflinfo;
+ FmgrInfo distcmpflinfo;
+ ScanKey distkey;
Oid collation;
bool reverse;
} BTSortArrayContext;
@@ -49,6 +54,9 @@ static void _bt_mark_scankey_required(ScanKey skey);
static bool _bt_check_rowcompare(ScanKey skey,
IndexTuple tuple, TupleDesc tupdesc,
ScanDirection dir, bool *continuescan);
+static void _bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval);
+
/*
@@ -445,6 +453,7 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
{
Relation rel = scan->indexRelation;
Oid elemtype;
+ Oid opfamily;
RegProcedure cmp_proc;
BTSortArrayContext cxt;
int last_non_dup;
@@ -462,6 +471,53 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
if (elemtype == InvalidOid)
elemtype = rel->rd_opcintype[skey->sk_attno - 1];
+ opfamily = rel->rd_opfamily[skey->sk_attno - 1];
+
+ if (scan->numberOfOrderBys <= 0)
+ {
+ cxt.distkey = NULL;
+ cxt.reverse = reverse;
+ }
+ else
+ {
+ /* Init procedures for distance calculation and comparison. */
+ ScanKey distkey = &scan->orderByData[0];
+ ScanKeyData distkey2;
+ Oid disttype = distkey->sk_subtype;
+ Oid distopr;
+ RegProcedure distproc;
+
+ if (!OidIsValid(disttype))
+ disttype = rel->rd_opcintype[skey->sk_attno - 1];
+
+ /* Lookup distance operator in index column's operator family. */
+ distopr = get_opfamily_member(opfamily,
+ elemtype,
+ disttype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(distopr))
+ elog(ERROR, "missing operator (%u,%u) for strategy %d in opfamily %u",
+ elemtype, disttype, BtreeKNNSearchStrategyNumber, opfamily);
+
+ distproc = get_opcode(distopr);
+
+ if (!RegProcedureIsValid(distproc))
+ elog(ERROR, "missing code for operator %u", distopr);
+
+ fmgr_info(distproc, &cxt.distflinfo);
+
+ distkey2 = *distkey;
+ fmgr_info_copy(&distkey2.sk_func, &cxt.distflinfo, CurrentMemoryContext);
+ distkey2.sk_subtype = disttype;
+
+ _bt_get_distance_cmp_proc(&distkey2, opfamily, elemtype,
+ &cxt.distcmpflinfo, NULL, NULL);
+
+ cxt.distkey = distkey;
+ cxt.reverse = false; /* supported only ascending ordering */
+ }
+
/*
* Look up the appropriate comparison function in the opfamily.
*
@@ -470,19 +526,17 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
* non-cross-type support functions for any datatype that it supports at
* all.
*/
- cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
+ cmp_proc = get_opfamily_proc(opfamily,
elemtype,
elemtype,
BTORDER_PROC);
if (!RegProcedureIsValid(cmp_proc))
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
- BTORDER_PROC, elemtype, elemtype,
- rel->rd_opfamily[skey->sk_attno - 1]);
+ BTORDER_PROC, elemtype, elemtype, opfamily);
/* Sort the array elements */
fmgr_info(cmp_proc, &cxt.flinfo);
cxt.collation = skey->sk_collation;
- cxt.reverse = reverse;
qsort_arg((void *) elems, nelems, sizeof(Datum),
_bt_compare_array_elements, (void *) &cxt);
@@ -514,6 +568,23 @@ _bt_compare_array_elements(const void *a, const void *b, void *arg)
BTSortArrayContext *cxt = (BTSortArrayContext *) arg;
int32 compare;
+ if (cxt->distkey)
+ {
+ Datum dista = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ da,
+ cxt->distkey->sk_argument);
+ Datum distb = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ db,
+ cxt->distkey->sk_argument);
+ bool cmp = DatumGetBool(FunctionCall2Coll(&cxt->distcmpflinfo,
+ cxt->collation,
+ dista,
+ distb));
+ return cmp ? -1 : 1;
+ }
+
compare = DatumGetInt32(FunctionCall2Coll(&cxt->flinfo,
cxt->collation,
da, db));
@@ -2075,6 +2146,39 @@ btproperty(Oid index_oid, int attno,
*res = true;
return true;
+ case AMPROP_DISTANCE_ORDERABLE:
+ {
+ Oid opclass,
+ opfamily,
+ opcindtype;
+
+ /* answer only for columns, not AM or whole index */
+ if (attno == 0)
+ return false;
+
+ opclass = get_index_column_opclass(index_oid, attno);
+
+ if (!OidIsValid(opclass))
+ {
+ *res = false; /* non-key attribute */
+ return true;
+ }
+
+ if (!get_opclass_opfamily_and_input_type(opclass,
+ &opfamily, &opcindtype))
+ {
+ *isnull = true;
+ return true;
+ }
+
+ *res = SearchSysCacheExists(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(opcindtype),
+ ObjectIdGetDatum(opcindtype),
+ Int16GetDatum(BtreeKNNSearchStrategyNumber));
+ return true;
+ }
+
default:
return false; /* punt to generic code */
}
@@ -2223,3 +2327,264 @@ _bt_allocate_tuple_workspaces(BTScanState state)
state->currTuples = (char *) palloc(BLCKSZ * 2);
state->markTuples = state->currTuples + BLCKSZ;
}
+
+static bool
+_bt_compare_row_key_with_ordering_key(ScanKey row, ScanKey ord, bool *result)
+{
+ ScanKey subkey = (ScanKey) DatumGetPointer(row->sk_argument);
+ int32 cmpresult;
+
+ Assert(subkey->sk_attno == 1);
+ Assert(subkey->sk_flags & SK_ROW_MEMBER);
+
+ if (subkey->sk_flags & SK_ISNULL)
+ return false;
+
+ /* Perform the test --- three-way comparison not bool operator */
+ cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
+ subkey->sk_collation,
+ ord->sk_argument,
+ subkey->sk_argument));
+
+ if (subkey->sk_flags & SK_BT_DESC)
+ cmpresult = -cmpresult;
+
+ /*
+ * At this point cmpresult indicates the overall result of the row
+ * comparison, and subkey points to the deciding column (or the last
+ * column if the result is "=").
+ */
+ switch (subkey->sk_strategy)
+ {
+ /* EQ and NE cases aren't allowed here */
+ case BTLessStrategyNumber:
+ *result = cmpresult < 0;
+ break;
+ case BTLessEqualStrategyNumber:
+ *result = cmpresult <= 0;
+ break;
+ case BTGreaterEqualStrategyNumber:
+ *result = cmpresult >= 0;
+ break;
+ case BTGreaterStrategyNumber:
+ *result = cmpresult > 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized RowCompareType: %d",
+ (int) subkey->sk_strategy);
+ *result = false; /* keep compiler quiet */
+ }
+
+ return true;
+}
+
+/* _bt_select_knn_search_strategy() -- Determine which KNN scan strategy to use:
+ * bidirectional or unidirectional. We are checking here if the
+ * ordering scankey argument falls into the scan range: if it falls
+ * we must use bidirectional scan, otherwise we use unidirectional.
+ *
+ * Returns BtreeKNNSearchStrategyNumber for bidirectional scan or
+ * strategy number of non-matched scankey for unidirectional.
+ */
+static StrategyNumber
+_bt_select_knn_search_strategy(IndexScanDesc scan, ScanKey ord)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey cond;
+
+ for (cond = so->keyData; cond < so->keyData + so->numberOfKeys; cond++)
+ {
+ bool result;
+
+ if (cond->sk_attno != 1)
+ break; /* only interesting in the first index attribute */
+
+ if (cond->sk_strategy == BTEqualStrategyNumber)
+ /* always use simple unidirectional scan for equals operators */
+ return BTEqualStrategyNumber;
+
+ if (cond->sk_flags & SK_ROW_HEADER)
+ {
+ if (!_bt_compare_row_key_with_ordering_key(cond, ord, &result))
+ return BTEqualStrategyNumber; /* ROW(fist_index_attr, ...) IS NULL */
+ }
+ else
+ {
+ if (!_bt_compare_scankey_args(scan, cond, ord, cond, &result))
+ elog(ERROR, "could not compare ordering key");
+ }
+
+ if (!result)
+ /*
+ * Ordering scankey argument is out of scan range,
+ * use unidirectional scan.
+ */
+ return cond->sk_strategy;
+ }
+
+ return BtreeKNNSearchStrategyNumber; /* use bidirectional scan */
+}
+
+static Oid
+_bt_get_sortfamily_for_opfamily_op(Oid opfamily, Oid lefttype, Oid righttype,
+ StrategyNumber strategy)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid sortfamily;
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(lefttype),
+ ObjectIdGetDatum(righttype),
+ Int16GetDatum(strategy));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ sortfamily = amop_tup->amopsortfamily;
+ ReleaseSysCache(tp);
+
+ return sortfamily;
+}
+
+/*
+ * _bt_get_distance_cmp_proc() -- Init procedure for comparsion of distances
+ * between "leftargtype" and "distkey".
+ */
+static void
+_bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval)
+{
+ RegProcedure opcode;
+ Oid sortfamily;
+ Oid opno;
+ Oid distanceType;
+
+ distanceType = get_func_rettype(distkey->sk_func.fn_oid);
+
+ sortfamily = _bt_get_sortfamily_for_opfamily_op(opfamily, leftargtype,
+ distkey->sk_subtype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(sortfamily))
+ elog(ERROR, "could not find sort family for btree ordering operator");
+
+ opno = get_opfamily_member(sortfamily,
+ distanceType,
+ distanceType,
+ BTLessEqualStrategyNumber);
+
+ if (!OidIsValid(opno))
+ elog(ERROR, "could not find operator for btree distance comparison");
+
+ opcode = get_opcode(opno);
+
+ if (!RegProcedureIsValid(opcode))
+ elog(ERROR,
+ "could not find procedure for btree distance comparison operator");
+
+ fmgr_info(opcode, finfo);
+
+ if (typlen)
+ get_typlenbyval(distanceType, typlen, typbyval);
+}
+
+/*
+ * _bt_init_distance_comparison() -- Init distance typlen/typbyval and its
+ * comparison procedure.
+ */
+static void
+_bt_init_distance_comparison(IndexScanDesc scan, ScanKey ord)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ Relation rel = scan->indexRelation;
+
+ _bt_get_distance_cmp_proc(ord,
+ rel->rd_opfamily[ord->sk_attno - 1],
+ rel->rd_opcintype[ord->sk_attno - 1],
+ &so->distanceCmpProc,
+ &so->distanceTypeLen,
+ &so->distanceTypeByVal);
+
+ if (!so->distanceTypeByVal)
+ {
+ so->state.currDistance = PointerGetDatum(NULL);
+ so->state.markDistance = PointerGetDatum(NULL);
+ }
+}
+
+/*
+ * _bt_process_orderings() -- Process ORDER BY distance scankeys and
+ * select corresponding KNN strategy.
+ *
+ * If bidirectional scan is selected then one scankey is initialized
+ * using bufKeys and placed into startKeys/keysCount, true is returned.
+ *
+ * Otherwise, so->scanDirection is set and false is returned.
+ */
+bool
+_bt_process_orderings(IndexScanDesc scan, ScanKey *startKeys, int *keysCount,
+ ScanKeyData bufKeys[])
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey ord = scan->orderByData;
+
+ if (scan->numberOfOrderBys > 1 || ord->sk_attno != 1)
+ /* it should not happen, see btmatchorderby() */
+ elog(ERROR, "only one btree ordering operator "
+ "for the first index column is supported");
+
+ Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
+
+ switch (_bt_select_knn_search_strategy(scan, ord))
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ /*
+ * Ordering key argument is greater than all values in scan range.
+ * select backward scan direction.
+ */
+ so->scanDirection = BackwardScanDirection;
+ return false;
+
+ case BTEqualStrategyNumber:
+ /* Use default unidirectional scan direction. */
+ return false;
+
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ /*
+ * Ordering key argument is lesser than all values in scan range.
+ * select forward scan direction.
+ */
+ so->scanDirection = ForwardScanDirection;
+ return false;
+
+ case BtreeKNNSearchStrategyNumber:
+ /*
+ * Ordering key argument falls into scan range,
+ * use bidirectional scan.
+ */
+ break;
+ }
+
+ _bt_init_distance_comparison(scan, ord);
+
+ /* Init btree search key with ordering key argument. */
+ ScanKeyEntryInitialize(&bufKeys[0],
+ (scan->indexRelation->rd_indoption[ord->sk_attno - 1] <<
+ SK_BT_INDOPTION_SHIFT) |
+ SK_ORDER_BY |
+ SK_SEARCHNULL /* only for invalid procedure oid, see
+ * assert in ScanKeyEntryInitialize */,
+ ord->sk_attno,
+ BtreeKNNSearchStrategyNumber,
+ ord->sk_subtype,
+ ord->sk_collation,
+ InvalidOid,
+ ord->sk_argument);
+
+ startKeys[(*keysCount)++] = &bufKeys[0];
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index f24091c..be4e843 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -22,9 +22,17 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
+#define BTRequiredOperatorSet \
+ ((1 << BTLessStrategyNumber) | \
+ (1 << BTLessEqualStrategyNumber) | \
+ (1 << BTEqualStrategyNumber) | \
+ (1 << BTGreaterEqualStrategyNumber) | \
+ (1 << BTGreaterStrategyNumber))
+
/*
* Validator for a btree opclass.
@@ -132,10 +140,11 @@ btvalidate(Oid opclassoid)
{
HeapTuple oprtup = &oprlist->members[i]->tuple;
Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ Oid op_rettype;
/* Check that only allowed strategy numbers exist */
if (oprform->amopstrategy < 1 ||
- oprform->amopstrategy > BTMaxStrategyNumber)
+ oprform->amopstrategy > BtreeMaxStrategyNumber)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -146,20 +155,29 @@ btvalidate(Oid opclassoid)
result = false;
}
- /* btree doesn't support ORDER BY operators */
- if (oprform->amoppurpose != AMOP_SEARCH ||
- OidIsValid(oprform->amopsortfamily))
+ /* btree supports ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH)
{
- ereport(INFO,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
- opfamilyname, "btree",
- format_operator(oprform->amopopr))));
- result = false;
+ /* ... and operator result must match the claimed btree opfamily */
+ op_rettype = get_op_rettype(oprform->amopopr);
+ if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
+ {
+ ereport(INFO,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
+ opfamilyname, "btree",
+ format_operator(oprform->amopopr))));
+ result = false;
+ }
+ }
+ else
+ {
+ /* Search operators must always return bool */
+ op_rettype = BOOLOID;
}
/* Check operator signature --- same for all btree strategies */
- if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ if (!check_amop_signature(oprform->amopopr, op_rettype,
oprform->amoplefttype,
oprform->amoprighttype))
{
@@ -214,12 +232,8 @@ btvalidate(Oid opclassoid)
* or support functions for this datatype pair. The only things
* considered optional are the sortsupport and in_range functions.
*/
- if (thisgroup->operatorset !=
- ((1 << BTLessStrategyNumber) |
- (1 << BTLessEqualStrategyNumber) |
- (1 << BTEqualStrategyNumber) |
- (1 << BTGreaterEqualStrategyNumber) |
- (1 << BTGreaterStrategyNumber)))
+ if ((thisgroup->operatorset & BTRequiredOperatorSet) !=
+ BTRequiredOperatorSet)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 2c21eae..24dc877 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -989,6 +989,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
* if we are only trying to build bitmap indexscans, nor if we have to
* assume the scan is unordered.
*/
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
!found_lower_saop_clause &&
has_useful_pathkeys(root, rel));
@@ -999,10 +1003,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
ForwardScanDirection);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
}
- else if (index->ammatchorderby && pathkeys_possibly_useful)
+
+ if (useful_pathkeys == NIL &&
+ index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -1013,12 +1017,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
else
useful_pathkeys = NIL;
}
- else
- {
- useful_pathkeys = NIL;
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
- }
/*
* 3. Check if an index-only scan is possible. If we're not building
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 1bfee58..a5207f1 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -459,6 +459,12 @@ typedef struct BTScanStateData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+
+ /* KNN-search fields: */
+ Datum currDistance; /* current distance */
+ Datum markDistance; /* marked distance */
+ bool currIsNull; /* current item is NULL */
+ bool markIsNull; /* marked item is NULL */
} BTScanStateData, *BTScanState;
typedef struct BTScanOpaqueData
@@ -477,7 +483,17 @@ typedef struct BTScanOpaqueData
BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
MemoryContext arrayContext; /* scan-lifespan context for array data */
- BTScanStateData state;
+ BTScanStateData state; /* main scan state */
+
+ /* KNN-search fields: */
+ BTScanState knnState; /* optional scan state for KNN search */
+ ScanDirection scanDirection; /* selected scan direction for
+ * unidirectional KNN scan */
+ FmgrInfo distanceCmpProc; /* distance comparison procedure */
+ int16 distanceTypeLen; /* distance typlen */
+ bool distanceTypeByVal; /* distance typebyval */
+ bool currRightIsNearest; /* current right item is nearest */
+ bool markRightIsNearest; /* marked right item is nearest */
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -523,11 +539,12 @@ extern bool btcanreturn(Relation index, int attno);
/*
* prototypes for internal functions in nbtree.c
*/
-extern bool _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno);
-extern void _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page);
-extern void _bt_parallel_done(IndexScanDesc scan);
+extern bool _bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno);
+extern void _bt_parallel_release(IndexScanDesc scan, BTScanState state, BlockNumber scan_page);
+extern void _bt_parallel_done(IndexScanDesc scan, BTScanState state);
extern void _bt_parallel_advance_array_keys(IndexScanDesc scan);
+
/*
* prototypes for functions in nbtinsert.c
*/
@@ -608,6 +625,8 @@ extern bool btproperty(Oid index_oid, int attno,
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
extern void _bt_allocate_tuple_workspaces(BTScanState state);
+extern bool _bt_process_orderings(IndexScanDesc scan,
+ ScanKey *startKeys, int *keysCount, ScanKeyData bufKeys[]);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/include/access/stratnum.h b/src/include/access/stratnum.h
index 0db11a1..f44b41a 100644
--- a/src/include/access/stratnum.h
+++ b/src/include/access/stratnum.h
@@ -32,7 +32,10 @@ typedef uint16 StrategyNumber;
#define BTGreaterEqualStrategyNumber 4
#define BTGreaterStrategyNumber 5
-#define BTMaxStrategyNumber 5
+#define BTMaxStrategyNumber 5 /* number of canonical B-tree strategies */
+
+#define BtreeKNNSearchStrategyNumber 6 /* for <-> (distance) */
+#define BtreeMaxStrategyNumber 6 /* number of extended B-tree strategies */
/*
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 6faa9d7..c75ef39 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -347,10 +347,10 @@ ROLLBACK;
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
ERROR: access method "invalid_index_method" does not exist
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 6, must be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 0, must be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 7, must be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 0, must be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -397,11 +397,12 @@ DROP OPERATOR FAMILY alt_opf8 USING btree;
CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
-ERROR: access method "btree" does not support ordering operators
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
ALTER OPERATOR FAMILY alt_opf11 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 84fd900..73e6e206 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -295,8 +295,8 @@ ROLLBACK;
-- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -340,10 +340,12 @@ CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
--
2.7.4
0005-Add-btree-distance-operators-v04.patchtext/x-patch; name=0005-Add-btree-distance-operators-v04.patchDownload
From 1084ec189c0f17fa89c0ff4fd45ec9a2bde593fe Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 30 Nov 2018 14:56:54 +0300
Subject: [PATCH 5/7] Add btree distance operators
---
doc/src/sgml/indices.sgml | 6 +
src/backend/utils/adt/cash.c | 20 +++
src/backend/utils/adt/date.c | 113 ++++++++++++++++
src/backend/utils/adt/float.c | 41 ++++++
src/backend/utils/adt/int.c | 52 ++++++++
src/backend/utils/adt/int8.c | 44 ++++++
src/backend/utils/adt/oid.c | 14 ++
src/backend/utils/adt/timestamp.c | 103 ++++++++++++++
src/include/catalog/pg_amop.dat | 104 +++++++++++++++
src/include/catalog/pg_operator.dat | 108 +++++++++++++++
src/include/catalog/pg_proc.dat | 82 ++++++++++++
src/include/utils/datetime.h | 2 +
src/include/utils/timestamp.h | 4 +-
src/test/regress/expected/amutils.out | 6 +-
src/test/regress/expected/date.out | 61 +++++++++
src/test/regress/expected/float4.out | 20 +++
src/test/regress/expected/float8.out | 21 +++
src/test/regress/expected/int2.out | 33 +++++
src/test/regress/expected/int4.out | 32 +++++
src/test/regress/expected/int8.out | 31 +++++
src/test/regress/expected/interval.out | 15 +++
src/test/regress/expected/money.out | 6 +
src/test/regress/expected/oid.out | 13 ++
src/test/regress/expected/opr_sanity.out | 3 +-
src/test/regress/expected/time.out | 16 +++
src/test/regress/expected/timestamp.out | 211 +++++++++++++++++++++++++++++
src/test/regress/expected/timestamptz.out | 214 ++++++++++++++++++++++++++++++
src/test/regress/sql/date.sql | 5 +
src/test/regress/sql/float4.sql | 3 +
src/test/regress/sql/float8.sql | 3 +
src/test/regress/sql/int2.sql | 10 ++
src/test/regress/sql/int4.sql | 10 ++
src/test/regress/sql/int8.sql | 5 +
src/test/regress/sql/interval.sql | 2 +
src/test/regress/sql/money.sql | 1 +
src/test/regress/sql/oid.sql | 2 +
src/test/regress/sql/time.sql | 3 +
src/test/regress/sql/timestamp.sql | 8 ++
src/test/regress/sql/timestamptz.sql | 8 ++
39 files changed, 1430 insertions(+), 5 deletions(-)
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index caec484..e656a8a 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -183,6 +183,12 @@ SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
</programlisting>
which finds the ten events closest to a given target date. The ability
to do this is again dependent on the particular operator class being used.
+ Built-in B-tree operator classes support distance ordering for data types
+ <type>int2</>, <type>int4</>, <type>int8</>,
+ <type>float4</>, <type>float8</>, <type>numeric</>,
+ <type>timestamp with time zone</>, <type>timestamp without time zone</>,
+ <type>time with time zone</>, <type>time without time zone</>,
+ <type>date</>, <type>interval</>, <type>oid</>, <type>money</>.
</para>
<para>
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index c92e9d5..b6bfbff 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -30,6 +30,7 @@
#include "utils/numeric.h"
#include "utils/pg_locale.h"
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
/*************************************************************************
* Private routines
@@ -1157,3 +1158,22 @@ int8_cash(PG_FUNCTION_ARGS)
PG_RETURN_CASH(result);
}
+
+Datum
+cash_dist(PG_FUNCTION_ARGS)
+{
+ Cash a = PG_GETARG_CASH(0);
+ Cash b = PG_GETARG_CASH(1);
+ Cash r;
+ Cash ra;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("money out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_CASH(ra);
+}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index cb6b5e55..237c3cb 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -562,6 +562,17 @@ date_mii(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(result);
}
+Datum
+date_dist(PG_FUNCTION_ARGS)
+{
+ /* we assume the difference can't overflow */
+ Datum diff = DirectFunctionCall2(date_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+}
+
/*
* Internal routines for promoting date to timestamp and timestamp with
* time zone
@@ -759,6 +770,29 @@ date_cmp_timestamp(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
+ Timestamp dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
date_eq_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
@@ -843,6 +877,30 @@ date_cmp_timestamptz(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
+
+
+Datum
timestamp_eq_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
@@ -927,6 +985,29 @@ timestamp_cmp_date(PG_FUNCTION_ARGS)
}
Datum
+timestamp_dist_date(PG_FUNCTION_ARGS)
+{
+ Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ Timestamp dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
timestamptz_eq_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
@@ -1038,6 +1119,28 @@ in_range_date_interval(PG_FUNCTION_ARGS)
BoolGetDatum(less));
}
+Datum
+timestamptz_dist_date(PG_FUNCTION_ARGS)
+{
+ TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ TimestampTz dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
/* Add an interval to a date, giving a new date.
* Must handle both positive and negative intervals.
@@ -1946,6 +2049,16 @@ time_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+Datum
+time_dist(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(time_mi_time,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
+
/*****************************************************************************
* Time With Time Zone ADT
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index cf9327f..f8c2702 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -3641,6 +3641,47 @@ width_bucket_float8(PG_FUNCTION_ARGS)
PG_RETURN_INT32(result);
}
+Datum
+float4_dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float4 r = float4_mi(a, b);
+
+ PG_RETURN_FLOAT4(Abs(r));
+}
+
+Datum
+float8_dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+
+Datum
+float48_dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+Datum
+float84_dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
/* ========== PRIVATE ROUTINES ========== */
#ifndef HAVE_CBRT
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 8149dc1..9335746 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -1427,3 +1427,55 @@ generate_series_step_int4(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+Datum
+int2_dist(PG_FUNCTION_ARGS)
+{
+ int16 a = PG_GETARG_INT16(0);
+ int16 b = PG_GETARG_INT16(1);
+ int16 r;
+ int16 ra;
+
+ if (pg_sub_s16_overflow(a, b, &r) ||
+ r == PG_INT16_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_INT16(ra);
+}
+
+static int32
+int44_dist(int32 a, int32 b)
+{
+ int32 r;
+
+ if (pg_sub_s32_overflow(a, b, &r) ||
+ r == PG_INT32_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ return Abs(r);
+}
+
+
+Datum
+int4_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int24_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT16(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int42_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT16(1)));
+}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 437d418..8bf8eda 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1373,3 +1373,47 @@ generate_series_step_int8(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+static int64
+int88_dist(int64 a, int64 b)
+{
+ int64 r;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+
+ return Abs(r);
+}
+
+Datum
+int8_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int82_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT16(1)));
+}
+
+Datum
+int84_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT32(1)));
+}
+
+Datum
+int28_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT16(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int48_dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT32(0), PG_GETARG_INT64(1)));
+}
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index b0670e0..536507b 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -469,3 +469,17 @@ oidvectorgt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+
+Datum
+oid_dist(PG_FUNCTION_ARGS)
+{
+ Oid a = PG_GETARG_OID(0);
+ Oid b = PG_GETARG_OID(1);
+ Oid res;
+
+ if (a < b)
+ res = b - a;
+ else
+ res = a - b;
+ PG_RETURN_OID(res);
+}
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index b377c38..b536645 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2672,6 +2672,86 @@ timestamp_mi(PG_FUNCTION_ARGS)
PG_RETURN_INTERVAL_P(result);
}
+Datum
+timestamp_dist(PG_FUNCTION_ARGS)
+{
+ Timestamp a = PG_GETARG_TIMESTAMP(0);
+ Timestamp b = PG_GETARG_TIMESTAMP(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+ p->time = PG_INT64_MAX;
+ PG_RETURN_INTERVAL_P(p);
+ }
+ else
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+Interval *
+timestamp_dist_internal(Timestamp a, Timestamp b)
+{
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ return r;
+ }
+
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ TimestampGetDatum(a),
+ TimestampGetDatum(b)));
+
+ return abs_interval(r);
+}
+
+Datum
+timestamptz_dist(PG_FUNCTION_ARGS)
+{
+ TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
+ TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(a, b));
+}
+
+Datum
+timestamp_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ Timestamp ts1 = PG_GETARG_TIMESTAMP(0);
+ TimestampTz tstz2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz tstz1;
+
+ tstz1 = timestamp2timestamptz(ts1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+Datum
+timestamptz_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ TimestampTz tstz1 = PG_GETARG_TIMESTAMPTZ(0);
+ Timestamp ts2 = PG_GETARG_TIMESTAMP(1);
+ TimestampTz tstz2;
+
+ tstz2 = timestamp2timestamptz(ts2);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+
/*
* interval_justify_interval()
*
@@ -3541,6 +3621,29 @@ interval_avg(PG_FUNCTION_ARGS)
Float8GetDatum((double) N.time));
}
+Interval *
+abs_interval(Interval *a)
+{
+ static Interval zero = {0, 0, 0};
+
+ if (DatumGetBool(DirectFunctionCall2(interval_lt,
+ IntervalPGetDatum(a),
+ IntervalPGetDatum(&zero))))
+ a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
+ IntervalPGetDatum(a)));
+
+ return a;
+}
+
+Datum
+interval_dist(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(interval_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
/* timestamp_age()
* Calculate time difference while retaining year/month fields.
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 075a54c..ded5951 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -30,6 +30,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int24
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -47,6 +51,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int28
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -64,6 +72,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int4
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -81,6 +93,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int42
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -98,6 +114,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int48
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -115,6 +135,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int8
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -132,6 +156,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int82
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -149,6 +177,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int84
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -166,6 +198,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# btree oid_ops
@@ -179,6 +215,10 @@
amopstrategy => '4', amopopr => '>=(oid,oid)', amopmethod => 'btree' },
{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid', amoprighttype => 'oid',
amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'btree' },
+{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(oid,oid)', amopmethod => 'btree',
+ amopsortfamily => 'btree/oid_ops' },
# btree tid_ops
@@ -229,6 +269,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float48
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
@@ -246,6 +290,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# default operators float8
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -263,6 +311,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float84
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -280,6 +332,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# btree char_ops
@@ -389,6 +445,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -406,6 +466,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(date,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -423,6 +487,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(date,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -440,6 +508,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamp,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -457,6 +529,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -474,6 +550,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamp,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -491,6 +571,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -508,6 +592,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'date', amopstrategy => '5',
amopopr => '>(timestamptz,date)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -525,6 +613,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamptz,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree time_ops
@@ -543,6 +635,10 @@
{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(time,time)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree timetz_ops
@@ -579,6 +675,10 @@
{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'btree' },
+{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(interval,interval)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree macaddr
@@ -754,6 +854,10 @@
{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
amoprighttype => 'money', amopstrategy => '5', amopopr => '>(money,money)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
+ amoprighttype => 'money', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(money,money)', amopmethod => 'btree',
+ amopsortfamily => 'btree/money_ops' },
# btree array_ops
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index ce23c2f..a2ae8cb 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -2791,6 +2791,114 @@
oprname => '-', oprleft => 'pg_lsn', oprright => 'pg_lsn',
oprresult => 'numeric', oprcode => 'pg_lsn_mi' },
+# distance operators
+{ oid => '4217', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int2', oprresult => 'int2', oprcom => '<->(int2,int2)',
+ oprcode => 'int2_dist'},
+{ oid => '4218', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int4)',
+ oprcode => 'int4_dist'},
+{ oid => '4219', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int8)',
+ oprcode => 'int8_dist'},
+{ oid => '4220', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'oid',
+ oprright => 'oid', oprresult => 'oid', oprcom => '<->(oid,oid)',
+ oprcode => 'oid_dist'},
+{ oid => '4221', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float4', oprresult => 'float4', oprcom => '<->(float4,float4)',
+ oprcode => 'float4_dist'},
+{ oid => '4222', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float8)',
+ oprcode => 'float8_dist'},
+{ oid => '4223', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'money',
+ oprright => 'money', oprresult => 'money', oprcom => '<->(money,money)',
+ oprcode => 'cash_dist'},
+{ oid => '4224', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'date', oprresult => 'int4', oprcom => '<->(date,date)',
+ oprcode => 'date_dist'},
+{ oid => '4225', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'time',
+ oprright => 'time', oprresult => 'interval', oprcom => '<->(time,time)',
+ oprcode => 'time_dist'},
+{ oid => '4226', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamp)',
+ oprcode => 'timestamp_dist'},
+{ oid => '4227', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamptz)',
+ oprcode => 'timestamptz_dist'},
+{ oid => '4228', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'interval',
+ oprright => 'interval', oprresult => 'interval', oprcom => '<->(interval,interval)',
+ oprcode => 'interval_dist'},
+
+# cross-type distance operators
+{ oid => '4229', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int2)',
+ oprcode => 'int24_dist'},
+{ oid => '4230', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int2', oprresult => 'int4', oprcom => '<->(int2,int4)',
+ oprcode => 'int42_dist'},
+{ oid => '4231', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int2)',
+ oprcode => 'int28_dist'},
+{ oid => '4232', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int2', oprresult => 'int8', oprcom => '<->(int2,int8)',
+ oprcode => 'int82_dist'},
+{ oid => '4233', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int4)',
+ oprcode => 'int48_dist'},
+{ oid => '4234', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int4', oprresult => 'int8', oprcom => '<->(int4,int8)',
+ oprcode => 'int84_dist'},
+{ oid => '4235', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float4)',
+ oprcode => 'float48_dist'},
+{ oid => '4236', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float4', oprresult => 'float8', oprcom => '<->(float4,float8)',
+ oprcode => 'float84_dist'},
+{ oid => '4237', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,date)',
+ oprcode => 'date_dist_timestamp'},
+{ oid => '4238', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamp)',
+ oprcode => 'timestamp_dist_date'},
+{ oid => '4239', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,date)',
+ oprcode => 'date_dist_timestamptz'},
+{ oid => '4240', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamptz)',
+ oprcode => 'timestamptz_dist_date'},
+{ oid => '4241', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamp)',
+ oprcode => 'timestamp_dist_timestamptz'},
+{ oid => '4242', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamptz)',
+ oprcode => 'timestamptz_dist_timestamp'},
+
# enum operators
{ oid => '3516', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anyenum',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 034a41e..ec9ff16 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10048,4 +10048,86 @@
proargnames => '{rootrelid,relid,parentrelid,isleaf,level}',
prosrc => 'pg_partition_tree' },
+# distance functions
+{ oid => '4243',
+ proname => 'int2_dist', prorettype => 'int2',
+ proargtypes => 'int2 int2', prosrc => 'int2_dist' },
+{ oid => '4244',
+ proname => 'int4_dist', prorettype => 'int4',
+ proargtypes => 'int4 int4', prosrc => 'int4_dist' },
+{ oid => '4245',
+ proname => 'int8_dist', prorettype => 'int8',
+ proargtypes => 'int8 int8', prosrc => 'int8_dist' },
+{ oid => '4246',
+ proname => 'oid_dist', prorettype => 'oid',
+ proargtypes => 'oid oid', prosrc => 'oid_dist' },
+{ oid => '4247',
+ proname => 'float4_dist', prorettype => 'float4',
+ proargtypes => 'float4 float4', prosrc => 'float4_dist' },
+{ oid => '4248',
+ proname => 'float8_dist', prorettype => 'float8',
+ proargtypes => 'float8 float8', prosrc => 'float8_dist' },
+{ oid => '4249',
+ proname => 'cash_dist', prorettype => 'money',
+ proargtypes => 'money money', prosrc => 'cash_dist' },
+{ oid => '4250',
+ proname => 'date_dist', prorettype => 'int4',
+ proargtypes => 'date date', prosrc => 'date_dist' },
+{ oid => '4251',
+ proname => 'time_dist', prorettype => 'interval',
+ proargtypes => 'time time', prosrc => 'time_dist' },
+{ oid => '4252',
+ proname => 'timestamp_dist', prorettype => 'interval',
+ proargtypes => 'timestamp timestamp', prosrc => 'timestamp_dist' },
+{ oid => '4253',
+ proname => 'timestamptz_dist', prorettype => 'interval',
+ proargtypes => 'timestamptz timestamptz', prosrc => 'timestamptz_dist' },
+{ oid => '4254',
+ proname => 'interval_dist', prorettype => 'interval',
+ proargtypes => 'interval interval', prosrc => 'interval_dist' },
+
+# cross-type distance functions
+{ oid => '4255',
+ proname => 'int24_dist', prorettype => 'int4',
+ proargtypes => 'int2 int4', prosrc => 'int24_dist' },
+{ oid => '4256',
+ proname => 'int28_dist', prorettype => 'int8',
+ proargtypes => 'int2 int8', prosrc => 'int28_dist' },
+{ oid => '4257',
+ proname => 'int42_dist', prorettype => 'int4',
+ proargtypes => 'int4 int2', prosrc => 'int42_dist' },
+{ oid => '4258',
+ proname => 'int48_dist', prorettype => 'int8',
+ proargtypes => 'int4 int8', prosrc => 'int48_dist' },
+{ oid => '4259',
+ proname => 'int82_dist', prorettype => 'int8',
+ proargtypes => 'int8 int2', prosrc => 'int82_dist' },
+{ oid => '4260',
+ proname => 'int84_dist', prorettype => 'int8',
+ proargtypes => 'int8 int4', prosrc => 'int84_dist' },
+{ oid => '4261',
+ proname => 'float48_dist', prorettype => 'float8',
+ proargtypes => 'float4 float8', prosrc => 'float48_dist' },
+{ oid => '4262',
+ proname => 'float84_dist', prorettype => 'float8',
+ proargtypes => 'float8 float4', prosrc => 'float84_dist' },
+{ oid => '4263',
+ proname => 'date_dist_timestamp', prorettype => 'interval',
+ proargtypes => 'date timestamp', prosrc => 'date_dist_timestamp' },
+{ oid => '4264',
+ proname => 'date_dist_timestamptz', prorettype => 'interval',
+ proargtypes => 'date timestamptz', prosrc => 'date_dist_timestamptz' },
+{ oid => '4265',
+ proname => 'timestamp_dist_date', prorettype => 'interval',
+ proargtypes => 'timestamp date', prosrc => 'timestamp_dist_date' },
+{ oid => '4266',
+ proname => 'timestamp_dist_timestamptz', prorettype => 'interval',
+ proargtypes => 'timestamp timestamptz', prosrc => 'timestamp_dist_timestamptz' },
+{ oid => '4267',
+ proname => 'timestamptz_dist_date', prorettype => 'interval',
+ proargtypes => 'timestamptz date', prosrc => 'timestamptz_dist_date' },
+{ oid => '4268',
+ proname => 'timestamptz_dist_timestamp', prorettype => 'interval',
+ proargtypes => 'timestamptz timestamp', prosrc => 'timestamptz_dist_timestamp' },
+
]
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index de9e9ad..6f8062d 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -338,4 +338,6 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
+extern Interval *abs_interval(Interval *a);
+
#endif /* DATETIME_H */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index 2b3b357..1fa56e5 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -93,9 +93,11 @@ extern Timestamp SetEpochTimestamp(void);
extern void GetEpochTime(struct pg_tm *tm);
extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
+extern Interval *timestamp_dist_internal(Timestamp a, Timestamp b);
-/* timestamp comparison works for timestamptz also */
+/* timestamp comparison and distance works for timestamptz also */
#define timestamptz_cmp_internal(dt1,dt2) timestamp_cmp_internal(dt1, dt2)
+#define timestamptz_dist_internal(dt1,dt2) timestamp_dist_internal(dt1, dt2)
extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday);
diff --git a/src/test/regress/expected/amutils.out b/src/test/regress/expected/amutils.out
index 4570a39..630dc6b 100644
--- a/src/test/regress/expected/amutils.out
+++ b/src/test/regress/expected/amutils.out
@@ -24,7 +24,7 @@ select prop,
nulls_first | | | f
nulls_last | | | t
orderable | | | t
- distance_orderable | | | f
+ distance_orderable | | | t
returnable | | | t
search_array | | | t
search_nulls | | | t
@@ -100,7 +100,7 @@ select prop,
nulls_first | f | f | f | f | f | f | f
nulls_last | t | f | f | f | f | f | f
orderable | t | f | f | f | f | f | f
- distance_orderable | f | f | t | f | t | f | f
+ distance_orderable | t | f | t | f | t | f | f
returnable | t | f | f | t | t | f | f
search_array | t | f | f | f | f | f | f
search_nulls | t | f | t | t | t | f | t
@@ -231,7 +231,7 @@ select col, prop, pg_index_column_has_property(o, col, prop)
1 | desc | f
1 | nulls_first | f
1 | nulls_last | t
- 1 | distance_orderable | f
+ 1 | distance_orderable | t
1 | returnable | t
1 | bogus |
2 | orderable | f
diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out
index 1bcc946..b9b819c 100644
--- a/src/test/regress/expected/date.out
+++ b/src/test/regress/expected/date.out
@@ -1477,3 +1477,64 @@ select make_time(10, 55, 100.1);
ERROR: time field value out of range: 10:55:100.1
select make_time(24, 0, 2.1);
ERROR: time field value out of range: 24:00:2.1
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+----------
+ | 16006
+ | 15941
+ | 1802
+ | 1801
+ | 1800
+ | 1799
+ | 1436
+ | 1435
+ | 1434
+ | 308
+ | 307
+ | 306
+ | 13578
+ | 13944
+ | 14311
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16006 days 1 hour 23 mins 45 secs
+ | @ 15941 days 1 hour 23 mins 45 secs
+ | @ 1802 days 1 hour 23 mins 45 secs
+ | @ 1801 days 1 hour 23 mins 45 secs
+ | @ 1800 days 1 hour 23 mins 45 secs
+ | @ 1799 days 1 hour 23 mins 45 secs
+ | @ 1436 days 1 hour 23 mins 45 secs
+ | @ 1435 days 1 hour 23 mins 45 secs
+ | @ 1434 days 1 hour 23 mins 45 secs
+ | @ 308 days 1 hour 23 mins 45 secs
+ | @ 307 days 1 hour 23 mins 45 secs
+ | @ 306 days 1 hour 23 mins 45 secs
+ | @ 13577 days 22 hours 36 mins 15 secs
+ | @ 13943 days 22 hours 36 mins 15 secs
+ | @ 14310 days 22 hours 36 mins 15 secs
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16005 days 14 hours 23 mins 45 secs
+ | @ 15940 days 14 hours 23 mins 45 secs
+ | @ 1801 days 14 hours 23 mins 45 secs
+ | @ 1800 days 14 hours 23 mins 45 secs
+ | @ 1799 days 14 hours 23 mins 45 secs
+ | @ 1798 days 14 hours 23 mins 45 secs
+ | @ 1435 days 14 hours 23 mins 45 secs
+ | @ 1434 days 14 hours 23 mins 45 secs
+ | @ 1433 days 14 hours 23 mins 45 secs
+ | @ 307 days 14 hours 23 mins 45 secs
+ | @ 306 days 14 hours 23 mins 45 secs
+ | @ 305 days 15 hours 23 mins 45 secs
+ | @ 13578 days 8 hours 36 mins 15 secs
+ | @ 13944 days 8 hours 36 mins 15 secs
+ | @ 14311 days 8 hours 36 mins 15 secs
+(15 rows)
+
diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out
index 2f47e1c..a9dddc4 100644
--- a/src/test/regress/expected/float4.out
+++ b/src/test/regress/expected/float4.out
@@ -257,6 +257,26 @@ SELECT '' AS five, * FROM FLOAT4_TBL;
| -1.23457e-20
(5 rows)
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+--------------+-------------
+ | 0 | 1004.3
+ | -34.84 | 1039.14
+ | -1004.3 | 2008.6
+ | -1.23457e+20 | 1.23457e+20
+ | -1.23457e-20 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+--------------+----------------------
+ | 0 | 1004.3
+ | -34.84 | 1039.14000015259
+ | -1004.3 | 2008.59998779297
+ | -1.23457e+20 | 1.23456789557014e+20
+ | -1.23457e-20 | 1004.3
+(5 rows)
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
int2
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 75c0bf3..abf9ef1 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -404,6 +404,27 @@ SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
| 1.2345678901234e-200 | 2.3112042409018e-67
(5 rows)
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.3
+ | 1004.3 | 0
+ | -34.84 | 1039.14
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.29998779297
+ | 1004.3 | 1.22070312045253e-05
+ | -34.84 | 1039.13998779297
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.29998779297
+(5 rows)
+
SELECT '' AS five, * FROM FLOAT8_TBL;
five | f1
------+----------------------
diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out
index 8c255b9..0edc57e 100644
--- a/src/test/regress/expected/int2.out
+++ b/src/test/regress/expected/int2.out
@@ -242,6 +242,39 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
| -32767 | -16383
(5 rows)
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+ERROR: smallint out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+ four | f1 | x
+------+-------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
-- corner cases
SELECT (-1::int2<<15)::text;
text
diff --git a/src/test/regress/expected/int4.out b/src/test/regress/expected/int4.out
index bda7a8d..3735dbc 100644
--- a/src/test/regress/expected/int4.out
+++ b/src/test/regress/expected/int4.out
@@ -247,6 +247,38 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
| -2147483647 | -1073741823
(5 rows)
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+ERROR: integer out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+ five | f1 | x
+------+-------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+ | -2147483647 | 2147483649
+(5 rows)
+
--
-- more complex expressions
--
diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out
index 35e3b3f..8940e81 100644
--- a/src/test/regress/expected/int8.out
+++ b/src/test/regress/expected/int8.out
@@ -432,6 +432,37 @@ SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 A
4567890123457035 | -4567890123456543 | 1123700970370370094 | 0
(5 rows)
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
SELECT q2, abs(q2) FROM INT8_TBL;
q2 | abs
-------------------+------------------
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index f88f345..cb95adf 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -207,6 +207,21 @@ SELECT '' AS fortyfive, r1.*, r2.*
| 34 years | 6 years
(45 rows)
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+ ten | ?column?
+-----+----------------------------
+ | 2 days 02:59:00
+ | 2 days -02:00:00
+ | 8 days -03:00:00
+ | 34 years -2 days -03:00:00
+ | 3 mons -2 days -03:00:00
+ | 2 days 03:00:14
+ | 1 day 00:56:56
+ | 6 years -2 days -03:00:00
+ | 5 mons -2 days -03:00:00
+ | 5 mons -2 days +09:00:00
+(10 rows)
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out
index ab86595..fb2a489 100644
--- a/src/test/regress/expected/money.out
+++ b/src/test/regress/expected/money.out
@@ -123,6 +123,12 @@ SELECT m / 2::float4 FROM money_data;
$61.50
(1 row)
+SELECT m <-> '$123.45' FROM money_data;
+ ?column?
+----------
+ $0.45
+(1 row)
+
-- All true
SELECT m = '$123.00' FROM money_data;
?column?
diff --git a/src/test/regress/expected/oid.out b/src/test/regress/expected/oid.out
index 1eab9cc..5339a48 100644
--- a/src/test/regress/expected/oid.out
+++ b/src/test/regress/expected/oid.out
@@ -119,4 +119,17 @@ SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
| 99999999
(3 rows)
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+ eight | f1 | ?column?
+-------+------------+------------
+ | 1234 | 1111
+ | 1235 | 1112
+ | 987 | 864
+ | 4294966256 | 4294966133
+ | 99999999 | 99999876
+ | 5 | 118
+ | 10 | 113
+ | 15 | 108
+(8 rows)
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 6072f6b..b951b75 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1801,6 +1801,7 @@ ORDER BY 1, 2, 3;
403 | 5 | *>
403 | 5 | >
403 | 5 | ~>~
+ 403 | 6 | <->
405 | 1 | =
783 | 1 | <<
783 | 1 | @@
@@ -1910,7 +1911,7 @@ ORDER BY 1, 2, 3;
4000 | 26 | >>
4000 | 27 | >>=
4000 | 28 | ^@
-(123 rows)
+(124 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/expected/time.out b/src/test/regress/expected/time.out
index 8e0afe6..ee74faa 100644
--- a/src/test/regress/expected/time.out
+++ b/src/test/regress/expected/time.out
@@ -86,3 +86,19 @@ ERROR: operator is not unique: time without time zone + time without time zone
LINE 1: SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
+ Ten | Distance
+-------------+-------------------------------
+ 00:00:00 | @ 1 hour 23 mins 45 secs
+ 01:00:00 | @ 23 mins 45 secs
+ 02:03:00 | @ 39 mins 15 secs
+ 11:59:00 | @ 10 hours 35 mins 15 secs
+ 12:00:00 | @ 10 hours 36 mins 15 secs
+ 12:01:00 | @ 10 hours 37 mins 15 secs
+ 23:59:00 | @ 22 hours 35 mins 15 secs
+ 23:59:59.99 | @ 22 hours 36 mins 14.99 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+(10 rows)
+
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index 4a2fabd..dcb4205 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -1604,3 +1604,214 @@ SELECT make_timestamp(2014,12,28,6,30,45.887);
Sun Dec 28 06:30:45.887 2014
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 231 days 18 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 10 hours 45 mins 58 secs
+ | @ 324 days 11 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 21 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 5 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 1 hour 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 231 days 16 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 12 hours 9 mins 43 secs
+ | @ 324 days 13 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 324 days 23 hours 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 6 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 14 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 1 hour 9 mins 43 secs
+ | @ 324 days 2 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 12 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(63 rows)
+
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 8a4c719..0a05e37 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -2544,3 +2544,217 @@ select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
Tue Jan 17 16:00:00 2017 PST
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 8 hours
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 7 hours 27 mins 59 secs
+ | @ 231 days 17 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 19 hours 45 mins 58 secs
+ | @ 324 days 21 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 22 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 9 hours 27 mins 59 secs
+ | @ 1303 days 10 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 9 hours 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 8 hours 51 mins 44 secs
+ | @ 231 days 15 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 21 hours 9 mins 43 secs
+ | @ 324 days 23 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 325 days 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 10 hours 51 mins 44 secs
+ | @ 1303 days 11 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 22 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 10 hours 9 mins 43 secs
+ | @ 324 days 12 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 13 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1452 days 23 hours 51 mins 44 secs
+ | @ 1303 days 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(64 rows)
+
diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql
index 22f80f2..24be476 100644
--- a/src/test/regress/sql/date.sql
+++ b/src/test/regress/sql/date.sql
@@ -346,3 +346,8 @@ select make_date(2013, 13, 1);
select make_date(2013, 11, -1);
select make_time(10, 55, 100.1);
select make_time(24, 0, 2.1);
+
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql
index 46a9166..f97f80a 100644
--- a/src/test/regress/sql/float4.sql
+++ b/src/test/regress/sql/float4.sql
@@ -82,6 +82,9 @@ UPDATE FLOAT4_TBL
SELECT '' AS five, * FROM FLOAT4_TBL;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
SELECT '32767.6'::float4::int2;
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 6595fd2..9b50210 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -125,6 +125,9 @@ SELECT ||/ float8 '27' AS three;
SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
SELECT '' AS five, * FROM FLOAT8_TBL;
diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql
index 7dbafb6..16dd5d8 100644
--- a/src/test/regress/sql/int2.sql
+++ b/src/test/regress/sql/int2.sql
@@ -84,6 +84,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+
-- corner cases
SELECT (-1::int2<<15)::text;
SELECT ((-1::int2<<15)+1::int2)::text;
diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql
index f014cb2..cff32946 100644
--- a/src/test/regress/sql/int4.sql
+++ b/src/test/regress/sql/int4.sql
@@ -93,6 +93,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT4_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+
--
-- more complex expressions
--
diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql
index e890452..d7f5bde 100644
--- a/src/test/regress/sql/int8.sql
+++ b/src/test/regress/sql/int8.sql
@@ -89,6 +89,11 @@ SELECT q1 + 42::int2 AS "8plus2", q1 - 42::int2 AS "8minus2", q1 * 42::int2 AS "
-- int2 op int8
SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 AS "2mul8", 246::int2 / q1 AS "2div8" FROM INT8_TBL;
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+
SELECT q2, abs(q2) FROM INT8_TBL;
SELECT min(q1), min(q2) FROM INT8_TBL;
SELECT max(q1), max(q2) FROM INT8_TBL;
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index bc5537d..d51c866 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -59,6 +59,8 @@ SELECT '' AS fortyfive, r1.*, r2.*
WHERE r1.f1 > r2.f1
ORDER BY r1.f1, r2.f1;
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql
index 37b9ecc..8428d59 100644
--- a/src/test/regress/sql/money.sql
+++ b/src/test/regress/sql/money.sql
@@ -25,6 +25,7 @@ SELECT m / 2::float8 FROM money_data;
SELECT m * 2::float4 FROM money_data;
SELECT 2::float4 * m FROM money_data;
SELECT m / 2::float4 FROM money_data;
+SELECT m <-> '$123.45' FROM money_data;
-- All true
SELECT m = '$123.00' FROM money_data;
diff --git a/src/test/regress/sql/oid.sql b/src/test/regress/sql/oid.sql
index 4a09689..9f54f92 100644
--- a/src/test/regress/sql/oid.sql
+++ b/src/test/regress/sql/oid.sql
@@ -40,4 +40,6 @@ SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 >= '1234';
SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/sql/time.sql b/src/test/regress/sql/time.sql
index 99a1562..31f0330 100644
--- a/src/test/regress/sql/time.sql
+++ b/src/test/regress/sql/time.sql
@@ -40,3 +40,6 @@ SELECT f1 AS "Eight" FROM TIME_TBL WHERE f1 >= '00:00';
-- where we do mixed-type arithmetic. - thomas 2000-12-02
SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
+
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index b7957cb..5d023dd 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -230,3 +230,11 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
-- timestamp numeric fields constructor
SELECT make_timestamp(2014,12,28,6,30,45.887);
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index c3bd46c..7f0525d 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -464,3 +464,11 @@ insert into tmptz values ('2017-01-18 00:00+00');
explain (costs off)
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
--
2.7.4
0006-Remove-distance-operators-from-btree_gist-v04.patchtext/x-patch; name=0006-Remove-distance-operators-from-btree_gist-v04.patchDownload
From 34f82a6c4e759af2b43d21af1eadaedfee9e7bd3 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 30 Nov 2018 14:56:54 +0300
Subject: [PATCH 6/7] Remove distance operators from btree_gist
---
contrib/btree_gist/Makefile | 5 +-
contrib/btree_gist/btree_cash.c | 20 -
contrib/btree_gist/btree_date.c | 13 -
contrib/btree_gist/btree_float4.c | 15 -
contrib/btree_gist/btree_float8.c | 15 -
contrib/btree_gist/btree_gist--1.2.sql | 1570 --------------------------
contrib/btree_gist/btree_gist--1.5--1.6.sql | 150 +++
contrib/btree_gist/btree_gist--1.6.sql | 1615 +++++++++++++++++++++++++++
contrib/btree_gist/btree_gist.control | 2 +-
contrib/btree_gist/btree_int2.c | 20 -
contrib/btree_gist/btree_int4.c | 21 -
contrib/btree_gist/btree_int8.c | 21 -
contrib/btree_gist/btree_interval.c | 26 -
contrib/btree_gist/btree_oid.c | 16 -
contrib/btree_gist/btree_time.c | 12 -
contrib/btree_gist/btree_ts.c | 49 -
contrib/btree_gist/btree_utils_num.h | 2 -
17 files changed, 1769 insertions(+), 1803 deletions(-)
delete mode 100644 contrib/btree_gist/btree_gist--1.2.sql
create mode 100644 contrib/btree_gist/btree_gist--1.5--1.6.sql
create mode 100644 contrib/btree_gist/btree_gist--1.6.sql
diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile
index af65120..46ab241 100644
--- a/contrib/btree_gist/Makefile
+++ b/contrib/btree_gist/Makefile
@@ -11,8 +11,9 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
EXTENSION = btree_gist
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
- btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \
- btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql
+ btree_gist--1.1--1.2.sql btree_gist--1.2--1.3.sql \
+ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \
+ btree_gist--1.5--1.6.sql btree_gist--1.6.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c
index 894d0a2..86e319d 100644
--- a/contrib/btree_gist/btree_cash.c
+++ b/contrib/btree_gist/btree_cash.c
@@ -91,26 +91,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(cash_dist);
-Datum
-cash_dist(PG_FUNCTION_ARGS)
-{
- Cash a = PG_GETARG_CASH(0);
- Cash b = PG_GETARG_CASH(1);
- Cash r;
- Cash ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("money out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_CASH(ra);
-}
-
/**************************************************
* Cash ops
**************************************************/
diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c
index 992ce57..05a4909 100644
--- a/contrib/btree_gist/btree_date.c
+++ b/contrib/btree_gist/btree_date.c
@@ -109,19 +109,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(date_dist);
-Datum
-date_dist(PG_FUNCTION_ARGS)
-{
- /* we assume the difference can't overflow */
- Datum diff = DirectFunctionCall2(date_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
-}
-
-
/**************************************************
* date ops
**************************************************/
diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c
index 6b20f44..26fa79b 100644
--- a/contrib/btree_gist/btree_float4.c
+++ b/contrib/btree_gist/btree_float4.c
@@ -89,21 +89,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(float4_dist);
-Datum
-float4_dist(PG_FUNCTION_ARGS)
-{
- float4 a = PG_GETARG_FLOAT4(0);
- float4 b = PG_GETARG_FLOAT4(1);
- float4 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT4(Abs(r));
-}
-
-
/**************************************************
* float4 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c
index ee114cb..63e5597 100644
--- a/contrib/btree_gist/btree_float8.c
+++ b/contrib/btree_gist/btree_float8.c
@@ -96,21 +96,6 @@ static const gbtree_ninfo tinfo =
gbt_float8_dist
};
-
-PG_FUNCTION_INFO_V1(float8_dist);
-Datum
-float8_dist(PG_FUNCTION_ARGS)
-{
- float8 a = PG_GETARG_FLOAT8(0);
- float8 b = PG_GETARG_FLOAT8(1);
- float8 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT8(Abs(r));
-}
-
/**************************************************
* float8 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_gist--1.2.sql b/contrib/btree_gist/btree_gist--1.2.sql
deleted file mode 100644
index 1efe753..0000000
--- a/contrib/btree_gist/btree_gist--1.2.sql
+++ /dev/null
@@ -1,1570 +0,0 @@
-/* contrib/btree_gist/btree_gist--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
-
-CREATE FUNCTION gbtreekey4_in(cstring)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey4_out(gbtreekey4)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey4 (
- INTERNALLENGTH = 4,
- INPUT = gbtreekey4_in,
- OUTPUT = gbtreekey4_out
-);
-
-CREATE FUNCTION gbtreekey8_in(cstring)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey8_out(gbtreekey8)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey8 (
- INTERNALLENGTH = 8,
- INPUT = gbtreekey8_in,
- OUTPUT = gbtreekey8_out
-);
-
-CREATE FUNCTION gbtreekey16_in(cstring)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey16_out(gbtreekey16)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey16 (
- INTERNALLENGTH = 16,
- INPUT = gbtreekey16_in,
- OUTPUT = gbtreekey16_out
-);
-
-CREATE FUNCTION gbtreekey32_in(cstring)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey32_out(gbtreekey32)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey32 (
- INTERNALLENGTH = 32,
- INPUT = gbtreekey32_in,
- OUTPUT = gbtreekey32_out
-);
-
-CREATE FUNCTION gbtreekey_var_in(cstring)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey_var (
- INTERNALLENGTH = VARIABLE,
- INPUT = gbtreekey_var_in,
- OUTPUT = gbtreekey_var_out,
- STORAGE = EXTENDED
-);
-
---distance operators
-
-CREATE FUNCTION cash_dist(money, money)
-RETURNS money
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = money,
- RIGHTARG = money,
- PROCEDURE = cash_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION date_dist(date, date)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = date,
- RIGHTARG = date,
- PROCEDURE = date_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float4_dist(float4, float4)
-RETURNS float4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float4,
- RIGHTARG = float4,
- PROCEDURE = float4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float8_dist(float8, float8)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float8,
- RIGHTARG = float8,
- PROCEDURE = float8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int2_dist(int2, int2)
-RETURNS int2
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int2,
- RIGHTARG = int2,
- PROCEDURE = int2_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int4_dist(int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int4,
- RIGHTARG = int4,
- PROCEDURE = int4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int8_dist(int8, int8)
-RETURNS int8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int8,
- RIGHTARG = int8,
- PROCEDURE = int8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION interval_dist(interval, interval)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = interval,
- RIGHTARG = interval,
- PROCEDURE = interval_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION oid_dist(oid, oid)
-RETURNS oid
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = oid,
- RIGHTARG = oid,
- PROCEDURE = oid_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION time_dist(time, time)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = time,
- RIGHTARG = time,
- PROCEDURE = time_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION ts_dist(timestamp, timestamp)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamp,
- RIGHTARG = timestamp,
- PROCEDURE = ts_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION tstz_dist(timestamptz, timestamptz)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamptz,
- RIGHTARG = timestamptz,
- PROCEDURE = tstz_dist,
- COMMUTATOR = '<->'
-);
-
-
---
---
---
--- oid ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_oid_ops
-DEFAULT FOR TYPE oid USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
- FUNCTION 2 gbt_oid_union (internal, internal),
- FUNCTION 3 gbt_oid_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
- FUNCTION 6 gbt_oid_picksplit (internal, internal),
- FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
--- Add operators that are new in 9.1. We do it like this, leaving them
--- "loose" in the operator family rather than bound into the opclass, because
--- that's the only state that can be reproduced during an upgrade from 9.0.
-ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
- OPERATOR 6 <> (oid, oid) ,
- OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
- FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
- -- Also add support function for index-only-scans, added in 9.5.
- FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
-
-
---
---
---
--- int2 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_union(internal, internal)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int2_ops
-DEFAULT FOR TYPE int2 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
- FUNCTION 2 gbt_int2_union (internal, internal),
- FUNCTION 3 gbt_int2_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int2_picksplit (internal, internal),
- FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
- STORAGE gbtreekey4;
-
-ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
- OPERATOR 6 <> (int2, int2) ,
- OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
- FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
-
---
---
---
--- int4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int4_ops
-DEFAULT FOR TYPE int4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
- FUNCTION 2 gbt_int4_union (internal, internal),
- FUNCTION 3 gbt_int4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int4_picksplit (internal, internal),
- FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
- OPERATOR 6 <> (int4, int4) ,
- OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
- FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
-
-
---
---
---
--- int8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int8_ops
-DEFAULT FOR TYPE int8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
- FUNCTION 2 gbt_int8_union (internal, internal),
- FUNCTION 3 gbt_int8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int8_picksplit (internal, internal),
- FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
- OPERATOR 6 <> (int8, int8) ,
- OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
- FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
-
---
---
---
--- float4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float4_ops
-DEFAULT FOR TYPE float4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
- FUNCTION 2 gbt_float4_union (internal, internal),
- FUNCTION 3 gbt_float4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float4_picksplit (internal, internal),
- FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
- OPERATOR 6 <> (float4, float4) ,
- OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
- FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
-
---
---
---
--- float8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float8_ops
-DEFAULT FOR TYPE float8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
- FUNCTION 2 gbt_float8_union (internal, internal),
- FUNCTION 3 gbt_float8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float8_picksplit (internal, internal),
- FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
- OPERATOR 6 <> (float8, float8) ,
- OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
- FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
-
---
---
---
--- timestamp ops
---
---
---
-
-CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamp_ops
-DEFAULT FOR TYPE timestamp USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_ts_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
- OPERATOR 6 <> (timestamp, timestamp) ,
- OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
- FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamptz_ops
-DEFAULT FOR TYPE timestamptz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_tstz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
- OPERATOR 6 <> (timestamptz, timestamptz) ,
- OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
- FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
-
---
---
---
--- time ops
---
---
---
-
-CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_time_ops
-DEFAULT FOR TYPE time USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_time_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
- OPERATOR 6 <> (time, time) ,
- OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
- FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
-
-
-CREATE OPERATOR CLASS gist_timetz_ops
-DEFAULT FOR TYPE timetz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_timetz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
- OPERATOR 6 <> (timetz, timetz) ;
- -- no 'fetch' function, as the compress function is lossy.
-
-
---
---
---
--- date ops
---
---
---
-
-CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_date_ops
-DEFAULT FOR TYPE date USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
- FUNCTION 2 gbt_date_union (internal, internal),
- FUNCTION 3 gbt_date_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_date_penalty (internal, internal, internal),
- FUNCTION 6 gbt_date_picksplit (internal, internal),
- FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
- OPERATOR 6 <> (date, date) ,
- OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
- FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
-
-
---
---
---
--- interval ops
---
---
---
-
-CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_union(internal, internal)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_interval_ops
-DEFAULT FOR TYPE interval USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
- FUNCTION 2 gbt_intv_union (internal, internal),
- FUNCTION 3 gbt_intv_compress (internal),
- FUNCTION 4 gbt_intv_decompress (internal),
- FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
- FUNCTION 6 gbt_intv_picksplit (internal, internal),
- FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
- STORAGE gbtreekey32;
-
-ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
- OPERATOR 6 <> (interval, interval) ,
- OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
- FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
-
-
---
---
---
--- cash ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cash_ops
-DEFAULT FOR TYPE money USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
- FUNCTION 2 gbt_cash_union (internal, internal),
- FUNCTION 3 gbt_cash_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
- FUNCTION 6 gbt_cash_picksplit (internal, internal),
- FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
- OPERATOR 6 <> (money, money) ,
- OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
- FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
- FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
-
-
---
---
---
--- macaddr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_macaddr_ops
-DEFAULT FOR TYPE macaddr USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
- FUNCTION 2 gbt_macad_union (internal, internal),
- FUNCTION 3 gbt_macad_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
- FUNCTION 6 gbt_macad_picksplit (internal, internal),
- FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
- OPERATOR 6 <> (macaddr, macaddr) ,
- FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
-
-
---
---
---
--- text/ bpchar ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_text_ops
-DEFAULT FOR TYPE text USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_text_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
- OPERATOR 6 <> (text, text) ,
- FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
-
-
----- Create the operator class
-CREATE OPERATOR CLASS gist_bpchar_ops
-DEFAULT FOR TYPE bpchar USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_bpchar_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
- OPERATOR 6 <> (bpchar, bpchar) ,
- FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
-
---
---
--- bytea ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bytea_ops
-DEFAULT FOR TYPE bytea USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
- FUNCTION 2 gbt_bytea_union (internal, internal),
- FUNCTION 3 gbt_bytea_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bytea_picksplit (internal, internal),
- FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
- OPERATOR 6 <> (bytea, bytea) ,
- FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
-
-
---
---
---
--- numeric ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_numeric_ops
-DEFAULT FOR TYPE numeric USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
- FUNCTION 2 gbt_numeric_union (internal, internal),
- FUNCTION 3 gbt_numeric_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
- FUNCTION 6 gbt_numeric_picksplit (internal, internal),
- FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
- OPERATOR 6 <> (numeric, numeric) ,
- FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
-
-
---
---
--- bit ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bit_ops
-DEFAULT FOR TYPE bit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
- OPERATOR 6 <> (bit, bit) ,
- FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
-
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_vbit_ops
-DEFAULT FOR TYPE varbit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
- OPERATOR 6 <> (varbit, varbit) ,
- FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
-
-
---
---
---
--- inet/cidr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_inet_ops
-DEFAULT FOR TYPE inet USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cidr_ops
-DEFAULT FOR TYPE cidr USING gist
-AS
- OPERATOR 1 < (inet, inet) ,
- OPERATOR 2 <= (inet, inet) ,
- OPERATOR 3 = (inet, inet) ,
- OPERATOR 4 >= (inet, inet) ,
- OPERATOR 5 > (inet, inet) ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
diff --git a/contrib/btree_gist/btree_gist--1.5--1.6.sql b/contrib/btree_gist/btree_gist--1.5--1.6.sql
new file mode 100644
index 0000000..fcd10f5
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.5--1.6.sql
@@ -0,0 +1,150 @@
+/* contrib/btree_gist/btree_gist--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.6'" to load this file. \quit
+
+-- update references to distance operators in pg_amop and pg_depend
+
+WITH
+btree_ops AS (
+ SELECT
+ amoplefttype, amoprighttype, amopopr
+ FROM
+ pg_amop
+ JOIN pg_am ON pg_am.oid = amopmethod
+ JOIN pg_opfamily ON pg_opfamily.oid = amopfamily
+ JOIN pg_namespace ON pg_namespace.oid = opfnamespace
+ WHERE
+ nspname = 'pg_catalog'
+ AND opfname IN (
+ 'integer_ops',
+ 'oid_ops',
+ 'money_ops',
+ 'float_ops',
+ 'datetime_ops',
+ 'time_ops',
+ 'interval_ops'
+ )
+ AND amname = 'btree'
+ AND amoppurpose = 'o'
+ AND amopstrategy = 6
+ ),
+gist_ops AS (
+ SELECT
+ pg_amop.oid AS oid, amoplefttype, amoprighttype, amopopr
+ FROM
+ pg_amop
+ JOIN pg_am ON amopmethod = pg_am.oid
+ JOIN pg_opfamily ON amopfamily = pg_opfamily.oid
+ JOIN pg_namespace ON pg_namespace.oid = opfnamespace
+ WHERE
+ nspname = current_schema()
+ AND opfname IN (
+ 'gist_oid_ops',
+ 'gist_int2_ops',
+ 'gist_int4_ops',
+ 'gist_int8_ops',
+ 'gist_float4_ops',
+ 'gist_float8_ops',
+ 'gist_timestamp_ops',
+ 'gist_timestamptz_ops',
+ 'gist_time_ops',
+ 'gist_date_ops',
+ 'gist_interval_ops',
+ 'gist_cash_ops'
+ )
+ AND amname = 'gist'
+ AND amoppurpose = 'o'
+ AND amopstrategy = 15
+ ),
+depend_update_data(gist_amop, gist_amopopr, btree_amopopr) AS (
+ SELECT
+ gist_ops.oid, gist_ops.amopopr, btree_ops.amopopr
+ FROM
+ btree_ops JOIN gist_ops USING (amoplefttype, amoprighttype)
+),
+amop_update_data AS (
+ UPDATE
+ pg_depend
+ SET
+ refobjid = btree_amopopr
+ FROM
+ depend_update_data
+ WHERE
+ objid = gist_amop AND refobjid = gist_amopopr
+ RETURNING
+ depend_update_data.*
+)
+UPDATE
+ pg_amop
+SET
+ amopopr = btree_amopopr
+FROM
+ amop_update_data
+WHERE
+ pg_amop.oid = gist_amop;
+
+-- disable implicit pg_catalog search
+
+DO
+$$
+BEGIN
+ EXECUTE 'SET LOCAL search_path TO ' || current_schema() || ', pg_catalog';
+END
+$$;
+
+-- drop distance operators
+
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int2, int2);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int4, int4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int8, int8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float4, float4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float8, float8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (oid, oid);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (money, money);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (date, date);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (time, time);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (interval, interval);
+
+DROP OPERATOR <-> (int2, int2);
+DROP OPERATOR <-> (int4, int4);
+DROP OPERATOR <-> (int8, int8);
+DROP OPERATOR <-> (float4, float4);
+DROP OPERATOR <-> (float8, float8);
+DROP OPERATOR <-> (oid, oid);
+DROP OPERATOR <-> (money, money);
+DROP OPERATOR <-> (date, date);
+DROP OPERATOR <-> (time, time);
+DROP OPERATOR <-> (timestamp, timestamp);
+DROP OPERATOR <-> (timestamptz, timestamptz);
+DROP OPERATOR <-> (interval, interval);
+
+-- drop distance functions
+
+ALTER EXTENSION btree_gist DROP FUNCTION int2_dist(int2, int2);
+ALTER EXTENSION btree_gist DROP FUNCTION int4_dist(int4, int4);
+ALTER EXTENSION btree_gist DROP FUNCTION int8_dist(int8, int8);
+ALTER EXTENSION btree_gist DROP FUNCTION float4_dist(float4, float4);
+ALTER EXTENSION btree_gist DROP FUNCTION float8_dist(float8, float8);
+ALTER EXTENSION btree_gist DROP FUNCTION oid_dist(oid, oid);
+ALTER EXTENSION btree_gist DROP FUNCTION cash_dist(money, money);
+ALTER EXTENSION btree_gist DROP FUNCTION date_dist(date, date);
+ALTER EXTENSION btree_gist DROP FUNCTION time_dist(time, time);
+ALTER EXTENSION btree_gist DROP FUNCTION ts_dist(timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP FUNCTION interval_dist(interval, interval);
+
+DROP FUNCTION int2_dist(int2, int2);
+DROP FUNCTION int4_dist(int4, int4);
+DROP FUNCTION int8_dist(int8, int8);
+DROP FUNCTION float4_dist(float4, float4);
+DROP FUNCTION float8_dist(float8, float8);
+DROP FUNCTION oid_dist(oid, oid);
+DROP FUNCTION cash_dist(money, money);
+DROP FUNCTION date_dist(date, date);
+DROP FUNCTION time_dist(time, time);
+DROP FUNCTION ts_dist(timestamp, timestamp);
+DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+DROP FUNCTION interval_dist(interval, interval);
diff --git a/contrib/btree_gist/btree_gist--1.6.sql b/contrib/btree_gist/btree_gist--1.6.sql
new file mode 100644
index 0000000..8ff8eb5
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.6.sql
@@ -0,0 +1,1615 @@
+/* contrib/btree_gist/btree_gist--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
+
+CREATE FUNCTION gbtreekey4_in(cstring)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey4_out(gbtreekey4)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey4 (
+ INTERNALLENGTH = 4,
+ INPUT = gbtreekey4_in,
+ OUTPUT = gbtreekey4_out
+);
+
+CREATE FUNCTION gbtreekey8_in(cstring)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey8_out(gbtreekey8)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey8 (
+ INTERNALLENGTH = 8,
+ INPUT = gbtreekey8_in,
+ OUTPUT = gbtreekey8_out
+);
+
+CREATE FUNCTION gbtreekey16_in(cstring)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey16_out(gbtreekey16)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey16 (
+ INTERNALLENGTH = 16,
+ INPUT = gbtreekey16_in,
+ OUTPUT = gbtreekey16_out
+);
+
+CREATE FUNCTION gbtreekey32_in(cstring)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey32_out(gbtreekey32)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey32 (
+ INTERNALLENGTH = 32,
+ INPUT = gbtreekey32_in,
+ OUTPUT = gbtreekey32_out
+);
+
+CREATE FUNCTION gbtreekey_var_in(cstring)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey_var (
+ INTERNALLENGTH = VARIABLE,
+ INPUT = gbtreekey_var_in,
+ OUTPUT = gbtreekey_var_out,
+ STORAGE = EXTENDED
+);
+
+
+--
+--
+--
+-- oid ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_oid_ops
+DEFAULT FOR TYPE oid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
+ FUNCTION 2 gbt_oid_union (internal, internal),
+ FUNCTION 3 gbt_oid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_oid_picksplit (internal, internal),
+ FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+-- Add operators that are new in 9.1. We do it like this, leaving them
+-- "loose" in the operator family rather than bound into the opclass, because
+-- that's the only state that can be reproduced during an upgrade from 9.0.
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
+ OPERATOR 6 <> (oid, oid) ,
+ OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
+ FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
+ -- Also add support function for index-only-scans, added in 9.5.
+ FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
+
+
+--
+--
+--
+-- int2 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_union(internal, internal)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int2_ops
+DEFAULT FOR TYPE int2 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
+ FUNCTION 2 gbt_int2_union (internal, internal),
+ FUNCTION 3 gbt_int2_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int2_picksplit (internal, internal),
+ FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
+ STORAGE gbtreekey4;
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
+ OPERATOR 6 <> (int2, int2) ,
+ OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
+ FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
+
+--
+--
+--
+-- int4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int4_ops
+DEFAULT FOR TYPE int4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
+ FUNCTION 2 gbt_int4_union (internal, internal),
+ FUNCTION 3 gbt_int4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int4_picksplit (internal, internal),
+ FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
+ OPERATOR 6 <> (int4, int4) ,
+ OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
+ FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
+
+
+--
+--
+--
+-- int8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int8_ops
+DEFAULT FOR TYPE int8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
+ FUNCTION 2 gbt_int8_union (internal, internal),
+ FUNCTION 3 gbt_int8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int8_picksplit (internal, internal),
+ FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
+ OPERATOR 6 <> (int8, int8) ,
+ OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
+ FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
+
+--
+--
+--
+-- float4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float4_ops
+DEFAULT FOR TYPE float4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
+ FUNCTION 2 gbt_float4_union (internal, internal),
+ FUNCTION 3 gbt_float4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float4_picksplit (internal, internal),
+ FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
+ OPERATOR 6 <> (float4, float4) ,
+ OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
+ FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
+
+--
+--
+--
+-- float8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float8_ops
+DEFAULT FOR TYPE float8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
+ FUNCTION 2 gbt_float8_union (internal, internal),
+ FUNCTION 3 gbt_float8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float8_picksplit (internal, internal),
+ FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
+ OPERATOR 6 <> (float8, float8) ,
+ OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
+ FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
+
+--
+--
+--
+-- timestamp ops
+--
+--
+--
+
+CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamp_ops
+DEFAULT FOR TYPE timestamp USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_ts_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
+ OPERATOR 6 <> (timestamp, timestamp) ,
+ OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
+ FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamptz_ops
+DEFAULT FOR TYPE timestamptz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_tstz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
+ OPERATOR 6 <> (timestamptz, timestamptz) ,
+ OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
+ FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
+
+--
+--
+--
+-- time ops
+--
+--
+--
+
+CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_time_ops
+DEFAULT FOR TYPE time USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_time_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
+ OPERATOR 6 <> (time, time) ,
+ OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
+ FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
+
+
+CREATE OPERATOR CLASS gist_timetz_ops
+DEFAULT FOR TYPE timetz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_timetz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
+ OPERATOR 6 <> (timetz, timetz) ;
+ -- no 'fetch' function, as the compress function is lossy.
+
+
+--
+--
+--
+-- date ops
+--
+--
+--
+
+CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_date_ops
+DEFAULT FOR TYPE date USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
+ FUNCTION 2 gbt_date_union (internal, internal),
+ FUNCTION 3 gbt_date_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_date_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_date_picksplit (internal, internal),
+ FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
+ OPERATOR 6 <> (date, date) ,
+ OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
+ FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
+
+
+--
+--
+--
+-- interval ops
+--
+--
+--
+
+CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_interval_ops
+DEFAULT FOR TYPE interval USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
+ FUNCTION 2 gbt_intv_union (internal, internal),
+ FUNCTION 3 gbt_intv_compress (internal),
+ FUNCTION 4 gbt_intv_decompress (internal),
+ FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_intv_picksplit (internal, internal),
+ FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
+ OPERATOR 6 <> (interval, interval) ,
+ OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
+ FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
+
+
+--
+--
+--
+-- cash ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cash_ops
+DEFAULT FOR TYPE money USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
+ FUNCTION 2 gbt_cash_union (internal, internal),
+ FUNCTION 3 gbt_cash_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_cash_picksplit (internal, internal),
+ FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
+ OPERATOR 6 <> (money, money) ,
+ OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
+ FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
+ FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
+
+
+--
+--
+--
+-- macaddr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr_ops
+DEFAULT FOR TYPE macaddr USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
+ FUNCTION 2 gbt_macad_union (internal, internal),
+ FUNCTION 3 gbt_macad_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
+ OPERATOR 6 <> (macaddr, macaddr) ,
+ FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
+
+
+--
+--
+--
+-- text/ bpchar ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_text_ops
+DEFAULT FOR TYPE text USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_text_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
+ OPERATOR 6 <> (text, text) ,
+ FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
+
+
+---- Create the operator class
+CREATE OPERATOR CLASS gist_bpchar_ops
+DEFAULT FOR TYPE bpchar USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_bpchar_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
+ OPERATOR 6 <> (bpchar, bpchar) ,
+ FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
+
+--
+--
+-- bytea ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bytea_ops
+DEFAULT FOR TYPE bytea USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
+ FUNCTION 2 gbt_bytea_union (internal, internal),
+ FUNCTION 3 gbt_bytea_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bytea_picksplit (internal, internal),
+ FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
+ OPERATOR 6 <> (bytea, bytea) ,
+ FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- numeric ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_numeric_ops
+DEFAULT FOR TYPE numeric USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
+ FUNCTION 2 gbt_numeric_union (internal, internal),
+ FUNCTION 3 gbt_numeric_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_numeric_picksplit (internal, internal),
+ FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
+ OPERATOR 6 <> (numeric, numeric) ,
+ FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
+
+
+--
+--
+-- bit ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bit_ops
+DEFAULT FOR TYPE bit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
+ OPERATOR 6 <> (bit, bit) ,
+ FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
+
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_vbit_ops
+DEFAULT FOR TYPE varbit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
+ OPERATOR 6 <> (varbit, varbit) ,
+ FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- inet/cidr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_inet_ops
+DEFAULT FOR TYPE inet USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cidr_ops
+DEFAULT FOR TYPE cidr USING gist
+AS
+ OPERATOR 1 < (inet, inet) ,
+ OPERATOR 2 <= (inet, inet) ,
+ OPERATOR 3 = (inet, inet) ,
+ OPERATOR 4 >= (inet, inet) ,
+ OPERATOR 5 > (inet, inet) ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+--
+--
+--
+-- uuid ops
+--
+--
+---- define the GiST support methods
+CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_uuid_ops
+DEFAULT FOR TYPE uuid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal),
+ FUNCTION 2 gbt_uuid_union (internal, internal),
+ FUNCTION 3 gbt_uuid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_uuid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_uuid_picksplit (internal, internal),
+ FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+-- These are "loose" in the opfamily for consistency with the rest of btree_gist
+ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
+ OPERATOR 6 <> (uuid, uuid) ,
+ FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ;
+
+
+-- Add support for indexing macaddr8 columns
+
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad8_consistent(internal,macaddr8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr8_ops
+DEFAULT FOR TYPE macaddr8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad8_consistent (internal, macaddr8, int2, oid, internal),
+ FUNCTION 2 gbt_macad8_union (internal, internal),
+ FUNCTION 3 gbt_macad8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad8_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD
+ OPERATOR 6 <> (macaddr8, macaddr8) ,
+ FUNCTION 9 (macaddr8, macaddr8) gbt_macad8_fetch (internal);
+
+--
+--
+--
+-- enum ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_enum_consistent(internal,anyenum,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_enum_ops
+DEFAULT FOR TYPE anyenum USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_enum_consistent (internal, anyenum, int2, oid, internal),
+ FUNCTION 2 gbt_enum_union (internal, internal),
+ FUNCTION 3 gbt_enum_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_enum_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_enum_picksplit (internal, internal),
+ FUNCTION 7 gbt_enum_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD
+ OPERATOR 6 <> (anyenum, anyenum) ,
+ FUNCTION 9 (anyenum, anyenum) gbt_enum_fetch (internal) ;
diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control
index 81c8509..9ced3bc 100644
--- a/contrib/btree_gist/btree_gist.control
+++ b/contrib/btree_gist/btree_gist.control
@@ -1,5 +1,5 @@
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
-default_version = '1.5'
+default_version = '1.6'
module_pathname = '$libdir/btree_gist'
relocatable = true
diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c
index 7674e2d..3c402c0 100644
--- a/contrib/btree_gist/btree_int2.c
+++ b/contrib/btree_gist/btree_int2.c
@@ -90,26 +90,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int2_dist);
-Datum
-int2_dist(PG_FUNCTION_ARGS)
-{
- int16 a = PG_GETARG_INT16(0);
- int16 b = PG_GETARG_INT16(1);
- int16 r;
- int16 ra;
-
- if (pg_sub_s16_overflow(a, b, &r) ||
- r == PG_INT16_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("smallint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT16(ra);
-}
-
/**************************************************
* int16 ops
diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c
index 80005ab..a4f1968 100644
--- a/contrib/btree_gist/btree_int4.c
+++ b/contrib/btree_gist/btree_int4.c
@@ -91,27 +91,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int4_dist);
-Datum
-int4_dist(PG_FUNCTION_ARGS)
-{
- int32 a = PG_GETARG_INT32(0);
- int32 b = PG_GETARG_INT32(1);
- int32 r;
- int32 ra;
-
- if (pg_sub_s32_overflow(a, b, &r) ||
- r == PG_INT32_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT32(ra);
-}
-
-
/**************************************************
* int32 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c
index b0fd3e1..efec64c 100644
--- a/contrib/btree_gist/btree_int8.c
+++ b/contrib/btree_gist/btree_int8.c
@@ -91,27 +91,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(int8_dist);
-Datum
-int8_dist(PG_FUNCTION_ARGS)
-{
- int64 a = PG_GETARG_INT64(0);
- int64 b = PG_GETARG_INT64(1);
- int64 r;
- int64 ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT64(ra);
-}
-
-
/**************************************************
* int64 ops
**************************************************/
diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c
index 3a527a7..b244fa4 100644
--- a/contrib/btree_gist/btree_interval.c
+++ b/contrib/btree_gist/btree_interval.c
@@ -110,32 +110,6 @@ static const gbtree_ninfo tinfo =
};
-Interval *
-abs_interval(Interval *a)
-{
- static Interval zero = {0, 0, 0};
-
- if (DatumGetBool(DirectFunctionCall2(interval_lt,
- IntervalPGetDatum(a),
- IntervalPGetDatum(&zero))))
- a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
- IntervalPGetDatum(a)));
-
- return a;
-}
-
-PG_FUNCTION_INFO_V1(interval_dist);
-Datum
-interval_dist(PG_FUNCTION_ARGS)
-{
- Datum diff = DirectFunctionCall2(interval_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
-}
-
-
/**************************************************
* interval ops
**************************************************/
diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c
index 00e7019..3a0bd73 100644
--- a/contrib/btree_gist/btree_oid.c
+++ b/contrib/btree_gist/btree_oid.c
@@ -96,22 +96,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(oid_dist);
-Datum
-oid_dist(PG_FUNCTION_ARGS)
-{
- Oid a = PG_GETARG_OID(0);
- Oid b = PG_GETARG_OID(1);
- Oid res;
-
- if (a < b)
- res = b - a;
- else
- res = a - b;
- PG_RETURN_OID(res);
-}
-
-
/**************************************************
* Oid ops
**************************************************/
diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c
index 90cf655..b6e7e4a 100644
--- a/contrib/btree_gist/btree_time.c
+++ b/contrib/btree_gist/btree_time.c
@@ -137,18 +137,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(time_dist);
-Datum
-time_dist(PG_FUNCTION_ARGS)
-{
- Datum diff = DirectFunctionCall2(time_mi_time,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
-}
-
-
/**************************************************
* time ops
**************************************************/
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index 49d1849..b0271a6 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -142,55 +142,6 @@ static const gbtree_ninfo tinfo =
};
-PG_FUNCTION_INFO_V1(ts_dist);
-Datum
-ts_dist(PG_FUNCTION_ARGS)
-{
- Timestamp a = PG_GETARG_TIMESTAMP(0);
- Timestamp b = PG_GETARG_TIMESTAMP(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
- else
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
-}
-
-PG_FUNCTION_INFO_V1(tstz_dist);
-Datum
-tstz_dist(PG_FUNCTION_ARGS)
-{
- TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
- TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
-
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
-}
-
-
/**************************************************
* timestamp ops
**************************************************/
diff --git a/contrib/btree_gist/btree_utils_num.h b/contrib/btree_gist/btree_utils_num.h
index d7945f8..794d92b 100644
--- a/contrib/btree_gist/btree_utils_num.h
+++ b/contrib/btree_gist/btree_utils_num.h
@@ -107,8 +107,6 @@ do { \
} while(0)
-extern Interval *abs_interval(Interval *a);
-
extern bool gbt_num_consistent(const GBT_NUMKEY_R *key, const void *query,
const StrategyNumber *strategy, bool is_leaf,
const gbtree_ninfo *tinfo, FmgrInfo *flinfo);
--
2.7.4
0007-Add-regression-tests-for-kNN-btree-v04.patchtext/x-patch; name=0007-Add-regression-tests-for-kNN-btree-v04.patchDownload
From 7255d3ff7753d97337098a0967dcb4ffe3886864 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 30 Nov 2018 14:56:54 +0300
Subject: [PATCH 7/7] Add regression tests for kNN btree
---
src/test/regress/expected/btree_index.out | 779 ++++++++++++++++++++++++++++++
src/test/regress/sql/btree_index.sql | 232 +++++++++
2 files changed, 1011 insertions(+)
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0bd48dc..a8ed9da 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -179,3 +179,782 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+---
+--- Test B-tree distance ordering
+---
+SET enable_bitmapscan = OFF;
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+ QUERY PLAN
+------------------------------
+ Sort
+ Sort Key: ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+ QUERY PLAN
+------------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((random <-> 1))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)))
+ Order By: (random <-> 4000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)) AND (random = ANY ('{1809552,1919087,2321799,2648497,3000193,3013326,4157193,4488889,5257716,5593978,NULL}'::integer[])))
+ Order By: (random <-> 3000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ seqno | random
+-------+---------
+ 5380 | 3000193
+ 6262 | 3013326
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 6448 | 4157193
+ 210 | 1809552
+ 4408 | 4488889
+ 6320 | 5257716
+ 1836 | 5593978
+(10 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+-- test parallel KNN scan
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+RESET enable_indexscan;
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Gather
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i > 8000000)
+(4 rows)
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+-----------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t2.n = t1.n)
+ Join Filter: (t1.i <> t2.i)
+ CTE bt_knn_test1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Order By: (i <-> 4000003)
+ -> Seq Scan on bt_knn_test2 t2
+ -> Hash
+ -> CTE Scan on bt_knn_test1 t1
+(12 rows)
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+-----------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t2.n = t1.n)
+ Join Filter: (t1.i <> t2.i)
+ CTE t1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i = ANY ('{3,4,7,8,2}'::integer[]))
+ Order By: (i <-> 4)
+ CTE t2
+ -> Nested Loop
+ -> Function Scan on generate_series i
+ -> Function Scan on generate_series j
+ -> CTE Scan on t2
+ -> Hash
+ -> CTE Scan on t1
+(17 rows)
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+ROLLBACK;
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ QUERY PLAN
+-----------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000))
+ Order By: (thousand <-> 998)
+(3 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 1 | 9001
+ 1 | 8001
+ 1 | 7001
+ 1 | 6001
+ 1 | 5001
+ 1 | 4001
+ 1 | 3001
+ 1 | 2001
+ 1 | 1001
+ 1 | 1
+ 0 | 9000
+ 0 | 8000
+ 0 | 7000
+ 0 | 6000
+ 0 | 5000
+ 0 | 4000
+ 0 | 3000
+ 0 | 2000
+ 0 | 1000
+ 0 | 0
+ | 1
+ | 2
+ | 3
+(33 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: ((thousand > 100) AND (thousand < 800) AND (thousand = ANY ('{0,123,234,345,456,678,901,NULL}'::smallint[])))
+ Order By: (thousand <-> '300'::bigint)
+(3 rows)
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ thousand | tenthous
+----------+----------
+ 345 | 345
+ 345 | 1345
+ 345 | 2345
+ 345 | 3345
+ 345 | 4345
+ 345 | 5345
+ 345 | 6345
+ 345 | 7345
+ 345 | 8345
+ 345 | 9345
+ 234 | 234
+ 234 | 1234
+ 234 | 2234
+ 234 | 3234
+ 234 | 4234
+ 234 | 5234
+ 234 | 6234
+ 234 | 7234
+ 234 | 8234
+ 234 | 9234
+ 456 | 456
+ 456 | 1456
+ 456 | 2456
+ 456 | 3456
+ 456 | 4456
+ 456 | 5456
+ 456 | 6456
+ 456 | 7456
+ 456 | 8456
+ 456 | 9456
+ 123 | 123
+ 123 | 1123
+ 123 | 2123
+ 123 | 3123
+ 123 | 4123
+ 123 | 5123
+ 123 | 6123
+ 123 | 7123
+ 123 | 8123
+ 123 | 9123
+ 678 | 678
+ 678 | 1678
+ 678 | 2678
+ 678 | 3678
+ 678 | 4678
+ 678 | 5678
+ 678 | 6678
+ 678 | 7678
+ 678 | 8678
+ 678 | 9678
+(50 rows)
+
+DROP INDEX tenk3_idx;
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 1 | 1
+ 1 | 1001
+ 1 | 2001
+ 1 | 3001
+ 1 | 4001
+ 1 | 5001
+ 1 | 6001
+ 1 | 7001
+ 1 | 8001
+ 1 | 9001
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 0 | 0
+ 0 | 1000
+ 0 | 2000
+ 0 | 3000
+ 0 | 4000
+ 0 | 5000
+ 0 | 6000
+ 0 | 7000
+ 0 | 8000
+ 0 | 9000
+ | 3
+ | 2
+ | 1
+(33 rows)
+
+DROP INDEX tenk3_idx;
+DROP TABLE tenk3;
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+-------------------
+ Wed May 03 00:00:00 2017 | @ 2 days
+ Wed May 03 01:00:00 2017 | @ 2 days 1 hour
+ Wed May 03 02:00:00 2017 | @ 2 days 2 hours
+ Wed May 03 03:00:00 2017 | @ 2 days 3 hours
+ Wed May 03 04:00:00 2017 | @ 2 days 4 hours
+ Wed May 03 05:00:00 2017 | @ 2 days 5 hours
+ Wed May 03 06:00:00 2017 | @ 2 days 6 hours
+ Wed May 03 07:00:00 2017 | @ 2 days 7 hours
+ Wed May 03 08:00:00 2017 | @ 2 days 8 hours
+ Wed May 03 09:00:00 2017 | @ 2 days 9 hours
+ Wed May 03 10:00:00 2017 | @ 2 days 10 hours
+ Wed May 03 11:00:00 2017 | @ 2 days 11 hours
+ Wed May 03 12:00:00 2017 | @ 2 days 12 hours
+ Wed May 03 13:00:00 2017 | @ 2 days 13 hours
+ Wed May 03 14:00:00 2017 | @ 2 days 14 hours
+ Wed May 03 15:00:00 2017 | @ 2 days 15 hours
+ Wed May 03 16:00:00 2017 | @ 2 days 16 hours
+ Wed May 03 17:00:00 2017 | @ 2 days 17 hours
+ Wed May 03 18:00:00 2017 | @ 2 days 18 hours
+ Wed May 03 19:00:00 2017 | @ 2 days 19 hours
+(20 rows)
+
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+------------
+ Mon Jan 01 00:00:00 2018 | @ 0
+ Mon Jan 01 01:00:00 2018 | @ 1 hour
+ Sun Dec 31 23:00:00 2017 | @ 1 hour
+ Mon Jan 01 02:00:00 2018 | @ 2 hours
+ Sun Dec 31 22:00:00 2017 | @ 2 hours
+ Mon Jan 01 03:00:00 2018 | @ 3 hours
+ Sun Dec 31 21:00:00 2017 | @ 3 hours
+ Mon Jan 01 04:00:00 2018 | @ 4 hours
+ Sun Dec 31 20:00:00 2017 | @ 4 hours
+ Mon Jan 01 05:00:00 2018 | @ 5 hours
+ Sun Dec 31 19:00:00 2017 | @ 5 hours
+ Mon Jan 01 06:00:00 2018 | @ 6 hours
+ Sun Dec 31 18:00:00 2017 | @ 6 hours
+ Mon Jan 01 07:00:00 2018 | @ 7 hours
+ Sun Dec 31 17:00:00 2017 | @ 7 hours
+ Mon Jan 01 08:00:00 2018 | @ 8 hours
+ Sun Dec 31 16:00:00 2017 | @ 8 hours
+ Mon Jan 01 09:00:00 2018 | @ 9 hours
+ Sun Dec 31 15:00:00 2017 | @ 9 hours
+ Mon Jan 01 10:00:00 2018 | @ 10 hours
+(20 rows)
+
+DROP TABLE knn_btree_ts;
+RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 21171f7..307f2f5 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -111,3 +111,235 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
-- Simple ALTER INDEX
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+---
+--- Test B-tree distance ordering
+---
+
+SET enable_bitmapscan = OFF;
+
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+-- test parallel KNN scan
+
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+
+RESET enable_indexscan;
+
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+
+ROLLBACK;
+
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+
+
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+DROP INDEX tenk3_idx;
+
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+DROP TABLE tenk3;
+
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+
+DROP TABLE knn_btree_ts;
+
+RESET enable_bitmapscan;
--
2.7.4
Hi!
On Fri, Nov 30, 2018 at 3:02 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
On 29.11.2018 18:24, Dmitry Dolgov wrote:
On Wed, Sep 26, 2018 at 5:41 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
Attached 3rd version of the patches rebased onto the current master.
Changes from the previous version:
- Added support of INCLUDE columns to get_index_column_opclass() (1st patch).
- Added parallel kNN scan support.
- amcanorderbyop() was transformed into ammatchorderby() which takes a List of
PathKeys and checks each of them with new function match_orderbyop_pathkey()
extracted from match_pathkeys_to_index(). I think that this design can be
used in the future to support a mix of ordinary and order-by-op PathKeys,
but I am not sure.Hi,
Unfortunately, the patch has some conflicts, could you rebase it? In the
meantime I'll move it to the next CF, hoping to have more reviewers for this
item.Attached 4th version of the patches rebased onto the current master.
I think this patchset in general has a good shape. After some rounds
of review, it might be committed during January commitfest.
For now, I have following notes.
* 0002-Introduce-ammatchorderby-function-v04.patch
I think match_orderbyop_pathkey() and match_orderbyop_pathkeys()
deserve some high-level commends describing what these functions are
expected to do.
* 0004-Add-kNN-support-to-btree-v04.patch
+ <para>
+ FIXME!!!
+ To implement the distance ordered (nearest-neighbor) search, we only need
+ to define a distance operator (usually it called <->) with a
correpsonding
+ operator family for distance comparison in the index's operator class.
+ These operators must satisfy the following assumptions for all non-null
+ values A,B,C of the datatype:
+
+ A <-> B = B <-> A symmetric law
+ if A = B, then A <-> C = B <-> C distance equivalence
+ if (A <= B and B <= C) or (A >= B and B >= C),
+ then A <-> B <= A <-> C monotonicity
+ </para>
What exactly you're going to fix here? I think you at least should
provide a proper formatting to this paragraph....
* 0006-Remove-distance-operators-from-btree_gist-v04.patch
I see you provide btree_gist--1.6.sql and remove btree_gist--1.2.sql.
Note, that in order to better checking of extension migrations, we're
now providing just migration script to new version. So, everybody
installing new version will go through the migration. However, in
this particular case we've mass deletion of former extension objects.
So, I think this case should be an exception to the rules. And it's
good to provide new version of extension script in this case. Other
opinions?
A see btree_gist--1.5--1.6.sql contains a sophisticated query
updating extension operators to builtin operators. However, what do
you think about just long sequence of ALTER OPERATOR FAMILY commands
removing old operators and adding new operators? It would be longer,
but more predictable and easier for understanding.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On Thu, Dec 27, 2018 at 5:46 AM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
* 0006-Remove-distance-operators-from-btree_gist-v04.patch
I see you provide btree_gist--1.6.sql and remove btree_gist--1.2.sql.
Note, that in order to better checking of extension migrations, we're
now providing just migration script to new version. So, everybody
installing new version will go through the migration. However, in
this particular case we've mass deletion of former extension objects.
So, I think this case should be an exception to the rules. And it's
good to provide new version of extension script in this case. Other
opinions?
I also note that you've removed implementation of distance functions
from btree_gist. But during pg_upgrade extensions are moved "as is".
Not just CREATE EXTENSION command is dumped, but the whole extension
content. pg_upgrade'd instances would have old version of extension
metadata with new .so until ALTER EXTENSION UPDATE. So, user would get
errors about missed function in .so until updates the extension.
We're typically evade this by inclusion of old functions into new .so.
Then user can work normally before extension update. In this
particular case, we can leave the distance functions in the .so, but
make them just wrappers over core functions.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On Sun, Dec 30, 2018 at 1:19 AM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
On Thu, Dec 27, 2018 at 5:46 AM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:* 0006-Remove-distance-operators-from-btree_gist-v04.patch
I see you provide btree_gist--1.6.sql and remove btree_gist--1.2.sql.
Note, that in order to better checking of extension migrations, we're
now providing just migration script to new version. So, everybody
installing new version will go through the migration. However, in
this particular case we've mass deletion of former extension objects.
So, I think this case should be an exception to the rules. And it's
good to provide new version of extension script in this case. Other
opinions?I also note that you've removed implementation of distance functions
from btree_gist. But during pg_upgrade extensions are moved "as is".
Not just CREATE EXTENSION command is dumped, but the whole extension
content. pg_upgrade'd instances would have old version of extension
metadata with new .so until ALTER EXTENSION UPDATE. So, user would get
errors about missed function in .so until updates the extension.We're typically evade this by inclusion of old functions into new .so.
Then user can work normally before extension update. In this
particular case, we can leave the distance functions in the .so, but
make them just wrappers over core functions.
I've run regression tests with patch applied and opr_sanity showed some errors:
1) date_dist_timestamptz(), timestamp_dist_timestamptz(),
timestamptz_dist_date(), timestamptz_dist_timestamp() should be
stable, not immutable. These functions use timezone during
conversion.
2) date_dist_timestamp(), date_dist_timestamptz(),
timestamp_dist_date(), timestamp_dist_timestamptz(),
timestamptz_dist_date(), timestamptz_dist_timestamp() should be not
leafproof. These functions perform conversion, which might fail in
corner case. So, this error should be considered as a leak.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hi!
I've couple more notes regarding this patch.
1) There are two loops over scan key determining scan strategy:
existing loop in _bt_first(), and in new function
_bt_select_knn_search_strategy(). It's kind of redundant that we've
to process scan keys twice for knn searches. I think scan keys
processing should be merged into one loop.
2) We're not supporting knn ordering only using the first key.
Supporting multiple knn keys would require significant reword of
B-tree traversal algorithm making it closer to GiST and SP-GiST. So,
I think supporting only one knn key is reasonable restriction, at
least for now. But we could support single-key knn ordering in more
cases. For instance, knn search in "SELECT * FROM tbl WHERE a =
const1 ORDER BY b <-> const2" could benefit from (a, b) B-tree index.
So, we can support knn search on n-th key if there are equality scan
keys for [1, n-1] index keys.
I've already discussed these issues with Nikita personally.
Hopefully, new version will be published soon.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attached 5th version of the patches.
On 11.01.2019 2:21, Alexander Korotkov wrote:
Hi!
I've couple more notes regarding this patch.
1) There are two loops over scan key determining scan strategy:
existing loop in _bt_first(), and in new function
_bt_select_knn_search_strategy(). It's kind of redundant that we've
to process scan keys twice for knn searches. I think scan keys
processing should be merged into one loop.
This redundant loop was removed and code from _bt_select_knn_search_strategy()
was moved into the new function _bt_emit_scan_key() extracted from
_bt_preprocess_keys().
2) We're not supporting knn ordering only using the first key.
Supporting multiple knn keys would require significant reword of
B-tree traversal algorithm making it closer to GiST and SP-GiST. So,
I think supporting only one knn key is reasonable restriction, at
least for now. But we could support single-key knn ordering in more
cases. For instance, knn search in "SELECT * FROM tbl WHERE a =
const1 ORDER BY b <-> const2" could benefit from (a, b) B-tree index.
So, we can support knn search on n-th key if there are equality scan
keys for [1, n-1] index keys.
I will try to implement this in the next version of the patch.
I also note that you've removed implementation of distance functions
from btree_gist. But during pg_upgrade extensions are moved "as is".
Not just CREATE EXTENSION command is dumped, but the whole extension
content. pg_upgrade'd instances would have old version of extension
metadata with new .so until ALTER EXTENSION UPDATE. So, user would get
errors about missed function in .so until updates the extension.We're typically evade this by inclusion of old functions into new .so.
Then user can work normally before extension update. In this
particular case, we can leave the distance functions in the .so, but
make them just wrappers over core functions.
Wrappers over core functions were left in btree_gist.
I've run regression tests with patch applied and opr_sanity showed some errors:
1) date_dist_timestamptz(), timestamp_dist_timestamptz(),
timestamptz_dist_date(), timestamptz_dist_timestamp() should be
stable, not immutable. These functions use timezone during
conversion.
Fixed.
2) date_dist_timestamp(), date_dist_timestamptz(),
timestamp_dist_date(), timestamp_dist_timestamptz(),
timestamptz_dist_date(), timestamptz_dist_timestamp() should be not
leafproof. These functions perform conversion, which might fail in
corner case. So, this error should be considered as a leak.
All new distance functions except oiddist() are not leakproof,
so I had to relax condition in opr_sanity.sql test:
- pp.proleakproof != po.proleakproof
+ (NOT pp.proleakproof AND po.proleakproof))
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-Fix-get_index_column_opclass-v05.patchtext/x-patch; name=0001-Fix-get_index_column_opclass-v05.patchDownload
From 9632809b67531dd3d04a5b478ed39b8b55063284 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 11 Jan 2019 15:51:28 +0300
Subject: [PATCH 1/7] Fix get_index_column_opclass
---
src/backend/utils/cache/lsyscache.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index fba0ee8..c97c8bd 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3131,9 +3131,6 @@ get_index_column_opclass(Oid index_oid, int attno)
{
HeapTuple tuple;
Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
- Datum datum;
- bool isnull;
- oidvector *indclass;
Oid opclass;
/* First we need to know the column's opclass. */
@@ -3147,12 +3144,22 @@ get_index_column_opclass(Oid index_oid, int attno)
/* caller is supposed to guarantee this */
Assert(attno > 0 && attno <= rd_index->indnatts);
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indclass, &isnull);
- Assert(!isnull);
+ if (attno >= 1 && attno <= rd_index->indnkeyatts)
+ {
+ oidvector *indclass;
+ bool isnull;
+ Datum datum = SysCacheGetAttr(INDEXRELID, tuple,
+ Anum_pg_index_indclass,
+ &isnull);
+ Assert(!isnull);
- indclass = ((oidvector *) DatumGetPointer(datum));
- opclass = indclass->values[attno - 1];
+ indclass = ((oidvector *) DatumGetPointer(datum));
+ opclass = indclass->values[attno - 1];
+ }
+ else
+ {
+ opclass = InvalidOid;
+ }
ReleaseSysCache(tuple);
--
2.7.4
0002-Introduce-ammatchorderby-function-v05.patchtext/x-patch; name=0002-Introduce-ammatchorderby-function-v05.patchDownload
From 2cf1738c6c7a614783fbdb411c09063ed13f7e01 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 11 Jan 2019 15:51:28 +0300
Subject: [PATCH 2/7] Introduce ammatchorderby function
---
contrib/bloom/blutils.c | 2 +-
src/backend/access/brin/brin.c | 2 +-
src/backend/access/gin/ginutil.c | 2 +-
src/backend/access/gist/gist.c | 3 +-
src/backend/access/hash/hash.c | 2 +-
src/backend/access/nbtree/nbtree.c | 2 +-
src/backend/access/spgist/spgutils.c | 6 +-
src/backend/commands/opclasscmds.c | 2 +-
src/backend/executor/nodeIndexscan.c | 4 +-
src/backend/optimizer/path/indxpath.c | 192 ++++++++++++++++++++--------------
src/backend/optimizer/util/plancat.c | 2 +-
src/backend/utils/adt/amutils.c | 2 +-
src/include/access/amapi.h | 14 ++-
src/include/nodes/plannodes.h | 2 +-
src/include/nodes/relation.h | 8 +-
src/include/optimizer/paths.h | 4 +
16 files changed, 148 insertions(+), 101 deletions(-)
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6458376..9bff793 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,7 +109,6 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BLOOM_NSTRATEGIES;
amroutine->amsupport = BLOOM_NPROC;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -143,6 +142,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 03c8a6b..d1d2b0f 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -86,7 +86,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -120,6 +119,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index afc2023..0f6714d 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -41,7 +41,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GINNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -75,6 +74,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index b75b3a8..77ca187 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,7 @@
#include "access/gistscan.h"
#include "catalog/pg_collation.h"
#include "miscadmin.h"
+#include "optimizer/paths.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "nodes/execnodes.h"
@@ -64,7 +65,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GISTNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -98,6 +98,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index f1f01a0..bb5c6a1 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -59,7 +59,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = HTMaxStrategyNumber;
amroutine->amsupport = HASHNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -93,6 +92,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 98917de..c56ee5e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -110,7 +110,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BTMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = true;
amroutine->amcanmulticol = true;
@@ -144,6 +143,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index de147d7..37de33c 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -32,10 +32,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-extern Expr *spgcanorderbyop(IndexOptInfo *index,
- PathKey *pathkey, int pathkeyno,
- Expr *orderby_clause, int *indexcol_p);
-
/*
* SP-GiST handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -48,7 +44,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = SPGISTNProc;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -82,6 +77,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5a16045..4f670e7 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -1098,7 +1098,7 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
*/
IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
- if (!amroutine->amcanorderbyop)
+ if (!amroutine->ammatchorderby)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("access method \"%s\" does not support ordering operators",
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index f255551..9115c16 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -199,7 +199,7 @@ IndexNextWithReorder(IndexScanState *node)
* with just Asserting here because the system will not try to run the
* plan backwards if ExecSupportsBackwardScan() says it won't work.
* Currently, that is guaranteed because no index AMs support both
- * amcanorderbyop and amcanbackward; if any ever do,
+ * ammatchorderby and amcanbackward; if any ever do,
* ExecSupportsBackwardScan() will need to consider indexorderbys
* explicitly.
*/
@@ -1149,7 +1149,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* 5. NullTest ("indexkey IS NULL/IS NOT NULL"). We just fill in the
* ScanKey properly.
*
- * This code is also used to prepare ORDER BY expressions for amcanorderbyop
+ * This code is also used to prepare ORDER BY expressions for ammatchorderby
* indexes. The behavior is exactly the same, except that we have to look up
* the operator differently. Note that only cases 1 and 2 are currently
* possible for ORDER BY.
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index f8e674c..2bf87ad 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -17,6 +17,7 @@
#include <math.h>
+#include "access/amapi.h"
#include "access/stratnum.h"
#include "access/sysattr.h"
#include "catalog/pg_am.h"
@@ -156,6 +157,10 @@ static void match_clause_to_index(IndexOptInfo *index,
static bool match_clause_to_indexcol(IndexOptInfo *index,
int indexcol,
RestrictInfo *rinfo);
+static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
+ int indexcol,
+ Expr *clause,
+ Oid pk_opfamily);
static bool is_indexable_operator(Oid expr_op, Oid opfamily,
bool indexkey_on_left);
static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
@@ -166,8 +171,6 @@ static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
List **orderby_clauses_p,
List **clause_columns_p);
-static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
- int indexcol, Expr *clause, Oid pk_opfamily);
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
EquivalenceClass *ec, EquivalenceMember *em,
void *arg);
@@ -1000,7 +1003,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
orderbyclauses = NIL;
orderbyclausecols = NIL;
}
- else if (index->amcanorderbyop && pathkeys_possibly_useful)
+ else if (index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -2576,6 +2579,99 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
return false;
}
+Expr *
+match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey, int *indexcol_p)
+{
+ ListCell *lc;
+
+ /* Pathkey must request default sort order for the target opfamily */
+ if (pathkey->pk_strategy != BTLessStrategyNumber ||
+ pathkey->pk_nulls_first)
+ return NULL;
+
+ /* If eclass is volatile, no hope of using an indexscan */
+ if (pathkey->pk_eclass->ec_has_volatile)
+ return NULL;
+
+ /*
+ * Try to match eclass member expression(s) to index. Note that child
+ * EC members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could
+ * be considered to match more than one pathkey list, which is OK
+ * here. See also get_eclass_for_sort_expr.)
+ */
+ foreach(lc, pathkey->pk_eclass->ec_members)
+ {
+ EquivalenceMember *member = castNode(EquivalenceMember, lfirst(lc));
+ int indexcol;
+ int indexcol_min;
+ int indexcol_max;
+
+ /* No possibility of match if it references other relations */
+ if (!bms_equal(member->em_relids, index->rel->relids))
+ continue;
+
+ /* If *indexcol_p is non-negative then try to match only to it */
+ if (*indexcol_p >= 0)
+ {
+ indexcol_min = *indexcol_p;
+ indexcol_max = *indexcol_p + 1;
+ }
+ else /* try to match all columns */
+ {
+ indexcol_min = 0;
+ indexcol_max = index->ncolumns;
+ }
+
+ /*
+ * We allow any column of the GiST index to match each pathkey;
+ * they don't have to match left-to-right as you might expect.
+ */
+ for (indexcol = indexcol_min; indexcol < indexcol_max; indexcol++)
+ {
+ Expr *expr = match_clause_to_ordering_op(index,
+ indexcol,
+ member->em_expr,
+ pathkey->pk_opfamily);
+ if (expr)
+ {
+ *indexcol_p = indexcol;
+ return expr; /* don't want to look at remaining members */
+ }
+ }
+ }
+
+ return NULL;
+}
+
+bool
+match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p)
+{
+ ListCell *lc;
+
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = castNode(PathKey, lfirst(lc));
+ Expr *expr;
+ int indexcol = -1; /* match all index columns */
+
+ expr = match_orderbyop_pathkey(index, pathkey, &indexcol);
+
+ /*
+ * Note: for any failure to match, we just return NIL immediately.
+ * There is no value in matching just some of the pathkeys.
+ */
+ if (!expr)
+ return false;
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *clause_columns_p = lappend_int(*clause_columns_p, indexcol);
+ }
+
+ return true; /* success */
+}
/****************************************************************************
* ---- ROUTINES TO CHECK ORDERING OPERATORS ----
@@ -2601,86 +2697,24 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
{
List *orderby_clauses = NIL;
List *clause_columns = NIL;
- ListCell *lc1;
+ ammatchorderby_function ammatchorderby =
+ (ammatchorderby_function) index->ammatchorderby;
- *orderby_clauses_p = NIL; /* set default results */
- *clause_columns_p = NIL;
-
- /* Only indexes with the amcanorderbyop property are interesting here */
- if (!index->amcanorderbyop)
- return;
-
- foreach(lc1, pathkeys)
+ /* Only indexes with the ammatchorderby function are interesting here */
+ if (ammatchorderby &&
+ ammatchorderby(index, pathkeys, &orderby_clauses, &clause_columns))
{
- PathKey *pathkey = (PathKey *) lfirst(lc1);
- bool found = false;
- ListCell *lc2;
-
- /*
- * Note: for any failure to match, we just return NIL immediately.
- * There is no value in matching just some of the pathkeys.
- */
-
- /* Pathkey must request default sort order for the target opfamily */
- if (pathkey->pk_strategy != BTLessStrategyNumber ||
- pathkey->pk_nulls_first)
- return;
+ Assert(list_length(pathkeys) == list_length(orderby_clauses));
+ Assert(list_length(pathkeys) == list_length(clause_columns));
- /* If eclass is volatile, no hope of using an indexscan */
- if (pathkey->pk_eclass->ec_has_volatile)
- return;
-
- /*
- * Try to match eclass member expression(s) to index. Note that child
- * EC members are considered, but only when they belong to the target
- * relation. (Unlike regular members, the same expression could be a
- * child member of more than one EC. Therefore, the same index could
- * be considered to match more than one pathkey list, which is OK
- * here. See also get_eclass_for_sort_expr.)
- */
- foreach(lc2, pathkey->pk_eclass->ec_members)
- {
- EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
- int indexcol;
-
- /* No possibility of match if it references other relations */
- if (!bms_equal(member->em_relids, index->rel->relids))
- continue;
-
- /*
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, which is the sole existing AM supporting
- * amcanorderbyop. We might need different logic in future for
- * other implementations.
- */
- for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
- {
- Expr *expr;
-
- expr = match_clause_to_ordering_op(index,
- indexcol,
- member->em_expr,
- pathkey->pk_opfamily);
- if (expr)
- {
- orderby_clauses = lappend(orderby_clauses, expr);
- clause_columns = lappend_int(clause_columns, indexcol);
- found = true;
- break;
- }
- }
-
- if (found) /* don't want to look at remaining members */
- break;
- }
-
- if (!found) /* fail if no match for this pathkey */
- return;
+ *orderby_clauses_p = orderby_clauses; /* success! */
+ *clause_columns_p = clause_columns;
+ }
+ else
+ {
+ *orderby_clauses_p = NIL; /* set default results */
+ *clause_columns_p = NIL;
}
-
- *orderby_clauses_p = orderby_clauses; /* success! */
- *clause_columns_p = clause_columns;
}
/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 48ffc5f..9c30709 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -265,7 +265,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* We copy just the fields we need, not all of rd_amroutine */
amroutine = indexRelation->rd_amroutine;
- info->amcanorderbyop = amroutine->amcanorderbyop;
+ info->ammatchorderby = amroutine->ammatchorderby;
info->amoptionalkey = amroutine->amoptionalkey;
info->amsearcharray = amroutine->amsearcharray;
info->amsearchnulls = amroutine->amsearchnulls;
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 060ffe5..3907065 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -298,7 +298,7 @@ indexam_property(FunctionCallInfo fcinfo,
* a nonkey column, and null otherwise (meaning we don't
* know).
*/
- if (!iskey || !routine->amcanorderbyop)
+ if (!iskey || !routine->ammatchorderby)
{
res = false;
isnull = false;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 653ddc9..09411a7 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -21,6 +21,9 @@
*/
struct PlannerInfo;
struct IndexPath;
+struct IndexOptInfo;
+struct PathKey;
+struct Expr;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
@@ -140,6 +143,12 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* does AM support ORDER BY result of an operator on indexed column? */
+typedef bool (*ammatchorderby_function) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List **orderby_clauses_p,
+ List **clause_columns_p);
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -170,8 +179,6 @@ typedef struct IndexAmRoutine
uint16 amsupport;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
- /* does AM support ORDER BY result of an operator on indexed column? */
- bool amcanorderbyop;
/* does AM support backward scanning? */
bool amcanbackward;
/* does AM support UNIQUE indexes? */
@@ -221,7 +228,8 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
-
+ ammatchorderby_function ammatchorderby; /* can be NULL */
+
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
aminitparallelscan_function aminitparallelscan; /* can be NULL */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6d087c2..bd6ce97 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -388,7 +388,7 @@ typedef struct SampleScan
* indexorderbyops is a list of the OIDs of the operators used to sort the
* ORDER BY expressions. This is used together with indexorderbyorig to
* recheck ordering at run time. (Note that indexorderby, indexorderbyorig,
- * and indexorderbyops are used for amcanorderbyop cases, not amcanorder.)
+ * and indexorderbyops are used for ammatchorderby cases, not amcanorder.)
*
* indexorderdir specifies the scan ordering, for indexscans on amcanorder
* indexes (for other indexes it should be "don't care").
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 3430061..af6963f 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -804,7 +804,6 @@ typedef struct IndexOptInfo
bool hypothetical; /* true if index doesn't really exist */
/* Remaining fields are copied from the index AM's API struct: */
- bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
@@ -813,6 +812,11 @@ typedef struct IndexOptInfo
bool amcanparallel; /* does AM support parallel scan? */
/* Rather than include amapi.h here, we declare amcostestimate like this */
void (*amcostestimate) (); /* AM's cost estimator */
+ /* AM order-by match function */
+ bool (*ammatchorderby) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List **orderby_clauses_p,
+ List **clause_columns_p);
} IndexOptInfo;
/*
@@ -1136,7 +1140,7 @@ typedef struct Path
* (The order of multiple quals for the same index column is unspecified.)
*
* 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
- * been found to be usable as ordering operators for an amcanorderbyop index.
+ * been found to be usable as ordering operators for an ammatchorderby index.
* The list must match the path's pathkeys, ie, one expression per pathkey
* in the same order. These are not RestrictInfos, just bare expressions,
* since they generally won't yield booleans. Also, unlike the case for
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 666217c..718182b 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -87,6 +87,10 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
int indexcol,
List **indexcolnos,
bool *var_on_left_p);
+extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
+ int *indexcol_p);
+extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p);
/*
* tidpath.h
--
2.7.4
0003-Extract-structure-BTScanState-v05.patchtext/x-patch; name=0003-Extract-structure-BTScanState-v05.patchDownload
From 8823f18e84c4d6e381a38f23e775043c184ee794 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 11 Jan 2019 15:51:28 +0300
Subject: [PATCH 3/7] Extract structure BTScanState
---
src/backend/access/nbtree/nbtree.c | 200 ++++++++++--------
src/backend/access/nbtree/nbtsearch.c | 379 +++++++++++++++++-----------------
src/backend/access/nbtree/nbtutils.c | 49 +++--
src/include/access/nbtree.h | 38 ++--
4 files changed, 355 insertions(+), 311 deletions(-)
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index c56ee5e..27d9c6f 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -214,6 +214,7 @@ bool
btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
bool res;
/* btree indexes are never lossy */
@@ -224,7 +225,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(so->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
@@ -241,7 +242,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(so->currPos))
+ if (!BTScanPosIsValid(state->currPos))
res = _bt_first(scan, dir);
else
{
@@ -259,11 +260,11 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* trying to optimize that, so we don't detect it, but instead
* just forget any excess entries.
*/
- if (so->killedItems == NULL)
- so->killedItems = (int *)
+ if (state->killedItems == NULL)
+ state->killedItems = (int *)
palloc(MaxIndexTuplesPerPage * sizeof(int));
- if (so->numKilled < MaxIndexTuplesPerPage)
- so->killedItems[so->numKilled++] = so->currPos.itemIndex;
+ if (state->numKilled < MaxIndexTuplesPerPage)
+ state->killedItems[so->state.numKilled++] = state->currPos.itemIndex;
}
/*
@@ -288,6 +289,7 @@ int64
btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
int64 ntids = 0;
ItemPointer heapTid;
@@ -320,7 +322,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* Advance to next tuple within page. This is the same as the
* easy case in _bt_next().
*/
- if (++so->currPos.itemIndex > so->currPos.lastItem)
+ if (++currPos->itemIndex > currPos->lastItem)
{
/* let _bt_next do the heavy lifting */
if (!_bt_next(scan, ForwardScanDirection))
@@ -328,7 +330,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
/* Save tuple ID, and continue scanning */
- heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
+ heapTid = &currPos->items[currPos->itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
}
@@ -356,8 +358,8 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
/* allocate private workspace */
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
- BTScanPosInvalidate(so->currPos);
- BTScanPosInvalidate(so->markPos);
+ BTScanPosInvalidate(so->state.currPos);
+ BTScanPosInvalidate(so->state.markPos);
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
else
@@ -368,15 +370,15 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
so->arrayKeys = NULL;
so->arrayContext = NULL;
- so->killedItems = NULL; /* until needed */
- so->numKilled = 0;
+ so->state.killedItems = NULL; /* until needed */
+ so->state.numKilled = 0;
/*
* We don't know yet whether the scan will be index-only, so we do not
* allocate the tuple workspace arrays until btrescan. However, we set up
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
- so->currTuples = so->markTuples = NULL;
+ so->state.currTuples = so->state.markTuples = NULL;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -385,6 +387,45 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
return scan;
}
+static void
+_bt_release_current_position(BTScanState state, Relation indexRelation,
+ bool invalidate)
+{
+ /* we aren't holding any read locks, but gotta drop the pins */
+ if (BTScanPosIsValid(state->currPos))
+ {
+ /* Before leaving current page, deal with any killed items */
+ if (state->numKilled > 0)
+ _bt_killitems(state, indexRelation);
+
+ BTScanPosUnpinIfPinned(state->currPos);
+
+ if (invalidate)
+ BTScanPosInvalidate(state->currPos);
+ }
+}
+
+static void
+_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
+{
+ /* No need to invalidate positions, if the RAM is about to be freed. */
+ _bt_release_current_position(state, scan->indexRelation, !free);
+
+ state->markItemIndex = -1;
+ BTScanPosUnpinIfPinned(state->markPos);
+
+ if (free)
+ {
+ if (state->killedItems != NULL)
+ pfree(state->killedItems);
+ if (state->currTuples != NULL)
+ pfree(state->currTuples);
+ /* markTuples should not be pfree'd (_bt_allocate_tuple_workspaces) */
+ }
+ else
+ BTScanPosInvalidate(state->markPos);
+}
+
/*
* btrescan() -- rescan an index relation
*/
@@ -393,21 +434,11 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
ScanKey orderbys, int norderbys)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
- }
+ _bt_release_scan_state(scan, state, false);
- so->markItemIndex = -1;
- so->arrayKeyCount = 0;
- BTScanPosUnpinIfPinned(so->markPos);
- BTScanPosInvalidate(so->markPos);
+ so->arrayKeyCount = 0; /* FIXME in _bt_release_scan_state */
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
@@ -425,11 +456,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
* a SIGSEGV is not possible. Yeah, this is ugly as sin, but it beats
* adding special-case treatment for name_ops elsewhere.
*/
- if (scan->xs_want_itup && so->currTuples == NULL)
- {
- so->currTuples = (char *) palloc(BLCKSZ * 2);
- so->markTuples = so->currTuples + BLCKSZ;
- }
+ if (scan->xs_want_itup && state->currTuples == NULL)
+ _bt_allocate_tuple_workspaces(state);
/*
* Reset the scan keys. Note that keys ordering stuff moved to _bt_first.
@@ -453,19 +481,7 @@ btendscan(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
-
- /* No need to invalidate positions, the RAM is about to be freed. */
+ _bt_release_scan_state(scan, &so->state, true);
/* Release storage */
if (so->keyData != NULL)
@@ -473,24 +489,15 @@ btendscan(IndexScanDesc scan)
/* so->arrayKeyData and so->arrayKeys are in arrayContext */
if (so->arrayContext != NULL)
MemoryContextDelete(so->arrayContext);
- if (so->killedItems != NULL)
- pfree(so->killedItems);
- if (so->currTuples != NULL)
- pfree(so->currTuples);
- /* so->markTuples should not be pfree'd, see btrescan */
+
pfree(so);
}
-/*
- * btmarkpos() -- save current scan position
- */
-void
-btmarkpos(IndexScanDesc scan)
+static void
+_bt_mark_current_position(BTScanState state)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
-
/* There may be an old mark with a pin (but no lock). */
- BTScanPosUnpinIfPinned(so->markPos);
+ BTScanPosUnpinIfPinned(state->markPos);
/*
* Just record the current itemIndex. If we later step to next page
@@ -498,32 +505,34 @@ btmarkpos(IndexScanDesc scan)
* the currPos struct in markPos. If (as often happens) the mark is moved
* before we leave the page, we don't have to do that work.
*/
- if (BTScanPosIsValid(so->currPos))
- so->markItemIndex = so->currPos.itemIndex;
+ if (BTScanPosIsValid(state->currPos))
+ state->markItemIndex = state->currPos.itemIndex;
else
{
- BTScanPosInvalidate(so->markPos);
- so->markItemIndex = -1;
+ BTScanPosInvalidate(state->markPos);
+ state->markItemIndex = -1;
}
-
- /* Also record the current positions of any array keys */
- if (so->numArrayKeys)
- _bt_mark_array_keys(scan);
}
/*
- * btrestrpos() -- restore scan to last saved position
+ * btmarkpos() -- save current scan position
*/
void
-btrestrpos(IndexScanDesc scan)
+btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* Restore the marked positions of any array keys */
+ _bt_mark_current_position(&so->state);
+
+ /* Also record the current positions of any array keys */
if (so->numArrayKeys)
- _bt_restore_array_keys(scan);
+ _bt_mark_array_keys(scan);
+}
- if (so->markItemIndex >= 0)
+static void
+_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
+{
+ if (state->markItemIndex >= 0)
{
/*
* The scan has never moved to a new page since the last mark. Just
@@ -532,7 +541,7 @@ btrestrpos(IndexScanDesc scan)
* NB: In this case we can't count on anything in so->markPos to be
* accurate.
*/
- so->currPos.itemIndex = so->markItemIndex;
+ state->currPos.itemIndex = state->markItemIndex;
}
else
{
@@ -542,28 +551,21 @@ btrestrpos(IndexScanDesc scan)
* locks, but if we're still holding the pin for the current position,
* we must drop it.
*/
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
+ _bt_release_current_position(state, scan->indexRelation,
+ !BTScanPosIsValid(state->markPos));
- if (BTScanPosIsValid(so->markPos))
+ if (BTScanPosIsValid(state->markPos))
{
/* bump pin on mark buffer for assignment to current buffer */
- if (BTScanPosIsPinned(so->markPos))
- IncrBufferRefCount(so->markPos.buf);
- memcpy(&so->currPos, &so->markPos,
+ if (BTScanPosIsPinned(state->markPos))
+ IncrBufferRefCount(state->markPos.buf);
+ memcpy(&state->currPos, &state->markPos,
offsetof(BTScanPosData, items[1]) +
- so->markPos.lastItem * sizeof(BTScanPosItem));
- if (so->currTuples)
- memcpy(so->currTuples, so->markTuples,
- so->markPos.nextTupleOffset);
+ state->markPos.lastItem * sizeof(BTScanPosItem));
+ if (state->currTuples)
+ memcpy(state->currTuples, state->markTuples,
+ state->markPos.nextTupleOffset);
}
- else
- BTScanPosInvalidate(so->currPos);
}
}
@@ -779,9 +781,10 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
}
/*
- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
- * btbulkdelete() wasn't called.
- */
+- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
+- * btbulkdelete() wasn't called.
++ * btrestrpos() -- restore scan to last saved position
+ */
static bool
_bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
{
@@ -844,6 +847,21 @@ _bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
}
/*
+ * btrestrpos() -- restore scan to last saved position
+ */
+void
+btrestrpos(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
+ /* Restore the marked positions of any array keys */
+ if (so->numArrayKeys)
+ _bt_restore_array_keys(scan);
+
+ _bt_restore_marked_position(scan, &so->state);
+}
+
+/*
* Bulk deletion of all index entries pointing to a set of heap tuples.
* The set of target tuples is specified via a callback routine that tells
* whether any given heap tuple (identified by ItemPointer) is being deleted.
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index b5244aa..9b7374e 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -25,18 +25,19 @@
#include "utils/tqual.h"
-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir,
+static bool _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
OffsetNumber offnum);
-static void _bt_saveitem(BTScanOpaque so, int itemIndex,
+static void _bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup);
-static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
-static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir);
+static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
+static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
-static inline void _bt_initialize_more_data(BTScanOpaque so, ScanDirection dir);
+static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
/*
@@ -545,6 +546,58 @@ _bt_compare(Relation rel,
}
/*
+ * _bt_return_current_item() -- Prepare current scan state item for return.
+ *
+ * This function is used only in "return _bt_return_current_item();" statements
+ * and always returns true.
+ */
+static inline bool
+_bt_return_current_item(IndexScanDesc scan, BTScanState state)
+{
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+
+ scan->xs_ctup.t_self = currItem->heapTid;
+
+ if (scan->xs_want_itup)
+ scan->xs_itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+
+ return true;
+}
+
+/*
+ * _bt_load_first_page() -- Load data from the first page of the scan.
+ *
+ * Caller must have pinned and read-locked state->currPos.buf.
+ *
+ * On success exit, state->currPos is updated to contain data from the next
+ * interesting page. For success on a scan using a non-MVCC snapshot we hold
+ * a pin, but not a read lock, on that page. If we do not hold the pin, we
+ * set state->currPos.buf to InvalidBuffer. We return true to indicate success.
+ *
+ * If there are no more matching records in the given direction at all,
+ * we drop all locks and pins, set state->currPos.buf to InvalidBuffer,
+ * and return false.
+ */
+static bool
+_bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
+{
+ if (!_bt_readpage(scan, state, dir, offnum))
+ {
+ /*
+ * There's no actually-matching data on this page. Try to advance to
+ * the next page. Return false if there's no matching data at all.
+ */
+ LockBuffer(state->currPos.buf, BUFFER_LOCK_UNLOCK);
+ return _bt_steppage(scan, state, dir);
+ }
+
+ /* Drop the lock, and maybe the pin, on the current page */
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
+ return true;
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -569,6 +622,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
BTStack stack;
OffsetNumber offnum;
@@ -582,10 +636,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
int i;
bool status = true;
StrategyNumber strat_total;
- BTScanPosItem *currItem;
BlockNumber blkno;
- Assert(!BTScanPosIsValid(so->currPos));
+ Assert(!BTScanPosIsValid(*currPos));
pgstat_count_index_scan(rel);
@@ -1076,7 +1129,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* their scan
*/
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1084,7 +1137,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
PredicateLockPage(rel, BufferGetBlockNumber(buf),
scan->xs_snapshot);
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
/* position to the precise item on the page */
offnum = _bt_binsrch(rel, buf, keysCount, scankeys, nextkey);
@@ -1111,36 +1164,36 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
offnum = OffsetNumberPrev(offnum);
/* remember which buffer we have pinned, if any */
- Assert(!BTScanPosIsValid(so->currPos));
- so->currPos.buf = buf;
+ Assert(!BTScanPosIsValid(*currPos));
+ currPos->buf = buf;
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, offnum))
+ if (!_bt_load_first_page(scan, &so->state, dir, offnum))
+ return false;
+
+readcomplete:
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
+}
+
+/*
+ * Advance to next tuple on current page; or if there's no more,
+ * try to step to the next page with data.
+ */
+static bool
+_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
+{
+ if (ScanDirectionIsForward(dir))
{
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
+ if (++state->currPos.itemIndex <= state->currPos.lastItem)
+ return true;
}
else
{
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ if (--state->currPos.itemIndex >= state->currPos.firstItem)
+ return true;
}
-readcomplete:
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_steppage(scan, state, dir);
}
/*
@@ -1161,44 +1214,20 @@ bool
_bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- BTScanPosItem *currItem;
- /*
- * Advance to next tuple on current page; or if there's no more, try to
- * step to the next page with data.
- */
- if (ScanDirectionIsForward(dir))
- {
- if (++so->currPos.itemIndex > so->currPos.lastItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
- else
- {
- if (--so->currPos.itemIndex < so->currPos.firstItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
+ if (!_bt_next_item(scan, &so->state, dir))
+ return false;
/* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_return_current_item(scan, &so->state);
}
/*
* _bt_readpage() -- Load data from current index page into so->currPos
*
- * Caller must have pinned and read-locked so->currPos.buf; the buffer's state
- * is not changed here. Also, currPos.moreLeft and moreRight must be valid;
- * they are updated as appropriate. All other fields of so->currPos are
+ * Caller must have pinned and read-locked pos->buf; the buffer's state
+ * is not changed here. Also, pos->moreLeft and moreRight must be valid;
+ * they are updated as appropriate. All other fields of pos are
* initialized from scratch here.
*
* We scan the current page starting at offnum and moving in the indicated
@@ -1213,9 +1242,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
* Returns true if any matching items found on the page, false if none.
*/
static bool
-_bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
+_bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
@@ -1228,9 +1258,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We must have the buffer pinned and locked, but the usual macro can't be
* used here; this function is what makes it good for currPos.
*/
- Assert(BufferIsValid(so->currPos.buf));
+ Assert(BufferIsValid(pos->buf));
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* allow next page be processed by parallel worker */
@@ -1239,7 +1269,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (ScanDirectionIsForward(dir))
_bt_parallel_release(scan, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1249,30 +1279,30 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We note the buffer's block number so that we can release the pin later.
* This allows us to re-read the buffer if it is needed again for hinting.
*/
- so->currPos.currPage = BufferGetBlockNumber(so->currPos.buf);
+ pos->currPage = BufferGetBlockNumber(pos->buf);
/*
* We save the LSN of the page as we read it, so that we know whether it
* safe to apply LP_DEAD hints to the page later. This allows us to drop
* the pin for MVCC scans, which allows vacuum to avoid blocking.
*/
- so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf);
+ pos->lsn = BufferGetLSNAtomic(pos->buf);
/*
* we must save the page's right-link while scanning it; this tells us
* where to step right to after we're done with these items. There is no
* corresponding need for the left-link, since splits always go right.
*/
- so->currPos.nextPage = opaque->btpo_next;
+ pos->nextPage = opaque->btpo_next;
/* initialize tuple workspace to empty */
- so->currPos.nextTupleOffset = 0;
+ pos->nextTupleOffset = 0;
/*
* Now that the current page has been made consistent, the macro should be
* good.
*/
- Assert(BTScanPosIsPinned(so->currPos));
+ Assert(BTScanPosIsPinned(*pos));
if (ScanDirectionIsForward(dir))
{
@@ -1287,13 +1317,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (itup != NULL)
{
/* tuple passes all scan key conditions, so remember it */
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
itemIndex++;
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreRight = false;
+ pos->moreRight = false;
break;
}
@@ -1301,9 +1331,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex <= MaxIndexTuplesPerPage);
- so->currPos.firstItem = 0;
- so->currPos.lastItem = itemIndex - 1;
- so->currPos.itemIndex = 0;
+ pos->firstItem = 0;
+ pos->lastItem = itemIndex - 1;
+ pos->itemIndex = 0;
}
else
{
@@ -1319,12 +1349,12 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
{
/* tuple passes all scan key conditions, so remember it */
itemIndex--;
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreLeft = false;
+ pos->moreLeft = false;
break;
}
@@ -1332,30 +1362,31 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex >= 0);
- so->currPos.firstItem = itemIndex;
- so->currPos.lastItem = MaxIndexTuplesPerPage - 1;
- so->currPos.itemIndex = MaxIndexTuplesPerPage - 1;
+ pos->firstItem = itemIndex;
+ pos->lastItem = MaxIndexTuplesPerPage - 1;
+ pos->itemIndex = MaxIndexTuplesPerPage - 1;
}
- return (so->currPos.firstItem <= so->currPos.lastItem);
+ return (pos->firstItem <= pos->lastItem);
}
/* Save an index item into so->currPos.items[itemIndex] */
static void
-_bt_saveitem(BTScanOpaque so, int itemIndex,
+_bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup)
{
- BTScanPosItem *currItem = &so->currPos.items[itemIndex];
+ BTScanPosItem *currItem = &state->currPos.items[itemIndex];
currItem->heapTid = itup->t_tid;
currItem->indexOffset = offnum;
- if (so->currTuples)
+ if (state->currTuples)
{
Size itupsz = IndexTupleSize(itup);
- currItem->tupleOffset = so->currPos.nextTupleOffset;
- memcpy(so->currTuples + so->currPos.nextTupleOffset, itup, itupsz);
- so->currPos.nextTupleOffset += MAXALIGN(itupsz);
+ currItem->tupleOffset = state->currPos.nextTupleOffset;
+ memcpy(state->currTuples + state->currPos.nextTupleOffset,
+ itup, itupsz);
+ state->currPos.nextTupleOffset += MAXALIGN(itupsz);
}
}
@@ -1371,35 +1402,36 @@ _bt_saveitem(BTScanOpaque so, int itemIndex,
* to InvalidBuffer. We return true to indicate success.
*/
static bool
-_bt_steppage(IndexScanDesc scan, ScanDirection dir)
+_bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
+ Relation rel = scan->indexRelation;
BlockNumber blkno = InvalidBlockNumber;
bool status = true;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(*currPos));
/* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
+ if (state->numKilled > 0)
+ _bt_killitems(state, rel);
/*
* Before we modify currPos, make a copy of the page data if there was a
* mark position that needs it.
*/
- if (so->markItemIndex >= 0)
+ if (state->markItemIndex >= 0)
{
/* bump pin on current buffer for assignment to mark buffer */
- if (BTScanPosIsPinned(so->currPos))
- IncrBufferRefCount(so->currPos.buf);
- memcpy(&so->markPos, &so->currPos,
+ if (BTScanPosIsPinned(*currPos))
+ IncrBufferRefCount(currPos->buf);
+ memcpy(&state->markPos, currPos,
offsetof(BTScanPosData, items[1]) +
- so->currPos.lastItem * sizeof(BTScanPosItem));
- if (so->markTuples)
- memcpy(so->markTuples, so->currTuples,
- so->currPos.nextTupleOffset);
- so->markPos.itemIndex = so->markItemIndex;
- so->markItemIndex = -1;
+ currPos->lastItem * sizeof(BTScanPosItem));
+ if (state->markTuples)
+ memcpy(state->markTuples, state->currTuples,
+ currPos->nextTupleOffset);
+ state->markPos.itemIndex = state->markItemIndex;
+ state->markItemIndex = -1;
}
if (ScanDirectionIsForward(dir))
@@ -1415,27 +1447,27 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
if (!status)
{
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so use the previously-saved nextPage link. */
- blkno = so->currPos.nextPage;
+ blkno = currPos->nextPage;
}
/* Remember we left a page with data */
- so->currPos.moreLeft = true;
+ currPos->moreLeft = true;
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
}
else
{
/* Remember we left a page with data */
- so->currPos.moreRight = true;
+ currPos->moreRight = true;
if (scan->parallel_scan != NULL)
{
@@ -1444,25 +1476,25 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* ended already, bail out.
*/
status = _bt_parallel_seize(scan, &blkno);
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so just use our own notion of the current page */
- blkno = so->currPos.currPage;
+ blkno = currPos->currPage;
}
}
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, currPos);
return true;
}
@@ -1478,9 +1510,10 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* locks and pins, set so->currPos.buf to InvalidBuffer, and return false.
*/
static bool
-_bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
Relation rel;
Page page;
BTPageOpaque opaque;
@@ -1496,17 +1529,17 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* if we're at end of scan, give up and mark parallel scan as
* done, so that all the workers can finish their scan
*/
- if (blkno == P_NONE || !so->currPos.moreRight)
+ if (blkno == P_NONE || !currPos->moreRight)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* check for interrupts while we're not holding any buffer lock */
CHECK_FOR_INTERRUPTS();
/* step right one page */
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
- page = BufferGetPage(so->currPos.buf);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* check for deleted page */
@@ -1515,7 +1548,7 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
PredicateLockPage(rel, blkno, scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreRight if we can stop */
- if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque)))
+ if (_bt_readpage(scan, state, dir, P_FIRSTDATAKEY(opaque)))
break;
}
else if (scan->parallel_scan != NULL)
@@ -1527,18 +1560,18 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
blkno = opaque->btpo_next;
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
}
}
}
@@ -1548,10 +1581,10 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* Should only happen in parallel cases, when some other backend
* advanced the scan.
*/
- if (so->currPos.currPage != blkno)
+ if (currPos->currPage != blkno)
{
- BTScanPosUnpinIfPinned(so->currPos);
- so->currPos.currPage = blkno;
+ BTScanPosUnpinIfPinned(*currPos);
+ currPos->currPage = blkno;
}
/*
@@ -1576,31 +1609,30 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* is MVCC the page cannot move past the half-dead state to fully
* deleted.
*/
- if (BTScanPosIsPinned(so->currPos))
- LockBuffer(so->currPos.buf, BT_READ);
+ if (BTScanPosIsPinned(*currPos))
+ LockBuffer(currPos->buf, BT_READ);
else
- so->currPos.buf = _bt_getbuf(rel, so->currPos.currPage, BT_READ);
+ currPos->buf = _bt_getbuf(rel, currPos->currPage, BT_READ);
for (;;)
{
/* Done if we know there are no matching keys to the left */
- if (!so->currPos.moreLeft)
+ if (!currPos->moreLeft)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* Step to next physical page */
- so->currPos.buf = _bt_walk_left(rel, so->currPos.buf,
- scan->xs_snapshot);
+ currPos->buf = _bt_walk_left(rel, currPos->buf, scan->xs_snapshot);
/* if we're physically at end of index, return failure */
- if (so->currPos.buf == InvalidBuffer)
+ if (currPos->buf == InvalidBuffer)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1609,21 +1641,21 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* it's not half-dead and contains matching tuples. Else loop back
* and do it all again.
*/
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
{
- PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
+ PredicateLockPage(rel, BufferGetBlockNumber(currPos->buf), scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreLeft if we can stop */
- if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page)))
+ if (_bt_readpage(scan, state, dir, PageGetMaxOffsetNumber(page)))
break;
}
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1634,14 +1666,14 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
}
}
}
@@ -1660,13 +1692,13 @@ _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, &so->state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
return true;
}
@@ -1891,11 +1923,11 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
Page page;
BTPageOpaque opaque;
OffsetNumber start;
- BTScanPosItem *currItem;
/*
* Scan down to the leftmost or rightmost leaf page. This is a simplified
@@ -1911,7 +1943,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* exists.
*/
PredicateLockRelation(rel, scan->xs_snapshot);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1940,36 +1972,15 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
}
/* remember which buffer we have pinned */
- so->currPos.buf = buf;
+ currPos->buf = buf;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, start))
- {
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
- }
- else
- {
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
- }
-
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
+ if (!_bt_load_first_page(scan, &so->state, dir, start))
+ return false;
- return true;
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
}
/*
@@ -1977,19 +1988,19 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* for scan direction
*/
static inline void
-_bt_initialize_more_data(BTScanOpaque so, ScanDirection dir)
+_bt_initialize_more_data(BTScanState state, ScanDirection dir)
{
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ state->currPos.moreLeft = false;
+ state->currPos.moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ state->currPos.moreLeft = true;
+ state->currPos.moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- so->markItemIndex = -1; /* ditto */
+ state->numKilled = 0; /* just paranoia */
+ state->markItemIndex = -1; /* ditto */
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 2c05fb5..e548354 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1741,26 +1741,26 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
* away and the TID was re-used by a completely different heap tuple.
*/
void
-_bt_killitems(IndexScanDesc scan)
+_bt_killitems(BTScanState state, Relation indexRelation)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
OffsetNumber maxoff;
int i;
- int numKilled = so->numKilled;
+ int numKilled = state->numKilled;
bool killedsomething = false;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(state->currPos));
/*
* Always reset the scan state, so we don't look for same items on other
* pages.
*/
- so->numKilled = 0;
+ state->numKilled = 0;
- if (BTScanPosIsPinned(so->currPos))
+ if (BTScanPosIsPinned(*pos))
{
/*
* We have held the pin on this page since we read the index tuples,
@@ -1768,44 +1768,42 @@ _bt_killitems(IndexScanDesc scan)
* re-use of any TID on the page, so there is no need to check the
* LSN.
*/
- LockBuffer(so->currPos.buf, BT_READ);
-
- page = BufferGetPage(so->currPos.buf);
+ LockBuffer(pos->buf, BT_READ);
}
else
{
Buffer buf;
/* Attempt to re-read the buffer, getting pin and lock. */
- buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, BT_READ);
+ buf = _bt_getbuf(indexRelation, pos->currPage, BT_READ);
/* It might not exist anymore; in which case we can't hint it. */
if (!BufferIsValid(buf))
return;
- page = BufferGetPage(buf);
- if (BufferGetLSNAtomic(buf) == so->currPos.lsn)
- so->currPos.buf = buf;
+ if (BufferGetLSNAtomic(buf) == pos->lsn)
+ pos->buf = buf;
else
{
/* Modified while not pinned means hinting is not safe. */
- _bt_relbuf(scan->indexRelation, buf);
+ _bt_relbuf(indexRelation, buf);
return;
}
}
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
for (i = 0; i < numKilled; i++)
{
- int itemIndex = so->killedItems[i];
- BTScanPosItem *kitem = &so->currPos.items[itemIndex];
+ int itemIndex = state->killedItems[i];
+ BTScanPosItem *kitem = &pos->items[itemIndex];
OffsetNumber offnum = kitem->indexOffset;
- Assert(itemIndex >= so->currPos.firstItem &&
- itemIndex <= so->currPos.lastItem);
+ Assert(itemIndex >= pos->firstItem &&
+ itemIndex <= pos->lastItem);
if (offnum < minoff)
continue; /* pure paranoia */
while (offnum <= maxoff)
@@ -1833,10 +1831,10 @@ _bt_killitems(IndexScanDesc scan)
if (killedsomething)
{
opaque->btpo_flags |= BTP_HAS_GARBAGE;
- MarkBufferDirtyHint(so->currPos.buf, true);
+ MarkBufferDirtyHint(pos->buf, true);
}
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(pos->buf, BUFFER_LOCK_UNLOCK);
}
@@ -2214,3 +2212,14 @@ _bt_check_natts(Relation rel, Page page, OffsetNumber offnum)
}
}
+
+/*
+ * _bt_allocate_tuple_workspaces() -- Allocate buffers for saving index tuples
+ * in index-only scans.
+ */
+void
+_bt_allocate_tuple_workspaces(BTScanState state)
+{
+ state->currTuples = (char *) palloc(BLCKSZ * 2);
+ state->markTuples = state->currTuples + BLCKSZ;
+}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 4fb92d6..472898f 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -433,22 +433,8 @@ typedef struct BTArrayKeyInfo
Datum *elem_values; /* array of num_elems Datums */
} BTArrayKeyInfo;
-typedef struct BTScanOpaqueData
+typedef struct BTScanStateData
{
- /* these fields are set by _bt_preprocess_keys(): */
- bool qual_ok; /* false if qual can never be satisfied */
- int numberOfKeys; /* number of preprocessed scan keys */
- ScanKey keyData; /* array of preprocessed scan keys */
-
- /* workspace for SK_SEARCHARRAY support */
- ScanKey arrayKeyData; /* modified copy of scan->keyData */
- int numArrayKeys; /* number of equality-type array keys (-1 if
- * there are any unsatisfiable array keys) */
- int arrayKeyCount; /* count indicating number of array scan keys
- * processed */
- BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
- MemoryContext arrayContext; /* scan-lifespan context for array data */
-
/* info about killed items if any (killedItems is NULL if never used) */
int *killedItems; /* currPos.items indexes of killed items */
int numKilled; /* number of currently stored items */
@@ -473,6 +459,25 @@ typedef struct BTScanOpaqueData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+} BTScanStateData, *BTScanState;
+
+typedef struct BTScanOpaqueData
+{
+ /* these fields are set by _bt_preprocess_keys(): */
+ bool qual_ok; /* false if qual can never be satisfied */
+ int numberOfKeys; /* number of preprocessed scan keys */
+ ScanKey keyData; /* array of preprocessed scan keys */
+
+ /* workspace for SK_SEARCHARRAY support */
+ ScanKey arrayKeyData; /* modified copy of scan->keyData */
+ int numArrayKeys; /* number of equality-type array keys (-1 if
+ * there are any unsatisfiable array keys) */
+ int arrayKeyCount; /* count indicating number of array scan keys
+ * processed */
+ BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
+ MemoryContext arrayContext; /* scan-lifespan context for array data */
+
+ BTScanStateData state;
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -589,7 +594,7 @@ extern void _bt_preprocess_keys(IndexScanDesc scan);
extern IndexTuple _bt_checkkeys(IndexScanDesc scan,
Page page, OffsetNumber offnum,
ScanDirection dir, bool *continuescan);
-extern void _bt_killitems(IndexScanDesc scan);
+extern void _bt_killitems(BTScanState state, Relation indexRelation);
extern BTCycleId _bt_vacuum_cycleid(Relation rel);
extern BTCycleId _bt_start_vacuum(Relation rel);
extern void _bt_end_vacuum(Relation rel);
@@ -602,6 +607,7 @@ extern bool btproperty(Oid index_oid, int attno,
bool *res, bool *isnull);
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
+extern void _bt_allocate_tuple_workspaces(BTScanState state);
/*
* prototypes for functions in nbtvalidate.c
--
2.7.4
0004-Add-kNN-support-to-btree-v05.patchtext/x-patch; name=0004-Add-kNN-support-to-btree-v05.patchDownload
From 227e18379002fea12d48f0d3e5dbf83afafd4892 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 11 Jan 2019 15:51:28 +0300
Subject: [PATCH 4/7] Add kNN support to btree
---
doc/src/sgml/btree.sgml | 13 +
doc/src/sgml/indices.sgml | 11 +
doc/src/sgml/xindex.sgml | 3 +-
src/backend/access/nbtree/README | 17 ++
src/backend/access/nbtree/nbtree.c | 218 ++++++++++++--
src/backend/access/nbtree/nbtsearch.c | 331 ++++++++++++++++++---
src/backend/access/nbtree/nbtutils.c | 438 +++++++++++++++++++++++++---
src/backend/access/nbtree/nbtvalidate.c | 48 +--
src/backend/optimizer/path/indxpath.c | 16 +-
src/include/access/nbtree.h | 29 +-
src/include/access/stratnum.h | 5 +-
src/test/regress/expected/alter_generic.out | 13 +-
src/test/regress/sql/alter_generic.sql | 8 +-
13 files changed, 1018 insertions(+), 132 deletions(-)
diff --git a/doc/src/sgml/btree.sgml b/doc/src/sgml/btree.sgml
index 996932e..613032e 100644
--- a/doc/src/sgml/btree.sgml
+++ b/doc/src/sgml/btree.sgml
@@ -200,6 +200,19 @@
planner relies on them for optimization purposes.
</para>
+ <para>
+ To implement the distance ordered (nearest-neighbor) search, we only need
+ to define a distance operator (usually it called <->) with a correpsonding
+ operator family for distance comparison in the index's operator class.
+ These operators must satisfy the following assumptions for all non-null
+ values A,B,C of the datatype:
+
+ A <-> B = B <-> A symmetric law
+ if A = B, then A <-> C = B <-> C distance equivalence
+ if (A <= B and B <= C) or (A >= B and B >= C),
+ then A <-> B <= A <-> C monotonicity
+ </para>
+
</sect1>
<sect1 id="btree-support-funcs">
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 46f427b..caec484 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -175,6 +175,17 @@ CREATE INDEX test1_id_index ON test1 (id);
</para>
<para>
+ B-tree indexes are also capable of optimizing <quote>nearest-neighbor</>
+ searches, such as
+<programlisting><![CDATA[
+SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
+]]>
+</programlisting>
+ which finds the ten events closest to a given target date. The ability
+ to do this is again dependent on the particular operator class being used.
+ </para>
+
+ <para>
<indexterm>
<primary>index</primary>
<secondary>hash</secondary>
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..93094bc 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -1242,7 +1242,8 @@ SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING)
<title>Ordering Operators</title>
<para>
- Some index access methods (currently, only GiST and SP-GiST) support the concept of
+ Some index access methods (currently, only B-tree, GiST and SP-GiST)
+ support the concept of
<firstterm>ordering operators</firstterm>. What we have been discussing so far
are <firstterm>search operators</firstterm>. A search operator is one for which
the index can be searched to find all rows satisfying
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
index 3680e69..3f7e1b1 100644
--- a/src/backend/access/nbtree/README
+++ b/src/backend/access/nbtree/README
@@ -659,3 +659,20 @@ routines must treat it accordingly. The actual key stored in the
item is irrelevant, and need not be stored at all. This arrangement
corresponds to the fact that an L&Y non-leaf page has one more pointer
than key.
+
+Nearest-neighbor search
+-----------------------
+
+There is a special scan strategy for nearest-neighbor (kNN) search,
+that is used in queries with ORDER BY distance clauses like this:
+SELECT * FROM tab WHERE col > const1 ORDER BY col <-> const2 LIMIT k.
+But, unlike GiST, B-tree supports only a one ordering operator on the
+first index column.
+
+At the beginning of kNN scan, we need to determine which strategy we
+will use --- a special bidirectional or a ordinary unidirectional.
+If the point from which we measure the distance falls into the scan range,
+we use bidirectional scan starting from this point, else we use simple
+unidirectional scan in the right direction. Algorithm of a bidirectional
+scan is very simple: at each step we advancing scan in that direction,
+which has the nearest point.
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 27d9c6f..87e4d61 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -25,6 +25,9 @@
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "optimizer/paths.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "storage/condition_variable.h"
@@ -33,6 +36,7 @@
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
@@ -79,6 +83,7 @@ typedef enum
typedef struct BTParallelScanDescData
{
BlockNumber btps_scanPage; /* latest or next page to be scanned */
+ BlockNumber btps_knnScanPage; /* secondary latest or next page to be scanned */
BTPS_State btps_pageStatus; /* indicates whether next page is
* available for scan. see above for
* possible states of parallel scan. */
@@ -97,6 +102,9 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
+static bool btmatchorderby(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p);
+
/*
* Btree handler function: return IndexAmRoutine with access method parameters
@@ -107,7 +115,7 @@ bthandler(PG_FUNCTION_ARGS)
{
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
- amroutine->amstrategies = BTMaxStrategyNumber;
+ amroutine->amstrategies = BtreeMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
amroutine->amcanbackward = true;
@@ -143,7 +151,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
- amroutine->ammatchorderby = NULL;
+ amroutine->ammatchorderby = btmatchorderby;
PG_RETURN_POINTER(amroutine);
}
@@ -215,23 +223,30 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTScanState state = &so->state;
+ ScanDirection arraydir =
+ scan->numberOfOrderBys > 0 ? ForwardScanDirection : dir;
bool res;
/* btree indexes are never lossy */
scan->xs_recheck = false;
+ scan->xs_recheckorderby = false;
+
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
/*
* If we have any array keys, initialize them during first call for a
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
return false;
- _bt_start_array_keys(scan, dir);
+ _bt_start_array_keys(scan, arraydir);
}
/* This loop handles advancing to the next array elements, if any */
@@ -242,7 +257,8 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(state->currPos))
+ if (!BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
res = _bt_first(scan, dir);
else
{
@@ -277,7 +293,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
if (res)
break;
/* ... otherwise see if we have more array keys to deal with */
- } while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
+ } while (so->numArrayKeys && _bt_advance_array_keys(scan, arraydir));
return res;
}
@@ -350,9 +366,6 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
IndexScanDesc scan;
BTScanOpaque so;
- /* no order by operators allowed */
- Assert(norderbys == 0);
-
/* get the scan */
scan = RelationGetIndexScan(rel, nkeys, norderbys);
@@ -379,6 +392,9 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
so->state.currTuples = so->state.markTuples = NULL;
+ so->knnState = NULL;
+ so->distanceTypeByVal = true;
+ so->scanDirection = NoMovementScanDirection;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -408,6 +424,8 @@ _bt_release_current_position(BTScanState state, Relation indexRelation,
static void
_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
/* No need to invalidate positions, if the RAM is about to be freed. */
_bt_release_current_position(state, scan->indexRelation, !free);
@@ -424,6 +442,17 @@ _bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
}
else
BTScanPosInvalidate(state->markPos);
+
+ if (!so->distanceTypeByVal)
+ {
+ if (DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+ state->currDistance = PointerGetDatum(NULL);
+
+ if (DatumGetPointer(state->markDistance))
+ pfree(DatumGetPointer(state->markDistance));
+ state->markDistance = PointerGetDatum(NULL);
+ }
}
/*
@@ -438,6 +467,13 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
_bt_release_scan_state(scan, state, false);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ so->knnState = NULL;
+ }
+
so->arrayKeyCount = 0; /* FIXME in _bt_release_scan_state */
/*
@@ -469,6 +505,14 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
scan->numberOfKeys * sizeof(ScanKeyData));
so->numberOfKeys = 0; /* until _bt_preprocess_keys sets it */
+ if (orderbys && scan->numberOfOrderBys > 0)
+ memmove(scan->orderByData,
+ orderbys,
+ scan->numberOfOrderBys * sizeof(ScanKeyData));
+
+ so->scanDirection = NoMovementScanDirection;
+ so->distanceTypeByVal = true;
+
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
_bt_preprocess_array_keys(scan);
}
@@ -483,6 +527,12 @@ btendscan(IndexScanDesc scan)
_bt_release_scan_state(scan, &so->state, true);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ }
+
/* Release storage */
if (so->keyData != NULL)
pfree(so->keyData);
@@ -494,7 +544,7 @@ btendscan(IndexScanDesc scan)
}
static void
-_bt_mark_current_position(BTScanState state)
+_bt_mark_current_position(BTScanOpaque so, BTScanState state)
{
/* There may be an old mark with a pin (but no lock). */
BTScanPosUnpinIfPinned(state->markPos);
@@ -512,6 +562,21 @@ _bt_mark_current_position(BTScanState state)
BTScanPosInvalidate(state->markPos);
state->markItemIndex = -1;
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->markDistance));
+
+ state->markIsNull = !BTScanPosIsValid(state->currPos) ||
+ state->currIsNull;
+
+ state->markDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->currDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -522,7 +587,13 @@ btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_mark_current_position(&so->state);
+ _bt_mark_current_position(so, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_mark_current_position(so, so->knnState);
+ so->markRightIsNearest = so->currRightIsNearest;
+ }
/* Also record the current positions of any array keys */
if (so->numArrayKeys)
@@ -532,6 +603,8 @@ btmarkpos(IndexScanDesc scan)
static void
_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
if (state->markItemIndex >= 0)
{
/*
@@ -567,6 +640,19 @@ _bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
state->markPos.nextTupleOffset);
}
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currIsNull = state->markIsNull;
+ state->currDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->markDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -588,6 +674,7 @@ btinitparallelscan(void *target)
SpinLockInit(&bt_target->btps_mutex);
bt_target->btps_scanPage = InvalidBlockNumber;
+ bt_target->btps_knnScanPage = InvalidBlockNumber;
bt_target->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
bt_target->btps_arrayKeyCount = 0;
ConditionVariableInit(&bt_target->btps_cv);
@@ -614,6 +701,7 @@ btparallelrescan(IndexScanDesc scan)
*/
SpinLockAcquire(&btscan->btps_mutex);
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount = 0;
SpinLockRelease(&btscan->btps_mutex);
@@ -638,7 +726,7 @@ btparallelrescan(IndexScanDesc scan)
* Callers should ignore the value of pageno if the return value is false.
*/
bool
-_bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
+_bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTPS_State pageStatus;
@@ -646,12 +734,17 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
bool status = true;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
*pageno = P_NONE;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ scanPage = state == &so->state
+ ? &btscan->btps_scanPage
+ : &btscan->btps_knnScanPage;
+
while (1)
{
SpinLockAcquire(&btscan->btps_mutex);
@@ -677,7 +770,7 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* of advancing it to a new page!
*/
btscan->btps_pageStatus = BTPARALLEL_ADVANCING;
- *pageno = btscan->btps_scanPage;
+ *pageno = *scanPage;
exit_loop = true;
}
SpinLockRelease(&btscan->btps_mutex);
@@ -696,19 +789,42 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* can now begin advancing the scan.
*/
void
-_bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
+_bt_parallel_release(IndexScanDesc scan, BTScanState state,
+ BlockNumber scan_page)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
+ bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
SpinLockAcquire(&btscan->btps_mutex);
- btscan->btps_scanPage = scan_page;
- btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ *scanPage = scan_page;
+ /* switch to idle state only if both KNN pages are initialized */
+ if (!knnScan || *otherScanPage != InvalidBlockNumber)
+ {
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ status_changed = true;
+ }
SpinLockRelease(&btscan->btps_mutex);
- ConditionVariableSignal(&btscan->btps_cv);
+
+ if (status_changed)
+ ConditionVariableSignal(&btscan->btps_cv);
}
/*
@@ -719,12 +835,15 @@ _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
* advance to the next page.
*/
void
-_bt_parallel_done(IndexScanDesc scan)
+_bt_parallel_done(IndexScanDesc scan, BTScanState state)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
/* Do nothing, for non-parallel scans */
if (parallel_scan == NULL)
@@ -733,18 +852,41 @@ _bt_parallel_done(IndexScanDesc scan)
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
+
/*
* Mark the parallel scan as done for this combination of scan keys,
* unless some other process already did so. See also
* _bt_advance_array_keys.
*/
SpinLockAcquire(&btscan->btps_mutex);
- if (so->arrayKeyCount >= btscan->btps_arrayKeyCount &&
- btscan->btps_pageStatus != BTPARALLEL_DONE)
+
+ Assert(btscan->btps_pageStatus == BTPARALLEL_ADVANCING);
+
+ if (so->arrayKeyCount >= btscan->btps_arrayKeyCount)
{
- btscan->btps_pageStatus = BTPARALLEL_DONE;
+ *scanPage = P_NONE;
status_changed = true;
+
+ /* switch to "done" state only if both KNN scans are done */
+ if (!knnScan || *otherScanPage == P_NONE)
+ btscan->btps_pageStatus = BTPARALLEL_DONE;
+ /* else switch to "idle" state only if both KNN scans are initialized */
+ else if (*otherScanPage != InvalidBlockNumber)
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ else
+ status_changed = false;
}
+
SpinLockRelease(&btscan->btps_mutex);
/* wake up all the workers associated with this parallel scan */
@@ -774,6 +916,7 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
if (btscan->btps_pageStatus == BTPARALLEL_DONE)
{
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount++;
}
@@ -859,6 +1002,12 @@ btrestrpos(IndexScanDesc scan)
_bt_restore_array_keys(scan);
_bt_restore_marked_position(scan, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_restore_marked_position(scan, so->knnState);
+ so->currRightIsNearest = so->markRightIsNearest;
+ }
}
/*
@@ -1394,3 +1543,30 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+/*
+ * btmatchorderby() -- Check whether KNN-search strategy is applicable to
+ * the given ORDER BY distance operator.
+ */
+static bool
+btmatchorderby(IndexOptInfo *index, List *pathkeys,
+ List **orderby_clauses_p, List **clause_columns_p)
+{
+ Expr *expr;
+ /* ORDER BY distance to the first index column is only supported */
+ int indexcol = 0;
+
+ if (list_length(pathkeys) != 1)
+ return false; /* only one ORDER BY clause is supported */
+
+ expr = match_orderbyop_pathkey(index, castNode(PathKey, linitial(pathkeys)),
+ &indexcol);
+
+ if (!expr)
+ return false;
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *clause_columns_p = lappend_int(*clause_columns_p, 0);
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 9b7374e..1ec1466 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -32,12 +32,14 @@ static void _bt_saveitem(BTScanState state, int itemIndex,
static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
BlockNumber blkno, ScanDirection dir);
-static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
- ScanDirection dir);
+static bool _bt_parallel_readpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
+static BTScanState _bt_alloc_knn_scan(IndexScanDesc scan);
+static bool _bt_start_knn_scan(IndexScanDesc scan, bool left, bool right);
/*
@@ -598,6 +600,156 @@ _bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
}
/*
+ * _bt_calc_current_dist() -- Calculate distance from the current item
+ * of the scan state to the target order-by ScanKey argument.
+ */
+static void
+_bt_calc_current_dist(IndexScanDesc scan, BTScanState state)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+ IndexTuple itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+ ScanKey scankey = &scan->orderByData[0];
+ Datum value;
+
+ value = index_getattr(itup, 1, scan->xs_itupdesc, &state->currIsNull);
+
+ if (state->currIsNull)
+ return; /* NULL distance */
+
+ value = FunctionCall2Coll(&scankey->sk_func,
+ scankey->sk_collation,
+ value,
+ scankey->sk_argument);
+
+ /* free previous distance value for by-ref types */
+ if (!so->distanceTypeByVal && DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currDistance = value;
+}
+
+/*
+ * _bt_compare_current_dist() -- Compare current distances of the left and right scan states.
+ *
+ * NULL distances are considered to be greater than any non-NULL distances.
+ *
+ * Returns true if right distance is lesser than left, otherwise false.
+ */
+static bool
+_bt_compare_current_dist(BTScanOpaque so, BTScanState rstate, BTScanState lstate)
+{
+ if (lstate->currIsNull)
+ return true; /* non-NULL < NULL */
+
+ if (rstate->currIsNull)
+ return false; /* NULL > non-NULL */
+
+ return DatumGetBool(FunctionCall2Coll(&so->distanceCmpProc,
+ InvalidOid, /* XXX collation for distance comparison */
+ rstate->currDistance,
+ lstate->currDistance));
+}
+
+/*
+ * _bt_alloc_knn_scan() -- Allocate additional backward scan state for KNN.
+ */
+static BTScanState
+_bt_alloc_knn_scan(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState lstate = (BTScanState) palloc(sizeof(BTScanStateData));
+
+ _bt_allocate_tuple_workspaces(lstate);
+
+ if (!scan->xs_want_itup)
+ {
+ /* We need to request index tuples for distance comparison. */
+ scan->xs_want_itup = true;
+ _bt_allocate_tuple_workspaces(&so->state);
+ }
+
+ BTScanPosInvalidate(lstate->currPos);
+ lstate->currPos.moreLeft = false;
+ lstate->currPos.moreRight = false;
+ BTScanPosInvalidate(lstate->markPos);
+ lstate->markItemIndex = -1;
+ lstate->killedItems = NULL;
+ lstate->numKilled = 0;
+ lstate->currDistance = PointerGetDatum(NULL);
+ lstate->markDistance = PointerGetDatum(NULL);
+
+ return so->knnState = lstate;
+}
+
+static bool
+_bt_start_knn_scan(IndexScanDesc scan, bool left, bool right)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+
+ if (!left && !right)
+ return false; /* empty result */
+
+ rstate = &so->state;
+ lstate = so->knnState;
+
+ if (left && right)
+ {
+ /*
+ * We have found items in both scan directions,
+ * determine nearest item to return.
+ */
+ _bt_calc_current_dist(scan, rstate);
+ _bt_calc_current_dist(scan, lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+
+ /* Reset right flag if the left item is nearer. */
+ right = so->currRightIsNearest;
+ }
+
+ /* Return current item of the selected scan direction. */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
+ * _bt_init_knn_scan() -- Init additional scan state for KNN search.
+ *
+ * Caller must pin and read-lock scan->state.currPos.buf buffer.
+ *
+ * If empty result was found returned false.
+ * Otherwise prepared current item, and returned true.
+ */
+static bool
+_bt_init_knn_scan(IndexScanDesc scan, OffsetNumber offnum)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+ Buffer buf = rstate->currPos.buf;
+ bool left,
+ right;
+
+ lstate = _bt_alloc_knn_scan(scan);
+
+ /* Bump pin and lock count before BTScanPosData copying. */
+ IncrBufferRefCount(buf);
+ LockBuffer(buf, BT_READ);
+
+ memcpy(&lstate->currPos, &rstate->currPos, sizeof(BTScanPosData));
+ lstate->currPos.moreLeft = true;
+ lstate->currPos.moreRight = false;
+
+ /* Load first pages from the both scans. */
+ right = _bt_load_first_page(scan, rstate, ForwardScanDirection, offnum);
+ left = _bt_load_first_page(scan, lstate, BackwardScanDirection,
+ OffsetNumberPrev(offnum));
+
+ return _bt_start_knn_scan(scan, left, right);
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -655,6 +807,15 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!so->qual_ok)
return false;
+ if (scan->numberOfOrderBys > 0)
+ {
+ if (so->useBidirectionalKnnScan)
+ _bt_init_distance_comparison(scan);
+ else if (so->scanDirection != NoMovementScanDirection)
+ /* use selected KNN scan direction */
+ dir = so->scanDirection;
+ }
+
/*
* For parallel scans, get the starting page from shared state. If the
* scan has not started, proceed to find out first leaf page in the usual
@@ -663,19 +824,50 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, &so->state, &blkno);
if (!status)
return false;
- else if (blkno == P_NONE)
- {
- _bt_parallel_done(scan);
- return false;
- }
else if (blkno != InvalidBlockNumber)
{
- if (!_bt_parallel_readpage(scan, blkno, dir))
- return false;
- goto readcomplete;
+ bool knn = so->useBidirectionalKnnScan;
+ bool right;
+ bool left;
+
+ if (knn)
+ _bt_alloc_knn_scan(scan);
+
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, &so->state);
+ right = false;
+ }
+ else
+ right = _bt_parallel_readpage(scan, &so->state, blkno,
+ knn ? ForwardScanDirection : dir);
+
+ if (!knn)
+ return right && _bt_return_current_item(scan, &so->state);
+
+ /* seize additional backward KNN scan */
+ left = _bt_parallel_seize(scan, so->knnState, &blkno);
+
+ if (left)
+ {
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, so->knnState);
+ left = false;
+ }
+ else
+ {
+ /* backward scan should be already initialized */
+ Assert(blkno != InvalidBlockNumber);
+ left = _bt_parallel_readpage(scan, so->knnState, blkno,
+ BackwardScanDirection);
+ }
+ }
+
+ return _bt_start_knn_scan(scan, left, right);
}
}
@@ -725,14 +917,20 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* storing their addresses into the local startKeys[] array.
*----------
*/
- strat_total = BTEqualStrategyNumber;
- if (so->numberOfKeys > 0)
+ if (so->useBidirectionalKnnScan)
+ {
+ keysCount = _bt_init_knn_start_keys(scan, startKeys, notnullkeys);
+ strat_total = BtreeKNNSearchStrategyNumber;
+ }
+ else if (so->numberOfKeys > 0)
{
AttrNumber curattr;
ScanKey chosen;
ScanKey impliesNN;
ScanKey cur;
+ strat_total = BTEqualStrategyNumber;
+
/*
* chosen is the so-far-chosen key for the current attribute, if any.
* We don't cast the decision in stone until we reach keys for the
@@ -866,7 +1064,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!match)
{
/* No match, so mark (parallel) scan finished */
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
}
return match;
@@ -901,7 +1099,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(subkey->sk_flags & SK_ROW_MEMBER);
if (subkey->sk_flags & SK_ISNULL)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
return false;
}
memcpy(scankeys + i, subkey, sizeof(ScanKeyData));
@@ -1081,6 +1279,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
break;
case BTGreaterEqualStrategyNumber:
+ case BtreeKNNSearchStrategyNumber:
/*
* Find first item >= scankey. (This is only used for forward
@@ -1128,7 +1327,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* mark parallel scan as done, so that all the workers can finish
* their scan
*/
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
BTScanPosInvalidate(*currPos);
return false;
@@ -1167,17 +1366,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(!BTScanPosIsValid(*currPos));
currPos->buf = buf;
+ if (strat_total == BtreeKNNSearchStrategyNumber)
+ return _bt_init_knn_scan(scan, offnum);
+
if (!_bt_load_first_page(scan, &so->state, dir, offnum))
- return false;
+ return false; /* empty result */
-readcomplete:
/* OK, currPos->itemIndex says what to return */
return _bt_return_current_item(scan, &so->state);
}
/*
- * Advance to next tuple on current page; or if there's no more,
- * try to step to the next page with data.
+ * _bt_next_item() -- Advance to next tuple on current page;
+ * or if there's no more, try to step to the next page with data.
+ *
+ * If there are no more matching records in the given direction
*/
static bool
_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
@@ -1197,6 +1400,51 @@ _bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
}
/*
+ * _bt_next_nearest() -- Return next nearest item from bidirectional KNN scan.
+ */
+static bool
+_bt_next_nearest(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state;
+ BTScanState lstate = so->knnState;
+ bool right = BTScanPosIsValid(rstate->currPos);
+ bool left = BTScanPosIsValid(lstate->currPos);
+ bool advanceRight;
+
+ if (right && left)
+ advanceRight = so->currRightIsNearest;
+ else if (right)
+ advanceRight = true;
+ else if (left)
+ advanceRight = false;
+ else
+ return false; /* end of the scan */
+
+ if (advanceRight)
+ right = _bt_next_item(scan, rstate, ForwardScanDirection);
+ else
+ left = _bt_next_item(scan, lstate, BackwardScanDirection);
+
+ if (!left && !right)
+ return false; /* end of the scan */
+
+ if (left && right)
+ {
+ /*
+ * If there are items in both scans we must recalculate distance
+ * in the advanced scan.
+ */
+ _bt_calc_current_dist(scan, advanceRight ? rstate : lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+ right = so->currRightIsNearest;
+ }
+
+ /* return nearest item */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_next() -- Get the next item in a scan.
*
* On entry, so->currPos describes the current page, which may be pinned
@@ -1215,6 +1463,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ if (so->knnState)
+ /* return next neareset item from KNN scan */
+ return _bt_next_nearest(scan);
+
if (!_bt_next_item(scan, &so->state, dir))
return false;
@@ -1267,9 +1519,9 @@ _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
if (scan->parallel_scan)
{
if (ScanDirectionIsForward(dir))
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1443,7 +1695,7 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the next block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
/* release the previous buffer, if pinned */
@@ -1475,13 +1727,19 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the current block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
BTScanPosInvalidate(*currPos);
return false;
}
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, state);
+ BTScanPosInvalidate(*currPos);
+ return false;
+ }
}
else
{
@@ -1531,7 +1789,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
*/
if (blkno == P_NONE || !currPos->moreRight)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1554,14 +1812,14 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
}
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1620,7 +1878,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (!currPos->moreLeft)
{
_bt_relbuf(rel, currPos->buf);
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1631,7 +1889,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
/* if we're physically at end of index, return failure */
if (currPos->buf == InvalidBuffer)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1655,7 +1913,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1667,7 +1925,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1688,17 +1946,16 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
* indicate success.
*/
static bool
-_bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_parallel_readpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ _bt_initialize_more_data(state, dir);
- _bt_initialize_more_data(&so->state, dir);
-
- if (!_bt_readnextpage(scan, &so->state, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
return true;
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index e548354..bf02de1 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -20,16 +20,21 @@
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "catalog/pg_amop.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
typedef struct BTSortArrayContext
{
FmgrInfo flinfo;
+ FmgrInfo distflinfo;
+ FmgrInfo distcmpflinfo;
+ ScanKey distkey;
Oid collation;
bool reverse;
} BTSortArrayContext;
@@ -49,6 +54,11 @@ static void _bt_mark_scankey_required(ScanKey skey);
static bool _bt_check_rowcompare(ScanKey skey,
IndexTuple tuple, TupleDesc tupdesc,
ScanDirection dir, bool *continuescan);
+static inline StrategyNumber _bt_select_knn_strategy_for_key(IndexScanDesc scan,
+ ScanKey cond);
+static void _bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval);
+
/*
@@ -445,6 +455,7 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
{
Relation rel = scan->indexRelation;
Oid elemtype;
+ Oid opfamily;
RegProcedure cmp_proc;
BTSortArrayContext cxt;
int last_non_dup;
@@ -462,6 +473,53 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
if (elemtype == InvalidOid)
elemtype = rel->rd_opcintype[skey->sk_attno - 1];
+ opfamily = rel->rd_opfamily[skey->sk_attno - 1];
+
+ if (scan->numberOfOrderBys <= 0)
+ {
+ cxt.distkey = NULL;
+ cxt.reverse = reverse;
+ }
+ else
+ {
+ /* Init procedures for distance calculation and comparison. */
+ ScanKey distkey = &scan->orderByData[0];
+ ScanKeyData distkey2;
+ Oid disttype = distkey->sk_subtype;
+ Oid distopr;
+ RegProcedure distproc;
+
+ if (!OidIsValid(disttype))
+ disttype = rel->rd_opcintype[skey->sk_attno - 1];
+
+ /* Lookup distance operator in index column's operator family. */
+ distopr = get_opfamily_member(opfamily,
+ elemtype,
+ disttype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(distopr))
+ elog(ERROR, "missing operator (%u,%u) for strategy %d in opfamily %u",
+ elemtype, disttype, BtreeKNNSearchStrategyNumber, opfamily);
+
+ distproc = get_opcode(distopr);
+
+ if (!RegProcedureIsValid(distproc))
+ elog(ERROR, "missing code for operator %u", distopr);
+
+ fmgr_info(distproc, &cxt.distflinfo);
+
+ distkey2 = *distkey;
+ fmgr_info_copy(&distkey2.sk_func, &cxt.distflinfo, CurrentMemoryContext);
+ distkey2.sk_subtype = disttype;
+
+ _bt_get_distance_cmp_proc(&distkey2, opfamily, elemtype,
+ &cxt.distcmpflinfo, NULL, NULL);
+
+ cxt.distkey = distkey;
+ cxt.reverse = false; /* supported only ascending ordering */
+ }
+
/*
* Look up the appropriate comparison function in the opfamily.
*
@@ -470,19 +528,17 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
* non-cross-type support functions for any datatype that it supports at
* all.
*/
- cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
+ cmp_proc = get_opfamily_proc(opfamily,
elemtype,
elemtype,
BTORDER_PROC);
if (!RegProcedureIsValid(cmp_proc))
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
- BTORDER_PROC, elemtype, elemtype,
- rel->rd_opfamily[skey->sk_attno - 1]);
+ BTORDER_PROC, elemtype, elemtype, opfamily);
/* Sort the array elements */
fmgr_info(cmp_proc, &cxt.flinfo);
cxt.collation = skey->sk_collation;
- cxt.reverse = reverse;
qsort_arg((void *) elems, nelems, sizeof(Datum),
_bt_compare_array_elements, (void *) &cxt);
@@ -514,6 +570,23 @@ _bt_compare_array_elements(const void *a, const void *b, void *arg)
BTSortArrayContext *cxt = (BTSortArrayContext *) arg;
int32 compare;
+ if (cxt->distkey)
+ {
+ Datum dista = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ da,
+ cxt->distkey->sk_argument);
+ Datum distb = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ db,
+ cxt->distkey->sk_argument);
+ bool cmp = DatumGetBool(FunctionCall2Coll(&cxt->distcmpflinfo,
+ cxt->collation,
+ dista,
+ distb));
+ return cmp ? -1 : 1;
+ }
+
compare = DatumGetInt32(FunctionCall2Coll(&cxt->flinfo,
cxt->collation,
da, db));
@@ -667,6 +740,66 @@ _bt_restore_array_keys(IndexScanDesc scan)
}
}
+/*
+ * _bt_emit_scan_key() -- Emit one prepared scan key
+ *
+ * Push the scan key into the so->keyData[] array, and then mark it if it is
+ * required. Also update selected kNN strategy.
+ */
+static void
+_bt_emit_scan_key(IndexScanDesc scan, ScanKey skey, int numberOfEqualCols)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey outkey = &so->keyData[so->numberOfKeys++];
+
+ memcpy(outkey, skey, sizeof(ScanKeyData));
+
+ /*
+ * We can mark the qual as required (possibly only in one direction) if all
+ * attrs before this one had "=".
+ */
+ if (outkey->sk_attno - 1 == numberOfEqualCols)
+ _bt_mark_scankey_required(outkey);
+
+ /* Update kNN strategy if it is not already selected. */
+ if (so->useBidirectionalKnnScan)
+ {
+ switch (_bt_select_knn_strategy_for_key(scan, outkey))
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ /*
+ * Ordering key argument is greater than all values in scan
+ * range, select backward scan direction.
+ */
+ so->scanDirection = BackwardScanDirection;
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BTEqualStrategyNumber:
+ /* Use default unidirectional scan direction. */
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ /*
+ * Ordering key argument is lesser than all values in scan
+ * range, select forward scan direction.
+ */
+ so->scanDirection = ForwardScanDirection;
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BtreeKNNSearchStrategyNumber:
+ /*
+ * Ordering key argument falls into scan range,
+ * keep using bidirectional scan.
+ */
+ break;
+ }
+ }
+}
/*
* _bt_preprocess_keys() -- Preprocess scan keys
@@ -758,10 +891,8 @@ _bt_preprocess_keys(IndexScanDesc scan)
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int numberOfKeys = scan->numberOfKeys;
int16 *indoption = scan->indexRelation->rd_indoption;
- int new_numberOfKeys;
int numberOfEqualCols;
ScanKey inkeys;
- ScanKey outkeys;
ScanKey cur;
ScanKey xform[BTMaxStrategyNumber];
bool test_result;
@@ -769,6 +900,25 @@ _bt_preprocess_keys(IndexScanDesc scan)
j;
AttrNumber attno;
+ if (scan->numberOfOrderBys > 0)
+ {
+ ScanKey ord = scan->orderByData;
+
+ if (scan->numberOfOrderBys > 1 || ord->sk_attno != 1)
+ /* it should not happen, see btmatchorderby() */
+ elog(ERROR, "only one btree ordering operator "
+ "for the first index column is supported");
+
+ Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
+
+ /* use bidirectional kNN scan by default */
+ so->useBidirectionalKnnScan = true;
+ }
+ else
+ {
+ so->useBidirectionalKnnScan = false;
+ }
+
/* initialize result variables */
so->qual_ok = true;
so->numberOfKeys = 0;
@@ -784,7 +934,6 @@ _bt_preprocess_keys(IndexScanDesc scan)
else
inkeys = scan->keyData;
- outkeys = so->keyData;
cur = &inkeys[0];
/* we check that input keys are correctly ordered */
if (cur->sk_attno < 1)
@@ -796,18 +945,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
/* Apply indoption to scankey (might change sk_strategy!) */
if (!_bt_fix_scankey_strategy(cur, indoption))
so->qual_ok = false;
- memcpy(outkeys, cur, sizeof(ScanKeyData));
- so->numberOfKeys = 1;
- /* We can mark the qual as required if it's for first index col */
- if (cur->sk_attno == 1)
- _bt_mark_scankey_required(outkeys);
+
+ _bt_emit_scan_key(scan, cur, 0);
return;
}
/*
* Otherwise, do the full set of pushups.
*/
- new_numberOfKeys = 0;
numberOfEqualCols = 0;
/*
@@ -931,20 +1076,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
}
/*
- * Emit the cleaned-up keys into the outkeys[] array, and then
+ * Emit the cleaned-up keys into the so->keyData[] array, and then
* mark them if they are required. They are required (possibly
* only in one direction) if all attrs before this one had "=".
*/
for (j = BTMaxStrategyNumber; --j >= 0;)
{
if (xform[j])
- {
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, xform[j], sizeof(ScanKeyData));
- if (priorNumberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
- }
+ _bt_emit_scan_key(scan, xform[j], priorNumberOfEqualCols);
}
/*
@@ -964,17 +1103,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
/* if row comparison, push it directly to the output array */
if (cur->sk_flags & SK_ROW_HEADER)
{
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, cur, sizeof(ScanKeyData));
- if (numberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
+ _bt_emit_scan_key(scan, cur, numberOfEqualCols);
/*
* We don't support RowCompare using equality; such a qual would
* mess up the numberOfEqualCols tracking.
*/
Assert(j != (BTEqualStrategyNumber - 1));
+
continue;
}
@@ -1007,16 +1143,10 @@ _bt_preprocess_keys(IndexScanDesc scan)
* previous one in xform[j] and push this one directly to the
* output array.
*/
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, cur, sizeof(ScanKeyData));
- if (numberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
+ _bt_emit_scan_key(scan, cur, numberOfEqualCols);
}
}
}
-
- so->numberOfKeys = new_numberOfKeys;
}
/*
@@ -2075,6 +2205,39 @@ btproperty(Oid index_oid, int attno,
*res = true;
return true;
+ case AMPROP_DISTANCE_ORDERABLE:
+ {
+ Oid opclass,
+ opfamily,
+ opcindtype;
+
+ /* answer only for columns, not AM or whole index */
+ if (attno == 0)
+ return false;
+
+ opclass = get_index_column_opclass(index_oid, attno);
+
+ if (!OidIsValid(opclass))
+ {
+ *res = false; /* non-key attribute */
+ return true;
+ }
+
+ if (!get_opclass_opfamily_and_input_type(opclass,
+ &opfamily, &opcindtype))
+ {
+ *isnull = true;
+ return true;
+ }
+
+ *res = SearchSysCacheExists(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(opcindtype),
+ ObjectIdGetDatum(opcindtype),
+ Int16GetDatum(BtreeKNNSearchStrategyNumber));
+ return true;
+ }
+
default:
return false; /* punt to generic code */
}
@@ -2223,3 +2386,212 @@ _bt_allocate_tuple_workspaces(BTScanState state)
state->currTuples = (char *) palloc(BLCKSZ * 2);
state->markTuples = state->currTuples + BLCKSZ;
}
+
+static bool
+_bt_compare_row_key_with_ordering_key(ScanKey row, ScanKey ord, bool *result)
+{
+ ScanKey subkey = (ScanKey) DatumGetPointer(row->sk_argument);
+ int32 cmpresult;
+
+ Assert(subkey->sk_attno == 1);
+ Assert(subkey->sk_flags & SK_ROW_MEMBER);
+
+ if (subkey->sk_flags & SK_ISNULL)
+ return false;
+
+ /* Perform the test --- three-way comparison not bool operator */
+ cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
+ subkey->sk_collation,
+ ord->sk_argument,
+ subkey->sk_argument));
+
+ if (subkey->sk_flags & SK_BT_DESC)
+ cmpresult = -cmpresult;
+
+ /*
+ * At this point cmpresult indicates the overall result of the row
+ * comparison, and subkey points to the deciding column (or the last
+ * column if the result is "=").
+ */
+ switch (subkey->sk_strategy)
+ {
+ /* EQ and NE cases aren't allowed here */
+ case BTLessStrategyNumber:
+ *result = cmpresult < 0;
+ break;
+ case BTLessEqualStrategyNumber:
+ *result = cmpresult <= 0;
+ break;
+ case BTGreaterEqualStrategyNumber:
+ *result = cmpresult >= 0;
+ break;
+ case BTGreaterStrategyNumber:
+ *result = cmpresult > 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized RowCompareType: %d",
+ (int) subkey->sk_strategy);
+ *result = false; /* keep compiler quiet */
+ }
+
+ return true;
+}
+
+/*
+ * _bt_select_knn_strategy_for_key() -- Determine which kNN scan strategy to use:
+ * bidirectional or unidirectional. We are checking here if the
+ * ordering scankey argument falls into the scan range: if it falls
+ * we must use bidirectional scan, otherwise we use unidirectional.
+ *
+ * Returns BtreeKNNSearchStrategyNumber for bidirectional scan or
+ * strategy number of non-matched scankey for unidirectional.
+ */
+static inline StrategyNumber
+_bt_select_knn_strategy_for_key(IndexScanDesc scan, ScanKey cond)
+{
+ ScanKey ord = scan->orderByData;
+ bool result;
+
+ /* only interesting in the first index attribute */
+ if (cond->sk_attno != 1)
+ return BtreeKNNSearchStrategyNumber;
+
+ if (cond->sk_strategy == BTEqualStrategyNumber)
+ /* always use simple unidirectional scan for equals operators */
+ return BTEqualStrategyNumber;
+
+ if (cond->sk_flags & SK_ROW_HEADER)
+ {
+ if (!_bt_compare_row_key_with_ordering_key(cond, ord, &result))
+ return BTEqualStrategyNumber; /* ROW(fist_index_attr, ...) IS NULL */
+ }
+ else
+ {
+ if (!_bt_compare_scankey_args(scan, cond, ord, cond, &result))
+ elog(ERROR, "could not compare ordering key");
+ }
+
+ if (!result)
+ /*
+ * Ordering scankey argument is out of scan range,
+ * use unidirectional scan.
+ */
+ return cond->sk_strategy;
+
+ return BtreeKNNSearchStrategyNumber;
+}
+
+int
+_bt_init_knn_start_keys(IndexScanDesc scan, ScanKey *startKeys, ScanKey bufKeys)
+{
+ ScanKey ord = scan->orderByData;
+ int indopt = scan->indexRelation->rd_indoption[ord->sk_attno - 1];
+ int flags = (indopt << SK_BT_INDOPTION_SHIFT) |
+ SK_ORDER_BY |
+ SK_SEARCHNULL; /* only for invalid procedure oid, see
+ * assert in ScanKeyEntryInitialize() */
+ int keysCount = 0;
+
+ /* Init btree search key with ordering key argument. */
+ ScanKeyEntryInitialize(&bufKeys[0],
+ flags,
+ ord->sk_attno,
+ BtreeKNNSearchStrategyNumber,
+ ord->sk_subtype,
+ ord->sk_collation,
+ InvalidOid,
+ ord->sk_argument);
+
+ startKeys[keysCount++] = &bufKeys[0];
+
+ return keysCount;
+}
+
+static Oid
+_bt_get_sortfamily_for_opfamily_op(Oid opfamily, Oid lefttype, Oid righttype,
+ StrategyNumber strategy)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid sortfamily;
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(lefttype),
+ ObjectIdGetDatum(righttype),
+ Int16GetDatum(strategy));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ sortfamily = amop_tup->amopsortfamily;
+ ReleaseSysCache(tp);
+
+ return sortfamily;
+}
+
+/*
+ * _bt_get_distance_cmp_proc() -- Init procedure for comparsion of distances
+ * between "leftargtype" and "distkey".
+ */
+static void
+_bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval)
+{
+ RegProcedure opcode;
+ Oid sortfamily;
+ Oid opno;
+ Oid distanceType;
+
+ distanceType = get_func_rettype(distkey->sk_func.fn_oid);
+
+ sortfamily = _bt_get_sortfamily_for_opfamily_op(opfamily, leftargtype,
+ distkey->sk_subtype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(sortfamily))
+ elog(ERROR, "could not find sort family for btree ordering operator");
+
+ opno = get_opfamily_member(sortfamily,
+ distanceType,
+ distanceType,
+ BTLessEqualStrategyNumber);
+
+ if (!OidIsValid(opno))
+ elog(ERROR, "could not find operator for btree distance comparison");
+
+ opcode = get_opcode(opno);
+
+ if (!RegProcedureIsValid(opcode))
+ elog(ERROR,
+ "could not find procedure for btree distance comparison operator");
+
+ fmgr_info(opcode, finfo);
+
+ if (typlen)
+ get_typlenbyval(distanceType, typlen, typbyval);
+}
+
+/*
+ * _bt_init_distance_comparison() -- Init distance typlen/typbyval and its
+ * comparison procedure.
+ */
+void
+_bt_init_distance_comparison(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ Relation rel = scan->indexRelation;
+ ScanKey ord = scan->orderByData;
+
+ _bt_get_distance_cmp_proc(ord,
+ rel->rd_opfamily[ord->sk_attno - 1],
+ rel->rd_opcintype[ord->sk_attno - 1],
+ &so->distanceCmpProc,
+ &so->distanceTypeLen,
+ &so->distanceTypeByVal);
+
+ if (!so->distanceTypeByVal)
+ {
+ so->state.currDistance = PointerGetDatum(NULL);
+ so->state.markDistance = PointerGetDatum(NULL);
+ }
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 0148ea7..4558fd3 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -22,9 +22,17 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
+#define BTRequiredOperatorSet \
+ ((1 << BTLessStrategyNumber) | \
+ (1 << BTLessEqualStrategyNumber) | \
+ (1 << BTEqualStrategyNumber) | \
+ (1 << BTGreaterEqualStrategyNumber) | \
+ (1 << BTGreaterStrategyNumber))
+
/*
* Validator for a btree opclass.
@@ -132,10 +140,11 @@ btvalidate(Oid opclassoid)
{
HeapTuple oprtup = &oprlist->members[i]->tuple;
Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ Oid op_rettype;
/* Check that only allowed strategy numbers exist */
if (oprform->amopstrategy < 1 ||
- oprform->amopstrategy > BTMaxStrategyNumber)
+ oprform->amopstrategy > BtreeMaxStrategyNumber)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -146,20 +155,29 @@ btvalidate(Oid opclassoid)
result = false;
}
- /* btree doesn't support ORDER BY operators */
- if (oprform->amoppurpose != AMOP_SEARCH ||
- OidIsValid(oprform->amopsortfamily))
+ /* btree supports ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH)
{
- ereport(INFO,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
- opfamilyname, "btree",
- format_operator(oprform->amopopr))));
- result = false;
+ /* ... and operator result must match the claimed btree opfamily */
+ op_rettype = get_op_rettype(oprform->amopopr);
+ if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
+ {
+ ereport(INFO,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
+ opfamilyname, "btree",
+ format_operator(oprform->amopopr))));
+ result = false;
+ }
+ }
+ else
+ {
+ /* Search operators must always return bool */
+ op_rettype = BOOLOID;
}
/* Check operator signature --- same for all btree strategies */
- if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ if (!check_amop_signature(oprform->amopopr, op_rettype,
oprform->amoplefttype,
oprform->amoprighttype))
{
@@ -214,12 +232,8 @@ btvalidate(Oid opclassoid)
* or support functions for this datatype pair. The only things
* considered optional are the sortsupport and in_range functions.
*/
- if (thisgroup->operatorset !=
- ((1 << BTLessStrategyNumber) |
- (1 << BTLessEqualStrategyNumber) |
- (1 << BTEqualStrategyNumber) |
- (1 << BTGreaterEqualStrategyNumber) |
- (1 << BTGreaterStrategyNumber)))
+ if ((thisgroup->operatorset & BTRequiredOperatorSet) !=
+ BTRequiredOperatorSet)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 2bf87ad..2a4cc11 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -990,6 +990,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
* if we are only trying to build bitmap indexscans, nor if we have to
* assume the scan is unordered.
*/
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
!found_lower_saop_clause &&
has_useful_pathkeys(root, rel));
@@ -1000,10 +1004,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
ForwardScanDirection);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
}
- else if (index->ammatchorderby && pathkeys_possibly_useful)
+
+ if (useful_pathkeys == NIL &&
+ index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -1014,12 +1018,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
else
useful_pathkeys = NIL;
}
- else
- {
- useful_pathkeys = NIL;
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
- }
/*
* 3. Check if an index-only scan is possible. If we're not building
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 472898f..4d5442f 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -459,6 +459,12 @@ typedef struct BTScanStateData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+
+ /* KNN-search fields: */
+ Datum currDistance; /* current distance */
+ Datum markDistance; /* marked distance */
+ bool currIsNull; /* current item is NULL */
+ bool markIsNull; /* marked item is NULL */
} BTScanStateData, *BTScanState;
typedef struct BTScanOpaqueData
@@ -477,7 +483,18 @@ typedef struct BTScanOpaqueData
BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
MemoryContext arrayContext; /* scan-lifespan context for array data */
- BTScanStateData state;
+ BTScanStateData state; /* main scan state */
+
+ /* kNN-search fields: */
+ BTScanState knnState; /* optional scan state for kNN search */
+ bool useBidirectionalKnnScan; /* use bidirectional kNN scan? */
+ ScanDirection scanDirection; /* selected scan direction for
+ * unidirectional kNN scan */
+ FmgrInfo distanceCmpProc; /* distance comparison procedure */
+ int16 distanceTypeLen; /* distance typlen */
+ bool distanceTypeByVal; /* distance typebyval */
+ bool currRightIsNearest; /* current right item is nearest */
+ bool markRightIsNearest; /* marked right item is nearest */
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -523,11 +540,12 @@ extern bool btcanreturn(Relation index, int attno);
/*
* prototypes for internal functions in nbtree.c
*/
-extern bool _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno);
-extern void _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page);
-extern void _bt_parallel_done(IndexScanDesc scan);
+extern bool _bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno);
+extern void _bt_parallel_release(IndexScanDesc scan, BTScanState state, BlockNumber scan_page);
+extern void _bt_parallel_done(IndexScanDesc scan, BTScanState state);
extern void _bt_parallel_advance_array_keys(IndexScanDesc scan);
+
/*
* prototypes for functions in nbtinsert.c
*/
@@ -608,6 +626,9 @@ extern bool btproperty(Oid index_oid, int attno,
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
extern void _bt_allocate_tuple_workspaces(BTScanState state);
+extern void _bt_init_distance_comparison(IndexScanDesc scan);
+extern int _bt_init_knn_start_keys(IndexScanDesc scan, ScanKey *startKeys,
+ ScanKey bufKeys);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/include/access/stratnum.h b/src/include/access/stratnum.h
index 8fdba28..8087ffe 100644
--- a/src/include/access/stratnum.h
+++ b/src/include/access/stratnum.h
@@ -32,7 +32,10 @@ typedef uint16 StrategyNumber;
#define BTGreaterEqualStrategyNumber 4
#define BTGreaterStrategyNumber 5
-#define BTMaxStrategyNumber 5
+#define BTMaxStrategyNumber 5 /* number of canonical B-tree strategies */
+
+#define BtreeKNNSearchStrategyNumber 6 /* for <-> (distance) */
+#define BtreeMaxStrategyNumber 6 /* number of extended B-tree strategies */
/*
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 6faa9d7..c75ef39 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -347,10 +347,10 @@ ROLLBACK;
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
ERROR: access method "invalid_index_method" does not exist
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 6, must be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 0, must be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 7, must be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 0, must be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -397,11 +397,12 @@ DROP OPERATOR FAMILY alt_opf8 USING btree;
CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
-ERROR: access method "btree" does not support ordering operators
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
ALTER OPERATOR FAMILY alt_opf11 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 84fd900..73e6e206 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -295,8 +295,8 @@ ROLLBACK;
-- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -340,10 +340,12 @@ CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
--
2.7.4
0005-Add-btree-distance-operators-v05.patchtext/x-patch; name=0005-Add-btree-distance-operators-v05.patchDownload
From 78187a17b539fbf4ef621cdabe3d3955ae15b40a Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 11 Jan 2019 15:51:28 +0300
Subject: [PATCH 5/7] Add btree distance operators
---
doc/src/sgml/indices.sgml | 6 +
src/backend/utils/adt/cash.c | 20 +++
src/backend/utils/adt/date.c | 113 ++++++++++++++++
src/backend/utils/adt/float.c | 41 ++++++
src/backend/utils/adt/int.c | 52 ++++++++
src/backend/utils/adt/int8.c | 44 ++++++
src/backend/utils/adt/oid.c | 14 ++
src/backend/utils/adt/timestamp.c | 103 ++++++++++++++
src/include/catalog/pg_amop.dat | 104 +++++++++++++++
src/include/catalog/pg_operator.dat | 108 +++++++++++++++
src/include/catalog/pg_proc.dat | 86 ++++++++++++
src/include/utils/datetime.h | 2 +
src/include/utils/timestamp.h | 4 +-
src/test/regress/expected/amutils.out | 6 +-
src/test/regress/expected/date.out | 61 +++++++++
src/test/regress/expected/float4.out | 20 +++
src/test/regress/expected/float8.out | 21 +++
src/test/regress/expected/int2.out | 33 +++++
src/test/regress/expected/int4.out | 32 +++++
src/test/regress/expected/int8.out | 31 +++++
src/test/regress/expected/interval.out | 15 +++
src/test/regress/expected/money.out | 6 +
src/test/regress/expected/oid.out | 13 ++
src/test/regress/expected/opr_sanity.out | 6 +-
src/test/regress/expected/time.out | 16 +++
src/test/regress/expected/timestamp.out | 211 +++++++++++++++++++++++++++++
src/test/regress/expected/timestamptz.out | 214 ++++++++++++++++++++++++++++++
src/test/regress/sql/date.sql | 5 +
src/test/regress/sql/float4.sql | 3 +
src/test/regress/sql/float8.sql | 3 +
src/test/regress/sql/int2.sql | 10 ++
src/test/regress/sql/int4.sql | 10 ++
src/test/regress/sql/int8.sql | 5 +
src/test/regress/sql/interval.sql | 2 +
src/test/regress/sql/money.sql | 1 +
src/test/regress/sql/oid.sql | 2 +
src/test/regress/sql/opr_sanity.sql | 2 +-
src/test/regress/sql/time.sql | 3 +
src/test/regress/sql/timestamp.sql | 8 ++
src/test/regress/sql/timestamptz.sql | 8 ++
40 files changed, 1437 insertions(+), 7 deletions(-)
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index caec484..e656a8a 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -183,6 +183,12 @@ SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
</programlisting>
which finds the ten events closest to a given target date. The ability
to do this is again dependent on the particular operator class being used.
+ Built-in B-tree operator classes support distance ordering for data types
+ <type>int2</>, <type>int4</>, <type>int8</>,
+ <type>float4</>, <type>float8</>, <type>numeric</>,
+ <type>timestamp with time zone</>, <type>timestamp without time zone</>,
+ <type>time with time zone</>, <type>time without time zone</>,
+ <type>date</>, <type>interval</>, <type>oid</>, <type>money</>.
</para>
<para>
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index c92e9d5..83073c4 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -30,6 +30,7 @@
#include "utils/numeric.h"
#include "utils/pg_locale.h"
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
/*************************************************************************
* Private routines
@@ -1157,3 +1158,22 @@ int8_cash(PG_FUNCTION_ARGS)
PG_RETURN_CASH(result);
}
+
+Datum
+cash_distance(PG_FUNCTION_ARGS)
+{
+ Cash a = PG_GETARG_CASH(0);
+ Cash b = PG_GETARG_CASH(1);
+ Cash r;
+ Cash ra;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("money out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_CASH(ra);
+}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 3810e4a..6153053 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -562,6 +562,17 @@ date_mii(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(result);
}
+Datum
+date_distance(PG_FUNCTION_ARGS)
+{
+ /* we assume the difference can't overflow */
+ Datum diff = DirectFunctionCall2(date_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+}
+
/*
* Internal routines for promoting date to timestamp and timestamp with
* time zone
@@ -759,6 +770,29 @@ date_cmp_timestamp(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
+ Timestamp dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
date_eq_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
@@ -843,6 +877,30 @@ date_cmp_timestamptz(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
+
+
+Datum
timestamp_eq_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
@@ -927,6 +985,29 @@ timestamp_cmp_date(PG_FUNCTION_ARGS)
}
Datum
+timestamp_dist_date(PG_FUNCTION_ARGS)
+{
+ Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ Timestamp dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
timestamptz_eq_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
@@ -1038,6 +1119,28 @@ in_range_date_interval(PG_FUNCTION_ARGS)
BoolGetDatum(less));
}
+Datum
+timestamptz_dist_date(PG_FUNCTION_ARGS)
+{
+ TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ TimestampTz dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
/* Add an interval to a date, giving a new date.
* Must handle both positive and negative intervals.
@@ -1946,6 +2049,16 @@ time_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+Datum
+time_distance(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(time_mi_time,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
+
/*****************************************************************************
* Time With Time Zone ADT
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 117ded8..656b94d 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -3676,6 +3676,47 @@ width_bucket_float8(PG_FUNCTION_ARGS)
PG_RETURN_INT32(result);
}
+Datum
+float4dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float4 r = float4_mi(a, b);
+
+ PG_RETURN_FLOAT4(Abs(r));
+}
+
+Datum
+float8dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+
+Datum
+float48dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+Datum
+float84dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
/* ========== PRIVATE ROUTINES ========== */
#ifndef HAVE_CBRT
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index fd82a83..92227f7 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -1427,3 +1427,55 @@ generate_series_step_int4(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+Datum
+int2dist(PG_FUNCTION_ARGS)
+{
+ int16 a = PG_GETARG_INT16(0);
+ int16 b = PG_GETARG_INT16(1);
+ int16 r;
+ int16 ra;
+
+ if (pg_sub_s16_overflow(a, b, &r) ||
+ r == PG_INT16_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_INT16(ra);
+}
+
+static int32
+int44_dist(int32 a, int32 b)
+{
+ int32 r;
+
+ if (pg_sub_s32_overflow(a, b, &r) ||
+ r == PG_INT32_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ return Abs(r);
+}
+
+
+Datum
+int4dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int24dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT16(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int42dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT16(1)));
+}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index d16cc9e..a988871 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1373,3 +1373,47 @@ generate_series_step_int8(PG_FUNCTION_ARGS)
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
+
+static int64
+int88_dist(int64 a, int64 b)
+{
+ int64 r;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+
+ return Abs(r);
+}
+
+Datum
+int8dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int82dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT16(1)));
+}
+
+Datum
+int84dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT32(1)));
+}
+
+Datum
+int28dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT16(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int48dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT32(0), PG_GETARG_INT64(1)));
+}
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index eb21b07..6a43c41 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -469,3 +469,17 @@ oidvectorgt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+
+Datum
+oiddist(PG_FUNCTION_ARGS)
+{
+ Oid a = PG_GETARG_OID(0);
+ Oid b = PG_GETARG_OID(1);
+ Oid res;
+
+ if (a < b)
+ res = b - a;
+ else
+ res = a - b;
+ PG_RETURN_OID(res);
+}
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 7befb6a..4826a77 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2672,6 +2672,86 @@ timestamp_mi(PG_FUNCTION_ARGS)
PG_RETURN_INTERVAL_P(result);
}
+Datum
+timestamp_distance(PG_FUNCTION_ARGS)
+{
+ Timestamp a = PG_GETARG_TIMESTAMP(0);
+ Timestamp b = PG_GETARG_TIMESTAMP(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+ p->time = PG_INT64_MAX;
+ PG_RETURN_INTERVAL_P(p);
+ }
+ else
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+Interval *
+timestamp_dist_internal(Timestamp a, Timestamp b)
+{
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ return r;
+ }
+
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ TimestampGetDatum(a),
+ TimestampGetDatum(b)));
+
+ return abs_interval(r);
+}
+
+Datum
+timestamptz_distance(PG_FUNCTION_ARGS)
+{
+ TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
+ TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(a, b));
+}
+
+Datum
+timestamp_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ Timestamp ts1 = PG_GETARG_TIMESTAMP(0);
+ TimestampTz tstz2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz tstz1;
+
+ tstz1 = timestamp2timestamptz(ts1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+Datum
+timestamptz_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ TimestampTz tstz1 = PG_GETARG_TIMESTAMPTZ(0);
+ Timestamp ts2 = PG_GETARG_TIMESTAMP(1);
+ TimestampTz tstz2;
+
+ tstz2 = timestamp2timestamptz(ts2);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+
/*
* interval_justify_interval()
*
@@ -3541,6 +3621,29 @@ interval_avg(PG_FUNCTION_ARGS)
Float8GetDatum((double) N.time));
}
+Interval *
+abs_interval(Interval *a)
+{
+ static Interval zero = {0, 0, 0};
+
+ if (DatumGetBool(DirectFunctionCall2(interval_lt,
+ IntervalPGetDatum(a),
+ IntervalPGetDatum(&zero))))
+ a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
+ IntervalPGetDatum(a)));
+
+ return a;
+}
+
+Datum
+interval_distance(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(interval_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
/* timestamp_age()
* Calculate time difference while retaining year/month fields.
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 0ab95d8..0e06b04 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -30,6 +30,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int24
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -47,6 +51,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int28
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -64,6 +72,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int4
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -81,6 +93,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int42
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -98,6 +114,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int48
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -115,6 +135,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int8
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -132,6 +156,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int82
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -149,6 +177,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int84
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -166,6 +198,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# btree oid_ops
@@ -179,6 +215,10 @@
amopstrategy => '4', amopopr => '>=(oid,oid)', amopmethod => 'btree' },
{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid', amoprighttype => 'oid',
amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'btree' },
+{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(oid,oid)', amopmethod => 'btree',
+ amopsortfamily => 'btree/oid_ops' },
# btree tid_ops
@@ -229,6 +269,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float48
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
@@ -246,6 +290,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# default operators float8
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -263,6 +311,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float84
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -280,6 +332,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# btree char_ops
@@ -416,6 +472,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -433,6 +493,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(date,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -450,6 +514,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(date,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -467,6 +535,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamp,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -484,6 +556,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -501,6 +577,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamp,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -518,6 +598,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -535,6 +619,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'date', amopstrategy => '5',
amopopr => '>(timestamptz,date)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -552,6 +640,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamptz,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree time_ops
@@ -570,6 +662,10 @@
{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(time,time)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree timetz_ops
@@ -606,6 +702,10 @@
{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'btree' },
+{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(interval,interval)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree macaddr
@@ -781,6 +881,10 @@
{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
amoprighttype => 'money', amopstrategy => '5', amopopr => '>(money,money)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
+ amoprighttype => 'money', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(money,money)', amopmethod => 'btree',
+ amopsortfamily => 'btree/money_ops' },
# btree array_ops
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 06aec07..914ca76 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -2851,6 +2851,114 @@
oprname => '-', oprleft => 'pg_lsn', oprright => 'pg_lsn',
oprresult => 'numeric', oprcode => 'pg_lsn_mi' },
+# distance operators
+{ oid => '4217', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int2', oprresult => 'int2', oprcom => '<->(int2,int2)',
+ oprcode => 'int2dist'},
+{ oid => '4218', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int4)',
+ oprcode => 'int4dist'},
+{ oid => '4219', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int8)',
+ oprcode => 'int8dist'},
+{ oid => '4220', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'oid',
+ oprright => 'oid', oprresult => 'oid', oprcom => '<->(oid,oid)',
+ oprcode => 'oiddist'},
+{ oid => '4221', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float4', oprresult => 'float4', oprcom => '<->(float4,float4)',
+ oprcode => 'float4dist'},
+{ oid => '4222', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float8)',
+ oprcode => 'float8dist'},
+{ oid => '4223', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'money',
+ oprright => 'money', oprresult => 'money', oprcom => '<->(money,money)',
+ oprcode => 'cash_distance'},
+{ oid => '4224', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'date', oprresult => 'int4', oprcom => '<->(date,date)',
+ oprcode => 'date_distance'},
+{ oid => '4225', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'time',
+ oprright => 'time', oprresult => 'interval', oprcom => '<->(time,time)',
+ oprcode => 'time_distance'},
+{ oid => '4226', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamp)',
+ oprcode => 'timestamp_distance'},
+{ oid => '4227', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamptz)',
+ oprcode => 'timestamptz_distance'},
+{ oid => '4228', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'interval',
+ oprright => 'interval', oprresult => 'interval', oprcom => '<->(interval,interval)',
+ oprcode => 'interval_distance'},
+
+# cross-type distance operators
+{ oid => '4229', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int2)',
+ oprcode => 'int24dist'},
+{ oid => '4230', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int2', oprresult => 'int4', oprcom => '<->(int2,int4)',
+ oprcode => 'int42dist'},
+{ oid => '4231', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int2)',
+ oprcode => 'int28dist'},
+{ oid => '4232', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int2', oprresult => 'int8', oprcom => '<->(int2,int8)',
+ oprcode => 'int82dist'},
+{ oid => '4233', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int4)',
+ oprcode => 'int48dist'},
+{ oid => '4234', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int4', oprresult => 'int8', oprcom => '<->(int4,int8)',
+ oprcode => 'int84dist'},
+{ oid => '4235', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float4)',
+ oprcode => 'float48dist'},
+{ oid => '4236', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float4', oprresult => 'float8', oprcom => '<->(float4,float8)',
+ oprcode => 'float84dist'},
+{ oid => '4237', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,date)',
+ oprcode => 'date_dist_timestamp'},
+{ oid => '4238', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamp)',
+ oprcode => 'timestamp_dist_date'},
+{ oid => '4239', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,date)',
+ oprcode => 'date_dist_timestamptz'},
+{ oid => '4240', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamptz)',
+ oprcode => 'timestamptz_dist_date'},
+{ oid => '4241', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamp)',
+ oprcode => 'timestamp_dist_timestamptz'},
+{ oid => '4242', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamptz)',
+ oprcode => 'timestamptz_dist_timestamp'},
+
# enum operators
{ oid => '3516', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anyenum',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3ecc2e1..21c8377 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10509,4 +10509,90 @@
proargnames => '{rootrelid,relid,parentrelid,isleaf,level}',
prosrc => 'pg_partition_tree' },
+# distance functions
+{ oid => '4243',
+ proname => 'int2dist', prorettype => 'int2',
+ proargtypes => 'int2 int2', prosrc => 'int2dist' },
+{ oid => '4244',
+ proname => 'int4dist', prorettype => 'int4',
+ proargtypes => 'int4 int4', prosrc => 'int4dist' },
+{ oid => '4245',
+ proname => 'int8dist', prorettype => 'int8',
+ proargtypes => 'int8 int8', prosrc => 'int8dist' },
+{ oid => '4246',
+ proname => 'oiddist', proleakproof => 't', prorettype => 'oid',
+ proargtypes => 'oid oid', prosrc => 'oiddist' },
+{ oid => '4247',
+ proname => 'float4dist', prorettype => 'float4',
+ proargtypes => 'float4 float4', prosrc => 'float4dist' },
+{ oid => '4248',
+ proname => 'float8dist', prorettype => 'float8',
+ proargtypes => 'float8 float8', prosrc => 'float8dist' },
+{ oid => '4249',
+ proname => 'cash_distance', prorettype => 'money',
+ proargtypes => 'money money', prosrc => 'cash_distance' },
+{ oid => '4250',
+ proname => 'date_distance', prorettype => 'int4',
+ proargtypes => 'date date', prosrc => 'date_distance' },
+{ oid => '4251',
+ proname => 'time_distance', prorettype => 'interval',
+ proargtypes => 'time time', prosrc => 'time_distance' },
+{ oid => '4252',
+ proname => 'timestamp_distance', prorettype => 'interval',
+ proargtypes => 'timestamp timestamp', prosrc => 'timestamp_distance' },
+{ oid => '4253',
+ proname => 'timestamptz_distance', prorettype => 'interval',
+ proargtypes => 'timestamptz timestamptz', prosrc => 'timestamptz_distance' },
+{ oid => '4254',
+ proname => 'interval_distance', prorettype => 'interval',
+ proargtypes => 'interval interval', prosrc => 'interval_distance' },
+
+# cross-type distance functions
+{ oid => '4255',
+ proname => 'int24dist', prorettype => 'int4',
+ proargtypes => 'int2 int4', prosrc => 'int24dist' },
+{ oid => '4256',
+ proname => 'int28dist', prorettype => 'int8',
+ proargtypes => 'int2 int8', prosrc => 'int28dist' },
+{ oid => '4257',
+ proname => 'int42dist', prorettype => 'int4',
+ proargtypes => 'int4 int2', prosrc => 'int42dist' },
+{ oid => '4258',
+ proname => 'int48dist', prorettype => 'int8',
+ proargtypes => 'int4 int8', prosrc => 'int48dist' },
+{ oid => '4259',
+ proname => 'int82dist', prorettype => 'int8',
+ proargtypes => 'int8 int2', prosrc => 'int82dist' },
+{ oid => '4260',
+ proname => 'int84dist', prorettype => 'int8',
+ proargtypes => 'int8 int4', prosrc => 'int84dist' },
+{ oid => '4261',
+ proname => 'float48dist', prorettype => 'float8',
+ proargtypes => 'float4 float8', prosrc => 'float48dist' },
+{ oid => '4262',
+ proname => 'float84dist', prorettype => 'float8',
+ proargtypes => 'float8 float4', prosrc => 'float84dist' },
+{ oid => '4263',
+ proname => 'date_dist_timestamp', prorettype => 'interval',
+ proargtypes => 'date timestamp', prosrc => 'date_dist_timestamp' },
+{ oid => '4264',
+ proname => 'date_dist_timestamptz', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'date timestamptz',
+ prosrc => 'date_dist_timestamptz' },
+{ oid => '4265',
+ proname => 'timestamp_dist_date', prorettype => 'interval',
+ proargtypes => 'timestamp date', prosrc => 'timestamp_dist_date' },
+{ oid => '4266',
+ proname => 'timestamp_dist_timestamptz', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamp timestamptz',
+ prosrc => 'timestamp_dist_timestamptz' },
+{ oid => '4267',
+ proname => 'timestamptz_dist_date', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamptz date',
+ prosrc => 'timestamptz_dist_date' },
+{ oid => '4268',
+ proname => 'timestamptz_dist_timestamp', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamptz timestamp',
+ prosrc => 'timestamptz_dist_timestamp' },
+
]
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index f5ec9bb..53a00da 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -338,4 +338,6 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
+extern Interval *abs_interval(Interval *a);
+
#endif /* DATETIME_H */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index aeb89dc..438a5eb 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -93,9 +93,11 @@ extern Timestamp SetEpochTimestamp(void);
extern void GetEpochTime(struct pg_tm *tm);
extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
+extern Interval *timestamp_dist_internal(Timestamp a, Timestamp b);
-/* timestamp comparison works for timestamptz also */
+/* timestamp comparison and distance works for timestamptz also */
#define timestamptz_cmp_internal(dt1,dt2) timestamp_cmp_internal(dt1, dt2)
+#define timestamptz_dist_internal(dt1,dt2) timestamp_dist_internal(dt1, dt2)
extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday);
diff --git a/src/test/regress/expected/amutils.out b/src/test/regress/expected/amutils.out
index 4570a39..630dc6b 100644
--- a/src/test/regress/expected/amutils.out
+++ b/src/test/regress/expected/amutils.out
@@ -24,7 +24,7 @@ select prop,
nulls_first | | | f
nulls_last | | | t
orderable | | | t
- distance_orderable | | | f
+ distance_orderable | | | t
returnable | | | t
search_array | | | t
search_nulls | | | t
@@ -100,7 +100,7 @@ select prop,
nulls_first | f | f | f | f | f | f | f
nulls_last | t | f | f | f | f | f | f
orderable | t | f | f | f | f | f | f
- distance_orderable | f | f | t | f | t | f | f
+ distance_orderable | t | f | t | f | t | f | f
returnable | t | f | f | t | t | f | f
search_array | t | f | f | f | f | f | f
search_nulls | t | f | t | t | t | f | t
@@ -231,7 +231,7 @@ select col, prop, pg_index_column_has_property(o, col, prop)
1 | desc | f
1 | nulls_first | f
1 | nulls_last | t
- 1 | distance_orderable | f
+ 1 | distance_orderable | t
1 | returnable | t
1 | bogus |
2 | orderable | f
diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out
index 1bcc946..b9b819c 100644
--- a/src/test/regress/expected/date.out
+++ b/src/test/regress/expected/date.out
@@ -1477,3 +1477,64 @@ select make_time(10, 55, 100.1);
ERROR: time field value out of range: 10:55:100.1
select make_time(24, 0, 2.1);
ERROR: time field value out of range: 24:00:2.1
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+----------
+ | 16006
+ | 15941
+ | 1802
+ | 1801
+ | 1800
+ | 1799
+ | 1436
+ | 1435
+ | 1434
+ | 308
+ | 307
+ | 306
+ | 13578
+ | 13944
+ | 14311
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16006 days 1 hour 23 mins 45 secs
+ | @ 15941 days 1 hour 23 mins 45 secs
+ | @ 1802 days 1 hour 23 mins 45 secs
+ | @ 1801 days 1 hour 23 mins 45 secs
+ | @ 1800 days 1 hour 23 mins 45 secs
+ | @ 1799 days 1 hour 23 mins 45 secs
+ | @ 1436 days 1 hour 23 mins 45 secs
+ | @ 1435 days 1 hour 23 mins 45 secs
+ | @ 1434 days 1 hour 23 mins 45 secs
+ | @ 308 days 1 hour 23 mins 45 secs
+ | @ 307 days 1 hour 23 mins 45 secs
+ | @ 306 days 1 hour 23 mins 45 secs
+ | @ 13577 days 22 hours 36 mins 15 secs
+ | @ 13943 days 22 hours 36 mins 15 secs
+ | @ 14310 days 22 hours 36 mins 15 secs
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16005 days 14 hours 23 mins 45 secs
+ | @ 15940 days 14 hours 23 mins 45 secs
+ | @ 1801 days 14 hours 23 mins 45 secs
+ | @ 1800 days 14 hours 23 mins 45 secs
+ | @ 1799 days 14 hours 23 mins 45 secs
+ | @ 1798 days 14 hours 23 mins 45 secs
+ | @ 1435 days 14 hours 23 mins 45 secs
+ | @ 1434 days 14 hours 23 mins 45 secs
+ | @ 1433 days 14 hours 23 mins 45 secs
+ | @ 307 days 14 hours 23 mins 45 secs
+ | @ 306 days 14 hours 23 mins 45 secs
+ | @ 305 days 15 hours 23 mins 45 secs
+ | @ 13578 days 8 hours 36 mins 15 secs
+ | @ 13944 days 8 hours 36 mins 15 secs
+ | @ 14311 days 8 hours 36 mins 15 secs
+(15 rows)
+
diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out
index 2f47e1c..a9dddc4 100644
--- a/src/test/regress/expected/float4.out
+++ b/src/test/regress/expected/float4.out
@@ -257,6 +257,26 @@ SELECT '' AS five, * FROM FLOAT4_TBL;
| -1.23457e-20
(5 rows)
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+--------------+-------------
+ | 0 | 1004.3
+ | -34.84 | 1039.14
+ | -1004.3 | 2008.6
+ | -1.23457e+20 | 1.23457e+20
+ | -1.23457e-20 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+--------------+----------------------
+ | 0 | 1004.3
+ | -34.84 | 1039.14000015259
+ | -1004.3 | 2008.59998779297
+ | -1.23457e+20 | 1.23456789557014e+20
+ | -1.23457e-20 | 1004.3
+(5 rows)
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
int2
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 75c0bf3..abf9ef1 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -404,6 +404,27 @@ SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
| 1.2345678901234e-200 | 2.3112042409018e-67
(5 rows)
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.3
+ | 1004.3 | 0
+ | -34.84 | 1039.14
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.29998779297
+ | 1004.3 | 1.22070312045253e-05
+ | -34.84 | 1039.13998779297
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.29998779297
+(5 rows)
+
SELECT '' AS five, * FROM FLOAT8_TBL;
five | f1
------+----------------------
diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out
index 8c255b9..0edc57e 100644
--- a/src/test/regress/expected/int2.out
+++ b/src/test/regress/expected/int2.out
@@ -242,6 +242,39 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
| -32767 | -16383
(5 rows)
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+ERROR: smallint out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+ four | f1 | x
+------+-------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
-- corner cases
SELECT (-1::int2<<15)::text;
text
diff --git a/src/test/regress/expected/int4.out b/src/test/regress/expected/int4.out
index bda7a8d..3735dbc 100644
--- a/src/test/regress/expected/int4.out
+++ b/src/test/regress/expected/int4.out
@@ -247,6 +247,38 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
| -2147483647 | -1073741823
(5 rows)
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+ERROR: integer out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+ five | f1 | x
+------+-------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+ | -2147483647 | 2147483649
+(5 rows)
+
--
-- more complex expressions
--
diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out
index 35e3b3f..8940e81 100644
--- a/src/test/regress/expected/int8.out
+++ b/src/test/regress/expected/int8.out
@@ -432,6 +432,37 @@ SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 A
4567890123457035 | -4567890123456543 | 1123700970370370094 | 0
(5 rows)
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
SELECT q2, abs(q2) FROM INT8_TBL;
q2 | abs
-------------------+------------------
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index f88f345..cb95adf 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -207,6 +207,21 @@ SELECT '' AS fortyfive, r1.*, r2.*
| 34 years | 6 years
(45 rows)
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+ ten | ?column?
+-----+----------------------------
+ | 2 days 02:59:00
+ | 2 days -02:00:00
+ | 8 days -03:00:00
+ | 34 years -2 days -03:00:00
+ | 3 mons -2 days -03:00:00
+ | 2 days 03:00:14
+ | 1 day 00:56:56
+ | 6 years -2 days -03:00:00
+ | 5 mons -2 days -03:00:00
+ | 5 mons -2 days +09:00:00
+(10 rows)
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out
index ab86595..fb2a489 100644
--- a/src/test/regress/expected/money.out
+++ b/src/test/regress/expected/money.out
@@ -123,6 +123,12 @@ SELECT m / 2::float4 FROM money_data;
$61.50
(1 row)
+SELECT m <-> '$123.45' FROM money_data;
+ ?column?
+----------
+ $0.45
+(1 row)
+
-- All true
SELECT m = '$123.00' FROM money_data;
?column?
diff --git a/src/test/regress/expected/oid.out b/src/test/regress/expected/oid.out
index 1eab9cc..5339a48 100644
--- a/src/test/regress/expected/oid.out
+++ b/src/test/regress/expected/oid.out
@@ -119,4 +119,17 @@ SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
| 99999999
(3 rows)
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+ eight | f1 | ?column?
+-------+------------+------------
+ | 1234 | 1111
+ | 1235 | 1112
+ | 987 | 864
+ | 4294966256 | 4294966133
+ | 99999999 | 99999876
+ | 5 | 118
+ | 10 | 113
+ | 15 | 108
+(8 rows)
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 7328095..9b1babc 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -750,6 +750,7 @@ macaddr8_gt(macaddr8,macaddr8)
macaddr8_ge(macaddr8,macaddr8)
macaddr8_ne(macaddr8,macaddr8)
macaddr8_cmp(macaddr8,macaddr8)
+oiddist(oid,oid)
-- restore normal output mode
\a\t
-- List of functions used by libpq's fe-lobj.c
@@ -1332,7 +1333,7 @@ WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND
ao.amoprighttype = ap.amprocrighttype AND
ap.amprocnum = 1 AND
(pp.provolatile != po.provolatile OR
- pp.proleakproof != po.proleakproof)
+ (NOT pp.proleakproof AND po.proleakproof))
ORDER BY 1;
proc | vp | lp | opr | vo | lo
-------------------------------------------+----+----+-------------------------------+----+----
@@ -1862,6 +1863,7 @@ ORDER BY 1, 2, 3;
403 | 5 | *>
403 | 5 | >
403 | 5 | ~>~
+ 403 | 6 | <->
405 | 1 | =
783 | 1 | <<
783 | 1 | @@
@@ -1971,7 +1973,7 @@ ORDER BY 1, 2, 3;
4000 | 26 | >>
4000 | 27 | >>=
4000 | 28 | ^@
-(123 rows)
+(124 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/expected/time.out b/src/test/regress/expected/time.out
index 8e0afe6..ee74faa 100644
--- a/src/test/regress/expected/time.out
+++ b/src/test/regress/expected/time.out
@@ -86,3 +86,19 @@ ERROR: operator is not unique: time without time zone + time without time zone
LINE 1: SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
+ Ten | Distance
+-------------+-------------------------------
+ 00:00:00 | @ 1 hour 23 mins 45 secs
+ 01:00:00 | @ 23 mins 45 secs
+ 02:03:00 | @ 39 mins 15 secs
+ 11:59:00 | @ 10 hours 35 mins 15 secs
+ 12:00:00 | @ 10 hours 36 mins 15 secs
+ 12:01:00 | @ 10 hours 37 mins 15 secs
+ 23:59:00 | @ 22 hours 35 mins 15 secs
+ 23:59:59.99 | @ 22 hours 36 mins 14.99 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+(10 rows)
+
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index 4a2fabd..dcb4205 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -1604,3 +1604,214 @@ SELECT make_timestamp(2014,12,28,6,30,45.887);
Sun Dec 28 06:30:45.887 2014
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 231 days 18 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 10 hours 45 mins 58 secs
+ | @ 324 days 11 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 21 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 5 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 1 hour 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 231 days 16 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 12 hours 9 mins 43 secs
+ | @ 324 days 13 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 324 days 23 hours 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 6 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 14 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 1 hour 9 mins 43 secs
+ | @ 324 days 2 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 12 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(63 rows)
+
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 8a4c719..0a05e37 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -2544,3 +2544,217 @@ select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
Tue Jan 17 16:00:00 2017 PST
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 8 hours
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 7 hours 27 mins 59 secs
+ | @ 231 days 17 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 19 hours 45 mins 58 secs
+ | @ 324 days 21 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 22 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 9 hours 27 mins 59 secs
+ | @ 1303 days 10 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 9 hours 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 8 hours 51 mins 44 secs
+ | @ 231 days 15 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 21 hours 9 mins 43 secs
+ | @ 324 days 23 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 325 days 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 10 hours 51 mins 44 secs
+ | @ 1303 days 11 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 22 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 10 hours 9 mins 43 secs
+ | @ 324 days 12 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 13 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1452 days 23 hours 51 mins 44 secs
+ | @ 1303 days 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(64 rows)
+
diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql
index 22f80f2..24be476 100644
--- a/src/test/regress/sql/date.sql
+++ b/src/test/regress/sql/date.sql
@@ -346,3 +346,8 @@ select make_date(2013, 13, 1);
select make_date(2013, 11, -1);
select make_time(10, 55, 100.1);
select make_time(24, 0, 2.1);
+
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql
index 46a9166..f97f80a 100644
--- a/src/test/regress/sql/float4.sql
+++ b/src/test/regress/sql/float4.sql
@@ -82,6 +82,9 @@ UPDATE FLOAT4_TBL
SELECT '' AS five, * FROM FLOAT4_TBL;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
SELECT '32767.6'::float4::int2;
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 6595fd2..9b50210 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -125,6 +125,9 @@ SELECT ||/ float8 '27' AS three;
SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
SELECT '' AS five, * FROM FLOAT8_TBL;
diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql
index 7dbafb6..16dd5d8 100644
--- a/src/test/regress/sql/int2.sql
+++ b/src/test/regress/sql/int2.sql
@@ -84,6 +84,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+
-- corner cases
SELECT (-1::int2<<15)::text;
SELECT ((-1::int2<<15)+1::int2)::text;
diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql
index f014cb2..cff32946 100644
--- a/src/test/regress/sql/int4.sql
+++ b/src/test/regress/sql/int4.sql
@@ -93,6 +93,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT4_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+
--
-- more complex expressions
--
diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql
index e890452..d7f5bde 100644
--- a/src/test/regress/sql/int8.sql
+++ b/src/test/regress/sql/int8.sql
@@ -89,6 +89,11 @@ SELECT q1 + 42::int2 AS "8plus2", q1 - 42::int2 AS "8minus2", q1 * 42::int2 AS "
-- int2 op int8
SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 AS "2mul8", 246::int2 / q1 AS "2div8" FROM INT8_TBL;
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+
SELECT q2, abs(q2) FROM INT8_TBL;
SELECT min(q1), min(q2) FROM INT8_TBL;
SELECT max(q1), max(q2) FROM INT8_TBL;
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index bc5537d..d51c866 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -59,6 +59,8 @@ SELECT '' AS fortyfive, r1.*, r2.*
WHERE r1.f1 > r2.f1
ORDER BY r1.f1, r2.f1;
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql
index 37b9ecc..8428d59 100644
--- a/src/test/regress/sql/money.sql
+++ b/src/test/regress/sql/money.sql
@@ -25,6 +25,7 @@ SELECT m / 2::float8 FROM money_data;
SELECT m * 2::float4 FROM money_data;
SELECT 2::float4 * m FROM money_data;
SELECT m / 2::float4 FROM money_data;
+SELECT m <-> '$123.45' FROM money_data;
-- All true
SELECT m = '$123.00' FROM money_data;
diff --git a/src/test/regress/sql/oid.sql b/src/test/regress/sql/oid.sql
index 4a09689..9f54f92 100644
--- a/src/test/regress/sql/oid.sql
+++ b/src/test/regress/sql/oid.sql
@@ -40,4 +40,6 @@ SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 >= '1234';
SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 8544cbe..e6688df 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -818,7 +818,7 @@ WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND
ao.amoprighttype = ap.amprocrighttype AND
ap.amprocnum = 1 AND
(pp.provolatile != po.provolatile OR
- pp.proleakproof != po.proleakproof)
+ (NOT pp.proleakproof AND po.proleakproof))
ORDER BY 1;
diff --git a/src/test/regress/sql/time.sql b/src/test/regress/sql/time.sql
index 99a1562..31f0330 100644
--- a/src/test/regress/sql/time.sql
+++ b/src/test/regress/sql/time.sql
@@ -40,3 +40,6 @@ SELECT f1 AS "Eight" FROM TIME_TBL WHERE f1 >= '00:00';
-- where we do mixed-type arithmetic. - thomas 2000-12-02
SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
+
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index b7957cb..5d023dd 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -230,3 +230,11 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
-- timestamp numeric fields constructor
SELECT make_timestamp(2014,12,28,6,30,45.887);
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index c3bd46c..7f0525d 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -464,3 +464,11 @@ insert into tmptz values ('2017-01-18 00:00+00');
explain (costs off)
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
--
2.7.4
0006-Remove-distance-operators-from-btree_gist-v05.patchtext/x-patch; name=0006-Remove-distance-operators-from-btree_gist-v05.patchDownload
From 229d8a167f9b4e6b31276ec70b46c2994a6c2f47 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 11 Jan 2019 15:51:29 +0300
Subject: [PATCH 6/7] Remove distance operators from btree_gist
---
contrib/btree_gist/Makefile | 5 +-
contrib/btree_gist/btree_cash.c | 15 +-
contrib/btree_gist/btree_date.c | 7 +-
contrib/btree_gist/btree_float4.c | 9 +-
contrib/btree_gist/btree_float8.c | 9 +-
contrib/btree_gist/btree_gist--1.2.sql | 1570 --------------------------
contrib/btree_gist/btree_gist--1.5--1.6.sql | 99 ++
contrib/btree_gist/btree_gist--1.6.sql | 1615 +++++++++++++++++++++++++++
contrib/btree_gist/btree_gist.control | 2 +-
contrib/btree_gist/btree_int2.c | 15 +-
contrib/btree_gist/btree_int4.c | 15 +-
contrib/btree_gist/btree_int8.c | 15 +-
contrib/btree_gist/btree_interval.c | 6 +-
contrib/btree_gist/btree_oid.c | 10 +-
contrib/btree_gist/btree_time.c | 6 +-
contrib/btree_gist/btree_ts.c | 38 +-
doc/src/sgml/btree-gist.sgml | 13 +
17 files changed, 1743 insertions(+), 1706 deletions(-)
delete mode 100644 contrib/btree_gist/btree_gist--1.2.sql
create mode 100644 contrib/btree_gist/btree_gist--1.5--1.6.sql
create mode 100644 contrib/btree_gist/btree_gist--1.6.sql
diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile
index af65120..46ab241 100644
--- a/contrib/btree_gist/Makefile
+++ b/contrib/btree_gist/Makefile
@@ -11,8 +11,9 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
EXTENSION = btree_gist
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
- btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \
- btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql
+ btree_gist--1.1--1.2.sql btree_gist--1.2--1.3.sql \
+ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \
+ btree_gist--1.5--1.6.sql btree_gist--1.6.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c
index 894d0a2..1b0e317 100644
--- a/contrib/btree_gist/btree_cash.c
+++ b/contrib/btree_gist/btree_cash.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(cash_dist);
Datum
cash_dist(PG_FUNCTION_ARGS)
{
- Cash a = PG_GETARG_CASH(0);
- Cash b = PG_GETARG_CASH(1);
- Cash r;
- Cash ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("money out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_CASH(ra);
+ return cash_distance(fcinfo);
}
/**************************************************
diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c
index 992ce57..f3f0fa1 100644
--- a/contrib/btree_gist/btree_date.c
+++ b/contrib/btree_gist/btree_date.c
@@ -113,12 +113,7 @@ PG_FUNCTION_INFO_V1(date_dist);
Datum
date_dist(PG_FUNCTION_ARGS)
{
- /* we assume the difference can't overflow */
- Datum diff = DirectFunctionCall2(date_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+ return date_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c
index 6b20f44..0a9148d 100644
--- a/contrib/btree_gist/btree_float4.c
+++ b/contrib/btree_gist/btree_float4.c
@@ -93,14 +93,7 @@ PG_FUNCTION_INFO_V1(float4_dist);
Datum
float4_dist(PG_FUNCTION_ARGS)
{
- float4 a = PG_GETARG_FLOAT4(0);
- float4 b = PG_GETARG_FLOAT4(1);
- float4 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT4(Abs(r));
+ return float4dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c
index ee114cb..8b73b57 100644
--- a/contrib/btree_gist/btree_float8.c
+++ b/contrib/btree_gist/btree_float8.c
@@ -101,14 +101,7 @@ PG_FUNCTION_INFO_V1(float8_dist);
Datum
float8_dist(PG_FUNCTION_ARGS)
{
- float8 a = PG_GETARG_FLOAT8(0);
- float8 b = PG_GETARG_FLOAT8(1);
- float8 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT8(Abs(r));
+ return float8dist(fcinfo);
}
/**************************************************
diff --git a/contrib/btree_gist/btree_gist--1.2.sql b/contrib/btree_gist/btree_gist--1.2.sql
deleted file mode 100644
index 1efe753..0000000
--- a/contrib/btree_gist/btree_gist--1.2.sql
+++ /dev/null
@@ -1,1570 +0,0 @@
-/* contrib/btree_gist/btree_gist--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
-
-CREATE FUNCTION gbtreekey4_in(cstring)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey4_out(gbtreekey4)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey4 (
- INTERNALLENGTH = 4,
- INPUT = gbtreekey4_in,
- OUTPUT = gbtreekey4_out
-);
-
-CREATE FUNCTION gbtreekey8_in(cstring)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey8_out(gbtreekey8)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey8 (
- INTERNALLENGTH = 8,
- INPUT = gbtreekey8_in,
- OUTPUT = gbtreekey8_out
-);
-
-CREATE FUNCTION gbtreekey16_in(cstring)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey16_out(gbtreekey16)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey16 (
- INTERNALLENGTH = 16,
- INPUT = gbtreekey16_in,
- OUTPUT = gbtreekey16_out
-);
-
-CREATE FUNCTION gbtreekey32_in(cstring)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey32_out(gbtreekey32)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey32 (
- INTERNALLENGTH = 32,
- INPUT = gbtreekey32_in,
- OUTPUT = gbtreekey32_out
-);
-
-CREATE FUNCTION gbtreekey_var_in(cstring)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey_var (
- INTERNALLENGTH = VARIABLE,
- INPUT = gbtreekey_var_in,
- OUTPUT = gbtreekey_var_out,
- STORAGE = EXTENDED
-);
-
---distance operators
-
-CREATE FUNCTION cash_dist(money, money)
-RETURNS money
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = money,
- RIGHTARG = money,
- PROCEDURE = cash_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION date_dist(date, date)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = date,
- RIGHTARG = date,
- PROCEDURE = date_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float4_dist(float4, float4)
-RETURNS float4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float4,
- RIGHTARG = float4,
- PROCEDURE = float4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float8_dist(float8, float8)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float8,
- RIGHTARG = float8,
- PROCEDURE = float8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int2_dist(int2, int2)
-RETURNS int2
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int2,
- RIGHTARG = int2,
- PROCEDURE = int2_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int4_dist(int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int4,
- RIGHTARG = int4,
- PROCEDURE = int4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int8_dist(int8, int8)
-RETURNS int8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int8,
- RIGHTARG = int8,
- PROCEDURE = int8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION interval_dist(interval, interval)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = interval,
- RIGHTARG = interval,
- PROCEDURE = interval_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION oid_dist(oid, oid)
-RETURNS oid
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = oid,
- RIGHTARG = oid,
- PROCEDURE = oid_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION time_dist(time, time)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = time,
- RIGHTARG = time,
- PROCEDURE = time_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION ts_dist(timestamp, timestamp)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamp,
- RIGHTARG = timestamp,
- PROCEDURE = ts_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION tstz_dist(timestamptz, timestamptz)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamptz,
- RIGHTARG = timestamptz,
- PROCEDURE = tstz_dist,
- COMMUTATOR = '<->'
-);
-
-
---
---
---
--- oid ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_oid_ops
-DEFAULT FOR TYPE oid USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
- FUNCTION 2 gbt_oid_union (internal, internal),
- FUNCTION 3 gbt_oid_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
- FUNCTION 6 gbt_oid_picksplit (internal, internal),
- FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
--- Add operators that are new in 9.1. We do it like this, leaving them
--- "loose" in the operator family rather than bound into the opclass, because
--- that's the only state that can be reproduced during an upgrade from 9.0.
-ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
- OPERATOR 6 <> (oid, oid) ,
- OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
- FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
- -- Also add support function for index-only-scans, added in 9.5.
- FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
-
-
---
---
---
--- int2 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_union(internal, internal)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int2_ops
-DEFAULT FOR TYPE int2 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
- FUNCTION 2 gbt_int2_union (internal, internal),
- FUNCTION 3 gbt_int2_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int2_picksplit (internal, internal),
- FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
- STORAGE gbtreekey4;
-
-ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
- OPERATOR 6 <> (int2, int2) ,
- OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
- FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
-
---
---
---
--- int4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int4_ops
-DEFAULT FOR TYPE int4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
- FUNCTION 2 gbt_int4_union (internal, internal),
- FUNCTION 3 gbt_int4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int4_picksplit (internal, internal),
- FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
- OPERATOR 6 <> (int4, int4) ,
- OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
- FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
-
-
---
---
---
--- int8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int8_ops
-DEFAULT FOR TYPE int8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
- FUNCTION 2 gbt_int8_union (internal, internal),
- FUNCTION 3 gbt_int8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int8_picksplit (internal, internal),
- FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
- OPERATOR 6 <> (int8, int8) ,
- OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
- FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
-
---
---
---
--- float4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float4_ops
-DEFAULT FOR TYPE float4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
- FUNCTION 2 gbt_float4_union (internal, internal),
- FUNCTION 3 gbt_float4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float4_picksplit (internal, internal),
- FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
- OPERATOR 6 <> (float4, float4) ,
- OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
- FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
-
---
---
---
--- float8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float8_ops
-DEFAULT FOR TYPE float8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
- FUNCTION 2 gbt_float8_union (internal, internal),
- FUNCTION 3 gbt_float8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float8_picksplit (internal, internal),
- FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
- OPERATOR 6 <> (float8, float8) ,
- OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
- FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
-
---
---
---
--- timestamp ops
---
---
---
-
-CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamp_ops
-DEFAULT FOR TYPE timestamp USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_ts_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
- OPERATOR 6 <> (timestamp, timestamp) ,
- OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
- FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamptz_ops
-DEFAULT FOR TYPE timestamptz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_tstz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
- OPERATOR 6 <> (timestamptz, timestamptz) ,
- OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
- FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
-
---
---
---
--- time ops
---
---
---
-
-CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_time_ops
-DEFAULT FOR TYPE time USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_time_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
- OPERATOR 6 <> (time, time) ,
- OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
- FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
-
-
-CREATE OPERATOR CLASS gist_timetz_ops
-DEFAULT FOR TYPE timetz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_timetz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
- OPERATOR 6 <> (timetz, timetz) ;
- -- no 'fetch' function, as the compress function is lossy.
-
-
---
---
---
--- date ops
---
---
---
-
-CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_date_ops
-DEFAULT FOR TYPE date USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
- FUNCTION 2 gbt_date_union (internal, internal),
- FUNCTION 3 gbt_date_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_date_penalty (internal, internal, internal),
- FUNCTION 6 gbt_date_picksplit (internal, internal),
- FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
- OPERATOR 6 <> (date, date) ,
- OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
- FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
-
-
---
---
---
--- interval ops
---
---
---
-
-CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_union(internal, internal)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_interval_ops
-DEFAULT FOR TYPE interval USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
- FUNCTION 2 gbt_intv_union (internal, internal),
- FUNCTION 3 gbt_intv_compress (internal),
- FUNCTION 4 gbt_intv_decompress (internal),
- FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
- FUNCTION 6 gbt_intv_picksplit (internal, internal),
- FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
- STORAGE gbtreekey32;
-
-ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
- OPERATOR 6 <> (interval, interval) ,
- OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
- FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
-
-
---
---
---
--- cash ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cash_ops
-DEFAULT FOR TYPE money USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
- FUNCTION 2 gbt_cash_union (internal, internal),
- FUNCTION 3 gbt_cash_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
- FUNCTION 6 gbt_cash_picksplit (internal, internal),
- FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
- OPERATOR 6 <> (money, money) ,
- OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
- FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
- FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
-
-
---
---
---
--- macaddr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_macaddr_ops
-DEFAULT FOR TYPE macaddr USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
- FUNCTION 2 gbt_macad_union (internal, internal),
- FUNCTION 3 gbt_macad_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
- FUNCTION 6 gbt_macad_picksplit (internal, internal),
- FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
- OPERATOR 6 <> (macaddr, macaddr) ,
- FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
-
-
---
---
---
--- text/ bpchar ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_text_ops
-DEFAULT FOR TYPE text USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_text_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
- OPERATOR 6 <> (text, text) ,
- FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
-
-
----- Create the operator class
-CREATE OPERATOR CLASS gist_bpchar_ops
-DEFAULT FOR TYPE bpchar USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_bpchar_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
- OPERATOR 6 <> (bpchar, bpchar) ,
- FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
-
---
---
--- bytea ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bytea_ops
-DEFAULT FOR TYPE bytea USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
- FUNCTION 2 gbt_bytea_union (internal, internal),
- FUNCTION 3 gbt_bytea_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bytea_picksplit (internal, internal),
- FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
- OPERATOR 6 <> (bytea, bytea) ,
- FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
-
-
---
---
---
--- numeric ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_numeric_ops
-DEFAULT FOR TYPE numeric USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
- FUNCTION 2 gbt_numeric_union (internal, internal),
- FUNCTION 3 gbt_numeric_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
- FUNCTION 6 gbt_numeric_picksplit (internal, internal),
- FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
- OPERATOR 6 <> (numeric, numeric) ,
- FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
-
-
---
---
--- bit ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bit_ops
-DEFAULT FOR TYPE bit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
- OPERATOR 6 <> (bit, bit) ,
- FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
-
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_vbit_ops
-DEFAULT FOR TYPE varbit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
- OPERATOR 6 <> (varbit, varbit) ,
- FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
-
-
---
---
---
--- inet/cidr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_inet_ops
-DEFAULT FOR TYPE inet USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cidr_ops
-DEFAULT FOR TYPE cidr USING gist
-AS
- OPERATOR 1 < (inet, inet) ,
- OPERATOR 2 <= (inet, inet) ,
- OPERATOR 3 = (inet, inet) ,
- OPERATOR 4 >= (inet, inet) ,
- OPERATOR 5 > (inet, inet) ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
diff --git a/contrib/btree_gist/btree_gist--1.5--1.6.sql b/contrib/btree_gist/btree_gist--1.5--1.6.sql
new file mode 100644
index 0000000..ef4424e
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.5--1.6.sql
@@ -0,0 +1,99 @@
+/* contrib/btree_gist/btree_gist--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.6'" to load this file. \quit
+
+-- drop btree_gist distance operators from opfamilies
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist DROP OPERATOR 15 (int2, int2);
+ALTER OPERATOR FAMILY gist_int4_ops USING gist DROP OPERATOR 15 (int4, int4);
+ALTER OPERATOR FAMILY gist_int8_ops USING gist DROP OPERATOR 15 (int8, int8);
+ALTER OPERATOR FAMILY gist_float4_ops USING gist DROP OPERATOR 15 (float4, float4);
+ALTER OPERATOR FAMILY gist_float8_ops USING gist DROP OPERATOR 15 (float8, float8);
+ALTER OPERATOR FAMILY gist_oid_ops USING gist DROP OPERATOR 15 (oid, oid);
+ALTER OPERATOR FAMILY gist_cash_ops USING gist DROP OPERATOR 15 (money, money);
+ALTER OPERATOR FAMILY gist_date_ops USING gist DROP OPERATOR 15 (date, date);
+ALTER OPERATOR FAMILY gist_time_ops USING gist DROP OPERATOR 15 (time, time);
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist DROP OPERATOR 15 (timestamp, timestamp);
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist DROP OPERATOR 15 (timestamptz, timestamptz);
+ALTER OPERATOR FAMILY gist_interval_ops USING gist DROP OPERATOR 15 (interval, interval);
+
+-- add pg_catalog distance operators to opfamilies
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops;
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops;
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops;
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops;
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops;
+
+-- disable implicit pg_catalog search
+
+DO
+$$
+BEGIN
+ EXECUTE 'SET LOCAL search_path TO ' || current_schema() || ', pg_catalog';
+END
+$$;
+
+-- drop distance operators
+
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int2, int2);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int4, int4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int8, int8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float4, float4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float8, float8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (oid, oid);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (money, money);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (date, date);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (time, time);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (interval, interval);
+
+DROP OPERATOR <-> (int2, int2);
+DROP OPERATOR <-> (int4, int4);
+DROP OPERATOR <-> (int8, int8);
+DROP OPERATOR <-> (float4, float4);
+DROP OPERATOR <-> (float8, float8);
+DROP OPERATOR <-> (oid, oid);
+DROP OPERATOR <-> (money, money);
+DROP OPERATOR <-> (date, date);
+DROP OPERATOR <-> (time, time);
+DROP OPERATOR <-> (timestamp, timestamp);
+DROP OPERATOR <-> (timestamptz, timestamptz);
+DROP OPERATOR <-> (interval, interval);
+
+-- drop distance functions
+
+ALTER EXTENSION btree_gist DROP FUNCTION int2_dist(int2, int2);
+ALTER EXTENSION btree_gist DROP FUNCTION int4_dist(int4, int4);
+ALTER EXTENSION btree_gist DROP FUNCTION int8_dist(int8, int8);
+ALTER EXTENSION btree_gist DROP FUNCTION float4_dist(float4, float4);
+ALTER EXTENSION btree_gist DROP FUNCTION float8_dist(float8, float8);
+ALTER EXTENSION btree_gist DROP FUNCTION oid_dist(oid, oid);
+ALTER EXTENSION btree_gist DROP FUNCTION cash_dist(money, money);
+ALTER EXTENSION btree_gist DROP FUNCTION date_dist(date, date);
+ALTER EXTENSION btree_gist DROP FUNCTION time_dist(time, time);
+ALTER EXTENSION btree_gist DROP FUNCTION ts_dist(timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP FUNCTION interval_dist(interval, interval);
+
+DROP FUNCTION int2_dist(int2, int2);
+DROP FUNCTION int4_dist(int4, int4);
+DROP FUNCTION int8_dist(int8, int8);
+DROP FUNCTION float4_dist(float4, float4);
+DROP FUNCTION float8_dist(float8, float8);
+DROP FUNCTION oid_dist(oid, oid);
+DROP FUNCTION cash_dist(money, money);
+DROP FUNCTION date_dist(date, date);
+DROP FUNCTION time_dist(time, time);
+DROP FUNCTION ts_dist(timestamp, timestamp);
+DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+DROP FUNCTION interval_dist(interval, interval);
diff --git a/contrib/btree_gist/btree_gist--1.6.sql b/contrib/btree_gist/btree_gist--1.6.sql
new file mode 100644
index 0000000..8ff8eb5
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.6.sql
@@ -0,0 +1,1615 @@
+/* contrib/btree_gist/btree_gist--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
+
+CREATE FUNCTION gbtreekey4_in(cstring)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey4_out(gbtreekey4)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey4 (
+ INTERNALLENGTH = 4,
+ INPUT = gbtreekey4_in,
+ OUTPUT = gbtreekey4_out
+);
+
+CREATE FUNCTION gbtreekey8_in(cstring)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey8_out(gbtreekey8)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey8 (
+ INTERNALLENGTH = 8,
+ INPUT = gbtreekey8_in,
+ OUTPUT = gbtreekey8_out
+);
+
+CREATE FUNCTION gbtreekey16_in(cstring)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey16_out(gbtreekey16)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey16 (
+ INTERNALLENGTH = 16,
+ INPUT = gbtreekey16_in,
+ OUTPUT = gbtreekey16_out
+);
+
+CREATE FUNCTION gbtreekey32_in(cstring)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey32_out(gbtreekey32)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey32 (
+ INTERNALLENGTH = 32,
+ INPUT = gbtreekey32_in,
+ OUTPUT = gbtreekey32_out
+);
+
+CREATE FUNCTION gbtreekey_var_in(cstring)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey_var (
+ INTERNALLENGTH = VARIABLE,
+ INPUT = gbtreekey_var_in,
+ OUTPUT = gbtreekey_var_out,
+ STORAGE = EXTENDED
+);
+
+
+--
+--
+--
+-- oid ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_oid_ops
+DEFAULT FOR TYPE oid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
+ FUNCTION 2 gbt_oid_union (internal, internal),
+ FUNCTION 3 gbt_oid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_oid_picksplit (internal, internal),
+ FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+-- Add operators that are new in 9.1. We do it like this, leaving them
+-- "loose" in the operator family rather than bound into the opclass, because
+-- that's the only state that can be reproduced during an upgrade from 9.0.
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
+ OPERATOR 6 <> (oid, oid) ,
+ OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
+ FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
+ -- Also add support function for index-only-scans, added in 9.5.
+ FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
+
+
+--
+--
+--
+-- int2 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_union(internal, internal)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int2_ops
+DEFAULT FOR TYPE int2 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
+ FUNCTION 2 gbt_int2_union (internal, internal),
+ FUNCTION 3 gbt_int2_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int2_picksplit (internal, internal),
+ FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
+ STORAGE gbtreekey4;
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
+ OPERATOR 6 <> (int2, int2) ,
+ OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
+ FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
+
+--
+--
+--
+-- int4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int4_ops
+DEFAULT FOR TYPE int4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
+ FUNCTION 2 gbt_int4_union (internal, internal),
+ FUNCTION 3 gbt_int4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int4_picksplit (internal, internal),
+ FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
+ OPERATOR 6 <> (int4, int4) ,
+ OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
+ FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
+
+
+--
+--
+--
+-- int8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int8_ops
+DEFAULT FOR TYPE int8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
+ FUNCTION 2 gbt_int8_union (internal, internal),
+ FUNCTION 3 gbt_int8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int8_picksplit (internal, internal),
+ FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
+ OPERATOR 6 <> (int8, int8) ,
+ OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
+ FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
+
+--
+--
+--
+-- float4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float4_ops
+DEFAULT FOR TYPE float4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
+ FUNCTION 2 gbt_float4_union (internal, internal),
+ FUNCTION 3 gbt_float4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float4_picksplit (internal, internal),
+ FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
+ OPERATOR 6 <> (float4, float4) ,
+ OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
+ FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
+
+--
+--
+--
+-- float8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float8_ops
+DEFAULT FOR TYPE float8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
+ FUNCTION 2 gbt_float8_union (internal, internal),
+ FUNCTION 3 gbt_float8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float8_picksplit (internal, internal),
+ FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
+ OPERATOR 6 <> (float8, float8) ,
+ OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
+ FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
+
+--
+--
+--
+-- timestamp ops
+--
+--
+--
+
+CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamp_ops
+DEFAULT FOR TYPE timestamp USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_ts_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
+ OPERATOR 6 <> (timestamp, timestamp) ,
+ OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
+ FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamptz_ops
+DEFAULT FOR TYPE timestamptz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_tstz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
+ OPERATOR 6 <> (timestamptz, timestamptz) ,
+ OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
+ FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
+
+--
+--
+--
+-- time ops
+--
+--
+--
+
+CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_time_ops
+DEFAULT FOR TYPE time USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_time_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
+ OPERATOR 6 <> (time, time) ,
+ OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
+ FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
+
+
+CREATE OPERATOR CLASS gist_timetz_ops
+DEFAULT FOR TYPE timetz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_timetz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
+ OPERATOR 6 <> (timetz, timetz) ;
+ -- no 'fetch' function, as the compress function is lossy.
+
+
+--
+--
+--
+-- date ops
+--
+--
+--
+
+CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_date_ops
+DEFAULT FOR TYPE date USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
+ FUNCTION 2 gbt_date_union (internal, internal),
+ FUNCTION 3 gbt_date_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_date_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_date_picksplit (internal, internal),
+ FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
+ OPERATOR 6 <> (date, date) ,
+ OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
+ FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
+
+
+--
+--
+--
+-- interval ops
+--
+--
+--
+
+CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_interval_ops
+DEFAULT FOR TYPE interval USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
+ FUNCTION 2 gbt_intv_union (internal, internal),
+ FUNCTION 3 gbt_intv_compress (internal),
+ FUNCTION 4 gbt_intv_decompress (internal),
+ FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_intv_picksplit (internal, internal),
+ FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
+ OPERATOR 6 <> (interval, interval) ,
+ OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
+ FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
+
+
+--
+--
+--
+-- cash ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cash_ops
+DEFAULT FOR TYPE money USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
+ FUNCTION 2 gbt_cash_union (internal, internal),
+ FUNCTION 3 gbt_cash_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_cash_picksplit (internal, internal),
+ FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
+ OPERATOR 6 <> (money, money) ,
+ OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
+ FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
+ FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
+
+
+--
+--
+--
+-- macaddr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr_ops
+DEFAULT FOR TYPE macaddr USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
+ FUNCTION 2 gbt_macad_union (internal, internal),
+ FUNCTION 3 gbt_macad_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
+ OPERATOR 6 <> (macaddr, macaddr) ,
+ FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
+
+
+--
+--
+--
+-- text/ bpchar ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_text_ops
+DEFAULT FOR TYPE text USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_text_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
+ OPERATOR 6 <> (text, text) ,
+ FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
+
+
+---- Create the operator class
+CREATE OPERATOR CLASS gist_bpchar_ops
+DEFAULT FOR TYPE bpchar USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_bpchar_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
+ OPERATOR 6 <> (bpchar, bpchar) ,
+ FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
+
+--
+--
+-- bytea ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bytea_ops
+DEFAULT FOR TYPE bytea USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
+ FUNCTION 2 gbt_bytea_union (internal, internal),
+ FUNCTION 3 gbt_bytea_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bytea_picksplit (internal, internal),
+ FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
+ OPERATOR 6 <> (bytea, bytea) ,
+ FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- numeric ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_numeric_ops
+DEFAULT FOR TYPE numeric USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
+ FUNCTION 2 gbt_numeric_union (internal, internal),
+ FUNCTION 3 gbt_numeric_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_numeric_picksplit (internal, internal),
+ FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
+ OPERATOR 6 <> (numeric, numeric) ,
+ FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
+
+
+--
+--
+-- bit ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bit_ops
+DEFAULT FOR TYPE bit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
+ OPERATOR 6 <> (bit, bit) ,
+ FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
+
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_vbit_ops
+DEFAULT FOR TYPE varbit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
+ OPERATOR 6 <> (varbit, varbit) ,
+ FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- inet/cidr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_inet_ops
+DEFAULT FOR TYPE inet USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cidr_ops
+DEFAULT FOR TYPE cidr USING gist
+AS
+ OPERATOR 1 < (inet, inet) ,
+ OPERATOR 2 <= (inet, inet) ,
+ OPERATOR 3 = (inet, inet) ,
+ OPERATOR 4 >= (inet, inet) ,
+ OPERATOR 5 > (inet, inet) ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+--
+--
+--
+-- uuid ops
+--
+--
+---- define the GiST support methods
+CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_uuid_ops
+DEFAULT FOR TYPE uuid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal),
+ FUNCTION 2 gbt_uuid_union (internal, internal),
+ FUNCTION 3 gbt_uuid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_uuid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_uuid_picksplit (internal, internal),
+ FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+-- These are "loose" in the opfamily for consistency with the rest of btree_gist
+ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
+ OPERATOR 6 <> (uuid, uuid) ,
+ FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ;
+
+
+-- Add support for indexing macaddr8 columns
+
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad8_consistent(internal,macaddr8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr8_ops
+DEFAULT FOR TYPE macaddr8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad8_consistent (internal, macaddr8, int2, oid, internal),
+ FUNCTION 2 gbt_macad8_union (internal, internal),
+ FUNCTION 3 gbt_macad8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad8_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD
+ OPERATOR 6 <> (macaddr8, macaddr8) ,
+ FUNCTION 9 (macaddr8, macaddr8) gbt_macad8_fetch (internal);
+
+--
+--
+--
+-- enum ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_enum_consistent(internal,anyenum,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_enum_ops
+DEFAULT FOR TYPE anyenum USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_enum_consistent (internal, anyenum, int2, oid, internal),
+ FUNCTION 2 gbt_enum_union (internal, internal),
+ FUNCTION 3 gbt_enum_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_enum_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_enum_picksplit (internal, internal),
+ FUNCTION 7 gbt_enum_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD
+ OPERATOR 6 <> (anyenum, anyenum) ,
+ FUNCTION 9 (anyenum, anyenum) gbt_enum_fetch (internal) ;
diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control
index 81c8509..9ced3bc 100644
--- a/contrib/btree_gist/btree_gist.control
+++ b/contrib/btree_gist/btree_gist.control
@@ -1,5 +1,5 @@
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
-default_version = '1.5'
+default_version = '1.6'
module_pathname = '$libdir/btree_gist'
relocatable = true
diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c
index 7674e2d..2afc343 100644
--- a/contrib/btree_gist/btree_int2.c
+++ b/contrib/btree_gist/btree_int2.c
@@ -94,20 +94,7 @@ PG_FUNCTION_INFO_V1(int2_dist);
Datum
int2_dist(PG_FUNCTION_ARGS)
{
- int16 a = PG_GETARG_INT16(0);
- int16 b = PG_GETARG_INT16(1);
- int16 r;
- int16 ra;
-
- if (pg_sub_s16_overflow(a, b, &r) ||
- r == PG_INT16_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("smallint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT16(ra);
+ return int2dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c
index 80005ab..2361ce7 100644
--- a/contrib/btree_gist/btree_int4.c
+++ b/contrib/btree_gist/btree_int4.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(int4_dist);
Datum
int4_dist(PG_FUNCTION_ARGS)
{
- int32 a = PG_GETARG_INT32(0);
- int32 b = PG_GETARG_INT32(1);
- int32 r;
- int32 ra;
-
- if (pg_sub_s32_overflow(a, b, &r) ||
- r == PG_INT32_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT32(ra);
+ return int4dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c
index b0fd3e1..182d7c4 100644
--- a/contrib/btree_gist/btree_int8.c
+++ b/contrib/btree_gist/btree_int8.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(int8_dist);
Datum
int8_dist(PG_FUNCTION_ARGS)
{
- int64 a = PG_GETARG_INT64(0);
- int64 b = PG_GETARG_INT64(1);
- int64 r;
- int64 ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT64(ra);
+ return int8dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c
index 3a527a7..c68d48e 100644
--- a/contrib/btree_gist/btree_interval.c
+++ b/contrib/btree_gist/btree_interval.c
@@ -128,11 +128,7 @@ PG_FUNCTION_INFO_V1(interval_dist);
Datum
interval_dist(PG_FUNCTION_ARGS)
{
- Datum diff = DirectFunctionCall2(interval_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+ return interval_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c
index 00e7019..c702d51 100644
--- a/contrib/btree_gist/btree_oid.c
+++ b/contrib/btree_gist/btree_oid.c
@@ -100,15 +100,7 @@ PG_FUNCTION_INFO_V1(oid_dist);
Datum
oid_dist(PG_FUNCTION_ARGS)
{
- Oid a = PG_GETARG_OID(0);
- Oid b = PG_GETARG_OID(1);
- Oid res;
-
- if (a < b)
- res = b - a;
- else
- res = a - b;
- PG_RETURN_OID(res);
+ return oiddist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c
index 90cf655..cfafd02 100644
--- a/contrib/btree_gist/btree_time.c
+++ b/contrib/btree_gist/btree_time.c
@@ -141,11 +141,7 @@ PG_FUNCTION_INFO_V1(time_dist);
Datum
time_dist(PG_FUNCTION_ARGS)
{
- Datum diff = DirectFunctionCall2(time_mi_time,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+ return time_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index 49d1849..9e62f7d 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -146,48 +146,14 @@ PG_FUNCTION_INFO_V1(ts_dist);
Datum
ts_dist(PG_FUNCTION_ARGS)
{
- Timestamp a = PG_GETARG_TIMESTAMP(0);
- Timestamp b = PG_GETARG_TIMESTAMP(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
- else
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
+ return timestamp_distance(fcinfo);
}
PG_FUNCTION_INFO_V1(tstz_dist);
Datum
tstz_dist(PG_FUNCTION_ARGS)
{
- TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
- TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
-
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
+ return timestamptz_distance(fcinfo);
}
diff --git a/doc/src/sgml/btree-gist.sgml b/doc/src/sgml/btree-gist.sgml
index 774442f..6eb4a18 100644
--- a/doc/src/sgml/btree-gist.sgml
+++ b/doc/src/sgml/btree-gist.sgml
@@ -96,6 +96,19 @@ INSERT 0 1
</sect2>
<sect2>
+ <title>Upgrade notes for version 1.6</title>
+
+ <para>
+ In version 1.6 <literal>btree_gist</literal> switched to using in-core
+ distance operators, and its own implementations were removed. References to
+ these operators in <literal>btree_gist</literal> opclasses will be updated
+ automatically during the extension upgrade, but if the user has created
+ objects referencing these operators or functions, then these objects must be
+ dropped manually before updating the extension.
+ </para>
+ </sect2>
+
+ <sect2>
<title>Authors</title>
<para>
--
2.7.4
0007-Add-regression-tests-for-kNN-btree-v05.patchtext/x-patch; name=0007-Add-regression-tests-for-kNN-btree-v05.patchDownload
From a4c3367e31d5b9b0a660c6131d9f61ca001a0138 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 11 Jan 2019 15:51:29 +0300
Subject: [PATCH 7/7] Add regression tests for kNN btree
---
src/test/regress/expected/btree_index.out | 779 ++++++++++++++++++++++++++++++
src/test/regress/sql/btree_index.sql | 232 +++++++++
2 files changed, 1011 insertions(+)
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0bd48dc..a8ed9da 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -179,3 +179,782 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+---
+--- Test B-tree distance ordering
+---
+SET enable_bitmapscan = OFF;
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+ QUERY PLAN
+------------------------------
+ Sort
+ Sort Key: ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+ QUERY PLAN
+------------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((random <-> 1))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)))
+ Order By: (random <-> 4000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)) AND (random = ANY ('{1809552,1919087,2321799,2648497,3000193,3013326,4157193,4488889,5257716,5593978,NULL}'::integer[])))
+ Order By: (random <-> 3000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ seqno | random
+-------+---------
+ 5380 | 3000193
+ 6262 | 3013326
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 6448 | 4157193
+ 210 | 1809552
+ 4408 | 4488889
+ 6320 | 5257716
+ 1836 | 5593978
+(10 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+-- test parallel KNN scan
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+RESET enable_indexscan;
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Gather
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i > 8000000)
+(4 rows)
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+-----------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t2.n = t1.n)
+ Join Filter: (t1.i <> t2.i)
+ CTE bt_knn_test1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Order By: (i <-> 4000003)
+ -> Seq Scan on bt_knn_test2 t2
+ -> Hash
+ -> CTE Scan on bt_knn_test1 t1
+(12 rows)
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+-----------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t2.n = t1.n)
+ Join Filter: (t1.i <> t2.i)
+ CTE t1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i = ANY ('{3,4,7,8,2}'::integer[]))
+ Order By: (i <-> 4)
+ CTE t2
+ -> Nested Loop
+ -> Function Scan on generate_series i
+ -> Function Scan on generate_series j
+ -> CTE Scan on t2
+ -> Hash
+ -> CTE Scan on t1
+(17 rows)
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+ROLLBACK;
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ QUERY PLAN
+-----------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000))
+ Order By: (thousand <-> 998)
+(3 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 1 | 9001
+ 1 | 8001
+ 1 | 7001
+ 1 | 6001
+ 1 | 5001
+ 1 | 4001
+ 1 | 3001
+ 1 | 2001
+ 1 | 1001
+ 1 | 1
+ 0 | 9000
+ 0 | 8000
+ 0 | 7000
+ 0 | 6000
+ 0 | 5000
+ 0 | 4000
+ 0 | 3000
+ 0 | 2000
+ 0 | 1000
+ 0 | 0
+ | 1
+ | 2
+ | 3
+(33 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: ((thousand > 100) AND (thousand < 800) AND (thousand = ANY ('{0,123,234,345,456,678,901,NULL}'::smallint[])))
+ Order By: (thousand <-> '300'::bigint)
+(3 rows)
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ thousand | tenthous
+----------+----------
+ 345 | 345
+ 345 | 1345
+ 345 | 2345
+ 345 | 3345
+ 345 | 4345
+ 345 | 5345
+ 345 | 6345
+ 345 | 7345
+ 345 | 8345
+ 345 | 9345
+ 234 | 234
+ 234 | 1234
+ 234 | 2234
+ 234 | 3234
+ 234 | 4234
+ 234 | 5234
+ 234 | 6234
+ 234 | 7234
+ 234 | 8234
+ 234 | 9234
+ 456 | 456
+ 456 | 1456
+ 456 | 2456
+ 456 | 3456
+ 456 | 4456
+ 456 | 5456
+ 456 | 6456
+ 456 | 7456
+ 456 | 8456
+ 456 | 9456
+ 123 | 123
+ 123 | 1123
+ 123 | 2123
+ 123 | 3123
+ 123 | 4123
+ 123 | 5123
+ 123 | 6123
+ 123 | 7123
+ 123 | 8123
+ 123 | 9123
+ 678 | 678
+ 678 | 1678
+ 678 | 2678
+ 678 | 3678
+ 678 | 4678
+ 678 | 5678
+ 678 | 6678
+ 678 | 7678
+ 678 | 8678
+ 678 | 9678
+(50 rows)
+
+DROP INDEX tenk3_idx;
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 1 | 1
+ 1 | 1001
+ 1 | 2001
+ 1 | 3001
+ 1 | 4001
+ 1 | 5001
+ 1 | 6001
+ 1 | 7001
+ 1 | 8001
+ 1 | 9001
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 0 | 0
+ 0 | 1000
+ 0 | 2000
+ 0 | 3000
+ 0 | 4000
+ 0 | 5000
+ 0 | 6000
+ 0 | 7000
+ 0 | 8000
+ 0 | 9000
+ | 3
+ | 2
+ | 1
+(33 rows)
+
+DROP INDEX tenk3_idx;
+DROP TABLE tenk3;
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+-------------------
+ Wed May 03 00:00:00 2017 | @ 2 days
+ Wed May 03 01:00:00 2017 | @ 2 days 1 hour
+ Wed May 03 02:00:00 2017 | @ 2 days 2 hours
+ Wed May 03 03:00:00 2017 | @ 2 days 3 hours
+ Wed May 03 04:00:00 2017 | @ 2 days 4 hours
+ Wed May 03 05:00:00 2017 | @ 2 days 5 hours
+ Wed May 03 06:00:00 2017 | @ 2 days 6 hours
+ Wed May 03 07:00:00 2017 | @ 2 days 7 hours
+ Wed May 03 08:00:00 2017 | @ 2 days 8 hours
+ Wed May 03 09:00:00 2017 | @ 2 days 9 hours
+ Wed May 03 10:00:00 2017 | @ 2 days 10 hours
+ Wed May 03 11:00:00 2017 | @ 2 days 11 hours
+ Wed May 03 12:00:00 2017 | @ 2 days 12 hours
+ Wed May 03 13:00:00 2017 | @ 2 days 13 hours
+ Wed May 03 14:00:00 2017 | @ 2 days 14 hours
+ Wed May 03 15:00:00 2017 | @ 2 days 15 hours
+ Wed May 03 16:00:00 2017 | @ 2 days 16 hours
+ Wed May 03 17:00:00 2017 | @ 2 days 17 hours
+ Wed May 03 18:00:00 2017 | @ 2 days 18 hours
+ Wed May 03 19:00:00 2017 | @ 2 days 19 hours
+(20 rows)
+
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+------------
+ Mon Jan 01 00:00:00 2018 | @ 0
+ Mon Jan 01 01:00:00 2018 | @ 1 hour
+ Sun Dec 31 23:00:00 2017 | @ 1 hour
+ Mon Jan 01 02:00:00 2018 | @ 2 hours
+ Sun Dec 31 22:00:00 2017 | @ 2 hours
+ Mon Jan 01 03:00:00 2018 | @ 3 hours
+ Sun Dec 31 21:00:00 2017 | @ 3 hours
+ Mon Jan 01 04:00:00 2018 | @ 4 hours
+ Sun Dec 31 20:00:00 2017 | @ 4 hours
+ Mon Jan 01 05:00:00 2018 | @ 5 hours
+ Sun Dec 31 19:00:00 2017 | @ 5 hours
+ Mon Jan 01 06:00:00 2018 | @ 6 hours
+ Sun Dec 31 18:00:00 2017 | @ 6 hours
+ Mon Jan 01 07:00:00 2018 | @ 7 hours
+ Sun Dec 31 17:00:00 2017 | @ 7 hours
+ Mon Jan 01 08:00:00 2018 | @ 8 hours
+ Sun Dec 31 16:00:00 2017 | @ 8 hours
+ Mon Jan 01 09:00:00 2018 | @ 9 hours
+ Sun Dec 31 15:00:00 2017 | @ 9 hours
+ Mon Jan 01 10:00:00 2018 | @ 10 hours
+(20 rows)
+
+DROP TABLE knn_btree_ts;
+RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 21171f7..307f2f5 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -111,3 +111,235 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
-- Simple ALTER INDEX
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+---
+--- Test B-tree distance ordering
+---
+
+SET enable_bitmapscan = OFF;
+
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+-- test parallel KNN scan
+
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+
+RESET enable_indexscan;
+
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+
+ROLLBACK;
+
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+
+
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+DROP INDEX tenk3_idx;
+
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+DROP TABLE tenk3;
+
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+
+DROP TABLE knn_btree_ts;
+
+RESET enable_bitmapscan;
--
2.7.4
On Fri, Jan 11, 2019 at 04:01:51PM +0300, Nikita Glukhov wrote:
All new distance functions except oiddist() are not leakproof,
so I had to relax condition in opr_sanity.sql test:
This patch set needs a rebase because of conflicts caused by the
recent patches for pluggable storage.
--
Michael
On 04.02.2019 8:35, Michael Paquier wrote:
This patch set needs a rebase because of conflicts caused by the
recent patches for pluggable storage.
Attached 6th version of the patches rebased onto current master:
* index_clauses now also passed into ammatchorderby()
* added support for queries like
SELECT * FROM tab WHERE col1 = val1 AND col2 = val2 ORDER BY col3 <-> val3
* (experimental patch #9)
added support for queries like
SELECT * FROM tab WHERE col1 IN (v1, v2, v3) ORDER BY col1, col2 <-> val
Patch #9 is experimental. In order to distinguish order-by-operator and
simple order-by-column clauses (index column can be operator expression)
in orderbyclauses lists I am trying to pass negative column numbers in
orderbyclausecols, but it looks ugly, so I think orderbyclauses passing needs
some refactoring like recent IndexClause refactoring. Also I doubt that I
correctly implemented match_pathkey_to_indexcol().
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-Fix-get_index_column_opclass-v06.patchtext/x-patch; name=0001-Fix-get_index_column_opclass-v06.patchDownload
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index e88c45d..2760748 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3118,9 +3118,6 @@ get_index_column_opclass(Oid index_oid, int attno)
{
HeapTuple tuple;
Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
- Datum datum;
- bool isnull;
- oidvector *indclass;
Oid opclass;
/* First we need to know the column's opclass. */
@@ -3134,12 +3131,23 @@ get_index_column_opclass(Oid index_oid, int attno)
/* caller is supposed to guarantee this */
Assert(attno > 0 && attno <= rd_index->indnatts);
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indclass, &isnull);
- Assert(!isnull);
+ if (attno >= 1 && attno <= rd_index->indnkeyatts)
+ {
+ oidvector *indclass;
+ bool isnull;
+ Datum datum = SysCacheGetAttr(INDEXRELID, tuple,
+ Anum_pg_index_indclass,
+ &isnull);
+
+ Assert(!isnull);
- indclass = ((oidvector *) DatumGetPointer(datum));
- opclass = indclass->values[attno - 1];
+ indclass = ((oidvector *) DatumGetPointer(datum));
+ opclass = indclass->values[attno - 1];
+ }
+ else
+ {
+ opclass = InvalidOid;
+ }
ReleaseSysCache(tuple);
0002-Introduce-ammatchorderby-function-v06.patchtext/x-patch; name=0002-Introduce-ammatchorderby-function-v06.patchDownload
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6458376..9bff793 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,7 +109,6 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BLOOM_NSTRATEGIES;
amroutine->amsupport = BLOOM_NPROC;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -143,6 +142,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 8f008dd..5cd06c7 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -88,7 +88,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -122,6 +121,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index afc2023..0f6714d 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -41,7 +41,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GINNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -75,6 +74,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index b75b3a8..77ca187 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,7 @@
#include "access/gistscan.h"
#include "catalog/pg_collation.h"
#include "miscadmin.h"
+#include "optimizer/paths.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "nodes/execnodes.h"
@@ -64,7 +65,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GISTNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -98,6 +98,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index f1f01a0..bb5c6a1 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -59,7 +59,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = HTMaxStrategyNumber;
amroutine->amsupport = HASHNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -93,6 +92,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 98917de..c56ee5e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -110,7 +110,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BTMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = true;
amroutine->amcanmulticol = true;
@@ -144,6 +143,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 8e63c1f..37de33c 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -22,6 +22,7 @@
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/pg_amop.h"
+#include "optimizer/paths.h"
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
@@ -31,7 +32,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-
/*
* SP-GiST handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -44,7 +44,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = SPGISTNProc;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -78,6 +77,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5d73848..a595c79 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -1097,7 +1097,7 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
*/
IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
- if (!amroutine->amcanorderbyop)
+ if (!amroutine->ammatchorderby)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("access method \"%s\" does not support ordering operators",
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 324356e..805abd2 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -198,7 +198,7 @@ IndexNextWithReorder(IndexScanState *node)
* with just Asserting here because the system will not try to run the
* plan backwards if ExecSupportsBackwardScan() says it won't work.
* Currently, that is guaranteed because no index AMs support both
- * amcanorderbyop and amcanbackward; if any ever do,
+ * ammatchorderby and amcanbackward; if any ever do,
* ExecSupportsBackwardScan() will need to consider indexorderbys
* explicitly.
*/
@@ -1148,7 +1148,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* 5. NullTest ("indexkey IS NULL/IS NOT NULL"). We just fill in the
* ScanKey properly.
*
- * This code is also used to prepare ORDER BY expressions for amcanorderbyop
+ * This code is also used to prepare ORDER BY expressions for ammatchorderby
* indexes. The behavior is exactly the same, except that we have to look up
* the operator differently. Note that only cases 1 and 2 are currently
* possible for ORDER BY.
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 3434219..6cf0b0d 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -17,6 +17,7 @@
#include <math.h>
+#include "access/amapi.h"
#include "access/stratnum.h"
#include "access/sysattr.h"
#include "catalog/pg_am.h"
@@ -182,8 +183,9 @@ static IndexClause *expand_indexqual_rowcompare(RestrictInfo *rinfo,
Oid expr_op,
bool var_on_left);
static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses,
List **orderby_clauses_p,
- List **clause_columns_p);
+ List **orderby_clause_columns_p);
static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
int indexcol, Expr *clause, Oid pk_opfamily);
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
@@ -1001,10 +1003,11 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
orderbyclauses = NIL;
orderbyclausecols = NIL;
}
- else if (index->amcanorderbyop && pathkeys_possibly_useful)
+ else if (index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
+ index_clauses,
&orderbyclauses,
&orderbyclausecols);
if (orderbyclauses)
@@ -2927,6 +2930,113 @@ match_rowcompare_to_indexcol(RestrictInfo *rinfo,
return NULL;
}
+/****************************************************************************
+ * ---- ROUTINES TO CHECK ORDERING OPERATORS ----
+ ****************************************************************************/
+
+/*
+ * Try to match order-by-operator pathkey to the specified index column
+ * (*indexcol_p >= 0) or to all index columns (*indexcol_p < 0).
+ *
+ * Returned matched index clause exression.
+ * Number of matched index column is returned in *indexcol_p.
+ */
+Expr *
+match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey, int *indexcol_p)
+{
+ ListCell *lc;
+
+ /* Pathkey must request default sort order for the target opfamily */
+ if (pathkey->pk_strategy != BTLessStrategyNumber ||
+ pathkey->pk_nulls_first)
+ return NULL;
+
+ /* If eclass is volatile, no hope of using an indexscan */
+ if (pathkey->pk_eclass->ec_has_volatile)
+ return NULL;
+
+ /*
+ * Try to match eclass member expression(s) to index. Note that child EC
+ * members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could be
+ * considered to match more than one pathkey list, which is OK here. See
+ * also get_eclass_for_sort_expr.)
+ */
+ foreach(lc, pathkey->pk_eclass->ec_members)
+ {
+ EquivalenceMember *member = lfirst_node(EquivalenceMember, lc);
+ Expr *expr;
+
+ /* No possibility of match if it references other relations. */
+ if (!bms_equal(member->em_relids, index->rel->relids))
+ continue;
+
+ /* If *indexcol_p is non-negative then try to match only to it. */
+ if (*indexcol_p >= 0)
+ {
+ expr = match_clause_to_ordering_op(index, *indexcol_p,
+ member->em_expr,
+ pathkey->pk_opfamily);
+
+ if (expr)
+ return expr; /* don't want to look at remaining members */
+ }
+ else
+ {
+ int indexcol;
+
+ /*
+ * We allow any column of this index to match each pathkey; they
+ * don't have to match left-to-right as you might expect.
+ */
+ for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
+ {
+ expr = match_clause_to_ordering_op(index, indexcol,
+ member->em_expr,
+ pathkey->pk_opfamily);
+ if (expr)
+ {
+ *indexcol_p = indexcol;
+ return expr; /* don't want to look at remaining members */
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* Try to match order-by-operator pathkeys to any index columns. */
+bool
+match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses, List **orderby_clauses_p,
+ List **orderby_clausecols_p)
+{
+ ListCell *lc;
+
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = lfirst_node(PathKey, lc);
+ Expr *expr;
+ int indexcol = -1; /* match all index columns */
+
+ expr = match_orderbyop_pathkey(index, pathkey, &indexcol);
+
+ /*
+ * Note: for any failure to match, we just return NIL immediately.
+ * There is no value in matching just some of the pathkeys.
+ */
+ if (!expr)
+ return false;
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *orderby_clausecols_p = lappend_int(*orderby_clausecols_p, indexcol);
+ }
+
+ return true; /* success */
+}
+
/*
* expand_indexqual_rowcompare --- expand a single indexqual condition
* that is a RowCompareExpr
@@ -3183,92 +3293,31 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
*/
static void
match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses,
List **orderby_clauses_p,
- List **clause_columns_p)
+ List **orderby_clause_columns_p)
{
List *orderby_clauses = NIL;
- List *clause_columns = NIL;
- ListCell *lc1;
-
- *orderby_clauses_p = NIL; /* set default results */
- *clause_columns_p = NIL;
+ List *orderby_clause_columns = NIL;
+ ammatchorderby_function ammatchorderby =
+ (ammatchorderby_function) index->ammatchorderby;
- /* Only indexes with the amcanorderbyop property are interesting here */
- if (!index->amcanorderbyop)
- return;
-
- foreach(lc1, pathkeys)
+ /* Only indexes with the ammatchorderby function are interesting here */
+ if (ammatchorderby &&
+ ammatchorderby(index, pathkeys, index_clauses,
+ &orderby_clauses, &orderby_clause_columns))
{
- PathKey *pathkey = (PathKey *) lfirst(lc1);
- bool found = false;
- ListCell *lc2;
-
- /*
- * Note: for any failure to match, we just return NIL immediately.
- * There is no value in matching just some of the pathkeys.
- */
-
- /* Pathkey must request default sort order for the target opfamily */
- if (pathkey->pk_strategy != BTLessStrategyNumber ||
- pathkey->pk_nulls_first)
- return;
-
- /* If eclass is volatile, no hope of using an indexscan */
- if (pathkey->pk_eclass->ec_has_volatile)
- return;
-
- /*
- * Try to match eclass member expression(s) to index. Note that child
- * EC members are considered, but only when they belong to the target
- * relation. (Unlike regular members, the same expression could be a
- * child member of more than one EC. Therefore, the same index could
- * be considered to match more than one pathkey list, which is OK
- * here. See also get_eclass_for_sort_expr.)
- */
- foreach(lc2, pathkey->pk_eclass->ec_members)
- {
- EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
- int indexcol;
-
- /* No possibility of match if it references other relations */
- if (!bms_equal(member->em_relids, index->rel->relids))
- continue;
+ Assert(list_length(pathkeys) == list_length(orderby_clauses));
+ Assert(list_length(pathkeys) == list_length(orderby_clause_columns));
- /*
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, and it doesn't matter for SP-GiST because
- * that doesn't handle multiple columns anyway, and no other
- * existing AMs support amcanorderbyop. We might need different
- * logic in future for other implementations.
- */
- for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
- {
- Expr *expr;
-
- expr = match_clause_to_ordering_op(index,
- indexcol,
- member->em_expr,
- pathkey->pk_opfamily);
- if (expr)
- {
- orderby_clauses = lappend(orderby_clauses, expr);
- clause_columns = lappend_int(clause_columns, indexcol);
- found = true;
- break;
- }
- }
-
- if (found) /* don't want to look at remaining members */
- break;
- }
-
- if (!found) /* fail if no match for this pathkey */
- return;
+ *orderby_clauses_p = orderby_clauses; /* success! */
+ *orderby_clause_columns_p = orderby_clause_columns;
+ }
+ else
+ {
+ *orderby_clauses_p = NIL; /* set default results */
+ *orderby_clause_columns_p = NIL;
}
-
- *orderby_clauses_p = orderby_clauses; /* success! */
- *clause_columns_p = clause_columns;
}
/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index d6dc83c..2d81fd1 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -267,7 +267,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* We copy just the fields we need, not all of rd_indam */
amroutine = indexRelation->rd_indam;
- info->amcanorderbyop = amroutine->amcanorderbyop;
+ info->ammatchorderby = amroutine->ammatchorderby;
info->amoptionalkey = amroutine->amoptionalkey;
info->amsearcharray = amroutine->amsearcharray;
info->amsearchnulls = amroutine->amsearchnulls;
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 060ffe5..3907065 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -298,7 +298,7 @@ indexam_property(FunctionCallInfo fcinfo,
* a nonkey column, and null otherwise (meaning we don't
* know).
*/
- if (!iskey || !routine->amcanorderbyop)
+ if (!iskey || !routine->ammatchorderby)
{
res = false;
isnull = false;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 653ddc9..d8fb7d3 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -21,6 +21,9 @@
*/
struct PlannerInfo;
struct IndexPath;
+struct IndexOptInfo;
+struct PathKey;
+struct Expr;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
@@ -140,6 +143,13 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* does AM support ORDER BY result of an operator on indexed column? */
+typedef bool (*ammatchorderby_function) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List *index_clauses,
+ List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -170,8 +180,6 @@ typedef struct IndexAmRoutine
uint16 amsupport;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
- /* does AM support ORDER BY result of an operator on indexed column? */
- bool amcanorderbyop;
/* does AM support backward scanning? */
bool amcanbackward;
/* does AM support UNIQUE indexes? */
@@ -221,6 +229,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ ammatchorderby_function ammatchorderby; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a008ae0..4680cb8 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -814,7 +814,6 @@ struct IndexOptInfo
bool hypothetical; /* true if index doesn't really exist */
/* Remaining fields are copied from the index AM's API struct: */
- bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
@@ -823,6 +822,12 @@ struct IndexOptInfo
bool amcanparallel; /* does AM support parallel scan? */
/* Rather than include amapi.h here, we declare amcostestimate like this */
void (*amcostestimate) (); /* AM's cost estimator */
+ /* AM order-by match function */
+ bool (*ammatchorderby) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List *index_clause_columns,
+ List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
};
/*
@@ -1133,7 +1138,7 @@ typedef struct Path
* An empty list implies a full index scan.
*
* 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
- * been found to be usable as ordering operators for an amcanorderbyop index.
+ * been found to be usable as ordering operators for an ammatchorderby index.
* The list must match the path's pathkeys, ie, one expression per pathkey
* in the same order. These are not RestrictInfos, just bare expressions,
* since they generally won't yield booleans. It's guaranteed that each
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6d087c2..bd6ce97 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -388,7 +388,7 @@ typedef struct SampleScan
* indexorderbyops is a list of the OIDs of the operators used to sort the
* ORDER BY expressions. This is used together with indexorderbyorig to
* recheck ordering at run time. (Note that indexorderby, indexorderbyorig,
- * and indexorderbyops are used for amcanorderbyop cases, not amcanorder.)
+ * and indexorderbyops are used for ammatchorderby cases, not amcanorder.)
*
* indexorderdir specifies the scan ordering, for indexscans on amcanorder
* indexes (for other indexes it should be "don't care").
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 040335a..e9f4f75 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -79,6 +79,11 @@ extern bool indexcol_is_bool_constant_for_query(IndexOptInfo *index,
extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index);
extern void check_index_predicates(PlannerInfo *root, RelOptInfo *rel);
+extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
+ int *indexcol_p);
+extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses, List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
/*
* tidpath.h
0003-Extract-structure-BTScanState-v06.patchtext/x-patch; name=0003-Extract-structure-BTScanState-v06.patchDownload
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index c56ee5e..bf6a6c6 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -214,6 +214,7 @@ bool
btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
bool res;
/* btree indexes are never lossy */
@@ -224,7 +225,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(so->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
@@ -241,7 +242,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(so->currPos))
+ if (!BTScanPosIsValid(state->currPos))
res = _bt_first(scan, dir);
else
{
@@ -259,11 +260,11 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* trying to optimize that, so we don't detect it, but instead
* just forget any excess entries.
*/
- if (so->killedItems == NULL)
- so->killedItems = (int *)
+ if (state->killedItems == NULL)
+ state->killedItems = (int *)
palloc(MaxIndexTuplesPerPage * sizeof(int));
- if (so->numKilled < MaxIndexTuplesPerPage)
- so->killedItems[so->numKilled++] = so->currPos.itemIndex;
+ if (state->numKilled < MaxIndexTuplesPerPage)
+ state->killedItems[so->state.numKilled++] = state->currPos.itemIndex;
}
/*
@@ -288,6 +289,7 @@ int64
btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
int64 ntids = 0;
ItemPointer heapTid;
@@ -320,7 +322,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* Advance to next tuple within page. This is the same as the
* easy case in _bt_next().
*/
- if (++so->currPos.itemIndex > so->currPos.lastItem)
+ if (++currPos->itemIndex > currPos->lastItem)
{
/* let _bt_next do the heavy lifting */
if (!_bt_next(scan, ForwardScanDirection))
@@ -328,7 +330,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
/* Save tuple ID, and continue scanning */
- heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
+ heapTid = &currPos->items[currPos->itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
}
@@ -356,8 +358,8 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
/* allocate private workspace */
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
- BTScanPosInvalidate(so->currPos);
- BTScanPosInvalidate(so->markPos);
+ BTScanPosInvalidate(so->state.currPos);
+ BTScanPosInvalidate(so->state.markPos);
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
else
@@ -368,15 +370,15 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
so->arrayKeys = NULL;
so->arrayContext = NULL;
- so->killedItems = NULL; /* until needed */
- so->numKilled = 0;
+ so->state.killedItems = NULL; /* until needed */
+ so->state.numKilled = 0;
/*
* We don't know yet whether the scan will be index-only, so we do not
* allocate the tuple workspace arrays until btrescan. However, we set up
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
- so->currTuples = so->markTuples = NULL;
+ so->state.currTuples = so->state.markTuples = NULL;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -385,6 +387,45 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
return scan;
}
+static void
+_bt_release_current_position(BTScanState state, Relation indexRelation,
+ bool invalidate)
+{
+ /* we aren't holding any read locks, but gotta drop the pins */
+ if (BTScanPosIsValid(state->currPos))
+ {
+ /* Before leaving current page, deal with any killed items */
+ if (state->numKilled > 0)
+ _bt_killitems(state, indexRelation);
+
+ BTScanPosUnpinIfPinned(state->currPos);
+
+ if (invalidate)
+ BTScanPosInvalidate(state->currPos);
+ }
+}
+
+static void
+_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
+{
+ /* No need to invalidate positions, if the RAM is about to be freed. */
+ _bt_release_current_position(state, scan->indexRelation, !free);
+
+ state->markItemIndex = -1;
+ BTScanPosUnpinIfPinned(state->markPos);
+
+ if (free)
+ {
+ if (state->killedItems != NULL)
+ pfree(state->killedItems);
+ if (state->currTuples != NULL)
+ pfree(state->currTuples);
+ /* markTuples should not be pfree'd (_bt_allocate_tuple_workspaces) */
+ }
+ else
+ BTScanPosInvalidate(state->markPos);
+}
+
/*
* btrescan() -- rescan an index relation
*/
@@ -393,21 +434,11 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
ScanKey orderbys, int norderbys)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
- }
+ _bt_release_scan_state(scan, state, false);
- so->markItemIndex = -1;
so->arrayKeyCount = 0;
- BTScanPosUnpinIfPinned(so->markPos);
- BTScanPosInvalidate(so->markPos);
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
@@ -425,11 +456,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
* a SIGSEGV is not possible. Yeah, this is ugly as sin, but it beats
* adding special-case treatment for name_ops elsewhere.
*/
- if (scan->xs_want_itup && so->currTuples == NULL)
- {
- so->currTuples = (char *) palloc(BLCKSZ * 2);
- so->markTuples = so->currTuples + BLCKSZ;
- }
+ if (scan->xs_want_itup && state->currTuples == NULL)
+ _bt_allocate_tuple_workspaces(state);
/*
* Reset the scan keys. Note that keys ordering stuff moved to _bt_first.
@@ -453,19 +481,7 @@ btendscan(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
-
- /* No need to invalidate positions, the RAM is about to be freed. */
+ _bt_release_scan_state(scan, &so->state, true);
/* Release storage */
if (so->keyData != NULL)
@@ -473,24 +489,15 @@ btendscan(IndexScanDesc scan)
/* so->arrayKeyData and so->arrayKeys are in arrayContext */
if (so->arrayContext != NULL)
MemoryContextDelete(so->arrayContext);
- if (so->killedItems != NULL)
- pfree(so->killedItems);
- if (so->currTuples != NULL)
- pfree(so->currTuples);
- /* so->markTuples should not be pfree'd, see btrescan */
+
pfree(so);
}
-/*
- * btmarkpos() -- save current scan position
- */
-void
-btmarkpos(IndexScanDesc scan)
+static void
+_bt_mark_current_position(BTScanState state)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
-
/* There may be an old mark with a pin (but no lock). */
- BTScanPosUnpinIfPinned(so->markPos);
+ BTScanPosUnpinIfPinned(state->markPos);
/*
* Just record the current itemIndex. If we later step to next page
@@ -498,32 +505,34 @@ btmarkpos(IndexScanDesc scan)
* the currPos struct in markPos. If (as often happens) the mark is moved
* before we leave the page, we don't have to do that work.
*/
- if (BTScanPosIsValid(so->currPos))
- so->markItemIndex = so->currPos.itemIndex;
+ if (BTScanPosIsValid(state->currPos))
+ state->markItemIndex = state->currPos.itemIndex;
else
{
- BTScanPosInvalidate(so->markPos);
- so->markItemIndex = -1;
+ BTScanPosInvalidate(state->markPos);
+ state->markItemIndex = -1;
}
-
- /* Also record the current positions of any array keys */
- if (so->numArrayKeys)
- _bt_mark_array_keys(scan);
}
/*
- * btrestrpos() -- restore scan to last saved position
+ * btmarkpos() -- save current scan position
*/
void
-btrestrpos(IndexScanDesc scan)
+btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* Restore the marked positions of any array keys */
+ _bt_mark_current_position(&so->state);
+
+ /* Also record the current positions of any array keys */
if (so->numArrayKeys)
- _bt_restore_array_keys(scan);
+ _bt_mark_array_keys(scan);
+}
- if (so->markItemIndex >= 0)
+static void
+_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
+{
+ if (state->markItemIndex >= 0)
{
/*
* The scan has never moved to a new page since the last mark. Just
@@ -532,7 +541,7 @@ btrestrpos(IndexScanDesc scan)
* NB: In this case we can't count on anything in so->markPos to be
* accurate.
*/
- so->currPos.itemIndex = so->markItemIndex;
+ state->currPos.itemIndex = state->markItemIndex;
}
else
{
@@ -542,28 +551,21 @@ btrestrpos(IndexScanDesc scan)
* locks, but if we're still holding the pin for the current position,
* we must drop it.
*/
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
+ _bt_release_current_position(state, scan->indexRelation,
+ !BTScanPosIsValid(state->markPos));
- if (BTScanPosIsValid(so->markPos))
+ if (BTScanPosIsValid(state->markPos))
{
/* bump pin on mark buffer for assignment to current buffer */
- if (BTScanPosIsPinned(so->markPos))
- IncrBufferRefCount(so->markPos.buf);
- memcpy(&so->currPos, &so->markPos,
+ if (BTScanPosIsPinned(state->markPos))
+ IncrBufferRefCount(state->markPos.buf);
+ memcpy(&state->currPos, &state->markPos,
offsetof(BTScanPosData, items[1]) +
- so->markPos.lastItem * sizeof(BTScanPosItem));
- if (so->currTuples)
- memcpy(so->currTuples, so->markTuples,
- so->markPos.nextTupleOffset);
+ state->markPos.lastItem * sizeof(BTScanPosItem));
+ if (state->currTuples)
+ memcpy(state->currTuples, state->markTuples,
+ state->markPos.nextTupleOffset);
}
- else
- BTScanPosInvalidate(so->currPos);
}
}
@@ -779,9 +781,10 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
}
/*
- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
- * btbulkdelete() wasn't called.
- */
+- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
+- * btbulkdelete() wasn't called.
++ * btrestrpos() -- restore scan to last saved position
+ */
static bool
_bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
{
@@ -844,6 +847,21 @@ _bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
}
/*
+ * btrestrpos() -- restore scan to last saved position
+ */
+void
+btrestrpos(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
+ /* Restore the marked positions of any array keys */
+ if (so->numArrayKeys)
+ _bt_restore_array_keys(scan);
+
+ _bt_restore_marked_position(scan, &so->state);
+}
+
+/*
* Bulk deletion of all index entries pointing to a set of heap tuples.
* The set of target tuples is specified via a callback routine that tells
* whether any given heap tuple (identified by ItemPointer) is being deleted.
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 9283223..cbd72bd 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -24,18 +24,19 @@
#include "utils/rel.h"
-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir,
+static bool _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
OffsetNumber offnum);
-static void _bt_saveitem(BTScanOpaque so, int itemIndex,
+static void _bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup);
-static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
-static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir);
+static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
+static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
-static inline void _bt_initialize_more_data(BTScanOpaque so, ScanDirection dir);
+static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
/*
@@ -544,6 +545,58 @@ _bt_compare(Relation rel,
}
/*
+ * _bt_return_current_item() -- Prepare current scan state item for return.
+ *
+ * This function is used only in "return _bt_return_current_item();" statements
+ * and always returns true.
+ */
+static inline bool
+_bt_return_current_item(IndexScanDesc scan, BTScanState state)
+{
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+
+ scan->xs_ctup.t_self = currItem->heapTid;
+
+ if (scan->xs_want_itup)
+ scan->xs_itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+
+ return true;
+}
+
+/*
+ * _bt_load_first_page() -- Load data from the first page of the scan.
+ *
+ * Caller must have pinned and read-locked state->currPos.buf.
+ *
+ * On success exit, state->currPos is updated to contain data from the next
+ * interesting page. For success on a scan using a non-MVCC snapshot we hold
+ * a pin, but not a read lock, on that page. If we do not hold the pin, we
+ * set state->currPos.buf to InvalidBuffer. We return true to indicate success.
+ *
+ * If there are no more matching records in the given direction at all,
+ * we drop all locks and pins, set state->currPos.buf to InvalidBuffer,
+ * and return false.
+ */
+static bool
+_bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
+{
+ if (!_bt_readpage(scan, state, dir, offnum))
+ {
+ /*
+ * There's no actually-matching data on this page. Try to advance to
+ * the next page. Return false if there's no matching data at all.
+ */
+ LockBuffer(state->currPos.buf, BUFFER_LOCK_UNLOCK);
+ return _bt_steppage(scan, state, dir);
+ }
+
+ /* Drop the lock, and maybe the pin, on the current page */
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
+ return true;
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -568,6 +621,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
BTStack stack;
OffsetNumber offnum;
@@ -581,10 +635,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
int i;
bool status = true;
StrategyNumber strat_total;
- BTScanPosItem *currItem;
BlockNumber blkno;
- Assert(!BTScanPosIsValid(so->currPos));
+ Assert(!BTScanPosIsValid(*currPos));
pgstat_count_index_scan(rel);
@@ -1075,7 +1128,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* their scan
*/
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1083,7 +1136,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
PredicateLockPage(rel, BufferGetBlockNumber(buf),
scan->xs_snapshot);
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
/* position to the precise item on the page */
offnum = _bt_binsrch(rel, buf, keysCount, scankeys, nextkey);
@@ -1110,36 +1163,36 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
offnum = OffsetNumberPrev(offnum);
/* remember which buffer we have pinned, if any */
- Assert(!BTScanPosIsValid(so->currPos));
- so->currPos.buf = buf;
+ Assert(!BTScanPosIsValid(*currPos));
+ currPos->buf = buf;
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, offnum))
+ if (!_bt_load_first_page(scan, &so->state, dir, offnum))
+ return false;
+
+readcomplete:
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
+}
+
+/*
+ * Advance to next tuple on current page; or if there's no more,
+ * try to step to the next page with data.
+ */
+static bool
+_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
+{
+ if (ScanDirectionIsForward(dir))
{
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
+ if (++state->currPos.itemIndex <= state->currPos.lastItem)
+ return true;
}
else
{
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ if (--state->currPos.itemIndex >= state->currPos.firstItem)
+ return true;
}
-readcomplete:
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_steppage(scan, state, dir);
}
/*
@@ -1160,44 +1213,20 @@ bool
_bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- BTScanPosItem *currItem;
- /*
- * Advance to next tuple on current page; or if there's no more, try to
- * step to the next page with data.
- */
- if (ScanDirectionIsForward(dir))
- {
- if (++so->currPos.itemIndex > so->currPos.lastItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
- else
- {
- if (--so->currPos.itemIndex < so->currPos.firstItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
+ if (!_bt_next_item(scan, &so->state, dir))
+ return false;
/* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_return_current_item(scan, &so->state);
}
/*
* _bt_readpage() -- Load data from current index page into so->currPos
*
- * Caller must have pinned and read-locked so->currPos.buf; the buffer's state
- * is not changed here. Also, currPos.moreLeft and moreRight must be valid;
- * they are updated as appropriate. All other fields of so->currPos are
+ * Caller must have pinned and read-locked pos->buf; the buffer's state
+ * is not changed here. Also, pos->moreLeft and moreRight must be valid;
+ * they are updated as appropriate. All other fields of pos are
* initialized from scratch here.
*
* We scan the current page starting at offnum and moving in the indicated
@@ -1212,9 +1241,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
* Returns true if any matching items found on the page, false if none.
*/
static bool
-_bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
+_bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
@@ -1227,9 +1257,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We must have the buffer pinned and locked, but the usual macro can't be
* used here; this function is what makes it good for currPos.
*/
- Assert(BufferIsValid(so->currPos.buf));
+ Assert(BufferIsValid(pos->buf));
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* allow next page be processed by parallel worker */
@@ -1238,7 +1268,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (ScanDirectionIsForward(dir))
_bt_parallel_release(scan, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1248,30 +1278,30 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We note the buffer's block number so that we can release the pin later.
* This allows us to re-read the buffer if it is needed again for hinting.
*/
- so->currPos.currPage = BufferGetBlockNumber(so->currPos.buf);
+ pos->currPage = BufferGetBlockNumber(pos->buf);
/*
* We save the LSN of the page as we read it, so that we know whether it
* safe to apply LP_DEAD hints to the page later. This allows us to drop
* the pin for MVCC scans, which allows vacuum to avoid blocking.
*/
- so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf);
+ pos->lsn = BufferGetLSNAtomic(pos->buf);
/*
* we must save the page's right-link while scanning it; this tells us
* where to step right to after we're done with these items. There is no
* corresponding need for the left-link, since splits always go right.
*/
- so->currPos.nextPage = opaque->btpo_next;
+ pos->nextPage = opaque->btpo_next;
/* initialize tuple workspace to empty */
- so->currPos.nextTupleOffset = 0;
+ pos->nextTupleOffset = 0;
/*
* Now that the current page has been made consistent, the macro should be
* good.
*/
- Assert(BTScanPosIsPinned(so->currPos));
+ Assert(BTScanPosIsPinned(*pos));
if (ScanDirectionIsForward(dir))
{
@@ -1286,13 +1316,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (itup != NULL)
{
/* tuple passes all scan key conditions, so remember it */
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
itemIndex++;
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreRight = false;
+ pos->moreRight = false;
break;
}
@@ -1300,9 +1330,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex <= MaxIndexTuplesPerPage);
- so->currPos.firstItem = 0;
- so->currPos.lastItem = itemIndex - 1;
- so->currPos.itemIndex = 0;
+ pos->firstItem = 0;
+ pos->lastItem = itemIndex - 1;
+ pos->itemIndex = 0;
}
else
{
@@ -1318,12 +1348,12 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
{
/* tuple passes all scan key conditions, so remember it */
itemIndex--;
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreLeft = false;
+ pos->moreLeft = false;
break;
}
@@ -1331,30 +1361,31 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex >= 0);
- so->currPos.firstItem = itemIndex;
- so->currPos.lastItem = MaxIndexTuplesPerPage - 1;
- so->currPos.itemIndex = MaxIndexTuplesPerPage - 1;
+ pos->firstItem = itemIndex;
+ pos->lastItem = MaxIndexTuplesPerPage - 1;
+ pos->itemIndex = MaxIndexTuplesPerPage - 1;
}
- return (so->currPos.firstItem <= so->currPos.lastItem);
+ return (pos->firstItem <= pos->lastItem);
}
/* Save an index item into so->currPos.items[itemIndex] */
static void
-_bt_saveitem(BTScanOpaque so, int itemIndex,
+_bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup)
{
- BTScanPosItem *currItem = &so->currPos.items[itemIndex];
+ BTScanPosItem *currItem = &state->currPos.items[itemIndex];
currItem->heapTid = itup->t_tid;
currItem->indexOffset = offnum;
- if (so->currTuples)
+ if (state->currTuples)
{
Size itupsz = IndexTupleSize(itup);
- currItem->tupleOffset = so->currPos.nextTupleOffset;
- memcpy(so->currTuples + so->currPos.nextTupleOffset, itup, itupsz);
- so->currPos.nextTupleOffset += MAXALIGN(itupsz);
+ currItem->tupleOffset = state->currPos.nextTupleOffset;
+ memcpy(state->currTuples + state->currPos.nextTupleOffset,
+ itup, itupsz);
+ state->currPos.nextTupleOffset += MAXALIGN(itupsz);
}
}
@@ -1370,35 +1401,36 @@ _bt_saveitem(BTScanOpaque so, int itemIndex,
* to InvalidBuffer. We return true to indicate success.
*/
static bool
-_bt_steppage(IndexScanDesc scan, ScanDirection dir)
+_bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
+ Relation rel = scan->indexRelation;
BlockNumber blkno = InvalidBlockNumber;
bool status = true;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(*currPos));
/* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
+ if (state->numKilled > 0)
+ _bt_killitems(state, rel);
/*
* Before we modify currPos, make a copy of the page data if there was a
* mark position that needs it.
*/
- if (so->markItemIndex >= 0)
+ if (state->markItemIndex >= 0)
{
/* bump pin on current buffer for assignment to mark buffer */
- if (BTScanPosIsPinned(so->currPos))
- IncrBufferRefCount(so->currPos.buf);
- memcpy(&so->markPos, &so->currPos,
+ if (BTScanPosIsPinned(*currPos))
+ IncrBufferRefCount(currPos->buf);
+ memcpy(&state->markPos, currPos,
offsetof(BTScanPosData, items[1]) +
- so->currPos.lastItem * sizeof(BTScanPosItem));
- if (so->markTuples)
- memcpy(so->markTuples, so->currTuples,
- so->currPos.nextTupleOffset);
- so->markPos.itemIndex = so->markItemIndex;
- so->markItemIndex = -1;
+ currPos->lastItem * sizeof(BTScanPosItem));
+ if (state->markTuples)
+ memcpy(state->markTuples, state->currTuples,
+ currPos->nextTupleOffset);
+ state->markPos.itemIndex = state->markItemIndex;
+ state->markItemIndex = -1;
}
if (ScanDirectionIsForward(dir))
@@ -1414,27 +1446,27 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
if (!status)
{
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so use the previously-saved nextPage link. */
- blkno = so->currPos.nextPage;
+ blkno = currPos->nextPage;
}
/* Remember we left a page with data */
- so->currPos.moreLeft = true;
+ currPos->moreLeft = true;
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
}
else
{
/* Remember we left a page with data */
- so->currPos.moreRight = true;
+ currPos->moreRight = true;
if (scan->parallel_scan != NULL)
{
@@ -1443,25 +1475,25 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* ended already, bail out.
*/
status = _bt_parallel_seize(scan, &blkno);
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so just use our own notion of the current page */
- blkno = so->currPos.currPage;
+ blkno = currPos->currPage;
}
}
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, currPos);
return true;
}
@@ -1477,9 +1509,10 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* locks and pins, set so->currPos.buf to InvalidBuffer, and return false.
*/
static bool
-_bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
Relation rel;
Page page;
BTPageOpaque opaque;
@@ -1495,17 +1528,17 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* if we're at end of scan, give up and mark parallel scan as
* done, so that all the workers can finish their scan
*/
- if (blkno == P_NONE || !so->currPos.moreRight)
+ if (blkno == P_NONE || !currPos->moreRight)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* check for interrupts while we're not holding any buffer lock */
CHECK_FOR_INTERRUPTS();
/* step right one page */
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
- page = BufferGetPage(so->currPos.buf);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* check for deleted page */
@@ -1514,7 +1547,7 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
PredicateLockPage(rel, blkno, scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreRight if we can stop */
- if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque)))
+ if (_bt_readpage(scan, state, dir, P_FIRSTDATAKEY(opaque)))
break;
}
else if (scan->parallel_scan != NULL)
@@ -1526,18 +1559,18 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
blkno = opaque->btpo_next;
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
}
}
}
@@ -1547,10 +1580,10 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* Should only happen in parallel cases, when some other backend
* advanced the scan.
*/
- if (so->currPos.currPage != blkno)
+ if (currPos->currPage != blkno)
{
- BTScanPosUnpinIfPinned(so->currPos);
- so->currPos.currPage = blkno;
+ BTScanPosUnpinIfPinned(*currPos);
+ currPos->currPage = blkno;
}
/*
@@ -1575,31 +1608,30 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* is MVCC the page cannot move past the half-dead state to fully
* deleted.
*/
- if (BTScanPosIsPinned(so->currPos))
- LockBuffer(so->currPos.buf, BT_READ);
+ if (BTScanPosIsPinned(*currPos))
+ LockBuffer(currPos->buf, BT_READ);
else
- so->currPos.buf = _bt_getbuf(rel, so->currPos.currPage, BT_READ);
+ currPos->buf = _bt_getbuf(rel, currPos->currPage, BT_READ);
for (;;)
{
/* Done if we know there are no matching keys to the left */
- if (!so->currPos.moreLeft)
+ if (!currPos->moreLeft)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* Step to next physical page */
- so->currPos.buf = _bt_walk_left(rel, so->currPos.buf,
- scan->xs_snapshot);
+ currPos->buf = _bt_walk_left(rel, currPos->buf, scan->xs_snapshot);
/* if we're physically at end of index, return failure */
- if (so->currPos.buf == InvalidBuffer)
+ if (currPos->buf == InvalidBuffer)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1608,21 +1640,21 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* it's not half-dead and contains matching tuples. Else loop back
* and do it all again.
*/
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
{
- PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
+ PredicateLockPage(rel, BufferGetBlockNumber(currPos->buf), scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreLeft if we can stop */
- if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page)))
+ if (_bt_readpage(scan, state, dir, PageGetMaxOffsetNumber(page)))
break;
}
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1633,14 +1665,14 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
}
}
}
@@ -1659,13 +1691,13 @@ _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, &so->state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
return true;
}
@@ -1890,11 +1922,11 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
Page page;
BTPageOpaque opaque;
OffsetNumber start;
- BTScanPosItem *currItem;
/*
* Scan down to the leftmost or rightmost leaf page. This is a simplified
@@ -1910,7 +1942,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* exists.
*/
PredicateLockRelation(rel, scan->xs_snapshot);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1939,36 +1971,15 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
}
/* remember which buffer we have pinned */
- so->currPos.buf = buf;
+ currPos->buf = buf;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, start))
- {
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
- }
- else
- {
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
- }
-
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
+ if (!_bt_load_first_page(scan, &so->state, dir, start))
+ return false;
- return true;
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
}
/*
@@ -1976,19 +1987,19 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* for scan direction
*/
static inline void
-_bt_initialize_more_data(BTScanOpaque so, ScanDirection dir)
+_bt_initialize_more_data(BTScanState state, ScanDirection dir)
{
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ state->currPos.moreLeft = false;
+ state->currPos.moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ state->currPos.moreLeft = true;
+ state->currPos.moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- so->markItemIndex = -1; /* ditto */
+ state->numKilled = 0; /* just paranoia */
+ state->markItemIndex = -1; /* ditto */
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 2c05fb5..e548354 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1741,26 +1741,26 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
* away and the TID was re-used by a completely different heap tuple.
*/
void
-_bt_killitems(IndexScanDesc scan)
+_bt_killitems(BTScanState state, Relation indexRelation)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
OffsetNumber maxoff;
int i;
- int numKilled = so->numKilled;
+ int numKilled = state->numKilled;
bool killedsomething = false;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(state->currPos));
/*
* Always reset the scan state, so we don't look for same items on other
* pages.
*/
- so->numKilled = 0;
+ state->numKilled = 0;
- if (BTScanPosIsPinned(so->currPos))
+ if (BTScanPosIsPinned(*pos))
{
/*
* We have held the pin on this page since we read the index tuples,
@@ -1768,44 +1768,42 @@ _bt_killitems(IndexScanDesc scan)
* re-use of any TID on the page, so there is no need to check the
* LSN.
*/
- LockBuffer(so->currPos.buf, BT_READ);
-
- page = BufferGetPage(so->currPos.buf);
+ LockBuffer(pos->buf, BT_READ);
}
else
{
Buffer buf;
/* Attempt to re-read the buffer, getting pin and lock. */
- buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, BT_READ);
+ buf = _bt_getbuf(indexRelation, pos->currPage, BT_READ);
/* It might not exist anymore; in which case we can't hint it. */
if (!BufferIsValid(buf))
return;
- page = BufferGetPage(buf);
- if (BufferGetLSNAtomic(buf) == so->currPos.lsn)
- so->currPos.buf = buf;
+ if (BufferGetLSNAtomic(buf) == pos->lsn)
+ pos->buf = buf;
else
{
/* Modified while not pinned means hinting is not safe. */
- _bt_relbuf(scan->indexRelation, buf);
+ _bt_relbuf(indexRelation, buf);
return;
}
}
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
for (i = 0; i < numKilled; i++)
{
- int itemIndex = so->killedItems[i];
- BTScanPosItem *kitem = &so->currPos.items[itemIndex];
+ int itemIndex = state->killedItems[i];
+ BTScanPosItem *kitem = &pos->items[itemIndex];
OffsetNumber offnum = kitem->indexOffset;
- Assert(itemIndex >= so->currPos.firstItem &&
- itemIndex <= so->currPos.lastItem);
+ Assert(itemIndex >= pos->firstItem &&
+ itemIndex <= pos->lastItem);
if (offnum < minoff)
continue; /* pure paranoia */
while (offnum <= maxoff)
@@ -1833,10 +1831,10 @@ _bt_killitems(IndexScanDesc scan)
if (killedsomething)
{
opaque->btpo_flags |= BTP_HAS_GARBAGE;
- MarkBufferDirtyHint(so->currPos.buf, true);
+ MarkBufferDirtyHint(pos->buf, true);
}
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(pos->buf, BUFFER_LOCK_UNLOCK);
}
@@ -2214,3 +2212,14 @@ _bt_check_natts(Relation rel, Page page, OffsetNumber offnum)
}
}
+
+/*
+ * _bt_allocate_tuple_workspaces() -- Allocate buffers for saving index tuples
+ * in index-only scans.
+ */
+void
+_bt_allocate_tuple_workspaces(BTScanState state)
+{
+ state->currTuples = (char *) palloc(BLCKSZ * 2);
+ state->markTuples = state->currTuples + BLCKSZ;
+}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 4fb92d6..d78df29 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -433,22 +433,8 @@ typedef struct BTArrayKeyInfo
Datum *elem_values; /* array of num_elems Datums */
} BTArrayKeyInfo;
-typedef struct BTScanOpaqueData
+typedef struct BTScanStateData
{
- /* these fields are set by _bt_preprocess_keys(): */
- bool qual_ok; /* false if qual can never be satisfied */
- int numberOfKeys; /* number of preprocessed scan keys */
- ScanKey keyData; /* array of preprocessed scan keys */
-
- /* workspace for SK_SEARCHARRAY support */
- ScanKey arrayKeyData; /* modified copy of scan->keyData */
- int numArrayKeys; /* number of equality-type array keys (-1 if
- * there are any unsatisfiable array keys) */
- int arrayKeyCount; /* count indicating number of array scan keys
- * processed */
- BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
- MemoryContext arrayContext; /* scan-lifespan context for array data */
-
/* info about killed items if any (killedItems is NULL if never used) */
int *killedItems; /* currPos.items indexes of killed items */
int numKilled; /* number of currently stored items */
@@ -473,6 +459,27 @@ typedef struct BTScanOpaqueData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+} BTScanStateData;
+
+typedef BTScanStateData *BTScanState;
+
+typedef struct BTScanOpaqueData
+{
+ /* these fields are set by _bt_preprocess_keys(): */
+ bool qual_ok; /* false if qual can never be satisfied */
+ int numberOfKeys; /* number of preprocessed scan keys */
+ ScanKey keyData; /* array of preprocessed scan keys */
+
+ /* workspace for SK_SEARCHARRAY support */
+ ScanKey arrayKeyData; /* modified copy of scan->keyData */
+ int numArrayKeys; /* number of equality-type array keys (-1 if
+ * there are any unsatisfiable array keys) */
+ int arrayKeyCount; /* count indicating number of array scan keys
+ * processed */
+ BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
+ MemoryContext arrayContext; /* scan-lifespan context for array data */
+
+ BTScanStateData state;
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -589,7 +596,7 @@ extern void _bt_preprocess_keys(IndexScanDesc scan);
extern IndexTuple _bt_checkkeys(IndexScanDesc scan,
Page page, OffsetNumber offnum,
ScanDirection dir, bool *continuescan);
-extern void _bt_killitems(IndexScanDesc scan);
+extern void _bt_killitems(BTScanState state, Relation indexRelation);
extern BTCycleId _bt_vacuum_cycleid(Relation rel);
extern BTCycleId _bt_start_vacuum(Relation rel);
extern void _bt_end_vacuum(Relation rel);
@@ -602,6 +609,7 @@ extern bool btproperty(Oid index_oid, int attno,
bool *res, bool *isnull);
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
+extern void _bt_allocate_tuple_workspaces(BTScanState state);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 3d3c76d..5c065a5 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -181,6 +181,8 @@ BTScanOpaqueData
BTScanPos
BTScanPosData
BTScanPosItem
+BTScanState
+BTScanStateData
BTShared
BTSortArrayContext
BTSpool
0004-Add-kNN-support-to-btree-v06.patchtext/x-patch; name=0004-Add-kNN-support-to-btree-v06.patchDownload
diff --git a/doc/src/sgml/btree.sgml b/doc/src/sgml/btree.sgml
index 996932e..613032e 100644
--- a/doc/src/sgml/btree.sgml
+++ b/doc/src/sgml/btree.sgml
@@ -200,6 +200,19 @@
planner relies on them for optimization purposes.
</para>
+ <para>
+ To implement the distance ordered (nearest-neighbor) search, we only need
+ to define a distance operator (usually it called <->) with a correpsonding
+ operator family for distance comparison in the index's operator class.
+ These operators must satisfy the following assumptions for all non-null
+ values A,B,C of the datatype:
+
+ A <-> B = B <-> A symmetric law
+ if A = B, then A <-> C = B <-> C distance equivalence
+ if (A <= B and B <= C) or (A >= B and B >= C),
+ then A <-> B <= A <-> C monotonicity
+ </para>
+
</sect1>
<sect1 id="btree-support-funcs">
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 46f427b..caec484 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -175,6 +175,17 @@ CREATE INDEX test1_id_index ON test1 (id);
</para>
<para>
+ B-tree indexes are also capable of optimizing <quote>nearest-neighbor</>
+ searches, such as
+<programlisting><![CDATA[
+SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
+]]>
+</programlisting>
+ which finds the ten events closest to a given target date. The ability
+ to do this is again dependent on the particular operator class being used.
+ </para>
+
+ <para>
<indexterm>
<primary>index</primary>
<secondary>hash</secondary>
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..93094bc 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -1242,7 +1242,8 @@ SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING)
<title>Ordering Operators</title>
<para>
- Some index access methods (currently, only GiST and SP-GiST) support the concept of
+ Some index access methods (currently, only B-tree, GiST and SP-GiST)
+ support the concept of
<firstterm>ordering operators</firstterm>. What we have been discussing so far
are <firstterm>search operators</firstterm>. A search operator is one for which
the index can be searched to find all rows satisfying
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
index 3680e69..3f7e1b1 100644
--- a/src/backend/access/nbtree/README
+++ b/src/backend/access/nbtree/README
@@ -659,3 +659,20 @@ routines must treat it accordingly. The actual key stored in the
item is irrelevant, and need not be stored at all. This arrangement
corresponds to the fact that an L&Y non-leaf page has one more pointer
than key.
+
+Nearest-neighbor search
+-----------------------
+
+There is a special scan strategy for nearest-neighbor (kNN) search,
+that is used in queries with ORDER BY distance clauses like this:
+SELECT * FROM tab WHERE col > const1 ORDER BY col <-> const2 LIMIT k.
+But, unlike GiST, B-tree supports only a one ordering operator on the
+first index column.
+
+At the beginning of kNN scan, we need to determine which strategy we
+will use --- a special bidirectional or a ordinary unidirectional.
+If the point from which we measure the distance falls into the scan range,
+we use bidirectional scan starting from this point, else we use simple
+unidirectional scan in the right direction. Algorithm of a bidirectional
+scan is very simple: at each step we advancing scan in that direction,
+which has the nearest point.
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index bf6a6c6..fb413e7 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -25,6 +25,9 @@
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
+#include "nodes/pathnodes.h"
+#include "nodes/primnodes.h"
+#include "optimizer/paths.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "storage/condition_variable.h"
@@ -33,7 +36,9 @@
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
+#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -79,6 +84,7 @@ typedef enum
typedef struct BTParallelScanDescData
{
BlockNumber btps_scanPage; /* latest or next page to be scanned */
+ BlockNumber btps_knnScanPage; /* secondary kNN page to be scanned */
BTPS_State btps_pageStatus; /* indicates whether next page is
* available for scan. see above for
* possible states of parallel scan. */
@@ -97,6 +103,10 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
+static bool btmatchorderby(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses, List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
+
/*
* Btree handler function: return IndexAmRoutine with access method parameters
@@ -107,7 +117,7 @@ bthandler(PG_FUNCTION_ARGS)
{
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
- amroutine->amstrategies = BTMaxStrategyNumber;
+ amroutine->amstrategies = BtreeMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
amroutine->amcanbackward = true;
@@ -143,7 +153,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
- amroutine->ammatchorderby = NULL;
+ amroutine->ammatchorderby = btmatchorderby;
PG_RETURN_POINTER(amroutine);
}
@@ -215,23 +225,30 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTScanState state = &so->state;
+ ScanDirection arraydir =
+ scan->numberOfOrderBys > 0 ? ForwardScanDirection : dir;
bool res;
/* btree indexes are never lossy */
scan->xs_recheck = false;
+ scan->xs_recheckorderby = false;
+
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
/*
* If we have any array keys, initialize them during first call for a
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
return false;
- _bt_start_array_keys(scan, dir);
+ _bt_start_array_keys(scan, arraydir);
}
/* This loop handles advancing to the next array elements, if any */
@@ -242,7 +259,8 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(state->currPos))
+ if (!BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
res = _bt_first(scan, dir);
else
{
@@ -277,7 +295,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
if (res)
break;
/* ... otherwise see if we have more array keys to deal with */
- } while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
+ } while (so->numArrayKeys && _bt_advance_array_keys(scan, arraydir));
return res;
}
@@ -350,9 +368,6 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
IndexScanDesc scan;
BTScanOpaque so;
- /* no order by operators allowed */
- Assert(norderbys == 0);
-
/* get the scan */
scan = RelationGetIndexScan(rel, nkeys, norderbys);
@@ -379,6 +394,9 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
so->state.currTuples = so->state.markTuples = NULL;
+ so->knnState = NULL;
+ so->distanceTypeByVal = true;
+ so->scanDirection = NoMovementScanDirection;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -408,6 +426,8 @@ _bt_release_current_position(BTScanState state, Relation indexRelation,
static void
_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
/* No need to invalidate positions, if the RAM is about to be freed. */
_bt_release_current_position(state, scan->indexRelation, !free);
@@ -424,6 +444,17 @@ _bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
}
else
BTScanPosInvalidate(state->markPos);
+
+ if (!so->distanceTypeByVal)
+ {
+ if (DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+ state->currDistance = PointerGetDatum(NULL);
+
+ if (DatumGetPointer(state->markDistance))
+ pfree(DatumGetPointer(state->markDistance));
+ state->markDistance = PointerGetDatum(NULL);
+ }
}
/*
@@ -438,6 +469,13 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
_bt_release_scan_state(scan, state, false);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ so->knnState = NULL;
+ }
+
so->arrayKeyCount = 0;
/*
@@ -469,6 +507,14 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
scan->numberOfKeys * sizeof(ScanKeyData));
so->numberOfKeys = 0; /* until _bt_preprocess_keys sets it */
+ if (orderbys && scan->numberOfOrderBys > 0)
+ memmove(scan->orderByData,
+ orderbys,
+ scan->numberOfOrderBys * sizeof(ScanKeyData));
+
+ so->scanDirection = NoMovementScanDirection;
+ so->distanceTypeByVal = true;
+
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
_bt_preprocess_array_keys(scan);
}
@@ -483,6 +529,12 @@ btendscan(IndexScanDesc scan)
_bt_release_scan_state(scan, &so->state, true);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ }
+
/* Release storage */
if (so->keyData != NULL)
pfree(so->keyData);
@@ -494,7 +546,7 @@ btendscan(IndexScanDesc scan)
}
static void
-_bt_mark_current_position(BTScanState state)
+_bt_mark_current_position(BTScanOpaque so, BTScanState state)
{
/* There may be an old mark with a pin (but no lock). */
BTScanPosUnpinIfPinned(state->markPos);
@@ -512,6 +564,21 @@ _bt_mark_current_position(BTScanState state)
BTScanPosInvalidate(state->markPos);
state->markItemIndex = -1;
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->markDistance));
+
+ state->markIsNull = !BTScanPosIsValid(state->currPos) ||
+ state->currIsNull;
+
+ state->markDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->currDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -522,7 +589,13 @@ btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_mark_current_position(&so->state);
+ _bt_mark_current_position(so, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_mark_current_position(so, so->knnState);
+ so->markRightIsNearest = so->currRightIsNearest;
+ }
/* Also record the current positions of any array keys */
if (so->numArrayKeys)
@@ -532,6 +605,8 @@ btmarkpos(IndexScanDesc scan)
static void
_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
if (state->markItemIndex >= 0)
{
/*
@@ -567,6 +642,19 @@ _bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
state->markPos.nextTupleOffset);
}
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currIsNull = state->markIsNull;
+ state->currDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->markDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -588,6 +676,7 @@ btinitparallelscan(void *target)
SpinLockInit(&bt_target->btps_mutex);
bt_target->btps_scanPage = InvalidBlockNumber;
+ bt_target->btps_knnScanPage = InvalidBlockNumber;
bt_target->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
bt_target->btps_arrayKeyCount = 0;
ConditionVariableInit(&bt_target->btps_cv);
@@ -614,6 +703,7 @@ btparallelrescan(IndexScanDesc scan)
*/
SpinLockAcquire(&btscan->btps_mutex);
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount = 0;
SpinLockRelease(&btscan->btps_mutex);
@@ -638,7 +728,7 @@ btparallelrescan(IndexScanDesc scan)
* Callers should ignore the value of pageno if the return value is false.
*/
bool
-_bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
+_bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTPS_State pageStatus;
@@ -646,12 +736,17 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
bool status = true;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
*pageno = P_NONE;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ scanPage = state == &so->state
+ ? &btscan->btps_scanPage
+ : &btscan->btps_knnScanPage;
+
while (1)
{
SpinLockAcquire(&btscan->btps_mutex);
@@ -677,7 +772,7 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* of advancing it to a new page!
*/
btscan->btps_pageStatus = BTPARALLEL_ADVANCING;
- *pageno = btscan->btps_scanPage;
+ *pageno = *scanPage;
exit_loop = true;
}
SpinLockRelease(&btscan->btps_mutex);
@@ -696,19 +791,42 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* can now begin advancing the scan.
*/
void
-_bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
+_bt_parallel_release(IndexScanDesc scan, BTScanState state,
+ BlockNumber scan_page)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
+ bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
SpinLockAcquire(&btscan->btps_mutex);
- btscan->btps_scanPage = scan_page;
- btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ *scanPage = scan_page;
+ /* switch to idle state only if both KNN pages are initialized */
+ if (!knnScan || *otherScanPage != InvalidBlockNumber)
+ {
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ status_changed = true;
+ }
SpinLockRelease(&btscan->btps_mutex);
- ConditionVariableSignal(&btscan->btps_cv);
+
+ if (status_changed)
+ ConditionVariableSignal(&btscan->btps_cv);
}
/*
@@ -719,12 +837,15 @@ _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
* advance to the next page.
*/
void
-_bt_parallel_done(IndexScanDesc scan)
+_bt_parallel_done(IndexScanDesc scan, BTScanState state)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
/* Do nothing, for non-parallel scans */
if (parallel_scan == NULL)
@@ -733,18 +854,41 @@ _bt_parallel_done(IndexScanDesc scan)
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
+
/*
* Mark the parallel scan as done for this combination of scan keys,
* unless some other process already did so. See also
* _bt_advance_array_keys.
*/
SpinLockAcquire(&btscan->btps_mutex);
- if (so->arrayKeyCount >= btscan->btps_arrayKeyCount &&
- btscan->btps_pageStatus != BTPARALLEL_DONE)
+
+ Assert(btscan->btps_pageStatus == BTPARALLEL_ADVANCING);
+
+ if (so->arrayKeyCount >= btscan->btps_arrayKeyCount)
{
- btscan->btps_pageStatus = BTPARALLEL_DONE;
+ *scanPage = P_NONE;
status_changed = true;
+
+ /* switch to "done" state only if both KNN scans are done */
+ if (!knnScan || *otherScanPage == P_NONE)
+ btscan->btps_pageStatus = BTPARALLEL_DONE;
+ /* else switch to "idle" state only if both KNN scans are initialized */
+ else if (*otherScanPage != InvalidBlockNumber)
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ else
+ status_changed = false;
}
+
SpinLockRelease(&btscan->btps_mutex);
/* wake up all the workers associated with this parallel scan */
@@ -774,6 +918,7 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
if (btscan->btps_pageStatus == BTPARALLEL_DONE)
{
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount++;
}
@@ -859,6 +1004,12 @@ btrestrpos(IndexScanDesc scan)
_bt_restore_array_keys(scan);
_bt_restore_marked_position(scan, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_restore_marked_position(scan, so->knnState);
+ so->currRightIsNearest = so->markRightIsNearest;
+ }
}
/*
@@ -1394,3 +1545,91 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+/*
+ * btmatchorderby() -- Check whether KNN-search strategy is applicable to
+ * the given ORDER BY distance operator.
+ */
+static bool
+btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
+ List **orderby_clauses_p, List **orderby_clausecols_p)
+{
+ Expr *expr;
+ ListCell *lc;
+ int indexcol;
+ int num_eq_cols = 0;
+
+ /* only one ORDER BY clause is supported */
+ if (list_length(pathkeys) != 1)
+ return false;
+
+ /*
+ * Compute a number of leading consequent index columns with equality
+ * restriction clauses.
+ */
+ foreach(lc, index_clauses)
+ {
+ IndexClause *iclause = lfirst_node(IndexClause, lc);
+ ListCell *lcq;
+
+ indexcol = iclause->indexcol;
+
+ if (indexcol > num_eq_cols)
+ /* Sequence of equality-restricted columns is broken. */
+ break;
+
+ foreach(lcq, iclause->indexquals)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcq);
+ Expr *clause = rinfo->clause;
+ Oid opno;
+ StrategyNumber strat;
+
+ if (!clause)
+ continue;
+
+ if (IsA(clause, OpExpr))
+ {
+ OpExpr *opexpr = (OpExpr *) clause;
+
+ opno = opexpr->opno;
+ }
+ else
+ {
+ /* Skip unsupported expression */
+ continue;
+ }
+
+ /* Check if the operator is btree equality operator. */
+ strat = get_op_opfamily_strategy(opno, index->opfamily[indexcol]);
+
+ if (strat == BTEqualStrategyNumber)
+ num_eq_cols = indexcol + 1;
+ }
+ }
+
+ /*
+ * If there are no equality columns try to match only the first column,
+ * otherwise try all columns.
+ */
+ indexcol = num_eq_cols ? -1 : 0;
+
+ expr = match_orderbyop_pathkey(index, castNode(PathKey, linitial(pathkeys)),
+ &indexcol);
+
+ if (!expr)
+ return false;
+
+ /*
+ * ORDER BY distance is supported only for the first index column or if
+ * all previous columns have equality restrictions.
+ */
+ if (indexcol > num_eq_cols)
+ return false;
+
+ /* Return first ORDER BY clause's expression and column. */
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *orderby_clausecols_p = lappend_int(*orderby_clausecols_p, indexcol);
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index cbd72bd..28f94e7 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -31,12 +31,14 @@ static void _bt_saveitem(BTScanState state, int itemIndex,
static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
BlockNumber blkno, ScanDirection dir);
-static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
- ScanDirection dir);
+static bool _bt_parallel_readpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
+static BTScanState _bt_alloc_knn_scan(IndexScanDesc scan);
+static bool _bt_start_knn_scan(IndexScanDesc scan, bool left, bool right);
/*
@@ -597,6 +599,157 @@ _bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
}
/*
+ * _bt_calc_current_dist() -- Calculate distance from the current item
+ * of the scan state to the target order-by ScanKey argument.
+ */
+static void
+_bt_calc_current_dist(IndexScanDesc scan, BTScanState state)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+ IndexTuple itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+ ScanKey scankey = &scan->orderByData[0];
+ Datum value;
+
+ value = index_getattr(itup, scankey->sk_attno, scan->xs_itupdesc,
+ &state->currIsNull);
+
+ if (state->currIsNull)
+ return; /* NULL distance */
+
+ value = FunctionCall2Coll(&scankey->sk_func,
+ scankey->sk_collation,
+ value,
+ scankey->sk_argument);
+
+ /* free previous distance value for by-ref types */
+ if (!so->distanceTypeByVal && DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currDistance = value;
+}
+
+/*
+ * _bt_compare_current_dist() -- Compare current distances of the left and right scan states.
+ *
+ * NULL distances are considered to be greater than any non-NULL distances.
+ *
+ * Returns true if right distance is lesser than left, otherwise false.
+ */
+static bool
+_bt_compare_current_dist(BTScanOpaque so, BTScanState rstate, BTScanState lstate)
+{
+ if (lstate->currIsNull)
+ return true; /* non-NULL < NULL */
+
+ if (rstate->currIsNull)
+ return false; /* NULL > non-NULL */
+
+ return DatumGetBool(FunctionCall2Coll(&so->distanceCmpProc,
+ InvalidOid, /* XXX collation for distance comparison */
+ rstate->currDistance,
+ lstate->currDistance));
+}
+
+/*
+ * _bt_alloc_knn_scan() -- Allocate additional backward scan state for KNN.
+ */
+static BTScanState
+_bt_alloc_knn_scan(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState lstate = (BTScanState) palloc(sizeof(BTScanStateData));
+
+ _bt_allocate_tuple_workspaces(lstate);
+
+ if (!scan->xs_want_itup)
+ {
+ /* We need to request index tuples for distance comparison. */
+ scan->xs_want_itup = true;
+ _bt_allocate_tuple_workspaces(&so->state);
+ }
+
+ BTScanPosInvalidate(lstate->currPos);
+ lstate->currPos.moreLeft = false;
+ lstate->currPos.moreRight = false;
+ BTScanPosInvalidate(lstate->markPos);
+ lstate->markItemIndex = -1;
+ lstate->killedItems = NULL;
+ lstate->numKilled = 0;
+ lstate->currDistance = PointerGetDatum(NULL);
+ lstate->markDistance = PointerGetDatum(NULL);
+
+ return so->knnState = lstate;
+}
+
+static bool
+_bt_start_knn_scan(IndexScanDesc scan, bool left, bool right)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+
+ if (!left && !right)
+ return false; /* empty result */
+
+ rstate = &so->state;
+ lstate = so->knnState;
+
+ if (left && right)
+ {
+ /*
+ * We have found items in both scan directions,
+ * determine nearest item to return.
+ */
+ _bt_calc_current_dist(scan, rstate);
+ _bt_calc_current_dist(scan, lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+
+ /* Reset right flag if the left item is nearer. */
+ right = so->currRightIsNearest;
+ }
+
+ /* Return current item of the selected scan direction. */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
+ * _bt_init_knn_scan() -- Init additional scan state for KNN search.
+ *
+ * Caller must pin and read-lock scan->state.currPos.buf buffer.
+ *
+ * If empty result was found returned false.
+ * Otherwise prepared current item, and returned true.
+ */
+static bool
+_bt_init_knn_scan(IndexScanDesc scan, OffsetNumber offnum)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+ Buffer buf = rstate->currPos.buf;
+ bool left,
+ right;
+
+ lstate = _bt_alloc_knn_scan(scan);
+
+ /* Bump pin and lock count before BTScanPosData copying. */
+ IncrBufferRefCount(buf);
+ LockBuffer(buf, BT_READ);
+
+ memcpy(&lstate->currPos, &rstate->currPos, sizeof(BTScanPosData));
+ lstate->currPos.moreLeft = true;
+ lstate->currPos.moreRight = false;
+
+ /* Load first pages from the both scans. */
+ right = _bt_load_first_page(scan, rstate, ForwardScanDirection, offnum);
+ left = _bt_load_first_page(scan, lstate, BackwardScanDirection,
+ OffsetNumberPrev(offnum));
+
+ return _bt_start_knn_scan(scan, left, right);
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -654,6 +807,15 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!so->qual_ok)
return false;
+ if (scan->numberOfOrderBys > 0)
+ {
+ if (so->useBidirectionalKnnScan)
+ _bt_init_distance_comparison(scan);
+ else if (so->scanDirection != NoMovementScanDirection)
+ /* use selected KNN scan direction */
+ dir = so->scanDirection;
+ }
+
/*
* For parallel scans, get the starting page from shared state. If the
* scan has not started, proceed to find out first leaf page in the usual
@@ -662,19 +824,50 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, &so->state, &blkno);
if (!status)
return false;
- else if (blkno == P_NONE)
- {
- _bt_parallel_done(scan);
- return false;
- }
else if (blkno != InvalidBlockNumber)
{
- if (!_bt_parallel_readpage(scan, blkno, dir))
- return false;
- goto readcomplete;
+ bool knn = so->useBidirectionalKnnScan;
+ bool right;
+ bool left;
+
+ if (knn)
+ _bt_alloc_knn_scan(scan);
+
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, &so->state);
+ right = false;
+ }
+ else
+ right = _bt_parallel_readpage(scan, &so->state, blkno,
+ knn ? ForwardScanDirection : dir);
+
+ if (!knn)
+ return right && _bt_return_current_item(scan, &so->state);
+
+ /* seize additional backward KNN scan */
+ left = _bt_parallel_seize(scan, so->knnState, &blkno);
+
+ if (left)
+ {
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, so->knnState);
+ left = false;
+ }
+ else
+ {
+ /* backward scan should be already initialized */
+ Assert(blkno != InvalidBlockNumber);
+ left = _bt_parallel_readpage(scan, so->knnState, blkno,
+ BackwardScanDirection);
+ }
+ }
+
+ return _bt_start_knn_scan(scan, left, right);
}
}
@@ -724,7 +917,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* storing their addresses into the local startKeys[] array.
*----------
*/
+
strat_total = BTEqualStrategyNumber;
+
if (so->numberOfKeys > 0)
{
AttrNumber curattr;
@@ -749,6 +944,10 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*/
for (cur = so->keyData, i = 0;; cur++, i++)
{
+ if (so->useBidirectionalKnnScan &&
+ curattr >= scan->orderByData->sk_attno)
+ break;
+
if (i >= so->numberOfKeys || cur->sk_attno != curattr)
{
/*
@@ -851,6 +1050,16 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
}
}
+ if (so->useBidirectionalKnnScan)
+ {
+ Assert(strat_total == BTEqualStrategyNumber);
+ strat_total = BtreeKNNSearchStrategyNumber;
+
+ (void) _bt_init_knn_start_keys(scan, &startKeys[keysCount],
+ ¬nullkeys[keysCount]);
+ keysCount++;
+ }
+
/*
* If we found no usable boundary keys, we have to start from one end of
* the tree. Walk down that edge to the first or last key, and scan from
@@ -865,7 +1074,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!match)
{
/* No match, so mark (parallel) scan finished */
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
}
return match;
@@ -900,7 +1109,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(subkey->sk_flags & SK_ROW_MEMBER);
if (subkey->sk_flags & SK_ISNULL)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
return false;
}
memcpy(scankeys + i, subkey, sizeof(ScanKeyData));
@@ -1080,6 +1289,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
break;
case BTGreaterEqualStrategyNumber:
+ case BtreeKNNSearchStrategyNumber:
/*
* Find first item >= scankey. (This is only used for forward
@@ -1127,7 +1337,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* mark parallel scan as done, so that all the workers can finish
* their scan
*/
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
BTScanPosInvalidate(*currPos);
return false;
@@ -1166,17 +1376,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(!BTScanPosIsValid(*currPos));
currPos->buf = buf;
+ if (strat_total == BtreeKNNSearchStrategyNumber)
+ return _bt_init_knn_scan(scan, offnum);
+
if (!_bt_load_first_page(scan, &so->state, dir, offnum))
- return false;
+ return false; /* empty result */
-readcomplete:
/* OK, currPos->itemIndex says what to return */
return _bt_return_current_item(scan, &so->state);
}
/*
- * Advance to next tuple on current page; or if there's no more,
- * try to step to the next page with data.
+ * _bt_next_item() -- Advance to next tuple on current page;
+ * or if there's no more, try to step to the next page with data.
+ *
+ * If there are no more matching records in the given direction
*/
static bool
_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
@@ -1196,6 +1410,51 @@ _bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
}
/*
+ * _bt_next_nearest() -- Return next nearest item from bidirectional KNN scan.
+ */
+static bool
+_bt_next_nearest(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state;
+ BTScanState lstate = so->knnState;
+ bool right = BTScanPosIsValid(rstate->currPos);
+ bool left = BTScanPosIsValid(lstate->currPos);
+ bool advanceRight;
+
+ if (right && left)
+ advanceRight = so->currRightIsNearest;
+ else if (right)
+ advanceRight = true;
+ else if (left)
+ advanceRight = false;
+ else
+ return false; /* end of the scan */
+
+ if (advanceRight)
+ right = _bt_next_item(scan, rstate, ForwardScanDirection);
+ else
+ left = _bt_next_item(scan, lstate, BackwardScanDirection);
+
+ if (!left && !right)
+ return false; /* end of the scan */
+
+ if (left && right)
+ {
+ /*
+ * If there are items in both scans we must recalculate distance
+ * in the advanced scan.
+ */
+ _bt_calc_current_dist(scan, advanceRight ? rstate : lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+ right = so->currRightIsNearest;
+ }
+
+ /* return nearest item */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_next() -- Get the next item in a scan.
*
* On entry, so->currPos describes the current page, which may be pinned
@@ -1214,6 +1473,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ if (so->knnState)
+ /* return next neareset item from KNN scan */
+ return _bt_next_nearest(scan);
+
if (!_bt_next_item(scan, &so->state, dir))
return false;
@@ -1266,9 +1529,9 @@ _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
if (scan->parallel_scan)
{
if (ScanDirectionIsForward(dir))
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1442,7 +1705,7 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the next block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
/* release the previous buffer, if pinned */
@@ -1474,13 +1737,19 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the current block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
BTScanPosInvalidate(*currPos);
return false;
}
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, state);
+ BTScanPosInvalidate(*currPos);
+ return false;
+ }
}
else
{
@@ -1530,7 +1799,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
*/
if (blkno == P_NONE || !currPos->moreRight)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1553,14 +1822,14 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
}
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1619,7 +1888,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (!currPos->moreLeft)
{
_bt_relbuf(rel, currPos->buf);
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1630,7 +1899,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
/* if we're physically at end of index, return failure */
if (currPos->buf == InvalidBuffer)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1654,7 +1923,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1666,7 +1935,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1687,17 +1956,16 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
* indicate success.
*/
static bool
-_bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_parallel_readpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ _bt_initialize_more_data(state, dir);
- _bt_initialize_more_data(&so->state, dir);
-
- if (!_bt_readnextpage(scan, &so->state, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
return true;
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index e548354..5b9294b 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -20,16 +20,21 @@
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "catalog/pg_amop.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
typedef struct BTSortArrayContext
{
FmgrInfo flinfo;
+ FmgrInfo distflinfo;
+ FmgrInfo distcmpflinfo;
+ ScanKey distkey;
Oid collation;
bool reverse;
} BTSortArrayContext;
@@ -49,6 +54,11 @@ static void _bt_mark_scankey_required(ScanKey skey);
static bool _bt_check_rowcompare(ScanKey skey,
IndexTuple tuple, TupleDesc tupdesc,
ScanDirection dir, bool *continuescan);
+static inline StrategyNumber _bt_select_knn_strategy_for_key(IndexScanDesc scan,
+ ScanKey cond);
+static void _bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval);
+
/*
@@ -445,6 +455,7 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
{
Relation rel = scan->indexRelation;
Oid elemtype;
+ Oid opfamily;
RegProcedure cmp_proc;
BTSortArrayContext cxt;
int last_non_dup;
@@ -462,6 +473,54 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
if (elemtype == InvalidOid)
elemtype = rel->rd_opcintype[skey->sk_attno - 1];
+ opfamily = rel->rd_opfamily[skey->sk_attno - 1];
+
+ if (scan->numberOfOrderBys <= 0 ||
+ scan->orderByData[0].sk_attno != skey->sk_attno)
+ {
+ cxt.distkey = NULL;
+ cxt.reverse = reverse;
+ }
+ else
+ {
+ /* Init procedures for distance calculation and comparison. */
+ ScanKey distkey = &scan->orderByData[0];
+ ScanKeyData distkey2;
+ Oid disttype = distkey->sk_subtype;
+ Oid distopr;
+ RegProcedure distproc;
+
+ if (!OidIsValid(disttype))
+ disttype = rel->rd_opcintype[skey->sk_attno - 1];
+
+ /* Lookup distance operator in index column's operator family. */
+ distopr = get_opfamily_member(opfamily,
+ elemtype,
+ disttype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(distopr))
+ elog(ERROR, "missing operator (%u,%u) for strategy %d in opfamily %u",
+ elemtype, disttype, BtreeKNNSearchStrategyNumber, opfamily);
+
+ distproc = get_opcode(distopr);
+
+ if (!RegProcedureIsValid(distproc))
+ elog(ERROR, "missing code for operator %u", distopr);
+
+ fmgr_info(distproc, &cxt.distflinfo);
+
+ distkey2 = *distkey;
+ fmgr_info_copy(&distkey2.sk_func, &cxt.distflinfo, CurrentMemoryContext);
+ distkey2.sk_subtype = disttype;
+
+ _bt_get_distance_cmp_proc(&distkey2, opfamily, elemtype,
+ &cxt.distcmpflinfo, NULL, NULL);
+
+ cxt.distkey = distkey;
+ cxt.reverse = false; /* supported only ascending ordering */
+ }
+
/*
* Look up the appropriate comparison function in the opfamily.
*
@@ -470,19 +529,17 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
* non-cross-type support functions for any datatype that it supports at
* all.
*/
- cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
+ cmp_proc = get_opfamily_proc(opfamily,
elemtype,
elemtype,
BTORDER_PROC);
if (!RegProcedureIsValid(cmp_proc))
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
- BTORDER_PROC, elemtype, elemtype,
- rel->rd_opfamily[skey->sk_attno - 1]);
+ BTORDER_PROC, elemtype, elemtype, opfamily);
/* Sort the array elements */
fmgr_info(cmp_proc, &cxt.flinfo);
cxt.collation = skey->sk_collation;
- cxt.reverse = reverse;
qsort_arg((void *) elems, nelems, sizeof(Datum),
_bt_compare_array_elements, (void *) &cxt);
@@ -514,6 +571,23 @@ _bt_compare_array_elements(const void *a, const void *b, void *arg)
BTSortArrayContext *cxt = (BTSortArrayContext *) arg;
int32 compare;
+ if (cxt->distkey)
+ {
+ Datum dista = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ da,
+ cxt->distkey->sk_argument);
+ Datum distb = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ db,
+ cxt->distkey->sk_argument);
+ bool cmp = DatumGetBool(FunctionCall2Coll(&cxt->distcmpflinfo,
+ cxt->collation,
+ dista,
+ distb));
+ return cmp ? -1 : 1;
+ }
+
compare = DatumGetInt32(FunctionCall2Coll(&cxt->flinfo,
cxt->collation,
da, db));
@@ -667,6 +741,66 @@ _bt_restore_array_keys(IndexScanDesc scan)
}
}
+/*
+ * _bt_emit_scan_key() -- Emit one prepared scan key
+ *
+ * Push the scan key into the so->keyData[] array, and then mark it if it is
+ * required. Also update selected kNN strategy.
+ */
+static void
+_bt_emit_scan_key(IndexScanDesc scan, ScanKey skey, int numberOfEqualCols)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey outkey = &so->keyData[so->numberOfKeys++];
+
+ memcpy(outkey, skey, sizeof(ScanKeyData));
+
+ /*
+ * We can mark the qual as required (possibly only in one direction) if all
+ * attrs before this one had "=".
+ */
+ if (outkey->sk_attno - 1 == numberOfEqualCols)
+ _bt_mark_scankey_required(outkey);
+
+ /* Update kNN strategy if it is not already selected. */
+ if (so->useBidirectionalKnnScan)
+ {
+ switch (_bt_select_knn_strategy_for_key(scan, outkey))
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ /*
+ * Ordering key argument is greater than all values in scan
+ * range, select backward scan direction.
+ */
+ so->scanDirection = BackwardScanDirection;
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BTEqualStrategyNumber:
+ /* Use default unidirectional scan direction. */
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ /*
+ * Ordering key argument is lesser than all values in scan
+ * range, select forward scan direction.
+ */
+ so->scanDirection = ForwardScanDirection;
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BtreeKNNSearchStrategyNumber:
+ /*
+ * Ordering key argument falls into scan range,
+ * keep using bidirectional scan.
+ */
+ break;
+ }
+ }
+}
/*
* _bt_preprocess_keys() -- Preprocess scan keys
@@ -758,10 +892,8 @@ _bt_preprocess_keys(IndexScanDesc scan)
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int numberOfKeys = scan->numberOfKeys;
int16 *indoption = scan->indexRelation->rd_indoption;
- int new_numberOfKeys;
int numberOfEqualCols;
ScanKey inkeys;
- ScanKey outkeys;
ScanKey cur;
ScanKey xform[BTMaxStrategyNumber];
bool test_result;
@@ -769,6 +901,24 @@ _bt_preprocess_keys(IndexScanDesc scan)
j;
AttrNumber attno;
+ if (scan->numberOfOrderBys > 0)
+ {
+ ScanKey ord = scan->orderByData;
+
+ if (scan->numberOfOrderBys > 1)
+ /* it should not happen, see btmatchorderby() */
+ elog(ERROR, "only one btree ordering operator is supported");
+
+ Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
+
+ /* use bidirectional kNN scan by default */
+ so->useBidirectionalKnnScan = true;
+ }
+ else
+ {
+ so->useBidirectionalKnnScan = false;
+ }
+
/* initialize result variables */
so->qual_ok = true;
so->numberOfKeys = 0;
@@ -784,7 +934,6 @@ _bt_preprocess_keys(IndexScanDesc scan)
else
inkeys = scan->keyData;
- outkeys = so->keyData;
cur = &inkeys[0];
/* we check that input keys are correctly ordered */
if (cur->sk_attno < 1)
@@ -796,18 +945,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
/* Apply indoption to scankey (might change sk_strategy!) */
if (!_bt_fix_scankey_strategy(cur, indoption))
so->qual_ok = false;
- memcpy(outkeys, cur, sizeof(ScanKeyData));
- so->numberOfKeys = 1;
- /* We can mark the qual as required if it's for first index col */
- if (cur->sk_attno == 1)
- _bt_mark_scankey_required(outkeys);
+
+ _bt_emit_scan_key(scan, cur, 0);
return;
}
/*
* Otherwise, do the full set of pushups.
*/
- new_numberOfKeys = 0;
numberOfEqualCols = 0;
/*
@@ -931,20 +1076,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
}
/*
- * Emit the cleaned-up keys into the outkeys[] array, and then
+ * Emit the cleaned-up keys into the so->keyData[] array, and then
* mark them if they are required. They are required (possibly
* only in one direction) if all attrs before this one had "=".
*/
for (j = BTMaxStrategyNumber; --j >= 0;)
{
if (xform[j])
- {
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, xform[j], sizeof(ScanKeyData));
- if (priorNumberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
- }
+ _bt_emit_scan_key(scan, xform[j], priorNumberOfEqualCols);
}
/*
@@ -964,17 +1103,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
/* if row comparison, push it directly to the output array */
if (cur->sk_flags & SK_ROW_HEADER)
{
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, cur, sizeof(ScanKeyData));
- if (numberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
+ _bt_emit_scan_key(scan, cur, numberOfEqualCols);
/*
* We don't support RowCompare using equality; such a qual would
* mess up the numberOfEqualCols tracking.
*/
Assert(j != (BTEqualStrategyNumber - 1));
+
continue;
}
@@ -1007,16 +1143,10 @@ _bt_preprocess_keys(IndexScanDesc scan)
* previous one in xform[j] and push this one directly to the
* output array.
*/
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, cur, sizeof(ScanKeyData));
- if (numberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
+ _bt_emit_scan_key(scan, cur, numberOfEqualCols);
}
}
}
-
- so->numberOfKeys = new_numberOfKeys;
}
/*
@@ -2075,6 +2205,39 @@ btproperty(Oid index_oid, int attno,
*res = true;
return true;
+ case AMPROP_DISTANCE_ORDERABLE:
+ {
+ Oid opclass,
+ opfamily,
+ opcindtype;
+
+ /* answer only for columns, not AM or whole index */
+ if (attno == 0)
+ return false;
+
+ opclass = get_index_column_opclass(index_oid, attno);
+
+ if (!OidIsValid(opclass))
+ {
+ *res = false; /* non-key attribute */
+ return true;
+ }
+
+ if (!get_opclass_opfamily_and_input_type(opclass,
+ &opfamily, &opcindtype))
+ {
+ *isnull = true;
+ return true;
+ }
+
+ *res = SearchSysCacheExists(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(opcindtype),
+ ObjectIdGetDatum(opcindtype),
+ Int16GetDatum(BtreeKNNSearchStrategyNumber));
+ return true;
+ }
+
default:
return false; /* punt to generic code */
}
@@ -2223,3 +2386,212 @@ _bt_allocate_tuple_workspaces(BTScanState state)
state->currTuples = (char *) palloc(BLCKSZ * 2);
state->markTuples = state->currTuples + BLCKSZ;
}
+
+static bool
+_bt_compare_row_key_with_ordering_key(ScanKey row, ScanKey ord, bool *result)
+{
+ ScanKey subkey = (ScanKey) DatumGetPointer(row->sk_argument);
+ int32 cmpresult;
+
+ Assert(subkey->sk_attno == 1);
+ Assert(subkey->sk_flags & SK_ROW_MEMBER);
+
+ if (subkey->sk_flags & SK_ISNULL)
+ return false;
+
+ /* Perform the test --- three-way comparison not bool operator */
+ cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
+ subkey->sk_collation,
+ ord->sk_argument,
+ subkey->sk_argument));
+
+ if (subkey->sk_flags & SK_BT_DESC)
+ cmpresult = -cmpresult;
+
+ /*
+ * At this point cmpresult indicates the overall result of the row
+ * comparison, and subkey points to the deciding column (or the last
+ * column if the result is "=").
+ */
+ switch (subkey->sk_strategy)
+ {
+ /* EQ and NE cases aren't allowed here */
+ case BTLessStrategyNumber:
+ *result = cmpresult < 0;
+ break;
+ case BTLessEqualStrategyNumber:
+ *result = cmpresult <= 0;
+ break;
+ case BTGreaterEqualStrategyNumber:
+ *result = cmpresult >= 0;
+ break;
+ case BTGreaterStrategyNumber:
+ *result = cmpresult > 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized RowCompareType: %d",
+ (int) subkey->sk_strategy);
+ *result = false; /* keep compiler quiet */
+ }
+
+ return true;
+}
+
+/*
+ * _bt_select_knn_strategy_for_key() -- Determine which kNN scan strategy to use:
+ * bidirectional or unidirectional. We are checking here if the
+ * ordering scankey argument falls into the scan range: if it falls
+ * we must use bidirectional scan, otherwise we use unidirectional.
+ *
+ * Returns BtreeKNNSearchStrategyNumber for bidirectional scan or
+ * strategy number of non-matched scankey for unidirectional.
+ */
+static inline StrategyNumber
+_bt_select_knn_strategy_for_key(IndexScanDesc scan, ScanKey cond)
+{
+ ScanKey ord = scan->orderByData;
+ bool result;
+
+ /* only interesting in the index attribute that is ordered by a distance */
+ if (cond->sk_attno != ord->sk_attno)
+ return BtreeKNNSearchStrategyNumber;
+
+ if (cond->sk_strategy == BTEqualStrategyNumber)
+ /* always use simple unidirectional scan for equals operators */
+ return BTEqualStrategyNumber;
+
+ if (cond->sk_flags & SK_ROW_HEADER)
+ {
+ if (!_bt_compare_row_key_with_ordering_key(cond, ord, &result))
+ return BTEqualStrategyNumber; /* ROW(fist_index_attr, ...) IS NULL */
+ }
+ else
+ {
+ if (!_bt_compare_scankey_args(scan, cond, ord, cond, &result))
+ elog(ERROR, "could not compare ordering key");
+ }
+
+ if (!result)
+ /*
+ * Ordering scankey argument is out of scan range,
+ * use unidirectional scan.
+ */
+ return cond->sk_strategy;
+
+ return BtreeKNNSearchStrategyNumber;
+}
+
+int
+_bt_init_knn_start_keys(IndexScanDesc scan, ScanKey *startKeys, ScanKey bufKeys)
+{
+ ScanKey ord = scan->orderByData;
+ int indopt = scan->indexRelation->rd_indoption[ord->sk_attno - 1];
+ int flags = (indopt << SK_BT_INDOPTION_SHIFT) |
+ SK_ORDER_BY |
+ SK_SEARCHNULL; /* only for invalid procedure oid, see
+ * assert in ScanKeyEntryInitialize() */
+ int keysCount = 0;
+
+ /* Init btree search key with ordering key argument. */
+ ScanKeyEntryInitialize(&bufKeys[0],
+ flags,
+ ord->sk_attno,
+ BtreeKNNSearchStrategyNumber,
+ ord->sk_subtype,
+ ord->sk_collation,
+ InvalidOid,
+ ord->sk_argument);
+
+ startKeys[keysCount++] = &bufKeys[0];
+
+ return keysCount;
+}
+
+static Oid
+_bt_get_sortfamily_for_opfamily_op(Oid opfamily, Oid lefttype, Oid righttype,
+ StrategyNumber strategy)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid sortfamily;
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(lefttype),
+ ObjectIdGetDatum(righttype),
+ Int16GetDatum(strategy));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ sortfamily = amop_tup->amopsortfamily;
+ ReleaseSysCache(tp);
+
+ return sortfamily;
+}
+
+/*
+ * _bt_get_distance_cmp_proc() -- Init procedure for comparsion of distances
+ * between "leftargtype" and "distkey".
+ */
+static void
+_bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval)
+{
+ RegProcedure opcode;
+ Oid sortfamily;
+ Oid opno;
+ Oid distanceType;
+
+ distanceType = get_func_rettype(distkey->sk_func.fn_oid);
+
+ sortfamily = _bt_get_sortfamily_for_opfamily_op(opfamily, leftargtype,
+ distkey->sk_subtype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(sortfamily))
+ elog(ERROR, "could not find sort family for btree ordering operator");
+
+ opno = get_opfamily_member(sortfamily,
+ distanceType,
+ distanceType,
+ BTLessEqualStrategyNumber);
+
+ if (!OidIsValid(opno))
+ elog(ERROR, "could not find operator for btree distance comparison");
+
+ opcode = get_opcode(opno);
+
+ if (!RegProcedureIsValid(opcode))
+ elog(ERROR,
+ "could not find procedure for btree distance comparison operator");
+
+ fmgr_info(opcode, finfo);
+
+ if (typlen)
+ get_typlenbyval(distanceType, typlen, typbyval);
+}
+
+/*
+ * _bt_init_distance_comparison() -- Init distance typlen/typbyval and its
+ * comparison procedure.
+ */
+void
+_bt_init_distance_comparison(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ Relation rel = scan->indexRelation;
+ ScanKey ord = scan->orderByData;
+
+ _bt_get_distance_cmp_proc(ord,
+ rel->rd_opfamily[ord->sk_attno - 1],
+ rel->rd_opcintype[ord->sk_attno - 1],
+ &so->distanceCmpProc,
+ &so->distanceTypeLen,
+ &so->distanceTypeByVal);
+
+ if (!so->distanceTypeByVal)
+ {
+ so->state.currDistance = PointerGetDatum(NULL);
+ so->state.markDistance = PointerGetDatum(NULL);
+ }
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 0148ea7..4558fd3 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -22,9 +22,17 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
+#define BTRequiredOperatorSet \
+ ((1 << BTLessStrategyNumber) | \
+ (1 << BTLessEqualStrategyNumber) | \
+ (1 << BTEqualStrategyNumber) | \
+ (1 << BTGreaterEqualStrategyNumber) | \
+ (1 << BTGreaterStrategyNumber))
+
/*
* Validator for a btree opclass.
@@ -132,10 +140,11 @@ btvalidate(Oid opclassoid)
{
HeapTuple oprtup = &oprlist->members[i]->tuple;
Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ Oid op_rettype;
/* Check that only allowed strategy numbers exist */
if (oprform->amopstrategy < 1 ||
- oprform->amopstrategy > BTMaxStrategyNumber)
+ oprform->amopstrategy > BtreeMaxStrategyNumber)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -146,20 +155,29 @@ btvalidate(Oid opclassoid)
result = false;
}
- /* btree doesn't support ORDER BY operators */
- if (oprform->amoppurpose != AMOP_SEARCH ||
- OidIsValid(oprform->amopsortfamily))
+ /* btree supports ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH)
{
- ereport(INFO,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
- opfamilyname, "btree",
- format_operator(oprform->amopopr))));
- result = false;
+ /* ... and operator result must match the claimed btree opfamily */
+ op_rettype = get_op_rettype(oprform->amopopr);
+ if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
+ {
+ ereport(INFO,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
+ opfamilyname, "btree",
+ format_operator(oprform->amopopr))));
+ result = false;
+ }
+ }
+ else
+ {
+ /* Search operators must always return bool */
+ op_rettype = BOOLOID;
}
/* Check operator signature --- same for all btree strategies */
- if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ if (!check_amop_signature(oprform->amopopr, op_rettype,
oprform->amoplefttype,
oprform->amoprighttype))
{
@@ -214,12 +232,8 @@ btvalidate(Oid opclassoid)
* or support functions for this datatype pair. The only things
* considered optional are the sortsupport and in_range functions.
*/
- if (thisgroup->operatorset !=
- ((1 << BTLessStrategyNumber) |
- (1 << BTLessEqualStrategyNumber) |
- (1 << BTEqualStrategyNumber) |
- (1 << BTGreaterEqualStrategyNumber) |
- (1 << BTGreaterStrategyNumber)))
+ if ((thisgroup->operatorset & BTRequiredOperatorSet) !=
+ BTRequiredOperatorSet)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6cf0b0d..279d8ba 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -990,6 +990,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
* if we are only trying to build bitmap indexscans, nor if we have to
* assume the scan is unordered.
*/
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
!found_lower_saop_clause &&
has_useful_pathkeys(root, rel));
@@ -1000,10 +1004,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
ForwardScanDirection);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
}
- else if (index->ammatchorderby && pathkeys_possibly_useful)
+
+ if (useful_pathkeys == NIL &&
+ index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -1015,12 +1019,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
else
useful_pathkeys = NIL;
}
- else
- {
- useful_pathkeys = NIL;
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
- }
/*
* 3. Check if an index-only scan is possible. If we're not building
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index d78df29..4f98e91 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -459,6 +459,12 @@ typedef struct BTScanStateData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+
+ /* KNN-search fields: */
+ Datum currDistance; /* current distance */
+ Datum markDistance; /* marked distance */
+ bool currIsNull; /* current item is NULL */
+ bool markIsNull; /* marked item is NULL */
} BTScanStateData;
typedef BTScanStateData *BTScanState;
@@ -479,7 +485,18 @@ typedef struct BTScanOpaqueData
BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
MemoryContext arrayContext; /* scan-lifespan context for array data */
- BTScanStateData state;
+ BTScanStateData state; /* main scan state */
+
+ /* kNN-search fields: */
+ BTScanState knnState; /* optional scan state for kNN search */
+ bool useBidirectionalKnnScan; /* use bidirectional kNN scan? */
+ ScanDirection scanDirection; /* selected scan direction for
+ * unidirectional kNN scan */
+ FmgrInfo distanceCmpProc; /* distance comparison procedure */
+ int16 distanceTypeLen; /* distance typlen */
+ bool distanceTypeByVal; /* distance typebyval */
+ bool currRightIsNearest; /* current right item is nearest */
+ bool markRightIsNearest; /* marked right item is nearest */
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -525,11 +542,12 @@ extern bool btcanreturn(Relation index, int attno);
/*
* prototypes for internal functions in nbtree.c
*/
-extern bool _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno);
-extern void _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page);
-extern void _bt_parallel_done(IndexScanDesc scan);
+extern bool _bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno);
+extern void _bt_parallel_release(IndexScanDesc scan, BTScanState state, BlockNumber scan_page);
+extern void _bt_parallel_done(IndexScanDesc scan, BTScanState state);
extern void _bt_parallel_advance_array_keys(IndexScanDesc scan);
+
/*
* prototypes for functions in nbtinsert.c
*/
@@ -610,6 +628,9 @@ extern bool btproperty(Oid index_oid, int attno,
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
extern void _bt_allocate_tuple_workspaces(BTScanState state);
+extern void _bt_init_distance_comparison(IndexScanDesc scan);
+extern int _bt_init_knn_start_keys(IndexScanDesc scan, ScanKey *startKeys,
+ ScanKey bufKeys);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/include/access/stratnum.h b/src/include/access/stratnum.h
index 8fdba28..8087ffe 100644
--- a/src/include/access/stratnum.h
+++ b/src/include/access/stratnum.h
@@ -32,7 +32,10 @@ typedef uint16 StrategyNumber;
#define BTGreaterEqualStrategyNumber 4
#define BTGreaterStrategyNumber 5
-#define BTMaxStrategyNumber 5
+#define BTMaxStrategyNumber 5 /* number of canonical B-tree strategies */
+
+#define BtreeKNNSearchStrategyNumber 6 /* for <-> (distance) */
+#define BtreeMaxStrategyNumber 6 /* number of extended B-tree strategies */
/*
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 6faa9d7..c75ef39 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -347,10 +347,10 @@ ROLLBACK;
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
ERROR: access method "invalid_index_method" does not exist
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 6, must be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 0, must be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 7, must be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 0, must be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -397,11 +397,12 @@ DROP OPERATOR FAMILY alt_opf8 USING btree;
CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
-ERROR: access method "btree" does not support ordering operators
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
ALTER OPERATOR FAMILY alt_opf11 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 84fd900..73e6e206 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -295,8 +295,8 @@ ROLLBACK;
-- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -340,10 +340,12 @@ CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
0005-Add-btree-distance-operators-v06.patchtext/x-patch; name=0005-Add-btree-distance-operators-v06.patchDownload
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index caec484..e656a8a 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -183,6 +183,12 @@ SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
</programlisting>
which finds the ten events closest to a given target date. The ability
to do this is again dependent on the particular operator class being used.
+ Built-in B-tree operator classes support distance ordering for data types
+ <type>int2</>, <type>int4</>, <type>int8</>,
+ <type>float4</>, <type>float8</>, <type>numeric</>,
+ <type>timestamp with time zone</>, <type>timestamp without time zone</>,
+ <type>time with time zone</>, <type>time without time zone</>,
+ <type>date</>, <type>interval</>, <type>oid</>, <type>money</>.
</para>
<para>
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index c92e9d5..83073c4 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -30,6 +30,7 @@
#include "utils/numeric.h"
#include "utils/pg_locale.h"
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
/*************************************************************************
* Private routines
@@ -1157,3 +1158,22 @@ int8_cash(PG_FUNCTION_ARGS)
PG_RETURN_CASH(result);
}
+
+Datum
+cash_distance(PG_FUNCTION_ARGS)
+{
+ Cash a = PG_GETARG_CASH(0);
+ Cash b = PG_GETARG_CASH(1);
+ Cash r;
+ Cash ra;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("money out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_CASH(ra);
+}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index cf5a1c6..4492f69 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -563,6 +563,17 @@ date_mii(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(result);
}
+Datum
+date_distance(PG_FUNCTION_ARGS)
+{
+ /* we assume the difference can't overflow */
+ Datum diff = DirectFunctionCall2(date_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+}
+
/*
* Internal routines for promoting date to timestamp and timestamp with
* time zone
@@ -760,6 +771,29 @@ date_cmp_timestamp(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
+ Timestamp dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
date_eq_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
@@ -844,6 +878,30 @@ date_cmp_timestamptz(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
+
+
+Datum
timestamp_eq_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
@@ -928,6 +986,29 @@ timestamp_cmp_date(PG_FUNCTION_ARGS)
}
Datum
+timestamp_dist_date(PG_FUNCTION_ARGS)
+{
+ Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ Timestamp dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
timestamptz_eq_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
@@ -1039,6 +1120,28 @@ in_range_date_interval(PG_FUNCTION_ARGS)
BoolGetDatum(less));
}
+Datum
+timestamptz_dist_date(PG_FUNCTION_ARGS)
+{
+ TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ TimestampTz dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
/* Add an interval to a date, giving a new date.
* Must handle both positive and negative intervals.
@@ -1957,6 +2060,16 @@ time_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+Datum
+time_distance(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(time_mi_time,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
+
/*****************************************************************************
* Time With Time Zone ADT
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 37c202d..d72fbef 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -3726,6 +3726,47 @@ width_bucket_float8(PG_FUNCTION_ARGS)
PG_RETURN_INT32(result);
}
+Datum
+float4dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float4 r = float4_mi(a, b);
+
+ PG_RETURN_FLOAT4(Abs(r));
+}
+
+Datum
+float8dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+
+Datum
+float48dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+Datum
+float84dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
/* ========== PRIVATE ROUTINES ========== */
#ifndef HAVE_CBRT
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 04825fc..76e92ec 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -1501,3 +1501,54 @@ generate_series_int4_support(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(ret);
}
+
+Datum
+int2dist(PG_FUNCTION_ARGS)
+{
+ int16 a = PG_GETARG_INT16(0);
+ int16 b = PG_GETARG_INT16(1);
+ int16 r;
+ int16 ra;
+
+ if (pg_sub_s16_overflow(a, b, &r) ||
+ r == PG_INT16_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_INT16(ra);
+}
+
+static int32
+int44_dist(int32 a, int32 b)
+{
+ int32 r;
+
+ if (pg_sub_s32_overflow(a, b, &r) ||
+ r == PG_INT32_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ return Abs(r);
+}
+
+Datum
+int4dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int24dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT16(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int42dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT16(1)));
+}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 0ff9394..2ceb16b 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1446,3 +1446,47 @@ generate_series_int8_support(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(ret);
}
+
+static int64
+int88_dist(int64 a, int64 b)
+{
+ int64 r;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+
+ return Abs(r);
+}
+
+Datum
+int8dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int82dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT16(1)));
+}
+
+Datum
+int84dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT32(1)));
+}
+
+Datum
+int28dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT16(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int48dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT32(0), PG_GETARG_INT64(1)));
+}
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index bb67e01..b8f48d5 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -469,3 +469,17 @@ oidvectorgt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+
+Datum
+oiddist(PG_FUNCTION_ARGS)
+{
+ Oid a = PG_GETARG_OID(0);
+ Oid b = PG_GETARG_OID(1);
+ Oid res;
+
+ if (a < b)
+ res = b - a;
+ else
+ res = a - b;
+ PG_RETURN_OID(res);
+}
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index e0ef2f7..c5d1042 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2694,6 +2694,86 @@ timestamp_mi(PG_FUNCTION_ARGS)
PG_RETURN_INTERVAL_P(result);
}
+Datum
+timestamp_distance(PG_FUNCTION_ARGS)
+{
+ Timestamp a = PG_GETARG_TIMESTAMP(0);
+ Timestamp b = PG_GETARG_TIMESTAMP(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+ p->time = PG_INT64_MAX;
+ PG_RETURN_INTERVAL_P(p);
+ }
+ else
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+Interval *
+timestamp_dist_internal(Timestamp a, Timestamp b)
+{
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ return r;
+ }
+
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ TimestampGetDatum(a),
+ TimestampGetDatum(b)));
+
+ return abs_interval(r);
+}
+
+Datum
+timestamptz_distance(PG_FUNCTION_ARGS)
+{
+ TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
+ TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(a, b));
+}
+
+Datum
+timestamp_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ Timestamp ts1 = PG_GETARG_TIMESTAMP(0);
+ TimestampTz tstz2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz tstz1;
+
+ tstz1 = timestamp2timestamptz(ts1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+Datum
+timestamptz_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ TimestampTz tstz1 = PG_GETARG_TIMESTAMPTZ(0);
+ Timestamp ts2 = PG_GETARG_TIMESTAMP(1);
+ TimestampTz tstz2;
+
+ tstz2 = timestamp2timestamptz(ts2);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+
/*
* interval_justify_interval()
*
@@ -3563,6 +3643,29 @@ interval_avg(PG_FUNCTION_ARGS)
Float8GetDatum((double) N.time));
}
+Interval *
+abs_interval(Interval *a)
+{
+ static Interval zero = {0, 0, 0};
+
+ if (DatumGetBool(DirectFunctionCall2(interval_lt,
+ IntervalPGetDatum(a),
+ IntervalPGetDatum(&zero))))
+ a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
+ IntervalPGetDatum(a)));
+
+ return a;
+}
+
+Datum
+interval_distance(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(interval_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
/* timestamp_age()
* Calculate time difference while retaining year/month fields.
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 0ab95d8..0e06b04 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -30,6 +30,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int24
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -47,6 +51,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int28
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -64,6 +72,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int4
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -81,6 +93,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int42
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -98,6 +114,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int48
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -115,6 +135,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int8
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -132,6 +156,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int82
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -149,6 +177,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int84
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -166,6 +198,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# btree oid_ops
@@ -179,6 +215,10 @@
amopstrategy => '4', amopopr => '>=(oid,oid)', amopmethod => 'btree' },
{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid', amoprighttype => 'oid',
amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'btree' },
+{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(oid,oid)', amopmethod => 'btree',
+ amopsortfamily => 'btree/oid_ops' },
# btree tid_ops
@@ -229,6 +269,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float48
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
@@ -246,6 +290,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# default operators float8
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -263,6 +311,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float84
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -280,6 +332,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# btree char_ops
@@ -416,6 +472,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -433,6 +493,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(date,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -450,6 +514,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(date,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -467,6 +535,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamp,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -484,6 +556,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -501,6 +577,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamp,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -518,6 +598,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -535,6 +619,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'date', amopstrategy => '5',
amopopr => '>(timestamptz,date)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -552,6 +640,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamptz,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree time_ops
@@ -570,6 +662,10 @@
{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(time,time)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree timetz_ops
@@ -606,6 +702,10 @@
{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'btree' },
+{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(interval,interval)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree macaddr
@@ -781,6 +881,10 @@
{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
amoprighttype => 'money', amopstrategy => '5', amopopr => '>(money,money)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
+ amoprighttype => 'money', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(money,money)', amopmethod => 'btree',
+ amopsortfamily => 'btree/money_ops' },
# btree array_ops
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 06aec07..914ca76 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -2851,6 +2851,114 @@
oprname => '-', oprleft => 'pg_lsn', oprright => 'pg_lsn',
oprresult => 'numeric', oprcode => 'pg_lsn_mi' },
+# distance operators
+{ oid => '4217', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int2', oprresult => 'int2', oprcom => '<->(int2,int2)',
+ oprcode => 'int2dist'},
+{ oid => '4218', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int4)',
+ oprcode => 'int4dist'},
+{ oid => '4219', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int8)',
+ oprcode => 'int8dist'},
+{ oid => '4220', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'oid',
+ oprright => 'oid', oprresult => 'oid', oprcom => '<->(oid,oid)',
+ oprcode => 'oiddist'},
+{ oid => '4221', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float4', oprresult => 'float4', oprcom => '<->(float4,float4)',
+ oprcode => 'float4dist'},
+{ oid => '4222', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float8)',
+ oprcode => 'float8dist'},
+{ oid => '4223', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'money',
+ oprright => 'money', oprresult => 'money', oprcom => '<->(money,money)',
+ oprcode => 'cash_distance'},
+{ oid => '4224', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'date', oprresult => 'int4', oprcom => '<->(date,date)',
+ oprcode => 'date_distance'},
+{ oid => '4225', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'time',
+ oprright => 'time', oprresult => 'interval', oprcom => '<->(time,time)',
+ oprcode => 'time_distance'},
+{ oid => '4226', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamp)',
+ oprcode => 'timestamp_distance'},
+{ oid => '4227', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamptz)',
+ oprcode => 'timestamptz_distance'},
+{ oid => '4228', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'interval',
+ oprright => 'interval', oprresult => 'interval', oprcom => '<->(interval,interval)',
+ oprcode => 'interval_distance'},
+
+# cross-type distance operators
+{ oid => '4229', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int2)',
+ oprcode => 'int24dist'},
+{ oid => '4230', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int2', oprresult => 'int4', oprcom => '<->(int2,int4)',
+ oprcode => 'int42dist'},
+{ oid => '4231', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int2)',
+ oprcode => 'int28dist'},
+{ oid => '4232', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int2', oprresult => 'int8', oprcom => '<->(int2,int8)',
+ oprcode => 'int82dist'},
+{ oid => '4233', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int4)',
+ oprcode => 'int48dist'},
+{ oid => '4234', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int4', oprresult => 'int8', oprcom => '<->(int4,int8)',
+ oprcode => 'int84dist'},
+{ oid => '4235', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float4)',
+ oprcode => 'float48dist'},
+{ oid => '4236', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float4', oprresult => 'float8', oprcom => '<->(float4,float8)',
+ oprcode => 'float84dist'},
+{ oid => '4237', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,date)',
+ oprcode => 'date_dist_timestamp'},
+{ oid => '4238', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamp)',
+ oprcode => 'timestamp_dist_date'},
+{ oid => '4239', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,date)',
+ oprcode => 'date_dist_timestamptz'},
+{ oid => '4240', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamptz)',
+ oprcode => 'timestamptz_dist_date'},
+{ oid => '4241', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamp)',
+ oprcode => 'timestamp_dist_timestamptz'},
+{ oid => '4242', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamptz)',
+ oprcode => 'timestamptz_dist_timestamp'},
+
# enum operators
{ oid => '3516', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anyenum',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a4e173b..96d5db0 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10534,4 +10534,90 @@
proname => 'pg_partition_root', prorettype => 'regclass',
proargtypes => 'regclass', prosrc => 'pg_partition_root' },
+# distance functions
+{ oid => '4243',
+ proname => 'int2dist', prorettype => 'int2',
+ proargtypes => 'int2 int2', prosrc => 'int2dist' },
+{ oid => '4244',
+ proname => 'int4dist', prorettype => 'int4',
+ proargtypes => 'int4 int4', prosrc => 'int4dist' },
+{ oid => '4245',
+ proname => 'int8dist', prorettype => 'int8',
+ proargtypes => 'int8 int8', prosrc => 'int8dist' },
+{ oid => '4246',
+ proname => 'oiddist', proleakproof => 't', prorettype => 'oid',
+ proargtypes => 'oid oid', prosrc => 'oiddist' },
+{ oid => '4247',
+ proname => 'float4dist', prorettype => 'float4',
+ proargtypes => 'float4 float4', prosrc => 'float4dist' },
+{ oid => '4248',
+ proname => 'float8dist', prorettype => 'float8',
+ proargtypes => 'float8 float8', prosrc => 'float8dist' },
+{ oid => '4249',
+ proname => 'cash_distance', prorettype => 'money',
+ proargtypes => 'money money', prosrc => 'cash_distance' },
+{ oid => '4250',
+ proname => 'date_distance', prorettype => 'int4',
+ proargtypes => 'date date', prosrc => 'date_distance' },
+{ oid => '4251',
+ proname => 'time_distance', prorettype => 'interval',
+ proargtypes => 'time time', prosrc => 'time_distance' },
+{ oid => '4252',
+ proname => 'timestamp_distance', prorettype => 'interval',
+ proargtypes => 'timestamp timestamp', prosrc => 'timestamp_distance' },
+{ oid => '4253',
+ proname => 'timestamptz_distance', prorettype => 'interval',
+ proargtypes => 'timestamptz timestamptz', prosrc => 'timestamptz_distance' },
+{ oid => '4254',
+ proname => 'interval_distance', prorettype => 'interval',
+ proargtypes => 'interval interval', prosrc => 'interval_distance' },
+
+# cross-type distance functions
+{ oid => '4255',
+ proname => 'int24dist', prorettype => 'int4',
+ proargtypes => 'int2 int4', prosrc => 'int24dist' },
+{ oid => '4256',
+ proname => 'int28dist', prorettype => 'int8',
+ proargtypes => 'int2 int8', prosrc => 'int28dist' },
+{ oid => '4257',
+ proname => 'int42dist', prorettype => 'int4',
+ proargtypes => 'int4 int2', prosrc => 'int42dist' },
+{ oid => '4258',
+ proname => 'int48dist', prorettype => 'int8',
+ proargtypes => 'int4 int8', prosrc => 'int48dist' },
+{ oid => '4259',
+ proname => 'int82dist', prorettype => 'int8',
+ proargtypes => 'int8 int2', prosrc => 'int82dist' },
+{ oid => '4260',
+ proname => 'int84dist', prorettype => 'int8',
+ proargtypes => 'int8 int4', prosrc => 'int84dist' },
+{ oid => '4261',
+ proname => 'float48dist', prorettype => 'float8',
+ proargtypes => 'float4 float8', prosrc => 'float48dist' },
+{ oid => '4262',
+ proname => 'float84dist', prorettype => 'float8',
+ proargtypes => 'float8 float4', prosrc => 'float84dist' },
+{ oid => '4263',
+ proname => 'date_dist_timestamp', prorettype => 'interval',
+ proargtypes => 'date timestamp', prosrc => 'date_dist_timestamp' },
+{ oid => '4264',
+ proname => 'date_dist_timestamptz', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'date timestamptz',
+ prosrc => 'date_dist_timestamptz' },
+{ oid => '4265',
+ proname => 'timestamp_dist_date', prorettype => 'interval',
+ proargtypes => 'timestamp date', prosrc => 'timestamp_dist_date' },
+{ oid => '4266',
+ proname => 'timestamp_dist_timestamptz', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamp timestamptz',
+ prosrc => 'timestamp_dist_timestamptz' },
+{ oid => '4267',
+ proname => 'timestamptz_dist_date', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamptz date',
+ prosrc => 'timestamptz_dist_date' },
+{ oid => '4268',
+ proname => 'timestamptz_dist_timestamp', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamptz timestamp',
+ prosrc => 'timestamptz_dist_timestamp' },
+
]
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 87f819e..01d2284 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -338,4 +338,6 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
+extern Interval *abs_interval(Interval *a);
+
#endif /* DATETIME_H */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index aeb89dc..438a5eb 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -93,9 +93,11 @@ extern Timestamp SetEpochTimestamp(void);
extern void GetEpochTime(struct pg_tm *tm);
extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
+extern Interval *timestamp_dist_internal(Timestamp a, Timestamp b);
-/* timestamp comparison works for timestamptz also */
+/* timestamp comparison and distance works for timestamptz also */
#define timestamptz_cmp_internal(dt1,dt2) timestamp_cmp_internal(dt1, dt2)
+#define timestamptz_dist_internal(dt1,dt2) timestamp_dist_internal(dt1, dt2)
extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday);
diff --git a/src/test/regress/expected/amutils.out b/src/test/regress/expected/amutils.out
index 4570a39..630dc6b 100644
--- a/src/test/regress/expected/amutils.out
+++ b/src/test/regress/expected/amutils.out
@@ -24,7 +24,7 @@ select prop,
nulls_first | | | f
nulls_last | | | t
orderable | | | t
- distance_orderable | | | f
+ distance_orderable | | | t
returnable | | | t
search_array | | | t
search_nulls | | | t
@@ -100,7 +100,7 @@ select prop,
nulls_first | f | f | f | f | f | f | f
nulls_last | t | f | f | f | f | f | f
orderable | t | f | f | f | f | f | f
- distance_orderable | f | f | t | f | t | f | f
+ distance_orderable | t | f | t | f | t | f | f
returnable | t | f | f | t | t | f | f
search_array | t | f | f | f | f | f | f
search_nulls | t | f | t | t | t | f | t
@@ -231,7 +231,7 @@ select col, prop, pg_index_column_has_property(o, col, prop)
1 | desc | f
1 | nulls_first | f
1 | nulls_last | t
- 1 | distance_orderable | f
+ 1 | distance_orderable | t
1 | returnable | t
1 | bogus |
2 | orderable | f
diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out
index 1bcc946..b9b819c 100644
--- a/src/test/regress/expected/date.out
+++ b/src/test/regress/expected/date.out
@@ -1477,3 +1477,64 @@ select make_time(10, 55, 100.1);
ERROR: time field value out of range: 10:55:100.1
select make_time(24, 0, 2.1);
ERROR: time field value out of range: 24:00:2.1
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+----------
+ | 16006
+ | 15941
+ | 1802
+ | 1801
+ | 1800
+ | 1799
+ | 1436
+ | 1435
+ | 1434
+ | 308
+ | 307
+ | 306
+ | 13578
+ | 13944
+ | 14311
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16006 days 1 hour 23 mins 45 secs
+ | @ 15941 days 1 hour 23 mins 45 secs
+ | @ 1802 days 1 hour 23 mins 45 secs
+ | @ 1801 days 1 hour 23 mins 45 secs
+ | @ 1800 days 1 hour 23 mins 45 secs
+ | @ 1799 days 1 hour 23 mins 45 secs
+ | @ 1436 days 1 hour 23 mins 45 secs
+ | @ 1435 days 1 hour 23 mins 45 secs
+ | @ 1434 days 1 hour 23 mins 45 secs
+ | @ 308 days 1 hour 23 mins 45 secs
+ | @ 307 days 1 hour 23 mins 45 secs
+ | @ 306 days 1 hour 23 mins 45 secs
+ | @ 13577 days 22 hours 36 mins 15 secs
+ | @ 13943 days 22 hours 36 mins 15 secs
+ | @ 14310 days 22 hours 36 mins 15 secs
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16005 days 14 hours 23 mins 45 secs
+ | @ 15940 days 14 hours 23 mins 45 secs
+ | @ 1801 days 14 hours 23 mins 45 secs
+ | @ 1800 days 14 hours 23 mins 45 secs
+ | @ 1799 days 14 hours 23 mins 45 secs
+ | @ 1798 days 14 hours 23 mins 45 secs
+ | @ 1435 days 14 hours 23 mins 45 secs
+ | @ 1434 days 14 hours 23 mins 45 secs
+ | @ 1433 days 14 hours 23 mins 45 secs
+ | @ 307 days 14 hours 23 mins 45 secs
+ | @ 306 days 14 hours 23 mins 45 secs
+ | @ 305 days 15 hours 23 mins 45 secs
+ | @ 13578 days 8 hours 36 mins 15 secs
+ | @ 13944 days 8 hours 36 mins 15 secs
+ | @ 14311 days 8 hours 36 mins 15 secs
+(15 rows)
+
diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out
index cf78277..ce29924e 100644
--- a/src/test/regress/expected/float4.out
+++ b/src/test/regress/expected/float4.out
@@ -273,6 +273,26 @@ SELECT '' AS five, * FROM FLOAT4_TBL;
| -1.2345679e-20
(5 rows)
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+----------------+---------------
+ | 0 | 1004.3
+ | -34.84 | 1039.14
+ | -1004.3 | 2008.6
+ | -1.2345679e+20 | 1.2345679e+20
+ | -1.2345679e-20 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+----------------+------------------------
+ | 0 | 1004.3
+ | -34.84 | 1039.1400001525878
+ | -1004.3 | 2008.5999877929687
+ | -1.2345679e+20 | 1.2345678955701443e+20
+ | -1.2345679e-20 | 1004.3
+(5 rows)
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
int2
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index c3a6f53..5485c19 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -413,6 +413,27 @@ SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
| 1.2345678901234e-200 | 2.3112042409018e-67
(5 rows)
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.3
+ | 1004.3 | 0
+ | -34.84 | 1039.14
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.29998779297
+ | 1004.3 | 1.22070312045253e-05
+ | -34.84 | 1039.13998779297
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.29998779297
+(5 rows)
+
SELECT '' AS five, * FROM FLOAT8_TBL;
five | f1
------+----------------------
diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out
index 8c255b9..0edc57e 100644
--- a/src/test/regress/expected/int2.out
+++ b/src/test/regress/expected/int2.out
@@ -242,6 +242,39 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
| -32767 | -16383
(5 rows)
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+ERROR: smallint out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+ four | f1 | x
+------+-------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
-- corner cases
SELECT (-1::int2<<15)::text;
text
diff --git a/src/test/regress/expected/int4.out b/src/test/regress/expected/int4.out
index bda7a8d..3735dbc 100644
--- a/src/test/regress/expected/int4.out
+++ b/src/test/regress/expected/int4.out
@@ -247,6 +247,38 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
| -2147483647 | -1073741823
(5 rows)
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+ERROR: integer out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+ five | f1 | x
+------+-------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+ | -2147483647 | 2147483649
+(5 rows)
+
--
-- more complex expressions
--
diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out
index 8447a28..d56886a 100644
--- a/src/test/regress/expected/int8.out
+++ b/src/test/regress/expected/int8.out
@@ -432,6 +432,37 @@ SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 A
4567890123457035 | -4567890123456543 | 1123700970370370094 | 0
(5 rows)
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
SELECT q2, abs(q2) FROM INT8_TBL;
q2 | abs
-------------------+------------------
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index f88f345..cb95adf 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -207,6 +207,21 @@ SELECT '' AS fortyfive, r1.*, r2.*
| 34 years | 6 years
(45 rows)
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+ ten | ?column?
+-----+----------------------------
+ | 2 days 02:59:00
+ | 2 days -02:00:00
+ | 8 days -03:00:00
+ | 34 years -2 days -03:00:00
+ | 3 mons -2 days -03:00:00
+ | 2 days 03:00:14
+ | 1 day 00:56:56
+ | 6 years -2 days -03:00:00
+ | 5 mons -2 days -03:00:00
+ | 5 mons -2 days +09:00:00
+(10 rows)
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out
index ab86595..fb2a489 100644
--- a/src/test/regress/expected/money.out
+++ b/src/test/regress/expected/money.out
@@ -123,6 +123,12 @@ SELECT m / 2::float4 FROM money_data;
$61.50
(1 row)
+SELECT m <-> '$123.45' FROM money_data;
+ ?column?
+----------
+ $0.45
+(1 row)
+
-- All true
SELECT m = '$123.00' FROM money_data;
?column?
diff --git a/src/test/regress/expected/oid.out b/src/test/regress/expected/oid.out
index 1eab9cc..5339a48 100644
--- a/src/test/regress/expected/oid.out
+++ b/src/test/regress/expected/oid.out
@@ -119,4 +119,17 @@ SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
| 99999999
(3 rows)
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+ eight | f1 | ?column?
+-------+------------+------------
+ | 1234 | 1111
+ | 1235 | 1112
+ | 987 | 864
+ | 4294966256 | 4294966133
+ | 99999999 | 99999876
+ | 5 | 118
+ | 10 | 113
+ | 15 | 108
+(8 rows)
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index ce25ee0..e637420 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -750,6 +750,7 @@ macaddr8_gt(macaddr8,macaddr8)
macaddr8_ge(macaddr8,macaddr8)
macaddr8_ne(macaddr8,macaddr8)
macaddr8_cmp(macaddr8,macaddr8)
+oiddist(oid,oid)
-- restore normal output mode
\a\t
-- List of functions used by libpq's fe-lobj.c
@@ -1332,7 +1333,7 @@ WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND
ao.amoprighttype = ap.amprocrighttype AND
ap.amprocnum = 1 AND
(pp.provolatile != po.provolatile OR
- pp.proleakproof != po.proleakproof)
+ (NOT pp.proleakproof AND po.proleakproof))
ORDER BY 1;
proc | vp | lp | opr | vo | lo
-------------------------------------------+----+----+-------------------------------+----+----
@@ -1862,6 +1863,7 @@ ORDER BY 1, 2, 3;
403 | 5 | *>
403 | 5 | >
403 | 5 | ~>~
+ 403 | 6 | <->
405 | 1 | =
783 | 1 | <<
783 | 1 | @@
@@ -1971,7 +1973,7 @@ ORDER BY 1, 2, 3;
4000 | 26 | >>
4000 | 27 | >>=
4000 | 28 | ^@
-(123 rows)
+(124 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/expected/time.out b/src/test/regress/expected/time.out
index 8e0afe6..ee74faa 100644
--- a/src/test/regress/expected/time.out
+++ b/src/test/regress/expected/time.out
@@ -86,3 +86,19 @@ ERROR: operator is not unique: time without time zone + time without time zone
LINE 1: SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
+ Ten | Distance
+-------------+-------------------------------
+ 00:00:00 | @ 1 hour 23 mins 45 secs
+ 01:00:00 | @ 23 mins 45 secs
+ 02:03:00 | @ 39 mins 15 secs
+ 11:59:00 | @ 10 hours 35 mins 15 secs
+ 12:00:00 | @ 10 hours 36 mins 15 secs
+ 12:01:00 | @ 10 hours 37 mins 15 secs
+ 23:59:00 | @ 22 hours 35 mins 15 secs
+ 23:59:59.99 | @ 22 hours 36 mins 14.99 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+(10 rows)
+
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index 4a2fabd..dcb4205 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -1604,3 +1604,214 @@ SELECT make_timestamp(2014,12,28,6,30,45.887);
Sun Dec 28 06:30:45.887 2014
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 231 days 18 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 10 hours 45 mins 58 secs
+ | @ 324 days 11 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 21 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 5 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 1 hour 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 231 days 16 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 12 hours 9 mins 43 secs
+ | @ 324 days 13 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 324 days 23 hours 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 6 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 14 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 1 hour 9 mins 43 secs
+ | @ 324 days 2 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 12 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(63 rows)
+
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 8a4c719..0a05e37 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -2544,3 +2544,217 @@ select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
Tue Jan 17 16:00:00 2017 PST
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 8 hours
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 7 hours 27 mins 59 secs
+ | @ 231 days 17 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 19 hours 45 mins 58 secs
+ | @ 324 days 21 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 22 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 9 hours 27 mins 59 secs
+ | @ 1303 days 10 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 9 hours 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 8 hours 51 mins 44 secs
+ | @ 231 days 15 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 21 hours 9 mins 43 secs
+ | @ 324 days 23 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 325 days 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 10 hours 51 mins 44 secs
+ | @ 1303 days 11 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 22 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 10 hours 9 mins 43 secs
+ | @ 324 days 12 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 13 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1452 days 23 hours 51 mins 44 secs
+ | @ 1303 days 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(64 rows)
+
diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql
index 22f80f2..24be476 100644
--- a/src/test/regress/sql/date.sql
+++ b/src/test/regress/sql/date.sql
@@ -346,3 +346,8 @@ select make_date(2013, 13, 1);
select make_date(2013, 11, -1);
select make_time(10, 55, 100.1);
select make_time(24, 0, 2.1);
+
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql
index 646027f..35b5942 100644
--- a/src/test/regress/sql/float4.sql
+++ b/src/test/regress/sql/float4.sql
@@ -87,6 +87,9 @@ UPDATE FLOAT4_TBL
SELECT '' AS five, * FROM FLOAT4_TBL;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
SELECT '32767.6'::float4::int2;
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index a333218..7fe9bca 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -131,6 +131,9 @@ SELECT ||/ float8 '27' AS three;
SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
SELECT '' AS five, * FROM FLOAT8_TBL;
diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql
index 7dbafb6..16dd5d8 100644
--- a/src/test/regress/sql/int2.sql
+++ b/src/test/regress/sql/int2.sql
@@ -84,6 +84,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+
-- corner cases
SELECT (-1::int2<<15)::text;
SELECT ((-1::int2<<15)+1::int2)::text;
diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql
index f014cb2..cff32946 100644
--- a/src/test/regress/sql/int4.sql
+++ b/src/test/regress/sql/int4.sql
@@ -93,6 +93,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT4_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+
--
-- more complex expressions
--
diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql
index e890452..d7f5bde 100644
--- a/src/test/regress/sql/int8.sql
+++ b/src/test/regress/sql/int8.sql
@@ -89,6 +89,11 @@ SELECT q1 + 42::int2 AS "8plus2", q1 - 42::int2 AS "8minus2", q1 * 42::int2 AS "
-- int2 op int8
SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 AS "2mul8", 246::int2 / q1 AS "2div8" FROM INT8_TBL;
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+
SELECT q2, abs(q2) FROM INT8_TBL;
SELECT min(q1), min(q2) FROM INT8_TBL;
SELECT max(q1), max(q2) FROM INT8_TBL;
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index bc5537d..d51c866 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -59,6 +59,8 @@ SELECT '' AS fortyfive, r1.*, r2.*
WHERE r1.f1 > r2.f1
ORDER BY r1.f1, r2.f1;
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql
index 37b9ecc..8428d59 100644
--- a/src/test/regress/sql/money.sql
+++ b/src/test/regress/sql/money.sql
@@ -25,6 +25,7 @@ SELECT m / 2::float8 FROM money_data;
SELECT m * 2::float4 FROM money_data;
SELECT 2::float4 * m FROM money_data;
SELECT m / 2::float4 FROM money_data;
+SELECT m <-> '$123.45' FROM money_data;
-- All true
SELECT m = '$123.00' FROM money_data;
diff --git a/src/test/regress/sql/oid.sql b/src/test/regress/sql/oid.sql
index 4a09689..9f54f92 100644
--- a/src/test/regress/sql/oid.sql
+++ b/src/test/regress/sql/oid.sql
@@ -40,4 +40,6 @@ SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 >= '1234';
SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index e2014fc..959928b 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -818,7 +818,7 @@ WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND
ao.amoprighttype = ap.amprocrighttype AND
ap.amprocnum = 1 AND
(pp.provolatile != po.provolatile OR
- pp.proleakproof != po.proleakproof)
+ (NOT pp.proleakproof AND po.proleakproof))
ORDER BY 1;
diff --git a/src/test/regress/sql/time.sql b/src/test/regress/sql/time.sql
index 99a1562..31f0330 100644
--- a/src/test/regress/sql/time.sql
+++ b/src/test/regress/sql/time.sql
@@ -40,3 +40,6 @@ SELECT f1 AS "Eight" FROM TIME_TBL WHERE f1 >= '00:00';
-- where we do mixed-type arithmetic. - thomas 2000-12-02
SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
+
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index b7957cb..5d023dd 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -230,3 +230,11 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
-- timestamp numeric fields constructor
SELECT make_timestamp(2014,12,28,6,30,45.887);
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index c3bd46c..7f0525d 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -464,3 +464,11 @@ insert into tmptz values ('2017-01-18 00:00+00');
explain (costs off)
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
0006-Remove-distance-operators-from-btree_gist-v06.patchtext/x-patch; name=0006-Remove-distance-operators-from-btree_gist-v06.patchDownload
diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile
index af65120..46ab241 100644
--- a/contrib/btree_gist/Makefile
+++ b/contrib/btree_gist/Makefile
@@ -11,8 +11,9 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
EXTENSION = btree_gist
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
- btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \
- btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql
+ btree_gist--1.1--1.2.sql btree_gist--1.2--1.3.sql \
+ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \
+ btree_gist--1.5--1.6.sql btree_gist--1.6.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c
index 894d0a2..1b0e317 100644
--- a/contrib/btree_gist/btree_cash.c
+++ b/contrib/btree_gist/btree_cash.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(cash_dist);
Datum
cash_dist(PG_FUNCTION_ARGS)
{
- Cash a = PG_GETARG_CASH(0);
- Cash b = PG_GETARG_CASH(1);
- Cash r;
- Cash ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("money out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_CASH(ra);
+ return cash_distance(fcinfo);
}
/**************************************************
diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c
index 992ce57..f3f0fa1 100644
--- a/contrib/btree_gist/btree_date.c
+++ b/contrib/btree_gist/btree_date.c
@@ -113,12 +113,7 @@ PG_FUNCTION_INFO_V1(date_dist);
Datum
date_dist(PG_FUNCTION_ARGS)
{
- /* we assume the difference can't overflow */
- Datum diff = DirectFunctionCall2(date_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+ return date_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c
index 6b20f44..0a9148d 100644
--- a/contrib/btree_gist/btree_float4.c
+++ b/contrib/btree_gist/btree_float4.c
@@ -93,14 +93,7 @@ PG_FUNCTION_INFO_V1(float4_dist);
Datum
float4_dist(PG_FUNCTION_ARGS)
{
- float4 a = PG_GETARG_FLOAT4(0);
- float4 b = PG_GETARG_FLOAT4(1);
- float4 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT4(Abs(r));
+ return float4dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c
index ee114cb..8b73b57 100644
--- a/contrib/btree_gist/btree_float8.c
+++ b/contrib/btree_gist/btree_float8.c
@@ -101,14 +101,7 @@ PG_FUNCTION_INFO_V1(float8_dist);
Datum
float8_dist(PG_FUNCTION_ARGS)
{
- float8 a = PG_GETARG_FLOAT8(0);
- float8 b = PG_GETARG_FLOAT8(1);
- float8 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT8(Abs(r));
+ return float8dist(fcinfo);
}
/**************************************************
diff --git a/contrib/btree_gist/btree_gist--1.2.sql b/contrib/btree_gist/btree_gist--1.2.sql
deleted file mode 100644
index 1efe753..0000000
--- a/contrib/btree_gist/btree_gist--1.2.sql
+++ /dev/null
@@ -1,1570 +0,0 @@
-/* contrib/btree_gist/btree_gist--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
-
-CREATE FUNCTION gbtreekey4_in(cstring)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey4_out(gbtreekey4)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey4 (
- INTERNALLENGTH = 4,
- INPUT = gbtreekey4_in,
- OUTPUT = gbtreekey4_out
-);
-
-CREATE FUNCTION gbtreekey8_in(cstring)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey8_out(gbtreekey8)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey8 (
- INTERNALLENGTH = 8,
- INPUT = gbtreekey8_in,
- OUTPUT = gbtreekey8_out
-);
-
-CREATE FUNCTION gbtreekey16_in(cstring)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey16_out(gbtreekey16)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey16 (
- INTERNALLENGTH = 16,
- INPUT = gbtreekey16_in,
- OUTPUT = gbtreekey16_out
-);
-
-CREATE FUNCTION gbtreekey32_in(cstring)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey32_out(gbtreekey32)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey32 (
- INTERNALLENGTH = 32,
- INPUT = gbtreekey32_in,
- OUTPUT = gbtreekey32_out
-);
-
-CREATE FUNCTION gbtreekey_var_in(cstring)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey_var (
- INTERNALLENGTH = VARIABLE,
- INPUT = gbtreekey_var_in,
- OUTPUT = gbtreekey_var_out,
- STORAGE = EXTENDED
-);
-
---distance operators
-
-CREATE FUNCTION cash_dist(money, money)
-RETURNS money
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = money,
- RIGHTARG = money,
- PROCEDURE = cash_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION date_dist(date, date)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = date,
- RIGHTARG = date,
- PROCEDURE = date_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float4_dist(float4, float4)
-RETURNS float4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float4,
- RIGHTARG = float4,
- PROCEDURE = float4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float8_dist(float8, float8)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float8,
- RIGHTARG = float8,
- PROCEDURE = float8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int2_dist(int2, int2)
-RETURNS int2
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int2,
- RIGHTARG = int2,
- PROCEDURE = int2_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int4_dist(int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int4,
- RIGHTARG = int4,
- PROCEDURE = int4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int8_dist(int8, int8)
-RETURNS int8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int8,
- RIGHTARG = int8,
- PROCEDURE = int8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION interval_dist(interval, interval)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = interval,
- RIGHTARG = interval,
- PROCEDURE = interval_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION oid_dist(oid, oid)
-RETURNS oid
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = oid,
- RIGHTARG = oid,
- PROCEDURE = oid_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION time_dist(time, time)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = time,
- RIGHTARG = time,
- PROCEDURE = time_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION ts_dist(timestamp, timestamp)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamp,
- RIGHTARG = timestamp,
- PROCEDURE = ts_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION tstz_dist(timestamptz, timestamptz)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamptz,
- RIGHTARG = timestamptz,
- PROCEDURE = tstz_dist,
- COMMUTATOR = '<->'
-);
-
-
---
---
---
--- oid ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_oid_ops
-DEFAULT FOR TYPE oid USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
- FUNCTION 2 gbt_oid_union (internal, internal),
- FUNCTION 3 gbt_oid_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
- FUNCTION 6 gbt_oid_picksplit (internal, internal),
- FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
--- Add operators that are new in 9.1. We do it like this, leaving them
--- "loose" in the operator family rather than bound into the opclass, because
--- that's the only state that can be reproduced during an upgrade from 9.0.
-ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
- OPERATOR 6 <> (oid, oid) ,
- OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
- FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
- -- Also add support function for index-only-scans, added in 9.5.
- FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
-
-
---
---
---
--- int2 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_union(internal, internal)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int2_ops
-DEFAULT FOR TYPE int2 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
- FUNCTION 2 gbt_int2_union (internal, internal),
- FUNCTION 3 gbt_int2_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int2_picksplit (internal, internal),
- FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
- STORAGE gbtreekey4;
-
-ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
- OPERATOR 6 <> (int2, int2) ,
- OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
- FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
-
---
---
---
--- int4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int4_ops
-DEFAULT FOR TYPE int4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
- FUNCTION 2 gbt_int4_union (internal, internal),
- FUNCTION 3 gbt_int4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int4_picksplit (internal, internal),
- FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
- OPERATOR 6 <> (int4, int4) ,
- OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
- FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
-
-
---
---
---
--- int8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int8_ops
-DEFAULT FOR TYPE int8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
- FUNCTION 2 gbt_int8_union (internal, internal),
- FUNCTION 3 gbt_int8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int8_picksplit (internal, internal),
- FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
- OPERATOR 6 <> (int8, int8) ,
- OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
- FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
-
---
---
---
--- float4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float4_ops
-DEFAULT FOR TYPE float4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
- FUNCTION 2 gbt_float4_union (internal, internal),
- FUNCTION 3 gbt_float4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float4_picksplit (internal, internal),
- FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
- OPERATOR 6 <> (float4, float4) ,
- OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
- FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
-
---
---
---
--- float8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float8_ops
-DEFAULT FOR TYPE float8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
- FUNCTION 2 gbt_float8_union (internal, internal),
- FUNCTION 3 gbt_float8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float8_picksplit (internal, internal),
- FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
- OPERATOR 6 <> (float8, float8) ,
- OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
- FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
-
---
---
---
--- timestamp ops
---
---
---
-
-CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamp_ops
-DEFAULT FOR TYPE timestamp USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_ts_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
- OPERATOR 6 <> (timestamp, timestamp) ,
- OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
- FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamptz_ops
-DEFAULT FOR TYPE timestamptz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_tstz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
- OPERATOR 6 <> (timestamptz, timestamptz) ,
- OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
- FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
-
---
---
---
--- time ops
---
---
---
-
-CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_time_ops
-DEFAULT FOR TYPE time USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_time_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
- OPERATOR 6 <> (time, time) ,
- OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
- FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
-
-
-CREATE OPERATOR CLASS gist_timetz_ops
-DEFAULT FOR TYPE timetz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_timetz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
- OPERATOR 6 <> (timetz, timetz) ;
- -- no 'fetch' function, as the compress function is lossy.
-
-
---
---
---
--- date ops
---
---
---
-
-CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_date_ops
-DEFAULT FOR TYPE date USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
- FUNCTION 2 gbt_date_union (internal, internal),
- FUNCTION 3 gbt_date_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_date_penalty (internal, internal, internal),
- FUNCTION 6 gbt_date_picksplit (internal, internal),
- FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
- OPERATOR 6 <> (date, date) ,
- OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
- FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
-
-
---
---
---
--- interval ops
---
---
---
-
-CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_union(internal, internal)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_interval_ops
-DEFAULT FOR TYPE interval USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
- FUNCTION 2 gbt_intv_union (internal, internal),
- FUNCTION 3 gbt_intv_compress (internal),
- FUNCTION 4 gbt_intv_decompress (internal),
- FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
- FUNCTION 6 gbt_intv_picksplit (internal, internal),
- FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
- STORAGE gbtreekey32;
-
-ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
- OPERATOR 6 <> (interval, interval) ,
- OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
- FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
-
-
---
---
---
--- cash ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cash_ops
-DEFAULT FOR TYPE money USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
- FUNCTION 2 gbt_cash_union (internal, internal),
- FUNCTION 3 gbt_cash_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
- FUNCTION 6 gbt_cash_picksplit (internal, internal),
- FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
- OPERATOR 6 <> (money, money) ,
- OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
- FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
- FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
-
-
---
---
---
--- macaddr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_macaddr_ops
-DEFAULT FOR TYPE macaddr USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
- FUNCTION 2 gbt_macad_union (internal, internal),
- FUNCTION 3 gbt_macad_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
- FUNCTION 6 gbt_macad_picksplit (internal, internal),
- FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
- OPERATOR 6 <> (macaddr, macaddr) ,
- FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
-
-
---
---
---
--- text/ bpchar ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_text_ops
-DEFAULT FOR TYPE text USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_text_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
- OPERATOR 6 <> (text, text) ,
- FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
-
-
----- Create the operator class
-CREATE OPERATOR CLASS gist_bpchar_ops
-DEFAULT FOR TYPE bpchar USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_bpchar_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
- OPERATOR 6 <> (bpchar, bpchar) ,
- FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
-
---
---
--- bytea ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bytea_ops
-DEFAULT FOR TYPE bytea USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
- FUNCTION 2 gbt_bytea_union (internal, internal),
- FUNCTION 3 gbt_bytea_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bytea_picksplit (internal, internal),
- FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
- OPERATOR 6 <> (bytea, bytea) ,
- FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
-
-
---
---
---
--- numeric ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_numeric_ops
-DEFAULT FOR TYPE numeric USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
- FUNCTION 2 gbt_numeric_union (internal, internal),
- FUNCTION 3 gbt_numeric_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
- FUNCTION 6 gbt_numeric_picksplit (internal, internal),
- FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
- OPERATOR 6 <> (numeric, numeric) ,
- FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
-
-
---
---
--- bit ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bit_ops
-DEFAULT FOR TYPE bit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
- OPERATOR 6 <> (bit, bit) ,
- FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
-
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_vbit_ops
-DEFAULT FOR TYPE varbit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
- OPERATOR 6 <> (varbit, varbit) ,
- FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
-
-
---
---
---
--- inet/cidr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_inet_ops
-DEFAULT FOR TYPE inet USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cidr_ops
-DEFAULT FOR TYPE cidr USING gist
-AS
- OPERATOR 1 < (inet, inet) ,
- OPERATOR 2 <= (inet, inet) ,
- OPERATOR 3 = (inet, inet) ,
- OPERATOR 4 >= (inet, inet) ,
- OPERATOR 5 > (inet, inet) ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
diff --git a/contrib/btree_gist/btree_gist--1.5--1.6.sql b/contrib/btree_gist/btree_gist--1.5--1.6.sql
new file mode 100644
index 0000000..ef4424e
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.5--1.6.sql
@@ -0,0 +1,99 @@
+/* contrib/btree_gist/btree_gist--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.6'" to load this file. \quit
+
+-- drop btree_gist distance operators from opfamilies
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist DROP OPERATOR 15 (int2, int2);
+ALTER OPERATOR FAMILY gist_int4_ops USING gist DROP OPERATOR 15 (int4, int4);
+ALTER OPERATOR FAMILY gist_int8_ops USING gist DROP OPERATOR 15 (int8, int8);
+ALTER OPERATOR FAMILY gist_float4_ops USING gist DROP OPERATOR 15 (float4, float4);
+ALTER OPERATOR FAMILY gist_float8_ops USING gist DROP OPERATOR 15 (float8, float8);
+ALTER OPERATOR FAMILY gist_oid_ops USING gist DROP OPERATOR 15 (oid, oid);
+ALTER OPERATOR FAMILY gist_cash_ops USING gist DROP OPERATOR 15 (money, money);
+ALTER OPERATOR FAMILY gist_date_ops USING gist DROP OPERATOR 15 (date, date);
+ALTER OPERATOR FAMILY gist_time_ops USING gist DROP OPERATOR 15 (time, time);
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist DROP OPERATOR 15 (timestamp, timestamp);
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist DROP OPERATOR 15 (timestamptz, timestamptz);
+ALTER OPERATOR FAMILY gist_interval_ops USING gist DROP OPERATOR 15 (interval, interval);
+
+-- add pg_catalog distance operators to opfamilies
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops;
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops;
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops;
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops;
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops;
+
+-- disable implicit pg_catalog search
+
+DO
+$$
+BEGIN
+ EXECUTE 'SET LOCAL search_path TO ' || current_schema() || ', pg_catalog';
+END
+$$;
+
+-- drop distance operators
+
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int2, int2);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int4, int4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int8, int8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float4, float4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float8, float8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (oid, oid);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (money, money);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (date, date);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (time, time);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (interval, interval);
+
+DROP OPERATOR <-> (int2, int2);
+DROP OPERATOR <-> (int4, int4);
+DROP OPERATOR <-> (int8, int8);
+DROP OPERATOR <-> (float4, float4);
+DROP OPERATOR <-> (float8, float8);
+DROP OPERATOR <-> (oid, oid);
+DROP OPERATOR <-> (money, money);
+DROP OPERATOR <-> (date, date);
+DROP OPERATOR <-> (time, time);
+DROP OPERATOR <-> (timestamp, timestamp);
+DROP OPERATOR <-> (timestamptz, timestamptz);
+DROP OPERATOR <-> (interval, interval);
+
+-- drop distance functions
+
+ALTER EXTENSION btree_gist DROP FUNCTION int2_dist(int2, int2);
+ALTER EXTENSION btree_gist DROP FUNCTION int4_dist(int4, int4);
+ALTER EXTENSION btree_gist DROP FUNCTION int8_dist(int8, int8);
+ALTER EXTENSION btree_gist DROP FUNCTION float4_dist(float4, float4);
+ALTER EXTENSION btree_gist DROP FUNCTION float8_dist(float8, float8);
+ALTER EXTENSION btree_gist DROP FUNCTION oid_dist(oid, oid);
+ALTER EXTENSION btree_gist DROP FUNCTION cash_dist(money, money);
+ALTER EXTENSION btree_gist DROP FUNCTION date_dist(date, date);
+ALTER EXTENSION btree_gist DROP FUNCTION time_dist(time, time);
+ALTER EXTENSION btree_gist DROP FUNCTION ts_dist(timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP FUNCTION interval_dist(interval, interval);
+
+DROP FUNCTION int2_dist(int2, int2);
+DROP FUNCTION int4_dist(int4, int4);
+DROP FUNCTION int8_dist(int8, int8);
+DROP FUNCTION float4_dist(float4, float4);
+DROP FUNCTION float8_dist(float8, float8);
+DROP FUNCTION oid_dist(oid, oid);
+DROP FUNCTION cash_dist(money, money);
+DROP FUNCTION date_dist(date, date);
+DROP FUNCTION time_dist(time, time);
+DROP FUNCTION ts_dist(timestamp, timestamp);
+DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+DROP FUNCTION interval_dist(interval, interval);
diff --git a/contrib/btree_gist/btree_gist--1.6.sql b/contrib/btree_gist/btree_gist--1.6.sql
new file mode 100644
index 0000000..8ff8eb5
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.6.sql
@@ -0,0 +1,1615 @@
+/* contrib/btree_gist/btree_gist--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
+
+CREATE FUNCTION gbtreekey4_in(cstring)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey4_out(gbtreekey4)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey4 (
+ INTERNALLENGTH = 4,
+ INPUT = gbtreekey4_in,
+ OUTPUT = gbtreekey4_out
+);
+
+CREATE FUNCTION gbtreekey8_in(cstring)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey8_out(gbtreekey8)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey8 (
+ INTERNALLENGTH = 8,
+ INPUT = gbtreekey8_in,
+ OUTPUT = gbtreekey8_out
+);
+
+CREATE FUNCTION gbtreekey16_in(cstring)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey16_out(gbtreekey16)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey16 (
+ INTERNALLENGTH = 16,
+ INPUT = gbtreekey16_in,
+ OUTPUT = gbtreekey16_out
+);
+
+CREATE FUNCTION gbtreekey32_in(cstring)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey32_out(gbtreekey32)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey32 (
+ INTERNALLENGTH = 32,
+ INPUT = gbtreekey32_in,
+ OUTPUT = gbtreekey32_out
+);
+
+CREATE FUNCTION gbtreekey_var_in(cstring)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey_var (
+ INTERNALLENGTH = VARIABLE,
+ INPUT = gbtreekey_var_in,
+ OUTPUT = gbtreekey_var_out,
+ STORAGE = EXTENDED
+);
+
+
+--
+--
+--
+-- oid ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_oid_ops
+DEFAULT FOR TYPE oid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
+ FUNCTION 2 gbt_oid_union (internal, internal),
+ FUNCTION 3 gbt_oid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_oid_picksplit (internal, internal),
+ FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+-- Add operators that are new in 9.1. We do it like this, leaving them
+-- "loose" in the operator family rather than bound into the opclass, because
+-- that's the only state that can be reproduced during an upgrade from 9.0.
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
+ OPERATOR 6 <> (oid, oid) ,
+ OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
+ FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
+ -- Also add support function for index-only-scans, added in 9.5.
+ FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
+
+
+--
+--
+--
+-- int2 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_union(internal, internal)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int2_ops
+DEFAULT FOR TYPE int2 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
+ FUNCTION 2 gbt_int2_union (internal, internal),
+ FUNCTION 3 gbt_int2_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int2_picksplit (internal, internal),
+ FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
+ STORAGE gbtreekey4;
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
+ OPERATOR 6 <> (int2, int2) ,
+ OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
+ FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
+
+--
+--
+--
+-- int4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int4_ops
+DEFAULT FOR TYPE int4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
+ FUNCTION 2 gbt_int4_union (internal, internal),
+ FUNCTION 3 gbt_int4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int4_picksplit (internal, internal),
+ FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
+ OPERATOR 6 <> (int4, int4) ,
+ OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
+ FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
+
+
+--
+--
+--
+-- int8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int8_ops
+DEFAULT FOR TYPE int8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
+ FUNCTION 2 gbt_int8_union (internal, internal),
+ FUNCTION 3 gbt_int8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int8_picksplit (internal, internal),
+ FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
+ OPERATOR 6 <> (int8, int8) ,
+ OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
+ FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
+
+--
+--
+--
+-- float4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float4_ops
+DEFAULT FOR TYPE float4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
+ FUNCTION 2 gbt_float4_union (internal, internal),
+ FUNCTION 3 gbt_float4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float4_picksplit (internal, internal),
+ FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
+ OPERATOR 6 <> (float4, float4) ,
+ OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
+ FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
+
+--
+--
+--
+-- float8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float8_ops
+DEFAULT FOR TYPE float8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
+ FUNCTION 2 gbt_float8_union (internal, internal),
+ FUNCTION 3 gbt_float8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float8_picksplit (internal, internal),
+ FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
+ OPERATOR 6 <> (float8, float8) ,
+ OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
+ FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
+
+--
+--
+--
+-- timestamp ops
+--
+--
+--
+
+CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamp_ops
+DEFAULT FOR TYPE timestamp USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_ts_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
+ OPERATOR 6 <> (timestamp, timestamp) ,
+ OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
+ FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamptz_ops
+DEFAULT FOR TYPE timestamptz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_tstz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
+ OPERATOR 6 <> (timestamptz, timestamptz) ,
+ OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
+ FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
+
+--
+--
+--
+-- time ops
+--
+--
+--
+
+CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_time_ops
+DEFAULT FOR TYPE time USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_time_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
+ OPERATOR 6 <> (time, time) ,
+ OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
+ FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
+
+
+CREATE OPERATOR CLASS gist_timetz_ops
+DEFAULT FOR TYPE timetz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_timetz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
+ OPERATOR 6 <> (timetz, timetz) ;
+ -- no 'fetch' function, as the compress function is lossy.
+
+
+--
+--
+--
+-- date ops
+--
+--
+--
+
+CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_date_ops
+DEFAULT FOR TYPE date USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
+ FUNCTION 2 gbt_date_union (internal, internal),
+ FUNCTION 3 gbt_date_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_date_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_date_picksplit (internal, internal),
+ FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
+ OPERATOR 6 <> (date, date) ,
+ OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
+ FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
+
+
+--
+--
+--
+-- interval ops
+--
+--
+--
+
+CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_interval_ops
+DEFAULT FOR TYPE interval USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
+ FUNCTION 2 gbt_intv_union (internal, internal),
+ FUNCTION 3 gbt_intv_compress (internal),
+ FUNCTION 4 gbt_intv_decompress (internal),
+ FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_intv_picksplit (internal, internal),
+ FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
+ OPERATOR 6 <> (interval, interval) ,
+ OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
+ FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
+
+
+--
+--
+--
+-- cash ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cash_ops
+DEFAULT FOR TYPE money USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
+ FUNCTION 2 gbt_cash_union (internal, internal),
+ FUNCTION 3 gbt_cash_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_cash_picksplit (internal, internal),
+ FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
+ OPERATOR 6 <> (money, money) ,
+ OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
+ FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
+ FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
+
+
+--
+--
+--
+-- macaddr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr_ops
+DEFAULT FOR TYPE macaddr USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
+ FUNCTION 2 gbt_macad_union (internal, internal),
+ FUNCTION 3 gbt_macad_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
+ OPERATOR 6 <> (macaddr, macaddr) ,
+ FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
+
+
+--
+--
+--
+-- text/ bpchar ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_text_ops
+DEFAULT FOR TYPE text USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_text_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
+ OPERATOR 6 <> (text, text) ,
+ FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
+
+
+---- Create the operator class
+CREATE OPERATOR CLASS gist_bpchar_ops
+DEFAULT FOR TYPE bpchar USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_bpchar_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
+ OPERATOR 6 <> (bpchar, bpchar) ,
+ FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
+
+--
+--
+-- bytea ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bytea_ops
+DEFAULT FOR TYPE bytea USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
+ FUNCTION 2 gbt_bytea_union (internal, internal),
+ FUNCTION 3 gbt_bytea_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bytea_picksplit (internal, internal),
+ FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
+ OPERATOR 6 <> (bytea, bytea) ,
+ FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- numeric ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_numeric_ops
+DEFAULT FOR TYPE numeric USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
+ FUNCTION 2 gbt_numeric_union (internal, internal),
+ FUNCTION 3 gbt_numeric_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_numeric_picksplit (internal, internal),
+ FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
+ OPERATOR 6 <> (numeric, numeric) ,
+ FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
+
+
+--
+--
+-- bit ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bit_ops
+DEFAULT FOR TYPE bit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
+ OPERATOR 6 <> (bit, bit) ,
+ FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
+
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_vbit_ops
+DEFAULT FOR TYPE varbit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
+ OPERATOR 6 <> (varbit, varbit) ,
+ FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- inet/cidr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_inet_ops
+DEFAULT FOR TYPE inet USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cidr_ops
+DEFAULT FOR TYPE cidr USING gist
+AS
+ OPERATOR 1 < (inet, inet) ,
+ OPERATOR 2 <= (inet, inet) ,
+ OPERATOR 3 = (inet, inet) ,
+ OPERATOR 4 >= (inet, inet) ,
+ OPERATOR 5 > (inet, inet) ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+--
+--
+--
+-- uuid ops
+--
+--
+---- define the GiST support methods
+CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_uuid_ops
+DEFAULT FOR TYPE uuid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal),
+ FUNCTION 2 gbt_uuid_union (internal, internal),
+ FUNCTION 3 gbt_uuid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_uuid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_uuid_picksplit (internal, internal),
+ FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+-- These are "loose" in the opfamily for consistency with the rest of btree_gist
+ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
+ OPERATOR 6 <> (uuid, uuid) ,
+ FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ;
+
+
+-- Add support for indexing macaddr8 columns
+
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad8_consistent(internal,macaddr8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr8_ops
+DEFAULT FOR TYPE macaddr8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad8_consistent (internal, macaddr8, int2, oid, internal),
+ FUNCTION 2 gbt_macad8_union (internal, internal),
+ FUNCTION 3 gbt_macad8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad8_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD
+ OPERATOR 6 <> (macaddr8, macaddr8) ,
+ FUNCTION 9 (macaddr8, macaddr8) gbt_macad8_fetch (internal);
+
+--
+--
+--
+-- enum ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_enum_consistent(internal,anyenum,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_enum_ops
+DEFAULT FOR TYPE anyenum USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_enum_consistent (internal, anyenum, int2, oid, internal),
+ FUNCTION 2 gbt_enum_union (internal, internal),
+ FUNCTION 3 gbt_enum_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_enum_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_enum_picksplit (internal, internal),
+ FUNCTION 7 gbt_enum_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD
+ OPERATOR 6 <> (anyenum, anyenum) ,
+ FUNCTION 9 (anyenum, anyenum) gbt_enum_fetch (internal) ;
diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control
index 81c8509..9ced3bc 100644
--- a/contrib/btree_gist/btree_gist.control
+++ b/contrib/btree_gist/btree_gist.control
@@ -1,5 +1,5 @@
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
-default_version = '1.5'
+default_version = '1.6'
module_pathname = '$libdir/btree_gist'
relocatable = true
diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c
index 7674e2d..2afc343 100644
--- a/contrib/btree_gist/btree_int2.c
+++ b/contrib/btree_gist/btree_int2.c
@@ -94,20 +94,7 @@ PG_FUNCTION_INFO_V1(int2_dist);
Datum
int2_dist(PG_FUNCTION_ARGS)
{
- int16 a = PG_GETARG_INT16(0);
- int16 b = PG_GETARG_INT16(1);
- int16 r;
- int16 ra;
-
- if (pg_sub_s16_overflow(a, b, &r) ||
- r == PG_INT16_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("smallint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT16(ra);
+ return int2dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c
index 80005ab..2361ce7 100644
--- a/contrib/btree_gist/btree_int4.c
+++ b/contrib/btree_gist/btree_int4.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(int4_dist);
Datum
int4_dist(PG_FUNCTION_ARGS)
{
- int32 a = PG_GETARG_INT32(0);
- int32 b = PG_GETARG_INT32(1);
- int32 r;
- int32 ra;
-
- if (pg_sub_s32_overflow(a, b, &r) ||
- r == PG_INT32_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT32(ra);
+ return int4dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c
index b0fd3e1..182d7c4 100644
--- a/contrib/btree_gist/btree_int8.c
+++ b/contrib/btree_gist/btree_int8.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(int8_dist);
Datum
int8_dist(PG_FUNCTION_ARGS)
{
- int64 a = PG_GETARG_INT64(0);
- int64 b = PG_GETARG_INT64(1);
- int64 r;
- int64 ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT64(ra);
+ return int8dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c
index 3a527a7..c68d48e 100644
--- a/contrib/btree_gist/btree_interval.c
+++ b/contrib/btree_gist/btree_interval.c
@@ -128,11 +128,7 @@ PG_FUNCTION_INFO_V1(interval_dist);
Datum
interval_dist(PG_FUNCTION_ARGS)
{
- Datum diff = DirectFunctionCall2(interval_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+ return interval_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c
index 00e7019..c702d51 100644
--- a/contrib/btree_gist/btree_oid.c
+++ b/contrib/btree_gist/btree_oid.c
@@ -100,15 +100,7 @@ PG_FUNCTION_INFO_V1(oid_dist);
Datum
oid_dist(PG_FUNCTION_ARGS)
{
- Oid a = PG_GETARG_OID(0);
- Oid b = PG_GETARG_OID(1);
- Oid res;
-
- if (a < b)
- res = b - a;
- else
- res = a - b;
- PG_RETURN_OID(res);
+ return oiddist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c
index 90cf655..cfafd02 100644
--- a/contrib/btree_gist/btree_time.c
+++ b/contrib/btree_gist/btree_time.c
@@ -141,11 +141,7 @@ PG_FUNCTION_INFO_V1(time_dist);
Datum
time_dist(PG_FUNCTION_ARGS)
{
- Datum diff = DirectFunctionCall2(time_mi_time,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+ return time_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index 49d1849..9e62f7d 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -146,48 +146,14 @@ PG_FUNCTION_INFO_V1(ts_dist);
Datum
ts_dist(PG_FUNCTION_ARGS)
{
- Timestamp a = PG_GETARG_TIMESTAMP(0);
- Timestamp b = PG_GETARG_TIMESTAMP(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
- else
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
+ return timestamp_distance(fcinfo);
}
PG_FUNCTION_INFO_V1(tstz_dist);
Datum
tstz_dist(PG_FUNCTION_ARGS)
{
- TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
- TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
-
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
+ return timestamptz_distance(fcinfo);
}
diff --git a/doc/src/sgml/btree-gist.sgml b/doc/src/sgml/btree-gist.sgml
index 774442f..6eb4a18 100644
--- a/doc/src/sgml/btree-gist.sgml
+++ b/doc/src/sgml/btree-gist.sgml
@@ -96,6 +96,19 @@ INSERT 0 1
</sect2>
<sect2>
+ <title>Upgrade notes for version 1.6</title>
+
+ <para>
+ In version 1.6 <literal>btree_gist</literal> switched to using in-core
+ distance operators, and its own implementations were removed. References to
+ these operators in <literal>btree_gist</literal> opclasses will be updated
+ automatically during the extension upgrade, but if the user has created
+ objects referencing these operators or functions, then these objects must be
+ dropped manually before updating the extension.
+ </para>
+ </sect2>
+
+ <sect2>
<title>Authors</title>
<para>
0007-Add-regression-tests-for-kNN-btree-v06.patchtext/x-patch; name=0007-Add-regression-tests-for-kNN-btree-v06.patchDownload
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index b21298a..0cefbcc 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -250,3 +250,1109 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+---
+--- Test B-tree distance ordering
+---
+SET enable_bitmapscan = OFF;
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+ QUERY PLAN
+------------------------------
+ Sort
+ Sort Key: ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+ QUERY PLAN
+------------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((random <-> 1))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)))
+ Order By: (random <-> 4000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)) AND (random = ANY ('{1809552,1919087,2321799,2648497,3000193,3013326,4157193,4488889,5257716,5593978,NULL}'::integer[])))
+ Order By: (random <-> 3000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ seqno | random
+-------+---------
+ 5380 | 3000193
+ 6262 | 3013326
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 6448 | 4157193
+ 210 | 1809552
+ 4408 | 4488889
+ 6320 | 5257716
+ 1836 | 5593978
+(10 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+-- test parallel KNN scan
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+RESET enable_indexscan;
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Gather
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i > 8000000)
+(4 rows)
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+---------------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t1.n = t2.n)
+ Join Filter: (t1.i <> t2.i)
+ -> Subquery Scan on t1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Order By: (i <-> 4000003)
+ -> Hash
+ -> Gather
+ Workers Planned: 4
+ -> Parallel Seq Scan on bt_knn_test2 t2
+(13 rows)
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+---------------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t1.n = ((i.i * 100000) + j.j))
+ Join Filter: (t1.i <> ('{4,3,2,7,8}'::integer[])[(i.i + 1)])
+ -> Subquery Scan on t1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i = ANY ('{3,4,7,8,2}'::integer[]))
+ Order By: (i <-> 4)
+ -> Hash
+ -> Nested Loop
+ -> Function Scan on generate_series i
+ -> Function Scan on generate_series j
+(14 rows)
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+ROLLBACK;
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ QUERY PLAN
+-----------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000))
+ Order By: (thousand <-> 998)
+(3 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 1 | 9001
+ 1 | 8001
+ 1 | 7001
+ 1 | 6001
+ 1 | 5001
+ 1 | 4001
+ 1 | 3001
+ 1 | 2001
+ 1 | 1001
+ 1 | 1
+ 0 | 9000
+ 0 | 8000
+ 0 | 7000
+ 0 | 6000
+ 0 | 5000
+ 0 | 4000
+ 0 | 3000
+ 0 | 2000
+ 0 | 1000
+ 0 | 0
+ | 1
+ | 2
+ | 3
+(33 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: ((thousand > 100) AND (thousand < 800) AND (thousand = ANY ('{0,123,234,345,456,678,901,NULL}'::smallint[])))
+ Order By: (thousand <-> '300'::bigint)
+(3 rows)
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ thousand | tenthous
+----------+----------
+ 345 | 345
+ 345 | 1345
+ 345 | 2345
+ 345 | 3345
+ 345 | 4345
+ 345 | 5345
+ 345 | 6345
+ 345 | 7345
+ 345 | 8345
+ 345 | 9345
+ 234 | 234
+ 234 | 1234
+ 234 | 2234
+ 234 | 3234
+ 234 | 4234
+ 234 | 5234
+ 234 | 6234
+ 234 | 7234
+ 234 | 8234
+ 234 | 9234
+ 456 | 456
+ 456 | 1456
+ 456 | 2456
+ 456 | 3456
+ 456 | 4456
+ 456 | 5456
+ 456 | 6456
+ 456 | 7456
+ 456 | 8456
+ 456 | 9456
+ 123 | 123
+ 123 | 1123
+ 123 | 2123
+ 123 | 3123
+ 123 | 4123
+ 123 | 5123
+ 123 | 6123
+ 123 | 7123
+ 123 | 8123
+ 123 | 9123
+ 678 | 678
+ 678 | 1678
+ 678 | 2678
+ 678 | 3678
+ 678 | 4678
+ 678 | 5678
+ 678 | 6678
+ 678 | 7678
+ 678 | 8678
+ 678 | 9678
+(50 rows)
+
+DROP INDEX tenk3_idx;
+-- Test order by distance ordering on non-first column
+SET enable_sort = OFF;
+-- Ranges are not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand > 120
+ORDER BY tenthous <-> 3500;
+ QUERY PLAN
+-----------------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 3500))
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand > 120)
+(4 rows)
+
+-- Equality restriction on the first column is supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+ QUERY PLAN
+-----------------------------------------------------
+ Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = 120)
+ Order By: (tenthous <-> 3500)
+(3 rows)
+
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+ thousand | tenthous
+----------+----------
+ 120 | 3120
+ 120 | 4120
+ 120 | 2120
+ 120 | 5120
+ 120 | 1120
+ 120 | 6120
+ 120 | 120
+ 120 | 7120
+ 120 | 8120
+ 120 | 9120
+(10 rows)
+
+-- IN restriction on the first column is not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY tenthous <-> 3500;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 3500))
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
+(4 rows)
+
+-- Test kNN search using 4-column index
+CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
+-- Ordering by distance to 3rd column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+ QUERY PLAN
+----------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43))
+ Order By: (thousand <-> 600)
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 43 | 643 | 643
+ 3 | 43 | 643 | 1643
+ 3 | 43 | 643 | 2643
+ 3 | 43 | 643 | 3643
+ 3 | 43 | 643 | 4643
+ 3 | 43 | 643 | 5643
+ 3 | 43 | 643 | 6643
+ 3 | 43 | 643 | 7643
+ 3 | 43 | 643 | 8643
+ 3 | 43 | 643 | 9643
+ 3 | 43 | 543 | 9543
+ 3 | 43 | 543 | 8543
+ 3 | 43 | 543 | 7543
+ 3 | 43 | 543 | 6543
+ 3 | 43 | 543 | 5543
+ 3 | 43 | 543 | 4543
+ 3 | 43 | 543 | 3543
+ 3 | 43 | 543 | 2543
+ 3 | 43 | 543 | 1543
+ 3 | 43 | 543 | 543
+ 3 | 43 | 743 | 743
+ 3 | 43 | 743 | 1743
+ 3 | 43 | 743 | 2743
+ 3 | 43 | 743 | 3743
+ 3 | 43 | 743 | 4743
+ 3 | 43 | 743 | 5743
+ 3 | 43 | 743 | 6743
+ 3 | 43 | 743 | 7743
+ 3 | 43 | 743 | 8743
+ 3 | 43 | 743 | 9743
+ 3 | 43 | 443 | 9443
+ 3 | 43 | 443 | 8443
+ 3 | 43 | 443 | 7443
+ 3 | 43 | 443 | 6443
+ 3 | 43 | 443 | 5443
+ 3 | 43 | 443 | 4443
+ 3 | 43 | 443 | 3443
+ 3 | 43 | 443 | 2443
+ 3 | 43 | 443 | 1443
+ 3 | 43 | 443 | 443
+ 3 | 43 | 843 | 843
+ 3 | 43 | 843 | 1843
+ 3 | 43 | 843 | 2843
+ 3 | 43 | 843 | 3843
+ 3 | 43 | 843 | 4843
+ 3 | 43 | 843 | 5843
+ 3 | 43 | 843 | 6843
+ 3 | 43 | 843 | 7843
+ 3 | 43 | 843 | 8843
+ 3 | 43 | 843 | 9843
+ 3 | 43 | 343 | 9343
+ 3 | 43 | 343 | 8343
+ 3 | 43 | 343 | 7343
+ 3 | 43 | 343 | 6343
+ 3 | 43 | 343 | 5343
+ 3 | 43 | 343 | 4343
+ 3 | 43 | 343 | 3343
+ 3 | 43 | 343 | 2343
+ 3 | 43 | 343 | 1343
+ 3 | 43 | 343 | 343
+ 3 | 43 | 943 | 943
+ 3 | 43 | 943 | 1943
+ 3 | 43 | 943 | 2943
+ 3 | 43 | 943 | 3943
+ 3 | 43 | 943 | 4943
+ 3 | 43 | 943 | 5943
+ 3 | 43 | 943 | 6943
+ 3 | 43 | 943 | 7943
+ 3 | 43 | 943 | 8943
+ 3 | 43 | 943 | 9943
+ 3 | 43 | 243 | 9243
+ 3 | 43 | 243 | 8243
+ 3 | 43 | 243 | 7243
+ 3 | 43 | 243 | 6243
+ 3 | 43 | 243 | 5243
+ 3 | 43 | 243 | 4243
+ 3 | 43 | 243 | 3243
+ 3 | 43 | 243 | 2243
+ 3 | 43 | 243 | 1243
+ 3 | 43 | 243 | 243
+ 3 | 43 | 143 | 9143
+ 3 | 43 | 143 | 8143
+ 3 | 43 | 143 | 7143
+ 3 | 43 | 143 | 6143
+ 3 | 43 | 143 | 5143
+ 3 | 43 | 143 | 4143
+ 3 | 43 | 143 | 3143
+ 3 | 43 | 143 | 2143
+ 3 | 43 | 143 | 1143
+ 3 | 43 | 143 | 143
+ 3 | 43 | 43 | 9043
+ 3 | 43 | 43 | 8043
+ 3 | 43 | 43 | 7043
+ 3 | 43 | 43 | 6043
+ 3 | 43 | 43 | 5043
+ 3 | 43 | 43 | 4043
+ 3 | 43 | 43 | 3043
+ 3 | 43 | 43 | 2043
+ 3 | 43 | 43 | 1043
+ 3 | 43 | 43 | 43
+(100 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43) AND (tenthous > 3000))
+ Order By: (thousand <-> 600)
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 43 | 643 | 3643
+ 3 | 43 | 643 | 4643
+ 3 | 43 | 643 | 5643
+ 3 | 43 | 643 | 6643
+ 3 | 43 | 643 | 7643
+ 3 | 43 | 643 | 8643
+ 3 | 43 | 643 | 9643
+ 3 | 43 | 543 | 9543
+ 3 | 43 | 543 | 8543
+ 3 | 43 | 543 | 7543
+ 3 | 43 | 543 | 6543
+ 3 | 43 | 543 | 5543
+ 3 | 43 | 543 | 4543
+ 3 | 43 | 543 | 3543
+ 3 | 43 | 743 | 3743
+ 3 | 43 | 743 | 4743
+ 3 | 43 | 743 | 5743
+ 3 | 43 | 743 | 6743
+ 3 | 43 | 743 | 7743
+ 3 | 43 | 743 | 8743
+ 3 | 43 | 743 | 9743
+ 3 | 43 | 443 | 9443
+ 3 | 43 | 443 | 8443
+ 3 | 43 | 443 | 7443
+ 3 | 43 | 443 | 6443
+ 3 | 43 | 443 | 5443
+ 3 | 43 | 443 | 4443
+ 3 | 43 | 443 | 3443
+ 3 | 43 | 843 | 3843
+ 3 | 43 | 843 | 4843
+ 3 | 43 | 843 | 5843
+ 3 | 43 | 843 | 6843
+ 3 | 43 | 843 | 7843
+ 3 | 43 | 843 | 8843
+ 3 | 43 | 843 | 9843
+ 3 | 43 | 343 | 9343
+ 3 | 43 | 343 | 8343
+ 3 | 43 | 343 | 7343
+ 3 | 43 | 343 | 6343
+ 3 | 43 | 343 | 5343
+ 3 | 43 | 343 | 4343
+ 3 | 43 | 343 | 3343
+ 3 | 43 | 943 | 3943
+ 3 | 43 | 943 | 4943
+ 3 | 43 | 943 | 5943
+ 3 | 43 | 943 | 6943
+ 3 | 43 | 943 | 7943
+ 3 | 43 | 943 | 8943
+ 3 | 43 | 943 | 9943
+ 3 | 43 | 243 | 9243
+ 3 | 43 | 243 | 8243
+ 3 | 43 | 243 | 7243
+ 3 | 43 | 243 | 6243
+ 3 | 43 | 243 | 5243
+ 3 | 43 | 243 | 4243
+ 3 | 43 | 243 | 3243
+ 3 | 43 | 143 | 9143
+ 3 | 43 | 143 | 8143
+ 3 | 43 | 143 | 7143
+ 3 | 43 | 143 | 6143
+ 3 | 43 | 143 | 5143
+ 3 | 43 | 143 | 4143
+ 3 | 43 | 143 | 3143
+ 3 | 43 | 43 | 9043
+ 3 | 43 | 43 | 8043
+ 3 | 43 | 43 | 7043
+ 3 | 43 | 43 | 6043
+ 3 | 43 | 43 | 5043
+ 3 | 43 | 43 | 4043
+ 3 | 43 | 43 | 3043
+(70 rows)
+
+-- Ordering by distance to 4th column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+-------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43) AND (thousand = 643))
+ Order By: (tenthous <-> 4000)
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 43 | 643 | 3643
+ 3 | 43 | 643 | 4643
+ 3 | 43 | 643 | 2643
+ 3 | 43 | 643 | 5643
+ 3 | 43 | 643 | 1643
+ 3 | 43 | 643 | 6643
+ 3 | 43 | 643 | 643
+ 3 | 43 | 643 | 7643
+ 3 | 43 | 643 | 8643
+ 3 | 43 | 643 | 9643
+(10 rows)
+
+-- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+------------------------------------------------
+ Index Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = 643)
+ Order By: (tenthous <-> 4000)
+ Filter: (hundred = 43)
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+----------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 4000))
+ -> Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43))
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+------------------------------------------------
+ Index Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = 643)
+ Order By: (tenthous <-> 4000)
+ Filter: (ten = 3)
+(4 rows)
+
+DROP INDEX tenk1_knn_idx;
+RESET enable_sort;
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 1 | 1
+ 1 | 1001
+ 1 | 2001
+ 1 | 3001
+ 1 | 4001
+ 1 | 5001
+ 1 | 6001
+ 1 | 7001
+ 1 | 8001
+ 1 | 9001
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 0 | 0
+ 0 | 1000
+ 0 | 2000
+ 0 | 3000
+ 0 | 4000
+ 0 | 5000
+ 0 | 6000
+ 0 | 7000
+ 0 | 8000
+ 0 | 9000
+ | 3
+ | 2
+ | 1
+(33 rows)
+
+DROP INDEX tenk3_idx;
+DROP TABLE tenk3;
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+-------------------
+ Wed May 03 00:00:00 2017 | @ 2 days
+ Wed May 03 01:00:00 2017 | @ 2 days 1 hour
+ Wed May 03 02:00:00 2017 | @ 2 days 2 hours
+ Wed May 03 03:00:00 2017 | @ 2 days 3 hours
+ Wed May 03 04:00:00 2017 | @ 2 days 4 hours
+ Wed May 03 05:00:00 2017 | @ 2 days 5 hours
+ Wed May 03 06:00:00 2017 | @ 2 days 6 hours
+ Wed May 03 07:00:00 2017 | @ 2 days 7 hours
+ Wed May 03 08:00:00 2017 | @ 2 days 8 hours
+ Wed May 03 09:00:00 2017 | @ 2 days 9 hours
+ Wed May 03 10:00:00 2017 | @ 2 days 10 hours
+ Wed May 03 11:00:00 2017 | @ 2 days 11 hours
+ Wed May 03 12:00:00 2017 | @ 2 days 12 hours
+ Wed May 03 13:00:00 2017 | @ 2 days 13 hours
+ Wed May 03 14:00:00 2017 | @ 2 days 14 hours
+ Wed May 03 15:00:00 2017 | @ 2 days 15 hours
+ Wed May 03 16:00:00 2017 | @ 2 days 16 hours
+ Wed May 03 17:00:00 2017 | @ 2 days 17 hours
+ Wed May 03 18:00:00 2017 | @ 2 days 18 hours
+ Wed May 03 19:00:00 2017 | @ 2 days 19 hours
+(20 rows)
+
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+------------
+ Mon Jan 01 00:00:00 2018 | @ 0
+ Mon Jan 01 01:00:00 2018 | @ 1 hour
+ Sun Dec 31 23:00:00 2017 | @ 1 hour
+ Mon Jan 01 02:00:00 2018 | @ 2 hours
+ Sun Dec 31 22:00:00 2017 | @ 2 hours
+ Mon Jan 01 03:00:00 2018 | @ 3 hours
+ Sun Dec 31 21:00:00 2017 | @ 3 hours
+ Mon Jan 01 04:00:00 2018 | @ 4 hours
+ Sun Dec 31 20:00:00 2017 | @ 4 hours
+ Mon Jan 01 05:00:00 2018 | @ 5 hours
+ Sun Dec 31 19:00:00 2017 | @ 5 hours
+ Mon Jan 01 06:00:00 2018 | @ 6 hours
+ Sun Dec 31 18:00:00 2017 | @ 6 hours
+ Mon Jan 01 07:00:00 2018 | @ 7 hours
+ Sun Dec 31 17:00:00 2017 | @ 7 hours
+ Mon Jan 01 08:00:00 2018 | @ 8 hours
+ Sun Dec 31 16:00:00 2017 | @ 8 hours
+ Mon Jan 01 09:00:00 2018 | @ 9 hours
+ Sun Dec 31 15:00:00 2017 | @ 9 hours
+ Mon Jan 01 10:00:00 2018 | @ 10 hours
+(20 rows)
+
+DROP TABLE knn_btree_ts;
+RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 2b087be..5e3bffe 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -129,3 +129,307 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
-- Simple ALTER INDEX
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+---
+--- Test B-tree distance ordering
+---
+
+SET enable_bitmapscan = OFF;
+
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+-- test parallel KNN scan
+
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+
+RESET enable_indexscan;
+
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+
+ROLLBACK;
+
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+
+
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+DROP INDEX tenk3_idx;
+
+-- Test order by distance ordering on non-first column
+SET enable_sort = OFF;
+
+-- Ranges are not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand > 120
+ORDER BY tenthous <-> 3500;
+
+-- Equality restriction on the first column is supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+
+-- IN restriction on the first column is not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY tenthous <-> 3500;
+
+-- Test kNN search using 4-column index
+CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
+
+-- Ordering by distance to 3rd column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+
+-- Ordering by distance to 4th column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+-- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+DROP INDEX tenk1_knn_idx;
+
+RESET enable_sort;
+
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+DROP TABLE tenk3;
+
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+
+DROP TABLE knn_btree_ts;
+
+RESET enable_bitmapscan;
0008-Allow-ammatchorderby-to-return-pathkey-sublists-v06.patchtext/x-patch; name=0008-Allow-ammatchorderby-to-return-pathkey-sublists-v06.patchDownload
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 279d8ba..b88044b 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1014,8 +1014,25 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
index_clauses,
&orderbyclauses,
&orderbyclausecols);
+
if (orderbyclauses)
- useful_pathkeys = root->query_pathkeys;
+ {
+ int norderbys = list_length(orderbyclauses);
+ int npathkeys = list_length(root->query_pathkeys);
+
+ if (norderbys < npathkeys)
+ {
+ /*
+ * We do not accept pathkey sublists until we implement
+ * partial sorting.
+ */
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+ }
+ else
+ useful_pathkeys = root->query_pathkeys;
+ }
else
useful_pathkeys = NIL;
}
@@ -3286,8 +3303,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
* index column numbers (zero based) that each clause would be used with.
* NIL lists are returned if the ordering is not achievable this way.
*
- * On success, the result list is ordered by pathkeys, and in fact is
- * one-to-one with the requested pathkeys.
+ * On success, the result list is ordered by pathkeys.
*/
static void
match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
@@ -3305,8 +3321,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
ammatchorderby(index, pathkeys, index_clauses,
&orderby_clauses, &orderby_clause_columns))
{
- Assert(list_length(pathkeys) == list_length(orderby_clauses));
- Assert(list_length(pathkeys) == list_length(orderby_clause_columns));
+ Assert(list_length(orderby_clauses) <= list_length(pathkeys));
+ Assert(list_length(orderby_clauses) == list_length(orderby_clause_columns));
*orderby_clauses_p = orderby_clauses; /* success! */
*orderby_clause_columns_p = orderby_clause_columns;
0009-Add-support-of-array-ops-to-btree-kNN-v06.patchtext/x-patch; name=0009-Add-support-of-array-ops-to-btree-kNN-v06.patchDownload
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index fb413e7..3085fae 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -509,8 +509,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
if (orderbys && scan->numberOfOrderBys > 0)
memmove(scan->orderByData,
- orderbys,
- scan->numberOfOrderBys * sizeof(ScanKeyData));
+ &orderbys[scan->numberOfOrderBys - 1],
+ 1 /* scan->numberOfOrderBys */ * sizeof(ScanKeyData));
so->scanDirection = NoMovementScanDirection;
so->distanceTypeByVal = true;
@@ -1554,13 +1554,15 @@ static bool
btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
List **orderby_clauses_p, List **orderby_clausecols_p)
{
- Expr *expr;
+ Expr *expr = NULL;
ListCell *lc;
int indexcol;
int num_eq_cols = 0;
+ int nsaops = 0;
+ int last_saop_ord_col = -1;
+ bool saops[INDEX_MAX_KEYS] = {0};
- /* only one ORDER BY clause is supported */
- if (list_length(pathkeys) != 1)
+ if (list_length(pathkeys) < 1)
return false;
/*
@@ -1584,6 +1586,7 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
Expr *clause = rinfo->clause;
Oid opno;
StrategyNumber strat;
+ bool is_saop;
if (!clause)
continue;
@@ -1593,6 +1596,18 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
OpExpr *opexpr = (OpExpr *) clause;
opno = opexpr->opno;
+ is_saop = false;
+ }
+ else if (IsA(clause, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+
+ /* We only accept ANY clauses, not ALL */
+ if (!saop->useOr)
+ continue;
+
+ opno = saop->opno;
+ is_saop = true;
}
else
{
@@ -1603,19 +1618,67 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
/* Check if the operator is btree equality operator. */
strat = get_op_opfamily_strategy(opno, index->opfamily[indexcol]);
- if (strat == BTEqualStrategyNumber)
- num_eq_cols = indexcol + 1;
+ if (strat != BTEqualStrategyNumber)
+ continue;
+
+ if (is_saop && indexcol == num_eq_cols)
+ {
+ saops[indexcol] = true;
+ nsaops++;
+ }
+ else if (!is_saop && saops[indexcol])
+ {
+ saops[indexcol] = false;
+ nsaops--;
+ }
+
+ num_eq_cols = indexcol + 1;
}
}
- /*
- * If there are no equality columns try to match only the first column,
- * otherwise try all columns.
- */
- indexcol = num_eq_cols ? -1 : 0;
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = lfirst_node(PathKey, lc);
+
+ /*
+ * If there are no equality columns try to match only the first column,
+ * otherwise try all columns.
+ */
+ indexcol = num_eq_cols ? -1 : 0;
+
+ if ((expr = match_orderbyop_pathkey(index, pathkey, &indexcol)))
+ break; /* found order-by-operator pathkey */
+
+ if (!num_eq_cols)
+ return false; /* first pathkey is not order-by-operator */
- expr = match_orderbyop_pathkey(index, castNode(PathKey, linitial(pathkeys)),
- &indexcol);
+ indexcol = -1;
+
+ if (!(expr = match_pathkey_to_indexcol(index, pathkey, &indexcol)))
+ return false;
+
+ if (indexcol >= num_eq_cols)
+ return false;
+
+ if (saops[indexcol])
+ {
+ saops[indexcol] = false;
+ nsaops--;
+
+ /*
+ * ORDER BY column numbers for array ops should go in
+ * non-decreasing order.
+ */
+ if (indexcol < last_saop_ord_col)
+ return false;
+
+ last_saop_ord_col = indexcol;
+ }
+ /* else: order of equality-restricted columns is arbitrary */
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *orderby_clausecols_p = lappend_int(*orderby_clausecols_p, -indexcol - 1);
+ }
if (!expr)
return false;
@@ -1627,6 +1690,19 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
if (indexcol > num_eq_cols)
return false;
+ if (nsaops)
+ {
+ int i;
+
+ /*
+ * Check that all preceding array-op columns are included into
+ * ORDER BY clause.
+ */
+ for (i = 0; i < indexcol; i++)
+ if (saops[i])
+ return false;
+ }
+
/* Return first ORDER BY clause's expression and column. */
*orderby_clauses_p = lappend(*orderby_clauses_p, expr);
*orderby_clausecols_p = lappend_int(*orderby_clausecols_p, indexcol);
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 5b9294b..e980351 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -905,10 +905,6 @@ _bt_preprocess_keys(IndexScanDesc scan)
{
ScanKey ord = scan->orderByData;
- if (scan->numberOfOrderBys > 1)
- /* it should not happen, see btmatchorderby() */
- elog(ERROR, "only one btree ordering operator is supported");
-
Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
/* use bidirectional kNN scan by default */
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 805abd2..034e3b8 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -1644,6 +1644,27 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
InvalidOid, /* no reg proc for this */
(Datum) 0); /* constant */
}
+ else if (IsA(clause, Var))
+ {
+ /* indexkey IS NULL or indexkey IS NOT NULL */
+ Var *var = (Var *) clause;
+
+ Assert(isorderby);
+
+ if (var->varno != INDEX_VAR)
+ elog(ERROR, "Var indexqual has wrong key");
+
+ varattno = var->varattno;
+
+ ScanKeyEntryInitialize(this_scan_key,
+ SK_ORDER_BY | SK_SEARCHNOTNULL,
+ varattno, /* attribute number to scan */
+ InvalidStrategy, /* no strategy */
+ InvalidOid, /* no strategy subtype */
+ var->varcollid, /* collation FIXME */
+ InvalidOid, /* no reg proc for this */
+ (Datum) 0); /* constant */
+ }
else
elog(ERROR, "unsupported indexqual type: %d",
(int) nodeTag(clause));
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index b88044b..a1a36ad 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -2945,6 +2945,62 @@ match_rowcompare_to_indexcol(RestrictInfo *rinfo,
return NULL;
}
+/*
+ * Try to match pathkey to the specified index column (*indexcol >= 0) or
+ * to all index columns (*indexcol < 0).
+ */
+Expr *
+match_pathkey_to_indexcol(IndexOptInfo *index, PathKey *pathkey, int *indexcol)
+{
+ ListCell *lc;
+
+ /* Pathkey must request default sort order for the target opfamily */
+ if (pathkey->pk_strategy != BTLessStrategyNumber ||
+ pathkey->pk_nulls_first)
+ return NULL;
+
+ /* If eclass is volatile, no hope of using an indexscan */
+ if (pathkey->pk_eclass->ec_has_volatile)
+ return NULL;
+
+ /*
+ * Try to match eclass member expression(s) to index. Note that child
+ * EC members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could
+ * be considered to match more than one pathkey list, which is OK
+ * here. See also get_eclass_for_sort_expr.)
+ */
+ foreach(lc, pathkey->pk_eclass->ec_members)
+ {
+ EquivalenceMember *member = lfirst_node(EquivalenceMember, lc);
+ Expr *expr = member->em_expr;
+
+ /* No possibility of match if it references other relations */
+ if (!bms_equal(member->em_relids, index->rel->relids))
+ continue;
+
+ /* If *indexcol is non-negative then try to match only to it */
+ if (*indexcol >= 0)
+ {
+ if (match_index_to_operand((Node *) expr, *indexcol, index))
+ /* don't want to look at remaining members */
+ return expr;
+ }
+ else /* try to match all columns */
+ {
+ for (*indexcol = 0; *indexcol < index->nkeycolumns; ++*indexcol)
+ {
+ if (match_index_to_operand((Node *) expr, *indexcol, index))
+ /* don't want to look at remaining members */
+ return expr;
+ }
+ }
+ }
+
+ return NULL;
+}
+
/****************************************************************************
* ---- ROUTINES TO CHECK ORDERING OPERATORS ----
****************************************************************************/
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 236f506..307af39 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4555,7 +4555,11 @@ fix_indexqual_clause(PlannerInfo *root, IndexOptInfo *index, int indexcol,
*/
clause = replace_nestloop_params(root, clause);
- if (IsA(clause, OpExpr))
+ if (indexcol < 0)
+ {
+ clause = fix_indexqual_operand(clause, index, -indexcol - 1);
+ }
+ else if (IsA(clause, OpExpr))
{
OpExpr *op = (OpExpr *) clause;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 2e3fab6..2076405 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5284,10 +5284,12 @@ get_quals_from_indexclauses(List *indexclauses)
* index key expression is on the left side of binary clauses.
*/
Cost
-index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
+index_other_operands_eval_cost(PlannerInfo *root, List *indexquals,
+ List *indexcolnos)
{
Cost qual_arg_cost = 0;
ListCell *lc;
+ ListCell *indexcolno_lc = indexcolnos ? list_head(indexcolnos) : NULL;
foreach(lc, indexquals)
{
@@ -5302,6 +5304,20 @@ index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
if (IsA(clause, RestrictInfo))
clause = ((RestrictInfo *) clause)->clause;
+ if (indexcolnos)
+ {
+ int indexcol = lfirst_int(indexcolno_lc);
+
+ if (indexcol < 0)
+ {
+ /* FIXME */
+ qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
+ continue;
+ }
+
+ indexcolno_lc = lnext(indexcolno_lc);
+ }
+
if (IsA(clause, OpExpr))
{
OpExpr *op = (OpExpr *) clause;
@@ -5331,7 +5347,8 @@ index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
other_operand = NULL; /* keep compiler quiet */
}
- cost_qual_eval_node(&index_qual_cost, other_operand, root);
+ if (other_operand)
+ cost_qual_eval_node(&index_qual_cost, other_operand, root);
qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
}
return qual_arg_cost;
@@ -5346,6 +5363,7 @@ genericcostestimate(PlannerInfo *root,
IndexOptInfo *index = path->indexinfo;
List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
List *indexOrderBys = path->indexorderbys;
+ List *indexOrderByCols = path->indexorderbycols;
Cost indexStartupCost;
Cost indexTotalCost;
Selectivity indexSelectivity;
@@ -5509,8 +5527,8 @@ genericcostestimate(PlannerInfo *root,
* Detecting that that might be needed seems more expensive than it's
* worth, though, considering all the other inaccuracies here ...
*/
- qual_arg_cost = index_other_operands_eval_cost(root, indexQuals) +
- index_other_operands_eval_cost(root, indexOrderBys);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals, NIL) +
+ index_other_operands_eval_cost(root, indexOrderBys, indexOrderByCols);
qual_op_cost = cpu_operator_cost *
(list_length(indexQuals) + list_length(indexOrderBys));
@@ -6629,7 +6647,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
* Add on index qual eval costs, much as in genericcostestimate. But we
* can disregard indexorderbys, since GIN doesn't support those.
*/
- qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals, NIL);
qual_op_cost = cpu_operator_cost * list_length(indexQuals);
*indexStartupCost += qual_arg_cost;
@@ -6809,7 +6827,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
* the index costs. We can disregard indexorderbys, since BRIN doesn't
* support those.
*/
- qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals, NIL);
/*
* Compute the startup cost as the cost to read the whole revmap
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index e9f4f75..6428932 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -79,6 +79,8 @@ extern bool indexcol_is_bool_constant_for_query(IndexOptInfo *index,
extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index);
extern void check_index_predicates(PlannerInfo *root, RelOptInfo *rel);
+extern Expr *match_pathkey_to_indexcol(IndexOptInfo *index, PathKey *pathkey,
+ int *indexcol_p);
extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
int *indexcol_p);
extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index 0ce2175..636e280 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -189,7 +189,7 @@ extern void estimate_hash_bucket_stats(PlannerInfo *root,
extern List *get_quals_from_indexclauses(List *indexclauses);
extern Cost index_other_operands_eval_cost(PlannerInfo *root,
- List *indexquals);
+ List *indexquals, List *indexcolnos);
extern List *add_predicate_to_index_quals(IndexOptInfo *index,
List *indexQuals);
extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0cefbcc..724890b 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -876,11 +876,9 @@ ORDER BY tenthous <-> 3500;
120 | 9120
(10 rows)
--- IN restriction on the first column is not supported
+-- IN restriction on the first column is not supported without 'ORDER BY col1 ASC'
EXPLAIN (COSTS OFF)
-SELECT thousand, tenthous
-FROM tenk1
-WHERE thousand IN (5, 120, 3456, 23)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY tenthous <-> 3500;
QUERY PLAN
---------------------------------------------------------------------
@@ -890,6 +888,63 @@ ORDER BY tenthous <-> 3500;
Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
(4 rows)
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand DESC, tenthous <-> 3500;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Sort
+ Sort Key: thousand DESC, ((tenthous <-> 3500))
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+ QUERY PLAN
+---------------------------------------------------------------
+ Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
+ Order By: (thousand AND (tenthous <-> 3500))
+(3 rows)
+
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+ thousand | tenthous
+----------+----------
+ 5 | 3005
+ 5 | 4005
+ 5 | 2005
+ 5 | 5005
+ 5 | 1005
+ 5 | 6005
+ 5 | 5
+ 5 | 7005
+ 5 | 8005
+ 5 | 9005
+ 23 | 3023
+ 23 | 4023
+ 23 | 2023
+ 23 | 5023
+ 23 | 1023
+ 23 | 6023
+ 23 | 23
+ 23 | 7023
+ 23 | 8023
+ 23 | 9023
+ 120 | 3120
+ 120 | 4120
+ 120 | 2120
+ 120 | 5120
+ 120 | 1120
+ 120 | 6120
+ 120 | 120
+ 120 | 7120
+ 120 | 8120
+ 120 | 9120
+(30 rows)
+
-- Test kNN search using 4-column index
CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
-- Ordering by distance to 3rd column
@@ -1122,38 +1177,132 @@ WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
3 | 43 | 643 | 9643
(10 rows)
--- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+-- Array ops on non-first columns are not supported
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
- QUERY PLAN
-------------------------------------------------
- Index Scan using tenk1_thous_tenthous on tenk1
- Index Cond: (thousand = 643)
- Order By: (tenthous <-> 4000)
- Filter: (hundred = 43)
+WHERE ten IN (3, 4, 5) AND hundred IN (23, 24, 35) AND thousand IN (843, 132, 623, 243)
+ORDER BY tenthous <-> 6000;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 6000))
+ -> Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = ANY ('{23,24,35}'::integer[])) AND (thousand = ANY ('{843,132,623,243}'::integer[])))
(4 rows)
+-- All array columns should be included into ORDER BY
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
- QUERY PLAN
-----------------------------------------------------
- Sort
- Sort Key: ((tenthous <-> 4000))
- -> Index Only Scan using tenk1_knn_idx on tenk1
- Index Cond: ((ten = 3) AND (hundred = 43))
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY tenthous <-> 6000;
+ QUERY PLAN
+-------------------------------------------------------------------
+ Index Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: ((thousand = 123) AND (tenthous > 2000))
+ Order By: (tenthous <-> 6000)
+ Filter: ((hundred = 23) AND (ten = ANY ('{3,4,5}'::integer[])))
(4 rows)
+-- Eq-restricted columns can be omitted from ORDER BY
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
- QUERY PLAN
-------------------------------------------------
- Index Scan using tenk1_thous_tenthous on tenk1
- Index Cond: (thousand = 643)
- Order By: (tenthous <-> 4000)
- Filter: (ten = 3)
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 23 | 123 | 6123
+ 3 | 23 | 123 | 5123
+ 3 | 23 | 123 | 7123
+ 3 | 23 | 123 | 4123
+ 3 | 23 | 123 | 8123
+ 3 | 23 | 123 | 3123
+ 3 | 23 | 123 | 9123
+ 3 | 23 | 123 | 2123
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 23 | 123 | 6123
+ 3 | 23 | 123 | 5123
+ 3 | 23 | 123 | 7123
+ 3 | 23 | 123 | 4123
+ 3 | 23 | 123 | 8123
+ 3 | 23 | 123 | 3123
+ 3 | 23 | 123 | 9123
+ 3 | 23 | 123 | 2123
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4 ,5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 23 | 123 | 6123
+ 3 | 23 | 123 | 5123
+ 3 | 23 | 123 | 7123
+ 3 | 23 | 123 | 4123
+ 3 | 23 | 123 | 8123
+ 3 | 23 | 123 | 3123
+ 3 | 23 | 123 | 9123
+ 3 | 23 | 123 | 2123
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, thousand, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+-- Extra ORDER BY columns after order-by-op are not supported
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 ORDER BY ten, thousand <-> 6000, tenthous;
+ QUERY PLAN
+-----------------------------------------------------------------------------
+ Sort
+ Sort Key: ten, ((thousand <-> 6000)), tenthous
+ -> Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23))
(4 rows)
DROP INDEX tenk1_knn_idx;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 5e3bffe..69c890e 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -345,13 +345,22 @@ FROM tenk1
WHERE thousand = 120
ORDER BY tenthous <-> 3500;
--- IN restriction on the first column is not supported
+-- IN restriction on the first column is not supported without 'ORDER BY col1 ASC'
EXPLAIN (COSTS OFF)
-SELECT thousand, tenthous
-FROM tenk1
-WHERE thousand IN (5, 120, 3456, 23)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY tenthous <-> 3500;
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand DESC, tenthous <-> 3500;
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+
-- Test kNN search using 4-column index
CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
@@ -378,18 +387,55 @@ WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
SELECT ten, hundred, thousand, tenthous FROM tenk1
WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
--- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+-- Array ops on non-first columns are not supported
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred IN (23, 24, 35) AND thousand IN (843, 132, 623, 243)
+ORDER BY tenthous <-> 6000;
+
+-- All array columns should be included into ORDER BY
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY tenthous <-> 6000;
+
+-- Eq-restricted columns can be omitted from ORDER BY
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4 ,5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, thousand, tenthous <-> 6000;
+-- Extra ORDER BY columns after order-by-op are not supported
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
+WHERE ten IN (3, 4, 5) AND hundred = 23 ORDER BY ten, thousand <-> 6000, tenthous;
DROP INDEX tenk1_knn_idx;
On Wed, Feb 20, 2019 at 2:18 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
On 04.02.2019 8:35, Michael Paquier wrote:
This patch set needs a rebase because of conflicts caused by the
recent patches for pluggable storage.
Hi Nikita,
From the department of trivialities: according to cfbot the
documentation doesn't build. Looks like you have some cases of </>,
but these days you have to write out </quote> (or whatever) in full.
--
Thomas Munro
https://enterprisedb.com
On 20.02.2019 7:35, Thomas Munro wrote:
On Wed, Feb 20, 2019 at 2:18 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
On 04.02.2019 8:35, Michael Paquier wrote:
This patch set needs a rebase because of conflicts caused by the
recent patches for pluggable storage.Hi Nikita,
From the department of trivialities: according to cfbot the
documentation doesn't build. Looks like you have some cases of </>,
but these days you have to write out </quote> (or whatever) in full.
Sorry, tags in docs were fixed. Also I fixed list of data types with built-in
distance operators and list of assumptions for btree distance operators.
Attached 7th version the patches (only documentation was changed).
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0003-Extract-structure-BTScanState-v07.patchtext/x-patch; name=0003-Extract-structure-BTScanState-v07.patchDownload
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index c56ee5e..bf6a6c6 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -214,6 +214,7 @@ bool
btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
bool res;
/* btree indexes are never lossy */
@@ -224,7 +225,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(so->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
@@ -241,7 +242,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(so->currPos))
+ if (!BTScanPosIsValid(state->currPos))
res = _bt_first(scan, dir);
else
{
@@ -259,11 +260,11 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* trying to optimize that, so we don't detect it, but instead
* just forget any excess entries.
*/
- if (so->killedItems == NULL)
- so->killedItems = (int *)
+ if (state->killedItems == NULL)
+ state->killedItems = (int *)
palloc(MaxIndexTuplesPerPage * sizeof(int));
- if (so->numKilled < MaxIndexTuplesPerPage)
- so->killedItems[so->numKilled++] = so->currPos.itemIndex;
+ if (state->numKilled < MaxIndexTuplesPerPage)
+ state->killedItems[so->state.numKilled++] = state->currPos.itemIndex;
}
/*
@@ -288,6 +289,7 @@ int64
btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
int64 ntids = 0;
ItemPointer heapTid;
@@ -320,7 +322,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* Advance to next tuple within page. This is the same as the
* easy case in _bt_next().
*/
- if (++so->currPos.itemIndex > so->currPos.lastItem)
+ if (++currPos->itemIndex > currPos->lastItem)
{
/* let _bt_next do the heavy lifting */
if (!_bt_next(scan, ForwardScanDirection))
@@ -328,7 +330,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
/* Save tuple ID, and continue scanning */
- heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
+ heapTid = &currPos->items[currPos->itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
}
@@ -356,8 +358,8 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
/* allocate private workspace */
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
- BTScanPosInvalidate(so->currPos);
- BTScanPosInvalidate(so->markPos);
+ BTScanPosInvalidate(so->state.currPos);
+ BTScanPosInvalidate(so->state.markPos);
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
else
@@ -368,15 +370,15 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
so->arrayKeys = NULL;
so->arrayContext = NULL;
- so->killedItems = NULL; /* until needed */
- so->numKilled = 0;
+ so->state.killedItems = NULL; /* until needed */
+ so->state.numKilled = 0;
/*
* We don't know yet whether the scan will be index-only, so we do not
* allocate the tuple workspace arrays until btrescan. However, we set up
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
- so->currTuples = so->markTuples = NULL;
+ so->state.currTuples = so->state.markTuples = NULL;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -385,6 +387,45 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
return scan;
}
+static void
+_bt_release_current_position(BTScanState state, Relation indexRelation,
+ bool invalidate)
+{
+ /* we aren't holding any read locks, but gotta drop the pins */
+ if (BTScanPosIsValid(state->currPos))
+ {
+ /* Before leaving current page, deal with any killed items */
+ if (state->numKilled > 0)
+ _bt_killitems(state, indexRelation);
+
+ BTScanPosUnpinIfPinned(state->currPos);
+
+ if (invalidate)
+ BTScanPosInvalidate(state->currPos);
+ }
+}
+
+static void
+_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
+{
+ /* No need to invalidate positions, if the RAM is about to be freed. */
+ _bt_release_current_position(state, scan->indexRelation, !free);
+
+ state->markItemIndex = -1;
+ BTScanPosUnpinIfPinned(state->markPos);
+
+ if (free)
+ {
+ if (state->killedItems != NULL)
+ pfree(state->killedItems);
+ if (state->currTuples != NULL)
+ pfree(state->currTuples);
+ /* markTuples should not be pfree'd (_bt_allocate_tuple_workspaces) */
+ }
+ else
+ BTScanPosInvalidate(state->markPos);
+}
+
/*
* btrescan() -- rescan an index relation
*/
@@ -393,21 +434,11 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
ScanKey orderbys, int norderbys)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
- }
+ _bt_release_scan_state(scan, state, false);
- so->markItemIndex = -1;
so->arrayKeyCount = 0;
- BTScanPosUnpinIfPinned(so->markPos);
- BTScanPosInvalidate(so->markPos);
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
@@ -425,11 +456,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
* a SIGSEGV is not possible. Yeah, this is ugly as sin, but it beats
* adding special-case treatment for name_ops elsewhere.
*/
- if (scan->xs_want_itup && so->currTuples == NULL)
- {
- so->currTuples = (char *) palloc(BLCKSZ * 2);
- so->markTuples = so->currTuples + BLCKSZ;
- }
+ if (scan->xs_want_itup && state->currTuples == NULL)
+ _bt_allocate_tuple_workspaces(state);
/*
* Reset the scan keys. Note that keys ordering stuff moved to _bt_first.
@@ -453,19 +481,7 @@ btendscan(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
-
- /* No need to invalidate positions, the RAM is about to be freed. */
+ _bt_release_scan_state(scan, &so->state, true);
/* Release storage */
if (so->keyData != NULL)
@@ -473,24 +489,15 @@ btendscan(IndexScanDesc scan)
/* so->arrayKeyData and so->arrayKeys are in arrayContext */
if (so->arrayContext != NULL)
MemoryContextDelete(so->arrayContext);
- if (so->killedItems != NULL)
- pfree(so->killedItems);
- if (so->currTuples != NULL)
- pfree(so->currTuples);
- /* so->markTuples should not be pfree'd, see btrescan */
+
pfree(so);
}
-/*
- * btmarkpos() -- save current scan position
- */
-void
-btmarkpos(IndexScanDesc scan)
+static void
+_bt_mark_current_position(BTScanState state)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
-
/* There may be an old mark with a pin (but no lock). */
- BTScanPosUnpinIfPinned(so->markPos);
+ BTScanPosUnpinIfPinned(state->markPos);
/*
* Just record the current itemIndex. If we later step to next page
@@ -498,32 +505,34 @@ btmarkpos(IndexScanDesc scan)
* the currPos struct in markPos. If (as often happens) the mark is moved
* before we leave the page, we don't have to do that work.
*/
- if (BTScanPosIsValid(so->currPos))
- so->markItemIndex = so->currPos.itemIndex;
+ if (BTScanPosIsValid(state->currPos))
+ state->markItemIndex = state->currPos.itemIndex;
else
{
- BTScanPosInvalidate(so->markPos);
- so->markItemIndex = -1;
+ BTScanPosInvalidate(state->markPos);
+ state->markItemIndex = -1;
}
-
- /* Also record the current positions of any array keys */
- if (so->numArrayKeys)
- _bt_mark_array_keys(scan);
}
/*
- * btrestrpos() -- restore scan to last saved position
+ * btmarkpos() -- save current scan position
*/
void
-btrestrpos(IndexScanDesc scan)
+btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* Restore the marked positions of any array keys */
+ _bt_mark_current_position(&so->state);
+
+ /* Also record the current positions of any array keys */
if (so->numArrayKeys)
- _bt_restore_array_keys(scan);
+ _bt_mark_array_keys(scan);
+}
- if (so->markItemIndex >= 0)
+static void
+_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
+{
+ if (state->markItemIndex >= 0)
{
/*
* The scan has never moved to a new page since the last mark. Just
@@ -532,7 +541,7 @@ btrestrpos(IndexScanDesc scan)
* NB: In this case we can't count on anything in so->markPos to be
* accurate.
*/
- so->currPos.itemIndex = so->markItemIndex;
+ state->currPos.itemIndex = state->markItemIndex;
}
else
{
@@ -542,28 +551,21 @@ btrestrpos(IndexScanDesc scan)
* locks, but if we're still holding the pin for the current position,
* we must drop it.
*/
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
+ _bt_release_current_position(state, scan->indexRelation,
+ !BTScanPosIsValid(state->markPos));
- if (BTScanPosIsValid(so->markPos))
+ if (BTScanPosIsValid(state->markPos))
{
/* bump pin on mark buffer for assignment to current buffer */
- if (BTScanPosIsPinned(so->markPos))
- IncrBufferRefCount(so->markPos.buf);
- memcpy(&so->currPos, &so->markPos,
+ if (BTScanPosIsPinned(state->markPos))
+ IncrBufferRefCount(state->markPos.buf);
+ memcpy(&state->currPos, &state->markPos,
offsetof(BTScanPosData, items[1]) +
- so->markPos.lastItem * sizeof(BTScanPosItem));
- if (so->currTuples)
- memcpy(so->currTuples, so->markTuples,
- so->markPos.nextTupleOffset);
+ state->markPos.lastItem * sizeof(BTScanPosItem));
+ if (state->currTuples)
+ memcpy(state->currTuples, state->markTuples,
+ state->markPos.nextTupleOffset);
}
- else
- BTScanPosInvalidate(so->currPos);
}
}
@@ -779,9 +781,10 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
}
/*
- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
- * btbulkdelete() wasn't called.
- */
+- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
+- * btbulkdelete() wasn't called.
++ * btrestrpos() -- restore scan to last saved position
+ */
static bool
_bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
{
@@ -844,6 +847,21 @@ _bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
}
/*
+ * btrestrpos() -- restore scan to last saved position
+ */
+void
+btrestrpos(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
+ /* Restore the marked positions of any array keys */
+ if (so->numArrayKeys)
+ _bt_restore_array_keys(scan);
+
+ _bt_restore_marked_position(scan, &so->state);
+}
+
+/*
* Bulk deletion of all index entries pointing to a set of heap tuples.
* The set of target tuples is specified via a callback routine that tells
* whether any given heap tuple (identified by ItemPointer) is being deleted.
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 9283223..cbd72bd 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -24,18 +24,19 @@
#include "utils/rel.h"
-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir,
+static bool _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
OffsetNumber offnum);
-static void _bt_saveitem(BTScanOpaque so, int itemIndex,
+static void _bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup);
-static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
-static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir);
+static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
+static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
-static inline void _bt_initialize_more_data(BTScanOpaque so, ScanDirection dir);
+static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
/*
@@ -544,6 +545,58 @@ _bt_compare(Relation rel,
}
/*
+ * _bt_return_current_item() -- Prepare current scan state item for return.
+ *
+ * This function is used only in "return _bt_return_current_item();" statements
+ * and always returns true.
+ */
+static inline bool
+_bt_return_current_item(IndexScanDesc scan, BTScanState state)
+{
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+
+ scan->xs_ctup.t_self = currItem->heapTid;
+
+ if (scan->xs_want_itup)
+ scan->xs_itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+
+ return true;
+}
+
+/*
+ * _bt_load_first_page() -- Load data from the first page of the scan.
+ *
+ * Caller must have pinned and read-locked state->currPos.buf.
+ *
+ * On success exit, state->currPos is updated to contain data from the next
+ * interesting page. For success on a scan using a non-MVCC snapshot we hold
+ * a pin, but not a read lock, on that page. If we do not hold the pin, we
+ * set state->currPos.buf to InvalidBuffer. We return true to indicate success.
+ *
+ * If there are no more matching records in the given direction at all,
+ * we drop all locks and pins, set state->currPos.buf to InvalidBuffer,
+ * and return false.
+ */
+static bool
+_bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
+{
+ if (!_bt_readpage(scan, state, dir, offnum))
+ {
+ /*
+ * There's no actually-matching data on this page. Try to advance to
+ * the next page. Return false if there's no matching data at all.
+ */
+ LockBuffer(state->currPos.buf, BUFFER_LOCK_UNLOCK);
+ return _bt_steppage(scan, state, dir);
+ }
+
+ /* Drop the lock, and maybe the pin, on the current page */
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
+ return true;
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -568,6 +621,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
BTStack stack;
OffsetNumber offnum;
@@ -581,10 +635,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
int i;
bool status = true;
StrategyNumber strat_total;
- BTScanPosItem *currItem;
BlockNumber blkno;
- Assert(!BTScanPosIsValid(so->currPos));
+ Assert(!BTScanPosIsValid(*currPos));
pgstat_count_index_scan(rel);
@@ -1075,7 +1128,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* their scan
*/
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1083,7 +1136,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
PredicateLockPage(rel, BufferGetBlockNumber(buf),
scan->xs_snapshot);
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
/* position to the precise item on the page */
offnum = _bt_binsrch(rel, buf, keysCount, scankeys, nextkey);
@@ -1110,36 +1163,36 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
offnum = OffsetNumberPrev(offnum);
/* remember which buffer we have pinned, if any */
- Assert(!BTScanPosIsValid(so->currPos));
- so->currPos.buf = buf;
+ Assert(!BTScanPosIsValid(*currPos));
+ currPos->buf = buf;
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, offnum))
+ if (!_bt_load_first_page(scan, &so->state, dir, offnum))
+ return false;
+
+readcomplete:
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
+}
+
+/*
+ * Advance to next tuple on current page; or if there's no more,
+ * try to step to the next page with data.
+ */
+static bool
+_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
+{
+ if (ScanDirectionIsForward(dir))
{
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
+ if (++state->currPos.itemIndex <= state->currPos.lastItem)
+ return true;
}
else
{
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ if (--state->currPos.itemIndex >= state->currPos.firstItem)
+ return true;
}
-readcomplete:
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_steppage(scan, state, dir);
}
/*
@@ -1160,44 +1213,20 @@ bool
_bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- BTScanPosItem *currItem;
- /*
- * Advance to next tuple on current page; or if there's no more, try to
- * step to the next page with data.
- */
- if (ScanDirectionIsForward(dir))
- {
- if (++so->currPos.itemIndex > so->currPos.lastItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
- else
- {
- if (--so->currPos.itemIndex < so->currPos.firstItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
+ if (!_bt_next_item(scan, &so->state, dir))
+ return false;
/* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_return_current_item(scan, &so->state);
}
/*
* _bt_readpage() -- Load data from current index page into so->currPos
*
- * Caller must have pinned and read-locked so->currPos.buf; the buffer's state
- * is not changed here. Also, currPos.moreLeft and moreRight must be valid;
- * they are updated as appropriate. All other fields of so->currPos are
+ * Caller must have pinned and read-locked pos->buf; the buffer's state
+ * is not changed here. Also, pos->moreLeft and moreRight must be valid;
+ * they are updated as appropriate. All other fields of pos are
* initialized from scratch here.
*
* We scan the current page starting at offnum and moving in the indicated
@@ -1212,9 +1241,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
* Returns true if any matching items found on the page, false if none.
*/
static bool
-_bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
+_bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
@@ -1227,9 +1257,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We must have the buffer pinned and locked, but the usual macro can't be
* used here; this function is what makes it good for currPos.
*/
- Assert(BufferIsValid(so->currPos.buf));
+ Assert(BufferIsValid(pos->buf));
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* allow next page be processed by parallel worker */
@@ -1238,7 +1268,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (ScanDirectionIsForward(dir))
_bt_parallel_release(scan, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1248,30 +1278,30 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We note the buffer's block number so that we can release the pin later.
* This allows us to re-read the buffer if it is needed again for hinting.
*/
- so->currPos.currPage = BufferGetBlockNumber(so->currPos.buf);
+ pos->currPage = BufferGetBlockNumber(pos->buf);
/*
* We save the LSN of the page as we read it, so that we know whether it
* safe to apply LP_DEAD hints to the page later. This allows us to drop
* the pin for MVCC scans, which allows vacuum to avoid blocking.
*/
- so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf);
+ pos->lsn = BufferGetLSNAtomic(pos->buf);
/*
* we must save the page's right-link while scanning it; this tells us
* where to step right to after we're done with these items. There is no
* corresponding need for the left-link, since splits always go right.
*/
- so->currPos.nextPage = opaque->btpo_next;
+ pos->nextPage = opaque->btpo_next;
/* initialize tuple workspace to empty */
- so->currPos.nextTupleOffset = 0;
+ pos->nextTupleOffset = 0;
/*
* Now that the current page has been made consistent, the macro should be
* good.
*/
- Assert(BTScanPosIsPinned(so->currPos));
+ Assert(BTScanPosIsPinned(*pos));
if (ScanDirectionIsForward(dir))
{
@@ -1286,13 +1316,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (itup != NULL)
{
/* tuple passes all scan key conditions, so remember it */
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
itemIndex++;
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreRight = false;
+ pos->moreRight = false;
break;
}
@@ -1300,9 +1330,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex <= MaxIndexTuplesPerPage);
- so->currPos.firstItem = 0;
- so->currPos.lastItem = itemIndex - 1;
- so->currPos.itemIndex = 0;
+ pos->firstItem = 0;
+ pos->lastItem = itemIndex - 1;
+ pos->itemIndex = 0;
}
else
{
@@ -1318,12 +1348,12 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
{
/* tuple passes all scan key conditions, so remember it */
itemIndex--;
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreLeft = false;
+ pos->moreLeft = false;
break;
}
@@ -1331,30 +1361,31 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex >= 0);
- so->currPos.firstItem = itemIndex;
- so->currPos.lastItem = MaxIndexTuplesPerPage - 1;
- so->currPos.itemIndex = MaxIndexTuplesPerPage - 1;
+ pos->firstItem = itemIndex;
+ pos->lastItem = MaxIndexTuplesPerPage - 1;
+ pos->itemIndex = MaxIndexTuplesPerPage - 1;
}
- return (so->currPos.firstItem <= so->currPos.lastItem);
+ return (pos->firstItem <= pos->lastItem);
}
/* Save an index item into so->currPos.items[itemIndex] */
static void
-_bt_saveitem(BTScanOpaque so, int itemIndex,
+_bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup)
{
- BTScanPosItem *currItem = &so->currPos.items[itemIndex];
+ BTScanPosItem *currItem = &state->currPos.items[itemIndex];
currItem->heapTid = itup->t_tid;
currItem->indexOffset = offnum;
- if (so->currTuples)
+ if (state->currTuples)
{
Size itupsz = IndexTupleSize(itup);
- currItem->tupleOffset = so->currPos.nextTupleOffset;
- memcpy(so->currTuples + so->currPos.nextTupleOffset, itup, itupsz);
- so->currPos.nextTupleOffset += MAXALIGN(itupsz);
+ currItem->tupleOffset = state->currPos.nextTupleOffset;
+ memcpy(state->currTuples + state->currPos.nextTupleOffset,
+ itup, itupsz);
+ state->currPos.nextTupleOffset += MAXALIGN(itupsz);
}
}
@@ -1370,35 +1401,36 @@ _bt_saveitem(BTScanOpaque so, int itemIndex,
* to InvalidBuffer. We return true to indicate success.
*/
static bool
-_bt_steppage(IndexScanDesc scan, ScanDirection dir)
+_bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
+ Relation rel = scan->indexRelation;
BlockNumber blkno = InvalidBlockNumber;
bool status = true;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(*currPos));
/* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
+ if (state->numKilled > 0)
+ _bt_killitems(state, rel);
/*
* Before we modify currPos, make a copy of the page data if there was a
* mark position that needs it.
*/
- if (so->markItemIndex >= 0)
+ if (state->markItemIndex >= 0)
{
/* bump pin on current buffer for assignment to mark buffer */
- if (BTScanPosIsPinned(so->currPos))
- IncrBufferRefCount(so->currPos.buf);
- memcpy(&so->markPos, &so->currPos,
+ if (BTScanPosIsPinned(*currPos))
+ IncrBufferRefCount(currPos->buf);
+ memcpy(&state->markPos, currPos,
offsetof(BTScanPosData, items[1]) +
- so->currPos.lastItem * sizeof(BTScanPosItem));
- if (so->markTuples)
- memcpy(so->markTuples, so->currTuples,
- so->currPos.nextTupleOffset);
- so->markPos.itemIndex = so->markItemIndex;
- so->markItemIndex = -1;
+ currPos->lastItem * sizeof(BTScanPosItem));
+ if (state->markTuples)
+ memcpy(state->markTuples, state->currTuples,
+ currPos->nextTupleOffset);
+ state->markPos.itemIndex = state->markItemIndex;
+ state->markItemIndex = -1;
}
if (ScanDirectionIsForward(dir))
@@ -1414,27 +1446,27 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
if (!status)
{
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so use the previously-saved nextPage link. */
- blkno = so->currPos.nextPage;
+ blkno = currPos->nextPage;
}
/* Remember we left a page with data */
- so->currPos.moreLeft = true;
+ currPos->moreLeft = true;
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
}
else
{
/* Remember we left a page with data */
- so->currPos.moreRight = true;
+ currPos->moreRight = true;
if (scan->parallel_scan != NULL)
{
@@ -1443,25 +1475,25 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* ended already, bail out.
*/
status = _bt_parallel_seize(scan, &blkno);
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so just use our own notion of the current page */
- blkno = so->currPos.currPage;
+ blkno = currPos->currPage;
}
}
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, currPos);
return true;
}
@@ -1477,9 +1509,10 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* locks and pins, set so->currPos.buf to InvalidBuffer, and return false.
*/
static bool
-_bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
Relation rel;
Page page;
BTPageOpaque opaque;
@@ -1495,17 +1528,17 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* if we're at end of scan, give up and mark parallel scan as
* done, so that all the workers can finish their scan
*/
- if (blkno == P_NONE || !so->currPos.moreRight)
+ if (blkno == P_NONE || !currPos->moreRight)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* check for interrupts while we're not holding any buffer lock */
CHECK_FOR_INTERRUPTS();
/* step right one page */
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
- page = BufferGetPage(so->currPos.buf);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* check for deleted page */
@@ -1514,7 +1547,7 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
PredicateLockPage(rel, blkno, scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreRight if we can stop */
- if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque)))
+ if (_bt_readpage(scan, state, dir, P_FIRSTDATAKEY(opaque)))
break;
}
else if (scan->parallel_scan != NULL)
@@ -1526,18 +1559,18 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
blkno = opaque->btpo_next;
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
}
}
}
@@ -1547,10 +1580,10 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* Should only happen in parallel cases, when some other backend
* advanced the scan.
*/
- if (so->currPos.currPage != blkno)
+ if (currPos->currPage != blkno)
{
- BTScanPosUnpinIfPinned(so->currPos);
- so->currPos.currPage = blkno;
+ BTScanPosUnpinIfPinned(*currPos);
+ currPos->currPage = blkno;
}
/*
@@ -1575,31 +1608,30 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* is MVCC the page cannot move past the half-dead state to fully
* deleted.
*/
- if (BTScanPosIsPinned(so->currPos))
- LockBuffer(so->currPos.buf, BT_READ);
+ if (BTScanPosIsPinned(*currPos))
+ LockBuffer(currPos->buf, BT_READ);
else
- so->currPos.buf = _bt_getbuf(rel, so->currPos.currPage, BT_READ);
+ currPos->buf = _bt_getbuf(rel, currPos->currPage, BT_READ);
for (;;)
{
/* Done if we know there are no matching keys to the left */
- if (!so->currPos.moreLeft)
+ if (!currPos->moreLeft)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* Step to next physical page */
- so->currPos.buf = _bt_walk_left(rel, so->currPos.buf,
- scan->xs_snapshot);
+ currPos->buf = _bt_walk_left(rel, currPos->buf, scan->xs_snapshot);
/* if we're physically at end of index, return failure */
- if (so->currPos.buf == InvalidBuffer)
+ if (currPos->buf == InvalidBuffer)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1608,21 +1640,21 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* it's not half-dead and contains matching tuples. Else loop back
* and do it all again.
*/
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
{
- PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
+ PredicateLockPage(rel, BufferGetBlockNumber(currPos->buf), scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreLeft if we can stop */
- if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page)))
+ if (_bt_readpage(scan, state, dir, PageGetMaxOffsetNumber(page)))
break;
}
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1633,14 +1665,14 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
}
}
}
@@ -1659,13 +1691,13 @@ _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, &so->state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
return true;
}
@@ -1890,11 +1922,11 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
Page page;
BTPageOpaque opaque;
OffsetNumber start;
- BTScanPosItem *currItem;
/*
* Scan down to the leftmost or rightmost leaf page. This is a simplified
@@ -1910,7 +1942,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* exists.
*/
PredicateLockRelation(rel, scan->xs_snapshot);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1939,36 +1971,15 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
}
/* remember which buffer we have pinned */
- so->currPos.buf = buf;
+ currPos->buf = buf;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, start))
- {
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
- }
- else
- {
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
- }
-
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
+ if (!_bt_load_first_page(scan, &so->state, dir, start))
+ return false;
- return true;
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
}
/*
@@ -1976,19 +1987,19 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* for scan direction
*/
static inline void
-_bt_initialize_more_data(BTScanOpaque so, ScanDirection dir)
+_bt_initialize_more_data(BTScanState state, ScanDirection dir)
{
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ state->currPos.moreLeft = false;
+ state->currPos.moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ state->currPos.moreLeft = true;
+ state->currPos.moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- so->markItemIndex = -1; /* ditto */
+ state->numKilled = 0; /* just paranoia */
+ state->markItemIndex = -1; /* ditto */
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 2c05fb5..e548354 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1741,26 +1741,26 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
* away and the TID was re-used by a completely different heap tuple.
*/
void
-_bt_killitems(IndexScanDesc scan)
+_bt_killitems(BTScanState state, Relation indexRelation)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
OffsetNumber maxoff;
int i;
- int numKilled = so->numKilled;
+ int numKilled = state->numKilled;
bool killedsomething = false;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(state->currPos));
/*
* Always reset the scan state, so we don't look for same items on other
* pages.
*/
- so->numKilled = 0;
+ state->numKilled = 0;
- if (BTScanPosIsPinned(so->currPos))
+ if (BTScanPosIsPinned(*pos))
{
/*
* We have held the pin on this page since we read the index tuples,
@@ -1768,44 +1768,42 @@ _bt_killitems(IndexScanDesc scan)
* re-use of any TID on the page, so there is no need to check the
* LSN.
*/
- LockBuffer(so->currPos.buf, BT_READ);
-
- page = BufferGetPage(so->currPos.buf);
+ LockBuffer(pos->buf, BT_READ);
}
else
{
Buffer buf;
/* Attempt to re-read the buffer, getting pin and lock. */
- buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, BT_READ);
+ buf = _bt_getbuf(indexRelation, pos->currPage, BT_READ);
/* It might not exist anymore; in which case we can't hint it. */
if (!BufferIsValid(buf))
return;
- page = BufferGetPage(buf);
- if (BufferGetLSNAtomic(buf) == so->currPos.lsn)
- so->currPos.buf = buf;
+ if (BufferGetLSNAtomic(buf) == pos->lsn)
+ pos->buf = buf;
else
{
/* Modified while not pinned means hinting is not safe. */
- _bt_relbuf(scan->indexRelation, buf);
+ _bt_relbuf(indexRelation, buf);
return;
}
}
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
for (i = 0; i < numKilled; i++)
{
- int itemIndex = so->killedItems[i];
- BTScanPosItem *kitem = &so->currPos.items[itemIndex];
+ int itemIndex = state->killedItems[i];
+ BTScanPosItem *kitem = &pos->items[itemIndex];
OffsetNumber offnum = kitem->indexOffset;
- Assert(itemIndex >= so->currPos.firstItem &&
- itemIndex <= so->currPos.lastItem);
+ Assert(itemIndex >= pos->firstItem &&
+ itemIndex <= pos->lastItem);
if (offnum < minoff)
continue; /* pure paranoia */
while (offnum <= maxoff)
@@ -1833,10 +1831,10 @@ _bt_killitems(IndexScanDesc scan)
if (killedsomething)
{
opaque->btpo_flags |= BTP_HAS_GARBAGE;
- MarkBufferDirtyHint(so->currPos.buf, true);
+ MarkBufferDirtyHint(pos->buf, true);
}
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(pos->buf, BUFFER_LOCK_UNLOCK);
}
@@ -2214,3 +2212,14 @@ _bt_check_natts(Relation rel, Page page, OffsetNumber offnum)
}
}
+
+/*
+ * _bt_allocate_tuple_workspaces() -- Allocate buffers for saving index tuples
+ * in index-only scans.
+ */
+void
+_bt_allocate_tuple_workspaces(BTScanState state)
+{
+ state->currTuples = (char *) palloc(BLCKSZ * 2);
+ state->markTuples = state->currTuples + BLCKSZ;
+}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 4fb92d6..d78df29 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -433,22 +433,8 @@ typedef struct BTArrayKeyInfo
Datum *elem_values; /* array of num_elems Datums */
} BTArrayKeyInfo;
-typedef struct BTScanOpaqueData
+typedef struct BTScanStateData
{
- /* these fields are set by _bt_preprocess_keys(): */
- bool qual_ok; /* false if qual can never be satisfied */
- int numberOfKeys; /* number of preprocessed scan keys */
- ScanKey keyData; /* array of preprocessed scan keys */
-
- /* workspace for SK_SEARCHARRAY support */
- ScanKey arrayKeyData; /* modified copy of scan->keyData */
- int numArrayKeys; /* number of equality-type array keys (-1 if
- * there are any unsatisfiable array keys) */
- int arrayKeyCount; /* count indicating number of array scan keys
- * processed */
- BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
- MemoryContext arrayContext; /* scan-lifespan context for array data */
-
/* info about killed items if any (killedItems is NULL if never used) */
int *killedItems; /* currPos.items indexes of killed items */
int numKilled; /* number of currently stored items */
@@ -473,6 +459,27 @@ typedef struct BTScanOpaqueData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+} BTScanStateData;
+
+typedef BTScanStateData *BTScanState;
+
+typedef struct BTScanOpaqueData
+{
+ /* these fields are set by _bt_preprocess_keys(): */
+ bool qual_ok; /* false if qual can never be satisfied */
+ int numberOfKeys; /* number of preprocessed scan keys */
+ ScanKey keyData; /* array of preprocessed scan keys */
+
+ /* workspace for SK_SEARCHARRAY support */
+ ScanKey arrayKeyData; /* modified copy of scan->keyData */
+ int numArrayKeys; /* number of equality-type array keys (-1 if
+ * there are any unsatisfiable array keys) */
+ int arrayKeyCount; /* count indicating number of array scan keys
+ * processed */
+ BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
+ MemoryContext arrayContext; /* scan-lifespan context for array data */
+
+ BTScanStateData state;
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -589,7 +596,7 @@ extern void _bt_preprocess_keys(IndexScanDesc scan);
extern IndexTuple _bt_checkkeys(IndexScanDesc scan,
Page page, OffsetNumber offnum,
ScanDirection dir, bool *continuescan);
-extern void _bt_killitems(IndexScanDesc scan);
+extern void _bt_killitems(BTScanState state, Relation indexRelation);
extern BTCycleId _bt_vacuum_cycleid(Relation rel);
extern BTCycleId _bt_start_vacuum(Relation rel);
extern void _bt_end_vacuum(Relation rel);
@@ -602,6 +609,7 @@ extern bool btproperty(Oid index_oid, int attno,
bool *res, bool *isnull);
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
+extern void _bt_allocate_tuple_workspaces(BTScanState state);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 3d3c76d..5c065a5 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -181,6 +181,8 @@ BTScanOpaqueData
BTScanPos
BTScanPosData
BTScanPosItem
+BTScanState
+BTScanStateData
BTShared
BTSortArrayContext
BTSpool
0001-Fix-get_index_column_opclass-v07.patchtext/x-patch; name=0001-Fix-get_index_column_opclass-v07.patchDownload
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index e88c45d..2760748 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3118,9 +3118,6 @@ get_index_column_opclass(Oid index_oid, int attno)
{
HeapTuple tuple;
Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
- Datum datum;
- bool isnull;
- oidvector *indclass;
Oid opclass;
/* First we need to know the column's opclass. */
@@ -3134,12 +3131,23 @@ get_index_column_opclass(Oid index_oid, int attno)
/* caller is supposed to guarantee this */
Assert(attno > 0 && attno <= rd_index->indnatts);
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indclass, &isnull);
- Assert(!isnull);
+ if (attno >= 1 && attno <= rd_index->indnkeyatts)
+ {
+ oidvector *indclass;
+ bool isnull;
+ Datum datum = SysCacheGetAttr(INDEXRELID, tuple,
+ Anum_pg_index_indclass,
+ &isnull);
+
+ Assert(!isnull);
- indclass = ((oidvector *) DatumGetPointer(datum));
- opclass = indclass->values[attno - 1];
+ indclass = ((oidvector *) DatumGetPointer(datum));
+ opclass = indclass->values[attno - 1];
+ }
+ else
+ {
+ opclass = InvalidOid;
+ }
ReleaseSysCache(tuple);
0002-Introduce-ammatchorderby-function-v07.patchtext/x-patch; name=0002-Introduce-ammatchorderby-function-v07.patchDownload
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6458376..9bff793 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,7 +109,6 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BLOOM_NSTRATEGIES;
amroutine->amsupport = BLOOM_NPROC;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -143,6 +142,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 8f008dd..5cd06c7 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -88,7 +88,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -122,6 +121,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index afc2023..0f6714d 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -41,7 +41,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GINNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -75,6 +74,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index b75b3a8..77ca187 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,7 @@
#include "access/gistscan.h"
#include "catalog/pg_collation.h"
#include "miscadmin.h"
+#include "optimizer/paths.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "nodes/execnodes.h"
@@ -64,7 +65,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GISTNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -98,6 +98,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index f1f01a0..bb5c6a1 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -59,7 +59,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = HTMaxStrategyNumber;
amroutine->amsupport = HASHNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -93,6 +92,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 98917de..c56ee5e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -110,7 +110,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BTMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = true;
amroutine->amcanmulticol = true;
@@ -144,6 +143,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 8e63c1f..37de33c 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -22,6 +22,7 @@
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/pg_amop.h"
+#include "optimizer/paths.h"
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
@@ -31,7 +32,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-
/*
* SP-GiST handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -44,7 +44,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = SPGISTNProc;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -78,6 +77,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5d73848..a595c79 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -1097,7 +1097,7 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
*/
IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
- if (!amroutine->amcanorderbyop)
+ if (!amroutine->ammatchorderby)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("access method \"%s\" does not support ordering operators",
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 324356e..805abd2 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -198,7 +198,7 @@ IndexNextWithReorder(IndexScanState *node)
* with just Asserting here because the system will not try to run the
* plan backwards if ExecSupportsBackwardScan() says it won't work.
* Currently, that is guaranteed because no index AMs support both
- * amcanorderbyop and amcanbackward; if any ever do,
+ * ammatchorderby and amcanbackward; if any ever do,
* ExecSupportsBackwardScan() will need to consider indexorderbys
* explicitly.
*/
@@ -1148,7 +1148,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* 5. NullTest ("indexkey IS NULL/IS NOT NULL"). We just fill in the
* ScanKey properly.
*
- * This code is also used to prepare ORDER BY expressions for amcanorderbyop
+ * This code is also used to prepare ORDER BY expressions for ammatchorderby
* indexes. The behavior is exactly the same, except that we have to look up
* the operator differently. Note that only cases 1 and 2 are currently
* possible for ORDER BY.
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 3434219..6cf0b0d 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -17,6 +17,7 @@
#include <math.h>
+#include "access/amapi.h"
#include "access/stratnum.h"
#include "access/sysattr.h"
#include "catalog/pg_am.h"
@@ -182,8 +183,9 @@ static IndexClause *expand_indexqual_rowcompare(RestrictInfo *rinfo,
Oid expr_op,
bool var_on_left);
static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses,
List **orderby_clauses_p,
- List **clause_columns_p);
+ List **orderby_clause_columns_p);
static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
int indexcol, Expr *clause, Oid pk_opfamily);
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
@@ -1001,10 +1003,11 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
orderbyclauses = NIL;
orderbyclausecols = NIL;
}
- else if (index->amcanorderbyop && pathkeys_possibly_useful)
+ else if (index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
+ index_clauses,
&orderbyclauses,
&orderbyclausecols);
if (orderbyclauses)
@@ -2927,6 +2930,113 @@ match_rowcompare_to_indexcol(RestrictInfo *rinfo,
return NULL;
}
+/****************************************************************************
+ * ---- ROUTINES TO CHECK ORDERING OPERATORS ----
+ ****************************************************************************/
+
+/*
+ * Try to match order-by-operator pathkey to the specified index column
+ * (*indexcol_p >= 0) or to all index columns (*indexcol_p < 0).
+ *
+ * Returned matched index clause exression.
+ * Number of matched index column is returned in *indexcol_p.
+ */
+Expr *
+match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey, int *indexcol_p)
+{
+ ListCell *lc;
+
+ /* Pathkey must request default sort order for the target opfamily */
+ if (pathkey->pk_strategy != BTLessStrategyNumber ||
+ pathkey->pk_nulls_first)
+ return NULL;
+
+ /* If eclass is volatile, no hope of using an indexscan */
+ if (pathkey->pk_eclass->ec_has_volatile)
+ return NULL;
+
+ /*
+ * Try to match eclass member expression(s) to index. Note that child EC
+ * members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could be
+ * considered to match more than one pathkey list, which is OK here. See
+ * also get_eclass_for_sort_expr.)
+ */
+ foreach(lc, pathkey->pk_eclass->ec_members)
+ {
+ EquivalenceMember *member = lfirst_node(EquivalenceMember, lc);
+ Expr *expr;
+
+ /* No possibility of match if it references other relations. */
+ if (!bms_equal(member->em_relids, index->rel->relids))
+ continue;
+
+ /* If *indexcol_p is non-negative then try to match only to it. */
+ if (*indexcol_p >= 0)
+ {
+ expr = match_clause_to_ordering_op(index, *indexcol_p,
+ member->em_expr,
+ pathkey->pk_opfamily);
+
+ if (expr)
+ return expr; /* don't want to look at remaining members */
+ }
+ else
+ {
+ int indexcol;
+
+ /*
+ * We allow any column of this index to match each pathkey; they
+ * don't have to match left-to-right as you might expect.
+ */
+ for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
+ {
+ expr = match_clause_to_ordering_op(index, indexcol,
+ member->em_expr,
+ pathkey->pk_opfamily);
+ if (expr)
+ {
+ *indexcol_p = indexcol;
+ return expr; /* don't want to look at remaining members */
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* Try to match order-by-operator pathkeys to any index columns. */
+bool
+match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses, List **orderby_clauses_p,
+ List **orderby_clausecols_p)
+{
+ ListCell *lc;
+
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = lfirst_node(PathKey, lc);
+ Expr *expr;
+ int indexcol = -1; /* match all index columns */
+
+ expr = match_orderbyop_pathkey(index, pathkey, &indexcol);
+
+ /*
+ * Note: for any failure to match, we just return NIL immediately.
+ * There is no value in matching just some of the pathkeys.
+ */
+ if (!expr)
+ return false;
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *orderby_clausecols_p = lappend_int(*orderby_clausecols_p, indexcol);
+ }
+
+ return true; /* success */
+}
+
/*
* expand_indexqual_rowcompare --- expand a single indexqual condition
* that is a RowCompareExpr
@@ -3183,92 +3293,31 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
*/
static void
match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses,
List **orderby_clauses_p,
- List **clause_columns_p)
+ List **orderby_clause_columns_p)
{
List *orderby_clauses = NIL;
- List *clause_columns = NIL;
- ListCell *lc1;
-
- *orderby_clauses_p = NIL; /* set default results */
- *clause_columns_p = NIL;
+ List *orderby_clause_columns = NIL;
+ ammatchorderby_function ammatchorderby =
+ (ammatchorderby_function) index->ammatchorderby;
- /* Only indexes with the amcanorderbyop property are interesting here */
- if (!index->amcanorderbyop)
- return;
-
- foreach(lc1, pathkeys)
+ /* Only indexes with the ammatchorderby function are interesting here */
+ if (ammatchorderby &&
+ ammatchorderby(index, pathkeys, index_clauses,
+ &orderby_clauses, &orderby_clause_columns))
{
- PathKey *pathkey = (PathKey *) lfirst(lc1);
- bool found = false;
- ListCell *lc2;
-
- /*
- * Note: for any failure to match, we just return NIL immediately.
- * There is no value in matching just some of the pathkeys.
- */
-
- /* Pathkey must request default sort order for the target opfamily */
- if (pathkey->pk_strategy != BTLessStrategyNumber ||
- pathkey->pk_nulls_first)
- return;
-
- /* If eclass is volatile, no hope of using an indexscan */
- if (pathkey->pk_eclass->ec_has_volatile)
- return;
-
- /*
- * Try to match eclass member expression(s) to index. Note that child
- * EC members are considered, but only when they belong to the target
- * relation. (Unlike regular members, the same expression could be a
- * child member of more than one EC. Therefore, the same index could
- * be considered to match more than one pathkey list, which is OK
- * here. See also get_eclass_for_sort_expr.)
- */
- foreach(lc2, pathkey->pk_eclass->ec_members)
- {
- EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
- int indexcol;
-
- /* No possibility of match if it references other relations */
- if (!bms_equal(member->em_relids, index->rel->relids))
- continue;
+ Assert(list_length(pathkeys) == list_length(orderby_clauses));
+ Assert(list_length(pathkeys) == list_length(orderby_clause_columns));
- /*
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, and it doesn't matter for SP-GiST because
- * that doesn't handle multiple columns anyway, and no other
- * existing AMs support amcanorderbyop. We might need different
- * logic in future for other implementations.
- */
- for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
- {
- Expr *expr;
-
- expr = match_clause_to_ordering_op(index,
- indexcol,
- member->em_expr,
- pathkey->pk_opfamily);
- if (expr)
- {
- orderby_clauses = lappend(orderby_clauses, expr);
- clause_columns = lappend_int(clause_columns, indexcol);
- found = true;
- break;
- }
- }
-
- if (found) /* don't want to look at remaining members */
- break;
- }
-
- if (!found) /* fail if no match for this pathkey */
- return;
+ *orderby_clauses_p = orderby_clauses; /* success! */
+ *orderby_clause_columns_p = orderby_clause_columns;
+ }
+ else
+ {
+ *orderby_clauses_p = NIL; /* set default results */
+ *orderby_clause_columns_p = NIL;
}
-
- *orderby_clauses_p = orderby_clauses; /* success! */
- *clause_columns_p = clause_columns;
}
/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index d6dc83c..2d81fd1 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -267,7 +267,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* We copy just the fields we need, not all of rd_indam */
amroutine = indexRelation->rd_indam;
- info->amcanorderbyop = amroutine->amcanorderbyop;
+ info->ammatchorderby = amroutine->ammatchorderby;
info->amoptionalkey = amroutine->amoptionalkey;
info->amsearcharray = amroutine->amsearcharray;
info->amsearchnulls = amroutine->amsearchnulls;
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 060ffe5..3907065 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -298,7 +298,7 @@ indexam_property(FunctionCallInfo fcinfo,
* a nonkey column, and null otherwise (meaning we don't
* know).
*/
- if (!iskey || !routine->amcanorderbyop)
+ if (!iskey || !routine->ammatchorderby)
{
res = false;
isnull = false;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 653ddc9..d8fb7d3 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -21,6 +21,9 @@
*/
struct PlannerInfo;
struct IndexPath;
+struct IndexOptInfo;
+struct PathKey;
+struct Expr;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
@@ -140,6 +143,13 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* does AM support ORDER BY result of an operator on indexed column? */
+typedef bool (*ammatchorderby_function) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List *index_clauses,
+ List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -170,8 +180,6 @@ typedef struct IndexAmRoutine
uint16 amsupport;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
- /* does AM support ORDER BY result of an operator on indexed column? */
- bool amcanorderbyop;
/* does AM support backward scanning? */
bool amcanbackward;
/* does AM support UNIQUE indexes? */
@@ -221,6 +229,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ ammatchorderby_function ammatchorderby; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a008ae0..4680cb8 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -814,7 +814,6 @@ struct IndexOptInfo
bool hypothetical; /* true if index doesn't really exist */
/* Remaining fields are copied from the index AM's API struct: */
- bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
@@ -823,6 +822,12 @@ struct IndexOptInfo
bool amcanparallel; /* does AM support parallel scan? */
/* Rather than include amapi.h here, we declare amcostestimate like this */
void (*amcostestimate) (); /* AM's cost estimator */
+ /* AM order-by match function */
+ bool (*ammatchorderby) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List *index_clause_columns,
+ List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
};
/*
@@ -1133,7 +1138,7 @@ typedef struct Path
* An empty list implies a full index scan.
*
* 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
- * been found to be usable as ordering operators for an amcanorderbyop index.
+ * been found to be usable as ordering operators for an ammatchorderby index.
* The list must match the path's pathkeys, ie, one expression per pathkey
* in the same order. These are not RestrictInfos, just bare expressions,
* since they generally won't yield booleans. It's guaranteed that each
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6d087c2..bd6ce97 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -388,7 +388,7 @@ typedef struct SampleScan
* indexorderbyops is a list of the OIDs of the operators used to sort the
* ORDER BY expressions. This is used together with indexorderbyorig to
* recheck ordering at run time. (Note that indexorderby, indexorderbyorig,
- * and indexorderbyops are used for amcanorderbyop cases, not amcanorder.)
+ * and indexorderbyops are used for ammatchorderby cases, not amcanorder.)
*
* indexorderdir specifies the scan ordering, for indexscans on amcanorder
* indexes (for other indexes it should be "don't care").
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 040335a..e9f4f75 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -79,6 +79,11 @@ extern bool indexcol_is_bool_constant_for_query(IndexOptInfo *index,
extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index);
extern void check_index_predicates(PlannerInfo *root, RelOptInfo *rel);
+extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
+ int *indexcol_p);
+extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses, List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
/*
* tidpath.h
0004-Add-kNN-support-to-btree-v07.patchtext/x-patch; name=0004-Add-kNN-support-to-btree-v07.patchDownload
diff --git a/doc/src/sgml/btree.sgml b/doc/src/sgml/btree.sgml
index 996932e..b94be7a 100644
--- a/doc/src/sgml/btree.sgml
+++ b/doc/src/sgml/btree.sgml
@@ -200,6 +200,53 @@
planner relies on them for optimization purposes.
</para>
+ <para>
+ To implement the distance ordered (nearest-neighbor) search, we only need
+ to define a distance operator (usually it called
+ <literal><-></literal>) with a correpsonding operator family for
+ distance comparison in the index's operator class. These operators must
+ satisfy the following assumptions for all non-null values
+ <replaceable>A</replaceable>, <replaceable>B</replaceable>,
+ <replaceable>C</replaceable> of the datatype:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <replaceable>A</replaceable> <literal><-></literal>
+ <replaceable>B</replaceable> <literal>=</literal>
+ <replaceable>B</replaceable> <literal><-></literal>
+ <replaceable>A</replaceable>
+ (<firstterm>symmetric law</firstterm>)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if <replaceable>A</replaceable> <literal>=</literal>
+ <replaceable>B</replaceable>, then <replaceable>A</replaceable>
+ <literal><-></literal> <replaceable>C</replaceable>
+ <literal>=</literal> <replaceable>B</replaceable>
+ <literal><-></literal> <replaceable>C</replaceable>
+ (<firstterm>distance equivalence</firstterm>)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if (<replaceable>A</replaceable> <literal><=</literal>
+ <replaceable>B</replaceable> and <replaceable>B</replaceable>
+ <literal><=</literal> <replaceable>C</replaceable>) or
+ (<replaceable>A</replaceable> <literal>>=</literal>
+ <replaceable>B</replaceable> and <replaceable>B</replaceable>
+ <literal>>=</literal> <replaceable>C</replaceable>),
+ then <replaceable>A</replaceable> <literal><-></literal>
+ <replaceable>B</replaceable> <literal><=</literal>
+ <replaceable>A</replaceable> <literal><-></literal>
+ <replaceable>C</replaceable>
+ (<firstterm>monotonicity</firstterm>)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
</sect1>
<sect1 id="btree-support-funcs">
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 46f427b..1998171 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -175,6 +175,17 @@ CREATE INDEX test1_id_index ON test1 (id);
</para>
<para>
+ B-tree indexes are also capable of optimizing <quote>nearest-neighbor</quote>
+ searches, such as
+<programlisting><![CDATA[
+SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
+]]>
+</programlisting>
+ which finds the ten events closest to a given target date. The ability
+ to do this is again dependent on the particular operator class being used.
+ </para>
+
+ <para>
<indexterm>
<primary>index</primary>
<secondary>hash</secondary>
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..93094bc 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -1242,7 +1242,8 @@ SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING)
<title>Ordering Operators</title>
<para>
- Some index access methods (currently, only GiST and SP-GiST) support the concept of
+ Some index access methods (currently, only B-tree, GiST and SP-GiST)
+ support the concept of
<firstterm>ordering operators</firstterm>. What we have been discussing so far
are <firstterm>search operators</firstterm>. A search operator is one for which
the index can be searched to find all rows satisfying
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
index 3680e69..3f7e1b1 100644
--- a/src/backend/access/nbtree/README
+++ b/src/backend/access/nbtree/README
@@ -659,3 +659,20 @@ routines must treat it accordingly. The actual key stored in the
item is irrelevant, and need not be stored at all. This arrangement
corresponds to the fact that an L&Y non-leaf page has one more pointer
than key.
+
+Nearest-neighbor search
+-----------------------
+
+There is a special scan strategy for nearest-neighbor (kNN) search,
+that is used in queries with ORDER BY distance clauses like this:
+SELECT * FROM tab WHERE col > const1 ORDER BY col <-> const2 LIMIT k.
+But, unlike GiST, B-tree supports only a one ordering operator on the
+first index column.
+
+At the beginning of kNN scan, we need to determine which strategy we
+will use --- a special bidirectional or a ordinary unidirectional.
+If the point from which we measure the distance falls into the scan range,
+we use bidirectional scan starting from this point, else we use simple
+unidirectional scan in the right direction. Algorithm of a bidirectional
+scan is very simple: at each step we advancing scan in that direction,
+which has the nearest point.
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index bf6a6c6..fb413e7 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -25,6 +25,9 @@
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
+#include "nodes/pathnodes.h"
+#include "nodes/primnodes.h"
+#include "optimizer/paths.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "storage/condition_variable.h"
@@ -33,7 +36,9 @@
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
+#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -79,6 +84,7 @@ typedef enum
typedef struct BTParallelScanDescData
{
BlockNumber btps_scanPage; /* latest or next page to be scanned */
+ BlockNumber btps_knnScanPage; /* secondary kNN page to be scanned */
BTPS_State btps_pageStatus; /* indicates whether next page is
* available for scan. see above for
* possible states of parallel scan. */
@@ -97,6 +103,10 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
+static bool btmatchorderby(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses, List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
+
/*
* Btree handler function: return IndexAmRoutine with access method parameters
@@ -107,7 +117,7 @@ bthandler(PG_FUNCTION_ARGS)
{
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
- amroutine->amstrategies = BTMaxStrategyNumber;
+ amroutine->amstrategies = BtreeMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
amroutine->amcanbackward = true;
@@ -143,7 +153,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
- amroutine->ammatchorderby = NULL;
+ amroutine->ammatchorderby = btmatchorderby;
PG_RETURN_POINTER(amroutine);
}
@@ -215,23 +225,30 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTScanState state = &so->state;
+ ScanDirection arraydir =
+ scan->numberOfOrderBys > 0 ? ForwardScanDirection : dir;
bool res;
/* btree indexes are never lossy */
scan->xs_recheck = false;
+ scan->xs_recheckorderby = false;
+
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
/*
* If we have any array keys, initialize them during first call for a
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
return false;
- _bt_start_array_keys(scan, dir);
+ _bt_start_array_keys(scan, arraydir);
}
/* This loop handles advancing to the next array elements, if any */
@@ -242,7 +259,8 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(state->currPos))
+ if (!BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
res = _bt_first(scan, dir);
else
{
@@ -277,7 +295,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
if (res)
break;
/* ... otherwise see if we have more array keys to deal with */
- } while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
+ } while (so->numArrayKeys && _bt_advance_array_keys(scan, arraydir));
return res;
}
@@ -350,9 +368,6 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
IndexScanDesc scan;
BTScanOpaque so;
- /* no order by operators allowed */
- Assert(norderbys == 0);
-
/* get the scan */
scan = RelationGetIndexScan(rel, nkeys, norderbys);
@@ -379,6 +394,9 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
so->state.currTuples = so->state.markTuples = NULL;
+ so->knnState = NULL;
+ so->distanceTypeByVal = true;
+ so->scanDirection = NoMovementScanDirection;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -408,6 +426,8 @@ _bt_release_current_position(BTScanState state, Relation indexRelation,
static void
_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
/* No need to invalidate positions, if the RAM is about to be freed. */
_bt_release_current_position(state, scan->indexRelation, !free);
@@ -424,6 +444,17 @@ _bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
}
else
BTScanPosInvalidate(state->markPos);
+
+ if (!so->distanceTypeByVal)
+ {
+ if (DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+ state->currDistance = PointerGetDatum(NULL);
+
+ if (DatumGetPointer(state->markDistance))
+ pfree(DatumGetPointer(state->markDistance));
+ state->markDistance = PointerGetDatum(NULL);
+ }
}
/*
@@ -438,6 +469,13 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
_bt_release_scan_state(scan, state, false);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ so->knnState = NULL;
+ }
+
so->arrayKeyCount = 0;
/*
@@ -469,6 +507,14 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
scan->numberOfKeys * sizeof(ScanKeyData));
so->numberOfKeys = 0; /* until _bt_preprocess_keys sets it */
+ if (orderbys && scan->numberOfOrderBys > 0)
+ memmove(scan->orderByData,
+ orderbys,
+ scan->numberOfOrderBys * sizeof(ScanKeyData));
+
+ so->scanDirection = NoMovementScanDirection;
+ so->distanceTypeByVal = true;
+
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
_bt_preprocess_array_keys(scan);
}
@@ -483,6 +529,12 @@ btendscan(IndexScanDesc scan)
_bt_release_scan_state(scan, &so->state, true);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ }
+
/* Release storage */
if (so->keyData != NULL)
pfree(so->keyData);
@@ -494,7 +546,7 @@ btendscan(IndexScanDesc scan)
}
static void
-_bt_mark_current_position(BTScanState state)
+_bt_mark_current_position(BTScanOpaque so, BTScanState state)
{
/* There may be an old mark with a pin (but no lock). */
BTScanPosUnpinIfPinned(state->markPos);
@@ -512,6 +564,21 @@ _bt_mark_current_position(BTScanState state)
BTScanPosInvalidate(state->markPos);
state->markItemIndex = -1;
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->markDistance));
+
+ state->markIsNull = !BTScanPosIsValid(state->currPos) ||
+ state->currIsNull;
+
+ state->markDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->currDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -522,7 +589,13 @@ btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_mark_current_position(&so->state);
+ _bt_mark_current_position(so, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_mark_current_position(so, so->knnState);
+ so->markRightIsNearest = so->currRightIsNearest;
+ }
/* Also record the current positions of any array keys */
if (so->numArrayKeys)
@@ -532,6 +605,8 @@ btmarkpos(IndexScanDesc scan)
static void
_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
if (state->markItemIndex >= 0)
{
/*
@@ -567,6 +642,19 @@ _bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
state->markPos.nextTupleOffset);
}
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currIsNull = state->markIsNull;
+ state->currDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->markDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -588,6 +676,7 @@ btinitparallelscan(void *target)
SpinLockInit(&bt_target->btps_mutex);
bt_target->btps_scanPage = InvalidBlockNumber;
+ bt_target->btps_knnScanPage = InvalidBlockNumber;
bt_target->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
bt_target->btps_arrayKeyCount = 0;
ConditionVariableInit(&bt_target->btps_cv);
@@ -614,6 +703,7 @@ btparallelrescan(IndexScanDesc scan)
*/
SpinLockAcquire(&btscan->btps_mutex);
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount = 0;
SpinLockRelease(&btscan->btps_mutex);
@@ -638,7 +728,7 @@ btparallelrescan(IndexScanDesc scan)
* Callers should ignore the value of pageno if the return value is false.
*/
bool
-_bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
+_bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTPS_State pageStatus;
@@ -646,12 +736,17 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
bool status = true;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
*pageno = P_NONE;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ scanPage = state == &so->state
+ ? &btscan->btps_scanPage
+ : &btscan->btps_knnScanPage;
+
while (1)
{
SpinLockAcquire(&btscan->btps_mutex);
@@ -677,7 +772,7 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* of advancing it to a new page!
*/
btscan->btps_pageStatus = BTPARALLEL_ADVANCING;
- *pageno = btscan->btps_scanPage;
+ *pageno = *scanPage;
exit_loop = true;
}
SpinLockRelease(&btscan->btps_mutex);
@@ -696,19 +791,42 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* can now begin advancing the scan.
*/
void
-_bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
+_bt_parallel_release(IndexScanDesc scan, BTScanState state,
+ BlockNumber scan_page)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
+ bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
SpinLockAcquire(&btscan->btps_mutex);
- btscan->btps_scanPage = scan_page;
- btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ *scanPage = scan_page;
+ /* switch to idle state only if both KNN pages are initialized */
+ if (!knnScan || *otherScanPage != InvalidBlockNumber)
+ {
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ status_changed = true;
+ }
SpinLockRelease(&btscan->btps_mutex);
- ConditionVariableSignal(&btscan->btps_cv);
+
+ if (status_changed)
+ ConditionVariableSignal(&btscan->btps_cv);
}
/*
@@ -719,12 +837,15 @@ _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
* advance to the next page.
*/
void
-_bt_parallel_done(IndexScanDesc scan)
+_bt_parallel_done(IndexScanDesc scan, BTScanState state)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
/* Do nothing, for non-parallel scans */
if (parallel_scan == NULL)
@@ -733,18 +854,41 @@ _bt_parallel_done(IndexScanDesc scan)
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
+
/*
* Mark the parallel scan as done for this combination of scan keys,
* unless some other process already did so. See also
* _bt_advance_array_keys.
*/
SpinLockAcquire(&btscan->btps_mutex);
- if (so->arrayKeyCount >= btscan->btps_arrayKeyCount &&
- btscan->btps_pageStatus != BTPARALLEL_DONE)
+
+ Assert(btscan->btps_pageStatus == BTPARALLEL_ADVANCING);
+
+ if (so->arrayKeyCount >= btscan->btps_arrayKeyCount)
{
- btscan->btps_pageStatus = BTPARALLEL_DONE;
+ *scanPage = P_NONE;
status_changed = true;
+
+ /* switch to "done" state only if both KNN scans are done */
+ if (!knnScan || *otherScanPage == P_NONE)
+ btscan->btps_pageStatus = BTPARALLEL_DONE;
+ /* else switch to "idle" state only if both KNN scans are initialized */
+ else if (*otherScanPage != InvalidBlockNumber)
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ else
+ status_changed = false;
}
+
SpinLockRelease(&btscan->btps_mutex);
/* wake up all the workers associated with this parallel scan */
@@ -774,6 +918,7 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
if (btscan->btps_pageStatus == BTPARALLEL_DONE)
{
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount++;
}
@@ -859,6 +1004,12 @@ btrestrpos(IndexScanDesc scan)
_bt_restore_array_keys(scan);
_bt_restore_marked_position(scan, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_restore_marked_position(scan, so->knnState);
+ so->currRightIsNearest = so->markRightIsNearest;
+ }
}
/*
@@ -1394,3 +1545,91 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+/*
+ * btmatchorderby() -- Check whether KNN-search strategy is applicable to
+ * the given ORDER BY distance operator.
+ */
+static bool
+btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
+ List **orderby_clauses_p, List **orderby_clausecols_p)
+{
+ Expr *expr;
+ ListCell *lc;
+ int indexcol;
+ int num_eq_cols = 0;
+
+ /* only one ORDER BY clause is supported */
+ if (list_length(pathkeys) != 1)
+ return false;
+
+ /*
+ * Compute a number of leading consequent index columns with equality
+ * restriction clauses.
+ */
+ foreach(lc, index_clauses)
+ {
+ IndexClause *iclause = lfirst_node(IndexClause, lc);
+ ListCell *lcq;
+
+ indexcol = iclause->indexcol;
+
+ if (indexcol > num_eq_cols)
+ /* Sequence of equality-restricted columns is broken. */
+ break;
+
+ foreach(lcq, iclause->indexquals)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcq);
+ Expr *clause = rinfo->clause;
+ Oid opno;
+ StrategyNumber strat;
+
+ if (!clause)
+ continue;
+
+ if (IsA(clause, OpExpr))
+ {
+ OpExpr *opexpr = (OpExpr *) clause;
+
+ opno = opexpr->opno;
+ }
+ else
+ {
+ /* Skip unsupported expression */
+ continue;
+ }
+
+ /* Check if the operator is btree equality operator. */
+ strat = get_op_opfamily_strategy(opno, index->opfamily[indexcol]);
+
+ if (strat == BTEqualStrategyNumber)
+ num_eq_cols = indexcol + 1;
+ }
+ }
+
+ /*
+ * If there are no equality columns try to match only the first column,
+ * otherwise try all columns.
+ */
+ indexcol = num_eq_cols ? -1 : 0;
+
+ expr = match_orderbyop_pathkey(index, castNode(PathKey, linitial(pathkeys)),
+ &indexcol);
+
+ if (!expr)
+ return false;
+
+ /*
+ * ORDER BY distance is supported only for the first index column or if
+ * all previous columns have equality restrictions.
+ */
+ if (indexcol > num_eq_cols)
+ return false;
+
+ /* Return first ORDER BY clause's expression and column. */
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *orderby_clausecols_p = lappend_int(*orderby_clausecols_p, indexcol);
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index cbd72bd..28f94e7 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -31,12 +31,14 @@ static void _bt_saveitem(BTScanState state, int itemIndex,
static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
BlockNumber blkno, ScanDirection dir);
-static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
- ScanDirection dir);
+static bool _bt_parallel_readpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
+static BTScanState _bt_alloc_knn_scan(IndexScanDesc scan);
+static bool _bt_start_knn_scan(IndexScanDesc scan, bool left, bool right);
/*
@@ -597,6 +599,157 @@ _bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
}
/*
+ * _bt_calc_current_dist() -- Calculate distance from the current item
+ * of the scan state to the target order-by ScanKey argument.
+ */
+static void
+_bt_calc_current_dist(IndexScanDesc scan, BTScanState state)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+ IndexTuple itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+ ScanKey scankey = &scan->orderByData[0];
+ Datum value;
+
+ value = index_getattr(itup, scankey->sk_attno, scan->xs_itupdesc,
+ &state->currIsNull);
+
+ if (state->currIsNull)
+ return; /* NULL distance */
+
+ value = FunctionCall2Coll(&scankey->sk_func,
+ scankey->sk_collation,
+ value,
+ scankey->sk_argument);
+
+ /* free previous distance value for by-ref types */
+ if (!so->distanceTypeByVal && DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currDistance = value;
+}
+
+/*
+ * _bt_compare_current_dist() -- Compare current distances of the left and right scan states.
+ *
+ * NULL distances are considered to be greater than any non-NULL distances.
+ *
+ * Returns true if right distance is lesser than left, otherwise false.
+ */
+static bool
+_bt_compare_current_dist(BTScanOpaque so, BTScanState rstate, BTScanState lstate)
+{
+ if (lstate->currIsNull)
+ return true; /* non-NULL < NULL */
+
+ if (rstate->currIsNull)
+ return false; /* NULL > non-NULL */
+
+ return DatumGetBool(FunctionCall2Coll(&so->distanceCmpProc,
+ InvalidOid, /* XXX collation for distance comparison */
+ rstate->currDistance,
+ lstate->currDistance));
+}
+
+/*
+ * _bt_alloc_knn_scan() -- Allocate additional backward scan state for KNN.
+ */
+static BTScanState
+_bt_alloc_knn_scan(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState lstate = (BTScanState) palloc(sizeof(BTScanStateData));
+
+ _bt_allocate_tuple_workspaces(lstate);
+
+ if (!scan->xs_want_itup)
+ {
+ /* We need to request index tuples for distance comparison. */
+ scan->xs_want_itup = true;
+ _bt_allocate_tuple_workspaces(&so->state);
+ }
+
+ BTScanPosInvalidate(lstate->currPos);
+ lstate->currPos.moreLeft = false;
+ lstate->currPos.moreRight = false;
+ BTScanPosInvalidate(lstate->markPos);
+ lstate->markItemIndex = -1;
+ lstate->killedItems = NULL;
+ lstate->numKilled = 0;
+ lstate->currDistance = PointerGetDatum(NULL);
+ lstate->markDistance = PointerGetDatum(NULL);
+
+ return so->knnState = lstate;
+}
+
+static bool
+_bt_start_knn_scan(IndexScanDesc scan, bool left, bool right)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+
+ if (!left && !right)
+ return false; /* empty result */
+
+ rstate = &so->state;
+ lstate = so->knnState;
+
+ if (left && right)
+ {
+ /*
+ * We have found items in both scan directions,
+ * determine nearest item to return.
+ */
+ _bt_calc_current_dist(scan, rstate);
+ _bt_calc_current_dist(scan, lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+
+ /* Reset right flag if the left item is nearer. */
+ right = so->currRightIsNearest;
+ }
+
+ /* Return current item of the selected scan direction. */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
+ * _bt_init_knn_scan() -- Init additional scan state for KNN search.
+ *
+ * Caller must pin and read-lock scan->state.currPos.buf buffer.
+ *
+ * If empty result was found returned false.
+ * Otherwise prepared current item, and returned true.
+ */
+static bool
+_bt_init_knn_scan(IndexScanDesc scan, OffsetNumber offnum)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+ Buffer buf = rstate->currPos.buf;
+ bool left,
+ right;
+
+ lstate = _bt_alloc_knn_scan(scan);
+
+ /* Bump pin and lock count before BTScanPosData copying. */
+ IncrBufferRefCount(buf);
+ LockBuffer(buf, BT_READ);
+
+ memcpy(&lstate->currPos, &rstate->currPos, sizeof(BTScanPosData));
+ lstate->currPos.moreLeft = true;
+ lstate->currPos.moreRight = false;
+
+ /* Load first pages from the both scans. */
+ right = _bt_load_first_page(scan, rstate, ForwardScanDirection, offnum);
+ left = _bt_load_first_page(scan, lstate, BackwardScanDirection,
+ OffsetNumberPrev(offnum));
+
+ return _bt_start_knn_scan(scan, left, right);
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -654,6 +807,15 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!so->qual_ok)
return false;
+ if (scan->numberOfOrderBys > 0)
+ {
+ if (so->useBidirectionalKnnScan)
+ _bt_init_distance_comparison(scan);
+ else if (so->scanDirection != NoMovementScanDirection)
+ /* use selected KNN scan direction */
+ dir = so->scanDirection;
+ }
+
/*
* For parallel scans, get the starting page from shared state. If the
* scan has not started, proceed to find out first leaf page in the usual
@@ -662,19 +824,50 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, &so->state, &blkno);
if (!status)
return false;
- else if (blkno == P_NONE)
- {
- _bt_parallel_done(scan);
- return false;
- }
else if (blkno != InvalidBlockNumber)
{
- if (!_bt_parallel_readpage(scan, blkno, dir))
- return false;
- goto readcomplete;
+ bool knn = so->useBidirectionalKnnScan;
+ bool right;
+ bool left;
+
+ if (knn)
+ _bt_alloc_knn_scan(scan);
+
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, &so->state);
+ right = false;
+ }
+ else
+ right = _bt_parallel_readpage(scan, &so->state, blkno,
+ knn ? ForwardScanDirection : dir);
+
+ if (!knn)
+ return right && _bt_return_current_item(scan, &so->state);
+
+ /* seize additional backward KNN scan */
+ left = _bt_parallel_seize(scan, so->knnState, &blkno);
+
+ if (left)
+ {
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, so->knnState);
+ left = false;
+ }
+ else
+ {
+ /* backward scan should be already initialized */
+ Assert(blkno != InvalidBlockNumber);
+ left = _bt_parallel_readpage(scan, so->knnState, blkno,
+ BackwardScanDirection);
+ }
+ }
+
+ return _bt_start_knn_scan(scan, left, right);
}
}
@@ -724,7 +917,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* storing their addresses into the local startKeys[] array.
*----------
*/
+
strat_total = BTEqualStrategyNumber;
+
if (so->numberOfKeys > 0)
{
AttrNumber curattr;
@@ -749,6 +944,10 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*/
for (cur = so->keyData, i = 0;; cur++, i++)
{
+ if (so->useBidirectionalKnnScan &&
+ curattr >= scan->orderByData->sk_attno)
+ break;
+
if (i >= so->numberOfKeys || cur->sk_attno != curattr)
{
/*
@@ -851,6 +1050,16 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
}
}
+ if (so->useBidirectionalKnnScan)
+ {
+ Assert(strat_total == BTEqualStrategyNumber);
+ strat_total = BtreeKNNSearchStrategyNumber;
+
+ (void) _bt_init_knn_start_keys(scan, &startKeys[keysCount],
+ ¬nullkeys[keysCount]);
+ keysCount++;
+ }
+
/*
* If we found no usable boundary keys, we have to start from one end of
* the tree. Walk down that edge to the first or last key, and scan from
@@ -865,7 +1074,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!match)
{
/* No match, so mark (parallel) scan finished */
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
}
return match;
@@ -900,7 +1109,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(subkey->sk_flags & SK_ROW_MEMBER);
if (subkey->sk_flags & SK_ISNULL)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
return false;
}
memcpy(scankeys + i, subkey, sizeof(ScanKeyData));
@@ -1080,6 +1289,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
break;
case BTGreaterEqualStrategyNumber:
+ case BtreeKNNSearchStrategyNumber:
/*
* Find first item >= scankey. (This is only used for forward
@@ -1127,7 +1337,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* mark parallel scan as done, so that all the workers can finish
* their scan
*/
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
BTScanPosInvalidate(*currPos);
return false;
@@ -1166,17 +1376,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(!BTScanPosIsValid(*currPos));
currPos->buf = buf;
+ if (strat_total == BtreeKNNSearchStrategyNumber)
+ return _bt_init_knn_scan(scan, offnum);
+
if (!_bt_load_first_page(scan, &so->state, dir, offnum))
- return false;
+ return false; /* empty result */
-readcomplete:
/* OK, currPos->itemIndex says what to return */
return _bt_return_current_item(scan, &so->state);
}
/*
- * Advance to next tuple on current page; or if there's no more,
- * try to step to the next page with data.
+ * _bt_next_item() -- Advance to next tuple on current page;
+ * or if there's no more, try to step to the next page with data.
+ *
+ * If there are no more matching records in the given direction
*/
static bool
_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
@@ -1196,6 +1410,51 @@ _bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
}
/*
+ * _bt_next_nearest() -- Return next nearest item from bidirectional KNN scan.
+ */
+static bool
+_bt_next_nearest(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state;
+ BTScanState lstate = so->knnState;
+ bool right = BTScanPosIsValid(rstate->currPos);
+ bool left = BTScanPosIsValid(lstate->currPos);
+ bool advanceRight;
+
+ if (right && left)
+ advanceRight = so->currRightIsNearest;
+ else if (right)
+ advanceRight = true;
+ else if (left)
+ advanceRight = false;
+ else
+ return false; /* end of the scan */
+
+ if (advanceRight)
+ right = _bt_next_item(scan, rstate, ForwardScanDirection);
+ else
+ left = _bt_next_item(scan, lstate, BackwardScanDirection);
+
+ if (!left && !right)
+ return false; /* end of the scan */
+
+ if (left && right)
+ {
+ /*
+ * If there are items in both scans we must recalculate distance
+ * in the advanced scan.
+ */
+ _bt_calc_current_dist(scan, advanceRight ? rstate : lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+ right = so->currRightIsNearest;
+ }
+
+ /* return nearest item */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_next() -- Get the next item in a scan.
*
* On entry, so->currPos describes the current page, which may be pinned
@@ -1214,6 +1473,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ if (so->knnState)
+ /* return next neareset item from KNN scan */
+ return _bt_next_nearest(scan);
+
if (!_bt_next_item(scan, &so->state, dir))
return false;
@@ -1266,9 +1529,9 @@ _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
if (scan->parallel_scan)
{
if (ScanDirectionIsForward(dir))
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1442,7 +1705,7 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the next block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
/* release the previous buffer, if pinned */
@@ -1474,13 +1737,19 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the current block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
BTScanPosInvalidate(*currPos);
return false;
}
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, state);
+ BTScanPosInvalidate(*currPos);
+ return false;
+ }
}
else
{
@@ -1530,7 +1799,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
*/
if (blkno == P_NONE || !currPos->moreRight)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1553,14 +1822,14 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
}
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1619,7 +1888,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (!currPos->moreLeft)
{
_bt_relbuf(rel, currPos->buf);
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1630,7 +1899,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
/* if we're physically at end of index, return failure */
if (currPos->buf == InvalidBuffer)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1654,7 +1923,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1666,7 +1935,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1687,17 +1956,16 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
* indicate success.
*/
static bool
-_bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_parallel_readpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ _bt_initialize_more_data(state, dir);
- _bt_initialize_more_data(&so->state, dir);
-
- if (!_bt_readnextpage(scan, &so->state, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
return true;
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index e548354..5b9294b 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -20,16 +20,21 @@
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "catalog/pg_amop.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
typedef struct BTSortArrayContext
{
FmgrInfo flinfo;
+ FmgrInfo distflinfo;
+ FmgrInfo distcmpflinfo;
+ ScanKey distkey;
Oid collation;
bool reverse;
} BTSortArrayContext;
@@ -49,6 +54,11 @@ static void _bt_mark_scankey_required(ScanKey skey);
static bool _bt_check_rowcompare(ScanKey skey,
IndexTuple tuple, TupleDesc tupdesc,
ScanDirection dir, bool *continuescan);
+static inline StrategyNumber _bt_select_knn_strategy_for_key(IndexScanDesc scan,
+ ScanKey cond);
+static void _bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval);
+
/*
@@ -445,6 +455,7 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
{
Relation rel = scan->indexRelation;
Oid elemtype;
+ Oid opfamily;
RegProcedure cmp_proc;
BTSortArrayContext cxt;
int last_non_dup;
@@ -462,6 +473,54 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
if (elemtype == InvalidOid)
elemtype = rel->rd_opcintype[skey->sk_attno - 1];
+ opfamily = rel->rd_opfamily[skey->sk_attno - 1];
+
+ if (scan->numberOfOrderBys <= 0 ||
+ scan->orderByData[0].sk_attno != skey->sk_attno)
+ {
+ cxt.distkey = NULL;
+ cxt.reverse = reverse;
+ }
+ else
+ {
+ /* Init procedures for distance calculation and comparison. */
+ ScanKey distkey = &scan->orderByData[0];
+ ScanKeyData distkey2;
+ Oid disttype = distkey->sk_subtype;
+ Oid distopr;
+ RegProcedure distproc;
+
+ if (!OidIsValid(disttype))
+ disttype = rel->rd_opcintype[skey->sk_attno - 1];
+
+ /* Lookup distance operator in index column's operator family. */
+ distopr = get_opfamily_member(opfamily,
+ elemtype,
+ disttype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(distopr))
+ elog(ERROR, "missing operator (%u,%u) for strategy %d in opfamily %u",
+ elemtype, disttype, BtreeKNNSearchStrategyNumber, opfamily);
+
+ distproc = get_opcode(distopr);
+
+ if (!RegProcedureIsValid(distproc))
+ elog(ERROR, "missing code for operator %u", distopr);
+
+ fmgr_info(distproc, &cxt.distflinfo);
+
+ distkey2 = *distkey;
+ fmgr_info_copy(&distkey2.sk_func, &cxt.distflinfo, CurrentMemoryContext);
+ distkey2.sk_subtype = disttype;
+
+ _bt_get_distance_cmp_proc(&distkey2, opfamily, elemtype,
+ &cxt.distcmpflinfo, NULL, NULL);
+
+ cxt.distkey = distkey;
+ cxt.reverse = false; /* supported only ascending ordering */
+ }
+
/*
* Look up the appropriate comparison function in the opfamily.
*
@@ -470,19 +529,17 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
* non-cross-type support functions for any datatype that it supports at
* all.
*/
- cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
+ cmp_proc = get_opfamily_proc(opfamily,
elemtype,
elemtype,
BTORDER_PROC);
if (!RegProcedureIsValid(cmp_proc))
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
- BTORDER_PROC, elemtype, elemtype,
- rel->rd_opfamily[skey->sk_attno - 1]);
+ BTORDER_PROC, elemtype, elemtype, opfamily);
/* Sort the array elements */
fmgr_info(cmp_proc, &cxt.flinfo);
cxt.collation = skey->sk_collation;
- cxt.reverse = reverse;
qsort_arg((void *) elems, nelems, sizeof(Datum),
_bt_compare_array_elements, (void *) &cxt);
@@ -514,6 +571,23 @@ _bt_compare_array_elements(const void *a, const void *b, void *arg)
BTSortArrayContext *cxt = (BTSortArrayContext *) arg;
int32 compare;
+ if (cxt->distkey)
+ {
+ Datum dista = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ da,
+ cxt->distkey->sk_argument);
+ Datum distb = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ db,
+ cxt->distkey->sk_argument);
+ bool cmp = DatumGetBool(FunctionCall2Coll(&cxt->distcmpflinfo,
+ cxt->collation,
+ dista,
+ distb));
+ return cmp ? -1 : 1;
+ }
+
compare = DatumGetInt32(FunctionCall2Coll(&cxt->flinfo,
cxt->collation,
da, db));
@@ -667,6 +741,66 @@ _bt_restore_array_keys(IndexScanDesc scan)
}
}
+/*
+ * _bt_emit_scan_key() -- Emit one prepared scan key
+ *
+ * Push the scan key into the so->keyData[] array, and then mark it if it is
+ * required. Also update selected kNN strategy.
+ */
+static void
+_bt_emit_scan_key(IndexScanDesc scan, ScanKey skey, int numberOfEqualCols)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey outkey = &so->keyData[so->numberOfKeys++];
+
+ memcpy(outkey, skey, sizeof(ScanKeyData));
+
+ /*
+ * We can mark the qual as required (possibly only in one direction) if all
+ * attrs before this one had "=".
+ */
+ if (outkey->sk_attno - 1 == numberOfEqualCols)
+ _bt_mark_scankey_required(outkey);
+
+ /* Update kNN strategy if it is not already selected. */
+ if (so->useBidirectionalKnnScan)
+ {
+ switch (_bt_select_knn_strategy_for_key(scan, outkey))
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ /*
+ * Ordering key argument is greater than all values in scan
+ * range, select backward scan direction.
+ */
+ so->scanDirection = BackwardScanDirection;
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BTEqualStrategyNumber:
+ /* Use default unidirectional scan direction. */
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ /*
+ * Ordering key argument is lesser than all values in scan
+ * range, select forward scan direction.
+ */
+ so->scanDirection = ForwardScanDirection;
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BtreeKNNSearchStrategyNumber:
+ /*
+ * Ordering key argument falls into scan range,
+ * keep using bidirectional scan.
+ */
+ break;
+ }
+ }
+}
/*
* _bt_preprocess_keys() -- Preprocess scan keys
@@ -758,10 +892,8 @@ _bt_preprocess_keys(IndexScanDesc scan)
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int numberOfKeys = scan->numberOfKeys;
int16 *indoption = scan->indexRelation->rd_indoption;
- int new_numberOfKeys;
int numberOfEqualCols;
ScanKey inkeys;
- ScanKey outkeys;
ScanKey cur;
ScanKey xform[BTMaxStrategyNumber];
bool test_result;
@@ -769,6 +901,24 @@ _bt_preprocess_keys(IndexScanDesc scan)
j;
AttrNumber attno;
+ if (scan->numberOfOrderBys > 0)
+ {
+ ScanKey ord = scan->orderByData;
+
+ if (scan->numberOfOrderBys > 1)
+ /* it should not happen, see btmatchorderby() */
+ elog(ERROR, "only one btree ordering operator is supported");
+
+ Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
+
+ /* use bidirectional kNN scan by default */
+ so->useBidirectionalKnnScan = true;
+ }
+ else
+ {
+ so->useBidirectionalKnnScan = false;
+ }
+
/* initialize result variables */
so->qual_ok = true;
so->numberOfKeys = 0;
@@ -784,7 +934,6 @@ _bt_preprocess_keys(IndexScanDesc scan)
else
inkeys = scan->keyData;
- outkeys = so->keyData;
cur = &inkeys[0];
/* we check that input keys are correctly ordered */
if (cur->sk_attno < 1)
@@ -796,18 +945,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
/* Apply indoption to scankey (might change sk_strategy!) */
if (!_bt_fix_scankey_strategy(cur, indoption))
so->qual_ok = false;
- memcpy(outkeys, cur, sizeof(ScanKeyData));
- so->numberOfKeys = 1;
- /* We can mark the qual as required if it's for first index col */
- if (cur->sk_attno == 1)
- _bt_mark_scankey_required(outkeys);
+
+ _bt_emit_scan_key(scan, cur, 0);
return;
}
/*
* Otherwise, do the full set of pushups.
*/
- new_numberOfKeys = 0;
numberOfEqualCols = 0;
/*
@@ -931,20 +1076,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
}
/*
- * Emit the cleaned-up keys into the outkeys[] array, and then
+ * Emit the cleaned-up keys into the so->keyData[] array, and then
* mark them if they are required. They are required (possibly
* only in one direction) if all attrs before this one had "=".
*/
for (j = BTMaxStrategyNumber; --j >= 0;)
{
if (xform[j])
- {
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, xform[j], sizeof(ScanKeyData));
- if (priorNumberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
- }
+ _bt_emit_scan_key(scan, xform[j], priorNumberOfEqualCols);
}
/*
@@ -964,17 +1103,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
/* if row comparison, push it directly to the output array */
if (cur->sk_flags & SK_ROW_HEADER)
{
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, cur, sizeof(ScanKeyData));
- if (numberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
+ _bt_emit_scan_key(scan, cur, numberOfEqualCols);
/*
* We don't support RowCompare using equality; such a qual would
* mess up the numberOfEqualCols tracking.
*/
Assert(j != (BTEqualStrategyNumber - 1));
+
continue;
}
@@ -1007,16 +1143,10 @@ _bt_preprocess_keys(IndexScanDesc scan)
* previous one in xform[j] and push this one directly to the
* output array.
*/
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, cur, sizeof(ScanKeyData));
- if (numberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
+ _bt_emit_scan_key(scan, cur, numberOfEqualCols);
}
}
}
-
- so->numberOfKeys = new_numberOfKeys;
}
/*
@@ -2075,6 +2205,39 @@ btproperty(Oid index_oid, int attno,
*res = true;
return true;
+ case AMPROP_DISTANCE_ORDERABLE:
+ {
+ Oid opclass,
+ opfamily,
+ opcindtype;
+
+ /* answer only for columns, not AM or whole index */
+ if (attno == 0)
+ return false;
+
+ opclass = get_index_column_opclass(index_oid, attno);
+
+ if (!OidIsValid(opclass))
+ {
+ *res = false; /* non-key attribute */
+ return true;
+ }
+
+ if (!get_opclass_opfamily_and_input_type(opclass,
+ &opfamily, &opcindtype))
+ {
+ *isnull = true;
+ return true;
+ }
+
+ *res = SearchSysCacheExists(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(opcindtype),
+ ObjectIdGetDatum(opcindtype),
+ Int16GetDatum(BtreeKNNSearchStrategyNumber));
+ return true;
+ }
+
default:
return false; /* punt to generic code */
}
@@ -2223,3 +2386,212 @@ _bt_allocate_tuple_workspaces(BTScanState state)
state->currTuples = (char *) palloc(BLCKSZ * 2);
state->markTuples = state->currTuples + BLCKSZ;
}
+
+static bool
+_bt_compare_row_key_with_ordering_key(ScanKey row, ScanKey ord, bool *result)
+{
+ ScanKey subkey = (ScanKey) DatumGetPointer(row->sk_argument);
+ int32 cmpresult;
+
+ Assert(subkey->sk_attno == 1);
+ Assert(subkey->sk_flags & SK_ROW_MEMBER);
+
+ if (subkey->sk_flags & SK_ISNULL)
+ return false;
+
+ /* Perform the test --- three-way comparison not bool operator */
+ cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
+ subkey->sk_collation,
+ ord->sk_argument,
+ subkey->sk_argument));
+
+ if (subkey->sk_flags & SK_BT_DESC)
+ cmpresult = -cmpresult;
+
+ /*
+ * At this point cmpresult indicates the overall result of the row
+ * comparison, and subkey points to the deciding column (or the last
+ * column if the result is "=").
+ */
+ switch (subkey->sk_strategy)
+ {
+ /* EQ and NE cases aren't allowed here */
+ case BTLessStrategyNumber:
+ *result = cmpresult < 0;
+ break;
+ case BTLessEqualStrategyNumber:
+ *result = cmpresult <= 0;
+ break;
+ case BTGreaterEqualStrategyNumber:
+ *result = cmpresult >= 0;
+ break;
+ case BTGreaterStrategyNumber:
+ *result = cmpresult > 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized RowCompareType: %d",
+ (int) subkey->sk_strategy);
+ *result = false; /* keep compiler quiet */
+ }
+
+ return true;
+}
+
+/*
+ * _bt_select_knn_strategy_for_key() -- Determine which kNN scan strategy to use:
+ * bidirectional or unidirectional. We are checking here if the
+ * ordering scankey argument falls into the scan range: if it falls
+ * we must use bidirectional scan, otherwise we use unidirectional.
+ *
+ * Returns BtreeKNNSearchStrategyNumber for bidirectional scan or
+ * strategy number of non-matched scankey for unidirectional.
+ */
+static inline StrategyNumber
+_bt_select_knn_strategy_for_key(IndexScanDesc scan, ScanKey cond)
+{
+ ScanKey ord = scan->orderByData;
+ bool result;
+
+ /* only interesting in the index attribute that is ordered by a distance */
+ if (cond->sk_attno != ord->sk_attno)
+ return BtreeKNNSearchStrategyNumber;
+
+ if (cond->sk_strategy == BTEqualStrategyNumber)
+ /* always use simple unidirectional scan for equals operators */
+ return BTEqualStrategyNumber;
+
+ if (cond->sk_flags & SK_ROW_HEADER)
+ {
+ if (!_bt_compare_row_key_with_ordering_key(cond, ord, &result))
+ return BTEqualStrategyNumber; /* ROW(fist_index_attr, ...) IS NULL */
+ }
+ else
+ {
+ if (!_bt_compare_scankey_args(scan, cond, ord, cond, &result))
+ elog(ERROR, "could not compare ordering key");
+ }
+
+ if (!result)
+ /*
+ * Ordering scankey argument is out of scan range,
+ * use unidirectional scan.
+ */
+ return cond->sk_strategy;
+
+ return BtreeKNNSearchStrategyNumber;
+}
+
+int
+_bt_init_knn_start_keys(IndexScanDesc scan, ScanKey *startKeys, ScanKey bufKeys)
+{
+ ScanKey ord = scan->orderByData;
+ int indopt = scan->indexRelation->rd_indoption[ord->sk_attno - 1];
+ int flags = (indopt << SK_BT_INDOPTION_SHIFT) |
+ SK_ORDER_BY |
+ SK_SEARCHNULL; /* only for invalid procedure oid, see
+ * assert in ScanKeyEntryInitialize() */
+ int keysCount = 0;
+
+ /* Init btree search key with ordering key argument. */
+ ScanKeyEntryInitialize(&bufKeys[0],
+ flags,
+ ord->sk_attno,
+ BtreeKNNSearchStrategyNumber,
+ ord->sk_subtype,
+ ord->sk_collation,
+ InvalidOid,
+ ord->sk_argument);
+
+ startKeys[keysCount++] = &bufKeys[0];
+
+ return keysCount;
+}
+
+static Oid
+_bt_get_sortfamily_for_opfamily_op(Oid opfamily, Oid lefttype, Oid righttype,
+ StrategyNumber strategy)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid sortfamily;
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(lefttype),
+ ObjectIdGetDatum(righttype),
+ Int16GetDatum(strategy));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ sortfamily = amop_tup->amopsortfamily;
+ ReleaseSysCache(tp);
+
+ return sortfamily;
+}
+
+/*
+ * _bt_get_distance_cmp_proc() -- Init procedure for comparsion of distances
+ * between "leftargtype" and "distkey".
+ */
+static void
+_bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval)
+{
+ RegProcedure opcode;
+ Oid sortfamily;
+ Oid opno;
+ Oid distanceType;
+
+ distanceType = get_func_rettype(distkey->sk_func.fn_oid);
+
+ sortfamily = _bt_get_sortfamily_for_opfamily_op(opfamily, leftargtype,
+ distkey->sk_subtype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(sortfamily))
+ elog(ERROR, "could not find sort family for btree ordering operator");
+
+ opno = get_opfamily_member(sortfamily,
+ distanceType,
+ distanceType,
+ BTLessEqualStrategyNumber);
+
+ if (!OidIsValid(opno))
+ elog(ERROR, "could not find operator for btree distance comparison");
+
+ opcode = get_opcode(opno);
+
+ if (!RegProcedureIsValid(opcode))
+ elog(ERROR,
+ "could not find procedure for btree distance comparison operator");
+
+ fmgr_info(opcode, finfo);
+
+ if (typlen)
+ get_typlenbyval(distanceType, typlen, typbyval);
+}
+
+/*
+ * _bt_init_distance_comparison() -- Init distance typlen/typbyval and its
+ * comparison procedure.
+ */
+void
+_bt_init_distance_comparison(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ Relation rel = scan->indexRelation;
+ ScanKey ord = scan->orderByData;
+
+ _bt_get_distance_cmp_proc(ord,
+ rel->rd_opfamily[ord->sk_attno - 1],
+ rel->rd_opcintype[ord->sk_attno - 1],
+ &so->distanceCmpProc,
+ &so->distanceTypeLen,
+ &so->distanceTypeByVal);
+
+ if (!so->distanceTypeByVal)
+ {
+ so->state.currDistance = PointerGetDatum(NULL);
+ so->state.markDistance = PointerGetDatum(NULL);
+ }
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 0148ea7..4558fd3 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -22,9 +22,17 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
+#define BTRequiredOperatorSet \
+ ((1 << BTLessStrategyNumber) | \
+ (1 << BTLessEqualStrategyNumber) | \
+ (1 << BTEqualStrategyNumber) | \
+ (1 << BTGreaterEqualStrategyNumber) | \
+ (1 << BTGreaterStrategyNumber))
+
/*
* Validator for a btree opclass.
@@ -132,10 +140,11 @@ btvalidate(Oid opclassoid)
{
HeapTuple oprtup = &oprlist->members[i]->tuple;
Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ Oid op_rettype;
/* Check that only allowed strategy numbers exist */
if (oprform->amopstrategy < 1 ||
- oprform->amopstrategy > BTMaxStrategyNumber)
+ oprform->amopstrategy > BtreeMaxStrategyNumber)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -146,20 +155,29 @@ btvalidate(Oid opclassoid)
result = false;
}
- /* btree doesn't support ORDER BY operators */
- if (oprform->amoppurpose != AMOP_SEARCH ||
- OidIsValid(oprform->amopsortfamily))
+ /* btree supports ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH)
{
- ereport(INFO,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
- opfamilyname, "btree",
- format_operator(oprform->amopopr))));
- result = false;
+ /* ... and operator result must match the claimed btree opfamily */
+ op_rettype = get_op_rettype(oprform->amopopr);
+ if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
+ {
+ ereport(INFO,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
+ opfamilyname, "btree",
+ format_operator(oprform->amopopr))));
+ result = false;
+ }
+ }
+ else
+ {
+ /* Search operators must always return bool */
+ op_rettype = BOOLOID;
}
/* Check operator signature --- same for all btree strategies */
- if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ if (!check_amop_signature(oprform->amopopr, op_rettype,
oprform->amoplefttype,
oprform->amoprighttype))
{
@@ -214,12 +232,8 @@ btvalidate(Oid opclassoid)
* or support functions for this datatype pair. The only things
* considered optional are the sortsupport and in_range functions.
*/
- if (thisgroup->operatorset !=
- ((1 << BTLessStrategyNumber) |
- (1 << BTLessEqualStrategyNumber) |
- (1 << BTEqualStrategyNumber) |
- (1 << BTGreaterEqualStrategyNumber) |
- (1 << BTGreaterStrategyNumber)))
+ if ((thisgroup->operatorset & BTRequiredOperatorSet) !=
+ BTRequiredOperatorSet)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6cf0b0d..279d8ba 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -990,6 +990,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
* if we are only trying to build bitmap indexscans, nor if we have to
* assume the scan is unordered.
*/
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
!found_lower_saop_clause &&
has_useful_pathkeys(root, rel));
@@ -1000,10 +1004,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
ForwardScanDirection);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
}
- else if (index->ammatchorderby && pathkeys_possibly_useful)
+
+ if (useful_pathkeys == NIL &&
+ index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -1015,12 +1019,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
else
useful_pathkeys = NIL;
}
- else
- {
- useful_pathkeys = NIL;
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
- }
/*
* 3. Check if an index-only scan is possible. If we're not building
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index d78df29..4f98e91 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -459,6 +459,12 @@ typedef struct BTScanStateData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+
+ /* KNN-search fields: */
+ Datum currDistance; /* current distance */
+ Datum markDistance; /* marked distance */
+ bool currIsNull; /* current item is NULL */
+ bool markIsNull; /* marked item is NULL */
} BTScanStateData;
typedef BTScanStateData *BTScanState;
@@ -479,7 +485,18 @@ typedef struct BTScanOpaqueData
BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
MemoryContext arrayContext; /* scan-lifespan context for array data */
- BTScanStateData state;
+ BTScanStateData state; /* main scan state */
+
+ /* kNN-search fields: */
+ BTScanState knnState; /* optional scan state for kNN search */
+ bool useBidirectionalKnnScan; /* use bidirectional kNN scan? */
+ ScanDirection scanDirection; /* selected scan direction for
+ * unidirectional kNN scan */
+ FmgrInfo distanceCmpProc; /* distance comparison procedure */
+ int16 distanceTypeLen; /* distance typlen */
+ bool distanceTypeByVal; /* distance typebyval */
+ bool currRightIsNearest; /* current right item is nearest */
+ bool markRightIsNearest; /* marked right item is nearest */
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -525,11 +542,12 @@ extern bool btcanreturn(Relation index, int attno);
/*
* prototypes for internal functions in nbtree.c
*/
-extern bool _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno);
-extern void _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page);
-extern void _bt_parallel_done(IndexScanDesc scan);
+extern bool _bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno);
+extern void _bt_parallel_release(IndexScanDesc scan, BTScanState state, BlockNumber scan_page);
+extern void _bt_parallel_done(IndexScanDesc scan, BTScanState state);
extern void _bt_parallel_advance_array_keys(IndexScanDesc scan);
+
/*
* prototypes for functions in nbtinsert.c
*/
@@ -610,6 +628,9 @@ extern bool btproperty(Oid index_oid, int attno,
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
extern void _bt_allocate_tuple_workspaces(BTScanState state);
+extern void _bt_init_distance_comparison(IndexScanDesc scan);
+extern int _bt_init_knn_start_keys(IndexScanDesc scan, ScanKey *startKeys,
+ ScanKey bufKeys);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/include/access/stratnum.h b/src/include/access/stratnum.h
index 8fdba28..8087ffe 100644
--- a/src/include/access/stratnum.h
+++ b/src/include/access/stratnum.h
@@ -32,7 +32,10 @@ typedef uint16 StrategyNumber;
#define BTGreaterEqualStrategyNumber 4
#define BTGreaterStrategyNumber 5
-#define BTMaxStrategyNumber 5
+#define BTMaxStrategyNumber 5 /* number of canonical B-tree strategies */
+
+#define BtreeKNNSearchStrategyNumber 6 /* for <-> (distance) */
+#define BtreeMaxStrategyNumber 6 /* number of extended B-tree strategies */
/*
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 6faa9d7..c75ef39 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -347,10 +347,10 @@ ROLLBACK;
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
ERROR: access method "invalid_index_method" does not exist
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 6, must be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 0, must be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 7, must be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 0, must be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -397,11 +397,12 @@ DROP OPERATOR FAMILY alt_opf8 USING btree;
CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
-ERROR: access method "btree" does not support ordering operators
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
ALTER OPERATOR FAMILY alt_opf11 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 84fd900..73e6e206 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -295,8 +295,8 @@ ROLLBACK;
-- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -340,10 +340,12 @@ CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
0005-Add-btree-distance-operators-v07.patchtext/x-patch; name=0005-Add-btree-distance-operators-v07.patchDownload
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 1998171..9203497 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -183,6 +183,24 @@ SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
</programlisting>
which finds the ten events closest to a given target date. The ability
to do this is again dependent on the particular operator class being used.
+ Built-in B-tree operator classes support distance ordering for the following
+ data types:
+ <simplelist>
+ <member><type>int2</type></member>
+ <member><type>int4</type></member>
+ <member><type>int8</type></member>
+ <member><type>float4</type></member>
+ <member><type>float8</type></member>
+ <member><type>numeric</type></member>
+ <member><type>timestamp with time zone</type></member>
+ <member><type>timestamp without time zone</type></member>
+ <member><type>time with time zone</type></member>
+ <member><type>time without time zone</type></member>
+ <member><type>date</type></member>
+ <member><type>interval</type></member>
+ <member><type>oid</type></member>
+ <member><type>money</type></member>
+ </simplelist>
</para>
<para>
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index c92e9d5..83073c4 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -30,6 +30,7 @@
#include "utils/numeric.h"
#include "utils/pg_locale.h"
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
/*************************************************************************
* Private routines
@@ -1157,3 +1158,22 @@ int8_cash(PG_FUNCTION_ARGS)
PG_RETURN_CASH(result);
}
+
+Datum
+cash_distance(PG_FUNCTION_ARGS)
+{
+ Cash a = PG_GETARG_CASH(0);
+ Cash b = PG_GETARG_CASH(1);
+ Cash r;
+ Cash ra;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("money out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_CASH(ra);
+}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index cf5a1c6..4492f69 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -563,6 +563,17 @@ date_mii(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(result);
}
+Datum
+date_distance(PG_FUNCTION_ARGS)
+{
+ /* we assume the difference can't overflow */
+ Datum diff = DirectFunctionCall2(date_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+}
+
/*
* Internal routines for promoting date to timestamp and timestamp with
* time zone
@@ -760,6 +771,29 @@ date_cmp_timestamp(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
+ Timestamp dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
date_eq_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
@@ -844,6 +878,30 @@ date_cmp_timestamptz(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
+
+
+Datum
timestamp_eq_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
@@ -928,6 +986,29 @@ timestamp_cmp_date(PG_FUNCTION_ARGS)
}
Datum
+timestamp_dist_date(PG_FUNCTION_ARGS)
+{
+ Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ Timestamp dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
timestamptz_eq_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
@@ -1039,6 +1120,28 @@ in_range_date_interval(PG_FUNCTION_ARGS)
BoolGetDatum(less));
}
+Datum
+timestamptz_dist_date(PG_FUNCTION_ARGS)
+{
+ TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ TimestampTz dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
/* Add an interval to a date, giving a new date.
* Must handle both positive and negative intervals.
@@ -1957,6 +2060,16 @@ time_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+Datum
+time_distance(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(time_mi_time,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
+
/*****************************************************************************
* Time With Time Zone ADT
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 37c202d..d72fbef 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -3726,6 +3726,47 @@ width_bucket_float8(PG_FUNCTION_ARGS)
PG_RETURN_INT32(result);
}
+Datum
+float4dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float4 r = float4_mi(a, b);
+
+ PG_RETURN_FLOAT4(Abs(r));
+}
+
+Datum
+float8dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+
+Datum
+float48dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+Datum
+float84dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
/* ========== PRIVATE ROUTINES ========== */
#ifndef HAVE_CBRT
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 04825fc..76e92ec 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -1501,3 +1501,54 @@ generate_series_int4_support(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(ret);
}
+
+Datum
+int2dist(PG_FUNCTION_ARGS)
+{
+ int16 a = PG_GETARG_INT16(0);
+ int16 b = PG_GETARG_INT16(1);
+ int16 r;
+ int16 ra;
+
+ if (pg_sub_s16_overflow(a, b, &r) ||
+ r == PG_INT16_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_INT16(ra);
+}
+
+static int32
+int44_dist(int32 a, int32 b)
+{
+ int32 r;
+
+ if (pg_sub_s32_overflow(a, b, &r) ||
+ r == PG_INT32_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ return Abs(r);
+}
+
+Datum
+int4dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int24dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT16(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int42dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT16(1)));
+}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 0ff9394..2ceb16b 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1446,3 +1446,47 @@ generate_series_int8_support(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(ret);
}
+
+static int64
+int88_dist(int64 a, int64 b)
+{
+ int64 r;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+
+ return Abs(r);
+}
+
+Datum
+int8dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int82dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT16(1)));
+}
+
+Datum
+int84dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT32(1)));
+}
+
+Datum
+int28dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT16(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int48dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT32(0), PG_GETARG_INT64(1)));
+}
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index bb67e01..b8f48d5 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -469,3 +469,17 @@ oidvectorgt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+
+Datum
+oiddist(PG_FUNCTION_ARGS)
+{
+ Oid a = PG_GETARG_OID(0);
+ Oid b = PG_GETARG_OID(1);
+ Oid res;
+
+ if (a < b)
+ res = b - a;
+ else
+ res = a - b;
+ PG_RETURN_OID(res);
+}
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index e0ef2f7..c5d1042 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2694,6 +2694,86 @@ timestamp_mi(PG_FUNCTION_ARGS)
PG_RETURN_INTERVAL_P(result);
}
+Datum
+timestamp_distance(PG_FUNCTION_ARGS)
+{
+ Timestamp a = PG_GETARG_TIMESTAMP(0);
+ Timestamp b = PG_GETARG_TIMESTAMP(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+ p->time = PG_INT64_MAX;
+ PG_RETURN_INTERVAL_P(p);
+ }
+ else
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+Interval *
+timestamp_dist_internal(Timestamp a, Timestamp b)
+{
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ return r;
+ }
+
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ TimestampGetDatum(a),
+ TimestampGetDatum(b)));
+
+ return abs_interval(r);
+}
+
+Datum
+timestamptz_distance(PG_FUNCTION_ARGS)
+{
+ TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
+ TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(a, b));
+}
+
+Datum
+timestamp_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ Timestamp ts1 = PG_GETARG_TIMESTAMP(0);
+ TimestampTz tstz2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz tstz1;
+
+ tstz1 = timestamp2timestamptz(ts1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+Datum
+timestamptz_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ TimestampTz tstz1 = PG_GETARG_TIMESTAMPTZ(0);
+ Timestamp ts2 = PG_GETARG_TIMESTAMP(1);
+ TimestampTz tstz2;
+
+ tstz2 = timestamp2timestamptz(ts2);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+
/*
* interval_justify_interval()
*
@@ -3563,6 +3643,29 @@ interval_avg(PG_FUNCTION_ARGS)
Float8GetDatum((double) N.time));
}
+Interval *
+abs_interval(Interval *a)
+{
+ static Interval zero = {0, 0, 0};
+
+ if (DatumGetBool(DirectFunctionCall2(interval_lt,
+ IntervalPGetDatum(a),
+ IntervalPGetDatum(&zero))))
+ a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
+ IntervalPGetDatum(a)));
+
+ return a;
+}
+
+Datum
+interval_distance(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(interval_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
/* timestamp_age()
* Calculate time difference while retaining year/month fields.
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 0ab95d8..0e06b04 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -30,6 +30,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int24
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -47,6 +51,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int28
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -64,6 +72,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int4
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -81,6 +93,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int42
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -98,6 +114,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int48
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -115,6 +135,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int8
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -132,6 +156,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int82
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -149,6 +177,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int84
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -166,6 +198,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# btree oid_ops
@@ -179,6 +215,10 @@
amopstrategy => '4', amopopr => '>=(oid,oid)', amopmethod => 'btree' },
{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid', amoprighttype => 'oid',
amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'btree' },
+{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(oid,oid)', amopmethod => 'btree',
+ amopsortfamily => 'btree/oid_ops' },
# btree tid_ops
@@ -229,6 +269,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float48
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
@@ -246,6 +290,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# default operators float8
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -263,6 +311,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float84
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -280,6 +332,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# btree char_ops
@@ -416,6 +472,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -433,6 +493,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(date,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -450,6 +514,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(date,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -467,6 +535,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamp,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -484,6 +556,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -501,6 +577,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamp,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -518,6 +598,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -535,6 +619,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'date', amopstrategy => '5',
amopopr => '>(timestamptz,date)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -552,6 +640,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamptz,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree time_ops
@@ -570,6 +662,10 @@
{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(time,time)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree timetz_ops
@@ -606,6 +702,10 @@
{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'btree' },
+{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(interval,interval)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree macaddr
@@ -781,6 +881,10 @@
{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
amoprighttype => 'money', amopstrategy => '5', amopopr => '>(money,money)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
+ amoprighttype => 'money', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(money,money)', amopmethod => 'btree',
+ amopsortfamily => 'btree/money_ops' },
# btree array_ops
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 06aec07..914ca76 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -2851,6 +2851,114 @@
oprname => '-', oprleft => 'pg_lsn', oprright => 'pg_lsn',
oprresult => 'numeric', oprcode => 'pg_lsn_mi' },
+# distance operators
+{ oid => '4217', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int2', oprresult => 'int2', oprcom => '<->(int2,int2)',
+ oprcode => 'int2dist'},
+{ oid => '4218', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int4)',
+ oprcode => 'int4dist'},
+{ oid => '4219', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int8)',
+ oprcode => 'int8dist'},
+{ oid => '4220', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'oid',
+ oprright => 'oid', oprresult => 'oid', oprcom => '<->(oid,oid)',
+ oprcode => 'oiddist'},
+{ oid => '4221', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float4', oprresult => 'float4', oprcom => '<->(float4,float4)',
+ oprcode => 'float4dist'},
+{ oid => '4222', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float8)',
+ oprcode => 'float8dist'},
+{ oid => '4223', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'money',
+ oprright => 'money', oprresult => 'money', oprcom => '<->(money,money)',
+ oprcode => 'cash_distance'},
+{ oid => '4224', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'date', oprresult => 'int4', oprcom => '<->(date,date)',
+ oprcode => 'date_distance'},
+{ oid => '4225', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'time',
+ oprright => 'time', oprresult => 'interval', oprcom => '<->(time,time)',
+ oprcode => 'time_distance'},
+{ oid => '4226', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamp)',
+ oprcode => 'timestamp_distance'},
+{ oid => '4227', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamptz)',
+ oprcode => 'timestamptz_distance'},
+{ oid => '4228', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'interval',
+ oprright => 'interval', oprresult => 'interval', oprcom => '<->(interval,interval)',
+ oprcode => 'interval_distance'},
+
+# cross-type distance operators
+{ oid => '4229', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int2)',
+ oprcode => 'int24dist'},
+{ oid => '4230', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int2', oprresult => 'int4', oprcom => '<->(int2,int4)',
+ oprcode => 'int42dist'},
+{ oid => '4231', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int2)',
+ oprcode => 'int28dist'},
+{ oid => '4232', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int2', oprresult => 'int8', oprcom => '<->(int2,int8)',
+ oprcode => 'int82dist'},
+{ oid => '4233', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int4)',
+ oprcode => 'int48dist'},
+{ oid => '4234', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int4', oprresult => 'int8', oprcom => '<->(int4,int8)',
+ oprcode => 'int84dist'},
+{ oid => '4235', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float4)',
+ oprcode => 'float48dist'},
+{ oid => '4236', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float4', oprresult => 'float8', oprcom => '<->(float4,float8)',
+ oprcode => 'float84dist'},
+{ oid => '4237', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,date)',
+ oprcode => 'date_dist_timestamp'},
+{ oid => '4238', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamp)',
+ oprcode => 'timestamp_dist_date'},
+{ oid => '4239', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,date)',
+ oprcode => 'date_dist_timestamptz'},
+{ oid => '4240', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamptz)',
+ oprcode => 'timestamptz_dist_date'},
+{ oid => '4241', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamp)',
+ oprcode => 'timestamp_dist_timestamptz'},
+{ oid => '4242', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamptz)',
+ oprcode => 'timestamptz_dist_timestamp'},
+
# enum operators
{ oid => '3516', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anyenum',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a4e173b..96d5db0 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10534,4 +10534,90 @@
proname => 'pg_partition_root', prorettype => 'regclass',
proargtypes => 'regclass', prosrc => 'pg_partition_root' },
+# distance functions
+{ oid => '4243',
+ proname => 'int2dist', prorettype => 'int2',
+ proargtypes => 'int2 int2', prosrc => 'int2dist' },
+{ oid => '4244',
+ proname => 'int4dist', prorettype => 'int4',
+ proargtypes => 'int4 int4', prosrc => 'int4dist' },
+{ oid => '4245',
+ proname => 'int8dist', prorettype => 'int8',
+ proargtypes => 'int8 int8', prosrc => 'int8dist' },
+{ oid => '4246',
+ proname => 'oiddist', proleakproof => 't', prorettype => 'oid',
+ proargtypes => 'oid oid', prosrc => 'oiddist' },
+{ oid => '4247',
+ proname => 'float4dist', prorettype => 'float4',
+ proargtypes => 'float4 float4', prosrc => 'float4dist' },
+{ oid => '4248',
+ proname => 'float8dist', prorettype => 'float8',
+ proargtypes => 'float8 float8', prosrc => 'float8dist' },
+{ oid => '4249',
+ proname => 'cash_distance', prorettype => 'money',
+ proargtypes => 'money money', prosrc => 'cash_distance' },
+{ oid => '4250',
+ proname => 'date_distance', prorettype => 'int4',
+ proargtypes => 'date date', prosrc => 'date_distance' },
+{ oid => '4251',
+ proname => 'time_distance', prorettype => 'interval',
+ proargtypes => 'time time', prosrc => 'time_distance' },
+{ oid => '4252',
+ proname => 'timestamp_distance', prorettype => 'interval',
+ proargtypes => 'timestamp timestamp', prosrc => 'timestamp_distance' },
+{ oid => '4253',
+ proname => 'timestamptz_distance', prorettype => 'interval',
+ proargtypes => 'timestamptz timestamptz', prosrc => 'timestamptz_distance' },
+{ oid => '4254',
+ proname => 'interval_distance', prorettype => 'interval',
+ proargtypes => 'interval interval', prosrc => 'interval_distance' },
+
+# cross-type distance functions
+{ oid => '4255',
+ proname => 'int24dist', prorettype => 'int4',
+ proargtypes => 'int2 int4', prosrc => 'int24dist' },
+{ oid => '4256',
+ proname => 'int28dist', prorettype => 'int8',
+ proargtypes => 'int2 int8', prosrc => 'int28dist' },
+{ oid => '4257',
+ proname => 'int42dist', prorettype => 'int4',
+ proargtypes => 'int4 int2', prosrc => 'int42dist' },
+{ oid => '4258',
+ proname => 'int48dist', prorettype => 'int8',
+ proargtypes => 'int4 int8', prosrc => 'int48dist' },
+{ oid => '4259',
+ proname => 'int82dist', prorettype => 'int8',
+ proargtypes => 'int8 int2', prosrc => 'int82dist' },
+{ oid => '4260',
+ proname => 'int84dist', prorettype => 'int8',
+ proargtypes => 'int8 int4', prosrc => 'int84dist' },
+{ oid => '4261',
+ proname => 'float48dist', prorettype => 'float8',
+ proargtypes => 'float4 float8', prosrc => 'float48dist' },
+{ oid => '4262',
+ proname => 'float84dist', prorettype => 'float8',
+ proargtypes => 'float8 float4', prosrc => 'float84dist' },
+{ oid => '4263',
+ proname => 'date_dist_timestamp', prorettype => 'interval',
+ proargtypes => 'date timestamp', prosrc => 'date_dist_timestamp' },
+{ oid => '4264',
+ proname => 'date_dist_timestamptz', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'date timestamptz',
+ prosrc => 'date_dist_timestamptz' },
+{ oid => '4265',
+ proname => 'timestamp_dist_date', prorettype => 'interval',
+ proargtypes => 'timestamp date', prosrc => 'timestamp_dist_date' },
+{ oid => '4266',
+ proname => 'timestamp_dist_timestamptz', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamp timestamptz',
+ prosrc => 'timestamp_dist_timestamptz' },
+{ oid => '4267',
+ proname => 'timestamptz_dist_date', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamptz date',
+ prosrc => 'timestamptz_dist_date' },
+{ oid => '4268',
+ proname => 'timestamptz_dist_timestamp', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamptz timestamp',
+ prosrc => 'timestamptz_dist_timestamp' },
+
]
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 87f819e..01d2284 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -338,4 +338,6 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
+extern Interval *abs_interval(Interval *a);
+
#endif /* DATETIME_H */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index aeb89dc..438a5eb 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -93,9 +93,11 @@ extern Timestamp SetEpochTimestamp(void);
extern void GetEpochTime(struct pg_tm *tm);
extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
+extern Interval *timestamp_dist_internal(Timestamp a, Timestamp b);
-/* timestamp comparison works for timestamptz also */
+/* timestamp comparison and distance works for timestamptz also */
#define timestamptz_cmp_internal(dt1,dt2) timestamp_cmp_internal(dt1, dt2)
+#define timestamptz_dist_internal(dt1,dt2) timestamp_dist_internal(dt1, dt2)
extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday);
diff --git a/src/test/regress/expected/amutils.out b/src/test/regress/expected/amutils.out
index 4570a39..630dc6b 100644
--- a/src/test/regress/expected/amutils.out
+++ b/src/test/regress/expected/amutils.out
@@ -24,7 +24,7 @@ select prop,
nulls_first | | | f
nulls_last | | | t
orderable | | | t
- distance_orderable | | | f
+ distance_orderable | | | t
returnable | | | t
search_array | | | t
search_nulls | | | t
@@ -100,7 +100,7 @@ select prop,
nulls_first | f | f | f | f | f | f | f
nulls_last | t | f | f | f | f | f | f
orderable | t | f | f | f | f | f | f
- distance_orderable | f | f | t | f | t | f | f
+ distance_orderable | t | f | t | f | t | f | f
returnable | t | f | f | t | t | f | f
search_array | t | f | f | f | f | f | f
search_nulls | t | f | t | t | t | f | t
@@ -231,7 +231,7 @@ select col, prop, pg_index_column_has_property(o, col, prop)
1 | desc | f
1 | nulls_first | f
1 | nulls_last | t
- 1 | distance_orderable | f
+ 1 | distance_orderable | t
1 | returnable | t
1 | bogus |
2 | orderable | f
diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out
index 1bcc946..b9b819c 100644
--- a/src/test/regress/expected/date.out
+++ b/src/test/regress/expected/date.out
@@ -1477,3 +1477,64 @@ select make_time(10, 55, 100.1);
ERROR: time field value out of range: 10:55:100.1
select make_time(24, 0, 2.1);
ERROR: time field value out of range: 24:00:2.1
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+----------
+ | 16006
+ | 15941
+ | 1802
+ | 1801
+ | 1800
+ | 1799
+ | 1436
+ | 1435
+ | 1434
+ | 308
+ | 307
+ | 306
+ | 13578
+ | 13944
+ | 14311
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16006 days 1 hour 23 mins 45 secs
+ | @ 15941 days 1 hour 23 mins 45 secs
+ | @ 1802 days 1 hour 23 mins 45 secs
+ | @ 1801 days 1 hour 23 mins 45 secs
+ | @ 1800 days 1 hour 23 mins 45 secs
+ | @ 1799 days 1 hour 23 mins 45 secs
+ | @ 1436 days 1 hour 23 mins 45 secs
+ | @ 1435 days 1 hour 23 mins 45 secs
+ | @ 1434 days 1 hour 23 mins 45 secs
+ | @ 308 days 1 hour 23 mins 45 secs
+ | @ 307 days 1 hour 23 mins 45 secs
+ | @ 306 days 1 hour 23 mins 45 secs
+ | @ 13577 days 22 hours 36 mins 15 secs
+ | @ 13943 days 22 hours 36 mins 15 secs
+ | @ 14310 days 22 hours 36 mins 15 secs
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16005 days 14 hours 23 mins 45 secs
+ | @ 15940 days 14 hours 23 mins 45 secs
+ | @ 1801 days 14 hours 23 mins 45 secs
+ | @ 1800 days 14 hours 23 mins 45 secs
+ | @ 1799 days 14 hours 23 mins 45 secs
+ | @ 1798 days 14 hours 23 mins 45 secs
+ | @ 1435 days 14 hours 23 mins 45 secs
+ | @ 1434 days 14 hours 23 mins 45 secs
+ | @ 1433 days 14 hours 23 mins 45 secs
+ | @ 307 days 14 hours 23 mins 45 secs
+ | @ 306 days 14 hours 23 mins 45 secs
+ | @ 305 days 15 hours 23 mins 45 secs
+ | @ 13578 days 8 hours 36 mins 15 secs
+ | @ 13944 days 8 hours 36 mins 15 secs
+ | @ 14311 days 8 hours 36 mins 15 secs
+(15 rows)
+
diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out
index cf78277..ce29924e 100644
--- a/src/test/regress/expected/float4.out
+++ b/src/test/regress/expected/float4.out
@@ -273,6 +273,26 @@ SELECT '' AS five, * FROM FLOAT4_TBL;
| -1.2345679e-20
(5 rows)
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+----------------+---------------
+ | 0 | 1004.3
+ | -34.84 | 1039.14
+ | -1004.3 | 2008.6
+ | -1.2345679e+20 | 1.2345679e+20
+ | -1.2345679e-20 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+----------------+------------------------
+ | 0 | 1004.3
+ | -34.84 | 1039.1400001525878
+ | -1004.3 | 2008.5999877929687
+ | -1.2345679e+20 | 1.2345678955701443e+20
+ | -1.2345679e-20 | 1004.3
+(5 rows)
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
int2
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index c3a6f53..5485c19 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -413,6 +413,27 @@ SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
| 1.2345678901234e-200 | 2.3112042409018e-67
(5 rows)
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.3
+ | 1004.3 | 0
+ | -34.84 | 1039.14
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.29998779297
+ | 1004.3 | 1.22070312045253e-05
+ | -34.84 | 1039.13998779297
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.29998779297
+(5 rows)
+
SELECT '' AS five, * FROM FLOAT8_TBL;
five | f1
------+----------------------
diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out
index 8c255b9..0edc57e 100644
--- a/src/test/regress/expected/int2.out
+++ b/src/test/regress/expected/int2.out
@@ -242,6 +242,39 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
| -32767 | -16383
(5 rows)
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+ERROR: smallint out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+ four | f1 | x
+------+-------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
-- corner cases
SELECT (-1::int2<<15)::text;
text
diff --git a/src/test/regress/expected/int4.out b/src/test/regress/expected/int4.out
index bda7a8d..3735dbc 100644
--- a/src/test/regress/expected/int4.out
+++ b/src/test/regress/expected/int4.out
@@ -247,6 +247,38 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
| -2147483647 | -1073741823
(5 rows)
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+ERROR: integer out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+ five | f1 | x
+------+-------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+ | -2147483647 | 2147483649
+(5 rows)
+
--
-- more complex expressions
--
diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out
index 8447a28..d56886a 100644
--- a/src/test/regress/expected/int8.out
+++ b/src/test/regress/expected/int8.out
@@ -432,6 +432,37 @@ SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 A
4567890123457035 | -4567890123456543 | 1123700970370370094 | 0
(5 rows)
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
SELECT q2, abs(q2) FROM INT8_TBL;
q2 | abs
-------------------+------------------
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index f88f345..cb95adf 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -207,6 +207,21 @@ SELECT '' AS fortyfive, r1.*, r2.*
| 34 years | 6 years
(45 rows)
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+ ten | ?column?
+-----+----------------------------
+ | 2 days 02:59:00
+ | 2 days -02:00:00
+ | 8 days -03:00:00
+ | 34 years -2 days -03:00:00
+ | 3 mons -2 days -03:00:00
+ | 2 days 03:00:14
+ | 1 day 00:56:56
+ | 6 years -2 days -03:00:00
+ | 5 mons -2 days -03:00:00
+ | 5 mons -2 days +09:00:00
+(10 rows)
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out
index ab86595..fb2a489 100644
--- a/src/test/regress/expected/money.out
+++ b/src/test/regress/expected/money.out
@@ -123,6 +123,12 @@ SELECT m / 2::float4 FROM money_data;
$61.50
(1 row)
+SELECT m <-> '$123.45' FROM money_data;
+ ?column?
+----------
+ $0.45
+(1 row)
+
-- All true
SELECT m = '$123.00' FROM money_data;
?column?
diff --git a/src/test/regress/expected/oid.out b/src/test/regress/expected/oid.out
index 1eab9cc..5339a48 100644
--- a/src/test/regress/expected/oid.out
+++ b/src/test/regress/expected/oid.out
@@ -119,4 +119,17 @@ SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
| 99999999
(3 rows)
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+ eight | f1 | ?column?
+-------+------------+------------
+ | 1234 | 1111
+ | 1235 | 1112
+ | 987 | 864
+ | 4294966256 | 4294966133
+ | 99999999 | 99999876
+ | 5 | 118
+ | 10 | 113
+ | 15 | 108
+(8 rows)
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index ce25ee0..e637420 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -750,6 +750,7 @@ macaddr8_gt(macaddr8,macaddr8)
macaddr8_ge(macaddr8,macaddr8)
macaddr8_ne(macaddr8,macaddr8)
macaddr8_cmp(macaddr8,macaddr8)
+oiddist(oid,oid)
-- restore normal output mode
\a\t
-- List of functions used by libpq's fe-lobj.c
@@ -1332,7 +1333,7 @@ WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND
ao.amoprighttype = ap.amprocrighttype AND
ap.amprocnum = 1 AND
(pp.provolatile != po.provolatile OR
- pp.proleakproof != po.proleakproof)
+ (NOT pp.proleakproof AND po.proleakproof))
ORDER BY 1;
proc | vp | lp | opr | vo | lo
-------------------------------------------+----+----+-------------------------------+----+----
@@ -1862,6 +1863,7 @@ ORDER BY 1, 2, 3;
403 | 5 | *>
403 | 5 | >
403 | 5 | ~>~
+ 403 | 6 | <->
405 | 1 | =
783 | 1 | <<
783 | 1 | @@
@@ -1971,7 +1973,7 @@ ORDER BY 1, 2, 3;
4000 | 26 | >>
4000 | 27 | >>=
4000 | 28 | ^@
-(123 rows)
+(124 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/expected/time.out b/src/test/regress/expected/time.out
index 8e0afe6..ee74faa 100644
--- a/src/test/regress/expected/time.out
+++ b/src/test/regress/expected/time.out
@@ -86,3 +86,19 @@ ERROR: operator is not unique: time without time zone + time without time zone
LINE 1: SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
+ Ten | Distance
+-------------+-------------------------------
+ 00:00:00 | @ 1 hour 23 mins 45 secs
+ 01:00:00 | @ 23 mins 45 secs
+ 02:03:00 | @ 39 mins 15 secs
+ 11:59:00 | @ 10 hours 35 mins 15 secs
+ 12:00:00 | @ 10 hours 36 mins 15 secs
+ 12:01:00 | @ 10 hours 37 mins 15 secs
+ 23:59:00 | @ 22 hours 35 mins 15 secs
+ 23:59:59.99 | @ 22 hours 36 mins 14.99 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+(10 rows)
+
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index 4a2fabd..dcb4205 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -1604,3 +1604,214 @@ SELECT make_timestamp(2014,12,28,6,30,45.887);
Sun Dec 28 06:30:45.887 2014
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 231 days 18 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 10 hours 45 mins 58 secs
+ | @ 324 days 11 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 21 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 5 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 1 hour 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 231 days 16 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 12 hours 9 mins 43 secs
+ | @ 324 days 13 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 324 days 23 hours 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 6 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 14 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 1 hour 9 mins 43 secs
+ | @ 324 days 2 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 12 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(63 rows)
+
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 8a4c719..0a05e37 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -2544,3 +2544,217 @@ select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
Tue Jan 17 16:00:00 2017 PST
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 8 hours
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 7 hours 27 mins 59 secs
+ | @ 231 days 17 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 19 hours 45 mins 58 secs
+ | @ 324 days 21 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 22 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 9 hours 27 mins 59 secs
+ | @ 1303 days 10 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 9 hours 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 8 hours 51 mins 44 secs
+ | @ 231 days 15 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 21 hours 9 mins 43 secs
+ | @ 324 days 23 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 325 days 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 10 hours 51 mins 44 secs
+ | @ 1303 days 11 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 22 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 10 hours 9 mins 43 secs
+ | @ 324 days 12 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 13 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1452 days 23 hours 51 mins 44 secs
+ | @ 1303 days 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(64 rows)
+
diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql
index 22f80f2..24be476 100644
--- a/src/test/regress/sql/date.sql
+++ b/src/test/regress/sql/date.sql
@@ -346,3 +346,8 @@ select make_date(2013, 13, 1);
select make_date(2013, 11, -1);
select make_time(10, 55, 100.1);
select make_time(24, 0, 2.1);
+
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql
index 646027f..35b5942 100644
--- a/src/test/regress/sql/float4.sql
+++ b/src/test/regress/sql/float4.sql
@@ -87,6 +87,9 @@ UPDATE FLOAT4_TBL
SELECT '' AS five, * FROM FLOAT4_TBL;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
SELECT '32767.6'::float4::int2;
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index a333218..7fe9bca 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -131,6 +131,9 @@ SELECT ||/ float8 '27' AS three;
SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
SELECT '' AS five, * FROM FLOAT8_TBL;
diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql
index 7dbafb6..16dd5d8 100644
--- a/src/test/regress/sql/int2.sql
+++ b/src/test/regress/sql/int2.sql
@@ -84,6 +84,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+
-- corner cases
SELECT (-1::int2<<15)::text;
SELECT ((-1::int2<<15)+1::int2)::text;
diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql
index f014cb2..cff32946 100644
--- a/src/test/regress/sql/int4.sql
+++ b/src/test/regress/sql/int4.sql
@@ -93,6 +93,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT4_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+
--
-- more complex expressions
--
diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql
index e890452..d7f5bde 100644
--- a/src/test/regress/sql/int8.sql
+++ b/src/test/regress/sql/int8.sql
@@ -89,6 +89,11 @@ SELECT q1 + 42::int2 AS "8plus2", q1 - 42::int2 AS "8minus2", q1 * 42::int2 AS "
-- int2 op int8
SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 AS "2mul8", 246::int2 / q1 AS "2div8" FROM INT8_TBL;
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+
SELECT q2, abs(q2) FROM INT8_TBL;
SELECT min(q1), min(q2) FROM INT8_TBL;
SELECT max(q1), max(q2) FROM INT8_TBL;
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index bc5537d..d51c866 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -59,6 +59,8 @@ SELECT '' AS fortyfive, r1.*, r2.*
WHERE r1.f1 > r2.f1
ORDER BY r1.f1, r2.f1;
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql
index 37b9ecc..8428d59 100644
--- a/src/test/regress/sql/money.sql
+++ b/src/test/regress/sql/money.sql
@@ -25,6 +25,7 @@ SELECT m / 2::float8 FROM money_data;
SELECT m * 2::float4 FROM money_data;
SELECT 2::float4 * m FROM money_data;
SELECT m / 2::float4 FROM money_data;
+SELECT m <-> '$123.45' FROM money_data;
-- All true
SELECT m = '$123.00' FROM money_data;
diff --git a/src/test/regress/sql/oid.sql b/src/test/regress/sql/oid.sql
index 4a09689..9f54f92 100644
--- a/src/test/regress/sql/oid.sql
+++ b/src/test/regress/sql/oid.sql
@@ -40,4 +40,6 @@ SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 >= '1234';
SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index e2014fc..959928b 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -818,7 +818,7 @@ WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND
ao.amoprighttype = ap.amprocrighttype AND
ap.amprocnum = 1 AND
(pp.provolatile != po.provolatile OR
- pp.proleakproof != po.proleakproof)
+ (NOT pp.proleakproof AND po.proleakproof))
ORDER BY 1;
diff --git a/src/test/regress/sql/time.sql b/src/test/regress/sql/time.sql
index 99a1562..31f0330 100644
--- a/src/test/regress/sql/time.sql
+++ b/src/test/regress/sql/time.sql
@@ -40,3 +40,6 @@ SELECT f1 AS "Eight" FROM TIME_TBL WHERE f1 >= '00:00';
-- where we do mixed-type arithmetic. - thomas 2000-12-02
SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
+
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index b7957cb..5d023dd 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -230,3 +230,11 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
-- timestamp numeric fields constructor
SELECT make_timestamp(2014,12,28,6,30,45.887);
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index c3bd46c..7f0525d 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -464,3 +464,11 @@ insert into tmptz values ('2017-01-18 00:00+00');
explain (costs off)
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
0006-Remove-distance-operators-from-btree_gist-v07.patchtext/x-patch; name=0006-Remove-distance-operators-from-btree_gist-v07.patchDownload
diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile
index af65120..46ab241 100644
--- a/contrib/btree_gist/Makefile
+++ b/contrib/btree_gist/Makefile
@@ -11,8 +11,9 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
EXTENSION = btree_gist
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
- btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \
- btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql
+ btree_gist--1.1--1.2.sql btree_gist--1.2--1.3.sql \
+ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \
+ btree_gist--1.5--1.6.sql btree_gist--1.6.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c
index 894d0a2..1b0e317 100644
--- a/contrib/btree_gist/btree_cash.c
+++ b/contrib/btree_gist/btree_cash.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(cash_dist);
Datum
cash_dist(PG_FUNCTION_ARGS)
{
- Cash a = PG_GETARG_CASH(0);
- Cash b = PG_GETARG_CASH(1);
- Cash r;
- Cash ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("money out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_CASH(ra);
+ return cash_distance(fcinfo);
}
/**************************************************
diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c
index 992ce57..f3f0fa1 100644
--- a/contrib/btree_gist/btree_date.c
+++ b/contrib/btree_gist/btree_date.c
@@ -113,12 +113,7 @@ PG_FUNCTION_INFO_V1(date_dist);
Datum
date_dist(PG_FUNCTION_ARGS)
{
- /* we assume the difference can't overflow */
- Datum diff = DirectFunctionCall2(date_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+ return date_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c
index 6b20f44..0a9148d 100644
--- a/contrib/btree_gist/btree_float4.c
+++ b/contrib/btree_gist/btree_float4.c
@@ -93,14 +93,7 @@ PG_FUNCTION_INFO_V1(float4_dist);
Datum
float4_dist(PG_FUNCTION_ARGS)
{
- float4 a = PG_GETARG_FLOAT4(0);
- float4 b = PG_GETARG_FLOAT4(1);
- float4 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT4(Abs(r));
+ return float4dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c
index ee114cb..8b73b57 100644
--- a/contrib/btree_gist/btree_float8.c
+++ b/contrib/btree_gist/btree_float8.c
@@ -101,14 +101,7 @@ PG_FUNCTION_INFO_V1(float8_dist);
Datum
float8_dist(PG_FUNCTION_ARGS)
{
- float8 a = PG_GETARG_FLOAT8(0);
- float8 b = PG_GETARG_FLOAT8(1);
- float8 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT8(Abs(r));
+ return float8dist(fcinfo);
}
/**************************************************
diff --git a/contrib/btree_gist/btree_gist--1.2.sql b/contrib/btree_gist/btree_gist--1.2.sql
deleted file mode 100644
index 1efe753..0000000
--- a/contrib/btree_gist/btree_gist--1.2.sql
+++ /dev/null
@@ -1,1570 +0,0 @@
-/* contrib/btree_gist/btree_gist--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
-
-CREATE FUNCTION gbtreekey4_in(cstring)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey4_out(gbtreekey4)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey4 (
- INTERNALLENGTH = 4,
- INPUT = gbtreekey4_in,
- OUTPUT = gbtreekey4_out
-);
-
-CREATE FUNCTION gbtreekey8_in(cstring)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey8_out(gbtreekey8)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey8 (
- INTERNALLENGTH = 8,
- INPUT = gbtreekey8_in,
- OUTPUT = gbtreekey8_out
-);
-
-CREATE FUNCTION gbtreekey16_in(cstring)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey16_out(gbtreekey16)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey16 (
- INTERNALLENGTH = 16,
- INPUT = gbtreekey16_in,
- OUTPUT = gbtreekey16_out
-);
-
-CREATE FUNCTION gbtreekey32_in(cstring)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey32_out(gbtreekey32)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey32 (
- INTERNALLENGTH = 32,
- INPUT = gbtreekey32_in,
- OUTPUT = gbtreekey32_out
-);
-
-CREATE FUNCTION gbtreekey_var_in(cstring)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey_var (
- INTERNALLENGTH = VARIABLE,
- INPUT = gbtreekey_var_in,
- OUTPUT = gbtreekey_var_out,
- STORAGE = EXTENDED
-);
-
---distance operators
-
-CREATE FUNCTION cash_dist(money, money)
-RETURNS money
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = money,
- RIGHTARG = money,
- PROCEDURE = cash_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION date_dist(date, date)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = date,
- RIGHTARG = date,
- PROCEDURE = date_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float4_dist(float4, float4)
-RETURNS float4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float4,
- RIGHTARG = float4,
- PROCEDURE = float4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float8_dist(float8, float8)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float8,
- RIGHTARG = float8,
- PROCEDURE = float8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int2_dist(int2, int2)
-RETURNS int2
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int2,
- RIGHTARG = int2,
- PROCEDURE = int2_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int4_dist(int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int4,
- RIGHTARG = int4,
- PROCEDURE = int4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int8_dist(int8, int8)
-RETURNS int8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int8,
- RIGHTARG = int8,
- PROCEDURE = int8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION interval_dist(interval, interval)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = interval,
- RIGHTARG = interval,
- PROCEDURE = interval_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION oid_dist(oid, oid)
-RETURNS oid
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = oid,
- RIGHTARG = oid,
- PROCEDURE = oid_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION time_dist(time, time)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = time,
- RIGHTARG = time,
- PROCEDURE = time_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION ts_dist(timestamp, timestamp)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamp,
- RIGHTARG = timestamp,
- PROCEDURE = ts_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION tstz_dist(timestamptz, timestamptz)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamptz,
- RIGHTARG = timestamptz,
- PROCEDURE = tstz_dist,
- COMMUTATOR = '<->'
-);
-
-
---
---
---
--- oid ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_oid_ops
-DEFAULT FOR TYPE oid USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
- FUNCTION 2 gbt_oid_union (internal, internal),
- FUNCTION 3 gbt_oid_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
- FUNCTION 6 gbt_oid_picksplit (internal, internal),
- FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
--- Add operators that are new in 9.1. We do it like this, leaving them
--- "loose" in the operator family rather than bound into the opclass, because
--- that's the only state that can be reproduced during an upgrade from 9.0.
-ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
- OPERATOR 6 <> (oid, oid) ,
- OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
- FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
- -- Also add support function for index-only-scans, added in 9.5.
- FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
-
-
---
---
---
--- int2 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_union(internal, internal)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int2_ops
-DEFAULT FOR TYPE int2 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
- FUNCTION 2 gbt_int2_union (internal, internal),
- FUNCTION 3 gbt_int2_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int2_picksplit (internal, internal),
- FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
- STORAGE gbtreekey4;
-
-ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
- OPERATOR 6 <> (int2, int2) ,
- OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
- FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
-
---
---
---
--- int4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int4_ops
-DEFAULT FOR TYPE int4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
- FUNCTION 2 gbt_int4_union (internal, internal),
- FUNCTION 3 gbt_int4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int4_picksplit (internal, internal),
- FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
- OPERATOR 6 <> (int4, int4) ,
- OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
- FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
-
-
---
---
---
--- int8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int8_ops
-DEFAULT FOR TYPE int8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
- FUNCTION 2 gbt_int8_union (internal, internal),
- FUNCTION 3 gbt_int8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int8_picksplit (internal, internal),
- FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
- OPERATOR 6 <> (int8, int8) ,
- OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
- FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
-
---
---
---
--- float4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float4_ops
-DEFAULT FOR TYPE float4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
- FUNCTION 2 gbt_float4_union (internal, internal),
- FUNCTION 3 gbt_float4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float4_picksplit (internal, internal),
- FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
- OPERATOR 6 <> (float4, float4) ,
- OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
- FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
-
---
---
---
--- float8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float8_ops
-DEFAULT FOR TYPE float8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
- FUNCTION 2 gbt_float8_union (internal, internal),
- FUNCTION 3 gbt_float8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float8_picksplit (internal, internal),
- FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
- OPERATOR 6 <> (float8, float8) ,
- OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
- FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
-
---
---
---
--- timestamp ops
---
---
---
-
-CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamp_ops
-DEFAULT FOR TYPE timestamp USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_ts_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
- OPERATOR 6 <> (timestamp, timestamp) ,
- OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
- FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamptz_ops
-DEFAULT FOR TYPE timestamptz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_tstz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
- OPERATOR 6 <> (timestamptz, timestamptz) ,
- OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
- FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
-
---
---
---
--- time ops
---
---
---
-
-CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_time_ops
-DEFAULT FOR TYPE time USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_time_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
- OPERATOR 6 <> (time, time) ,
- OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
- FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
-
-
-CREATE OPERATOR CLASS gist_timetz_ops
-DEFAULT FOR TYPE timetz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_timetz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
- OPERATOR 6 <> (timetz, timetz) ;
- -- no 'fetch' function, as the compress function is lossy.
-
-
---
---
---
--- date ops
---
---
---
-
-CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_date_ops
-DEFAULT FOR TYPE date USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
- FUNCTION 2 gbt_date_union (internal, internal),
- FUNCTION 3 gbt_date_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_date_penalty (internal, internal, internal),
- FUNCTION 6 gbt_date_picksplit (internal, internal),
- FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
- OPERATOR 6 <> (date, date) ,
- OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
- FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
-
-
---
---
---
--- interval ops
---
---
---
-
-CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_union(internal, internal)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_interval_ops
-DEFAULT FOR TYPE interval USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
- FUNCTION 2 gbt_intv_union (internal, internal),
- FUNCTION 3 gbt_intv_compress (internal),
- FUNCTION 4 gbt_intv_decompress (internal),
- FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
- FUNCTION 6 gbt_intv_picksplit (internal, internal),
- FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
- STORAGE gbtreekey32;
-
-ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
- OPERATOR 6 <> (interval, interval) ,
- OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
- FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
-
-
---
---
---
--- cash ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cash_ops
-DEFAULT FOR TYPE money USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
- FUNCTION 2 gbt_cash_union (internal, internal),
- FUNCTION 3 gbt_cash_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
- FUNCTION 6 gbt_cash_picksplit (internal, internal),
- FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
- OPERATOR 6 <> (money, money) ,
- OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
- FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
- FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
-
-
---
---
---
--- macaddr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_macaddr_ops
-DEFAULT FOR TYPE macaddr USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
- FUNCTION 2 gbt_macad_union (internal, internal),
- FUNCTION 3 gbt_macad_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
- FUNCTION 6 gbt_macad_picksplit (internal, internal),
- FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
- OPERATOR 6 <> (macaddr, macaddr) ,
- FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
-
-
---
---
---
--- text/ bpchar ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_text_ops
-DEFAULT FOR TYPE text USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_text_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
- OPERATOR 6 <> (text, text) ,
- FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
-
-
----- Create the operator class
-CREATE OPERATOR CLASS gist_bpchar_ops
-DEFAULT FOR TYPE bpchar USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_bpchar_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
- OPERATOR 6 <> (bpchar, bpchar) ,
- FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
-
---
---
--- bytea ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bytea_ops
-DEFAULT FOR TYPE bytea USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
- FUNCTION 2 gbt_bytea_union (internal, internal),
- FUNCTION 3 gbt_bytea_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bytea_picksplit (internal, internal),
- FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
- OPERATOR 6 <> (bytea, bytea) ,
- FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
-
-
---
---
---
--- numeric ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_numeric_ops
-DEFAULT FOR TYPE numeric USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
- FUNCTION 2 gbt_numeric_union (internal, internal),
- FUNCTION 3 gbt_numeric_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
- FUNCTION 6 gbt_numeric_picksplit (internal, internal),
- FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
- OPERATOR 6 <> (numeric, numeric) ,
- FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
-
-
---
---
--- bit ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bit_ops
-DEFAULT FOR TYPE bit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
- OPERATOR 6 <> (bit, bit) ,
- FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
-
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_vbit_ops
-DEFAULT FOR TYPE varbit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
- OPERATOR 6 <> (varbit, varbit) ,
- FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
-
-
---
---
---
--- inet/cidr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_inet_ops
-DEFAULT FOR TYPE inet USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cidr_ops
-DEFAULT FOR TYPE cidr USING gist
-AS
- OPERATOR 1 < (inet, inet) ,
- OPERATOR 2 <= (inet, inet) ,
- OPERATOR 3 = (inet, inet) ,
- OPERATOR 4 >= (inet, inet) ,
- OPERATOR 5 > (inet, inet) ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
diff --git a/contrib/btree_gist/btree_gist--1.5--1.6.sql b/contrib/btree_gist/btree_gist--1.5--1.6.sql
new file mode 100644
index 0000000..ef4424e
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.5--1.6.sql
@@ -0,0 +1,99 @@
+/* contrib/btree_gist/btree_gist--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.6'" to load this file. \quit
+
+-- drop btree_gist distance operators from opfamilies
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist DROP OPERATOR 15 (int2, int2);
+ALTER OPERATOR FAMILY gist_int4_ops USING gist DROP OPERATOR 15 (int4, int4);
+ALTER OPERATOR FAMILY gist_int8_ops USING gist DROP OPERATOR 15 (int8, int8);
+ALTER OPERATOR FAMILY gist_float4_ops USING gist DROP OPERATOR 15 (float4, float4);
+ALTER OPERATOR FAMILY gist_float8_ops USING gist DROP OPERATOR 15 (float8, float8);
+ALTER OPERATOR FAMILY gist_oid_ops USING gist DROP OPERATOR 15 (oid, oid);
+ALTER OPERATOR FAMILY gist_cash_ops USING gist DROP OPERATOR 15 (money, money);
+ALTER OPERATOR FAMILY gist_date_ops USING gist DROP OPERATOR 15 (date, date);
+ALTER OPERATOR FAMILY gist_time_ops USING gist DROP OPERATOR 15 (time, time);
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist DROP OPERATOR 15 (timestamp, timestamp);
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist DROP OPERATOR 15 (timestamptz, timestamptz);
+ALTER OPERATOR FAMILY gist_interval_ops USING gist DROP OPERATOR 15 (interval, interval);
+
+-- add pg_catalog distance operators to opfamilies
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops;
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops;
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops;
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops;
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops;
+
+-- disable implicit pg_catalog search
+
+DO
+$$
+BEGIN
+ EXECUTE 'SET LOCAL search_path TO ' || current_schema() || ', pg_catalog';
+END
+$$;
+
+-- drop distance operators
+
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int2, int2);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int4, int4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int8, int8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float4, float4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float8, float8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (oid, oid);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (money, money);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (date, date);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (time, time);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (interval, interval);
+
+DROP OPERATOR <-> (int2, int2);
+DROP OPERATOR <-> (int4, int4);
+DROP OPERATOR <-> (int8, int8);
+DROP OPERATOR <-> (float4, float4);
+DROP OPERATOR <-> (float8, float8);
+DROP OPERATOR <-> (oid, oid);
+DROP OPERATOR <-> (money, money);
+DROP OPERATOR <-> (date, date);
+DROP OPERATOR <-> (time, time);
+DROP OPERATOR <-> (timestamp, timestamp);
+DROP OPERATOR <-> (timestamptz, timestamptz);
+DROP OPERATOR <-> (interval, interval);
+
+-- drop distance functions
+
+ALTER EXTENSION btree_gist DROP FUNCTION int2_dist(int2, int2);
+ALTER EXTENSION btree_gist DROP FUNCTION int4_dist(int4, int4);
+ALTER EXTENSION btree_gist DROP FUNCTION int8_dist(int8, int8);
+ALTER EXTENSION btree_gist DROP FUNCTION float4_dist(float4, float4);
+ALTER EXTENSION btree_gist DROP FUNCTION float8_dist(float8, float8);
+ALTER EXTENSION btree_gist DROP FUNCTION oid_dist(oid, oid);
+ALTER EXTENSION btree_gist DROP FUNCTION cash_dist(money, money);
+ALTER EXTENSION btree_gist DROP FUNCTION date_dist(date, date);
+ALTER EXTENSION btree_gist DROP FUNCTION time_dist(time, time);
+ALTER EXTENSION btree_gist DROP FUNCTION ts_dist(timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP FUNCTION interval_dist(interval, interval);
+
+DROP FUNCTION int2_dist(int2, int2);
+DROP FUNCTION int4_dist(int4, int4);
+DROP FUNCTION int8_dist(int8, int8);
+DROP FUNCTION float4_dist(float4, float4);
+DROP FUNCTION float8_dist(float8, float8);
+DROP FUNCTION oid_dist(oid, oid);
+DROP FUNCTION cash_dist(money, money);
+DROP FUNCTION date_dist(date, date);
+DROP FUNCTION time_dist(time, time);
+DROP FUNCTION ts_dist(timestamp, timestamp);
+DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+DROP FUNCTION interval_dist(interval, interval);
diff --git a/contrib/btree_gist/btree_gist--1.6.sql b/contrib/btree_gist/btree_gist--1.6.sql
new file mode 100644
index 0000000..8ff8eb5
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.6.sql
@@ -0,0 +1,1615 @@
+/* contrib/btree_gist/btree_gist--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
+
+CREATE FUNCTION gbtreekey4_in(cstring)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey4_out(gbtreekey4)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey4 (
+ INTERNALLENGTH = 4,
+ INPUT = gbtreekey4_in,
+ OUTPUT = gbtreekey4_out
+);
+
+CREATE FUNCTION gbtreekey8_in(cstring)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey8_out(gbtreekey8)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey8 (
+ INTERNALLENGTH = 8,
+ INPUT = gbtreekey8_in,
+ OUTPUT = gbtreekey8_out
+);
+
+CREATE FUNCTION gbtreekey16_in(cstring)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey16_out(gbtreekey16)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey16 (
+ INTERNALLENGTH = 16,
+ INPUT = gbtreekey16_in,
+ OUTPUT = gbtreekey16_out
+);
+
+CREATE FUNCTION gbtreekey32_in(cstring)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey32_out(gbtreekey32)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey32 (
+ INTERNALLENGTH = 32,
+ INPUT = gbtreekey32_in,
+ OUTPUT = gbtreekey32_out
+);
+
+CREATE FUNCTION gbtreekey_var_in(cstring)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey_var (
+ INTERNALLENGTH = VARIABLE,
+ INPUT = gbtreekey_var_in,
+ OUTPUT = gbtreekey_var_out,
+ STORAGE = EXTENDED
+);
+
+
+--
+--
+--
+-- oid ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_oid_ops
+DEFAULT FOR TYPE oid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
+ FUNCTION 2 gbt_oid_union (internal, internal),
+ FUNCTION 3 gbt_oid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_oid_picksplit (internal, internal),
+ FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+-- Add operators that are new in 9.1. We do it like this, leaving them
+-- "loose" in the operator family rather than bound into the opclass, because
+-- that's the only state that can be reproduced during an upgrade from 9.0.
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
+ OPERATOR 6 <> (oid, oid) ,
+ OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
+ FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
+ -- Also add support function for index-only-scans, added in 9.5.
+ FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
+
+
+--
+--
+--
+-- int2 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_union(internal, internal)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int2_ops
+DEFAULT FOR TYPE int2 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
+ FUNCTION 2 gbt_int2_union (internal, internal),
+ FUNCTION 3 gbt_int2_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int2_picksplit (internal, internal),
+ FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
+ STORAGE gbtreekey4;
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
+ OPERATOR 6 <> (int2, int2) ,
+ OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
+ FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
+
+--
+--
+--
+-- int4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int4_ops
+DEFAULT FOR TYPE int4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
+ FUNCTION 2 gbt_int4_union (internal, internal),
+ FUNCTION 3 gbt_int4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int4_picksplit (internal, internal),
+ FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
+ OPERATOR 6 <> (int4, int4) ,
+ OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
+ FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
+
+
+--
+--
+--
+-- int8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int8_ops
+DEFAULT FOR TYPE int8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
+ FUNCTION 2 gbt_int8_union (internal, internal),
+ FUNCTION 3 gbt_int8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int8_picksplit (internal, internal),
+ FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
+ OPERATOR 6 <> (int8, int8) ,
+ OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
+ FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
+
+--
+--
+--
+-- float4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float4_ops
+DEFAULT FOR TYPE float4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
+ FUNCTION 2 gbt_float4_union (internal, internal),
+ FUNCTION 3 gbt_float4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float4_picksplit (internal, internal),
+ FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
+ OPERATOR 6 <> (float4, float4) ,
+ OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
+ FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
+
+--
+--
+--
+-- float8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float8_ops
+DEFAULT FOR TYPE float8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
+ FUNCTION 2 gbt_float8_union (internal, internal),
+ FUNCTION 3 gbt_float8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float8_picksplit (internal, internal),
+ FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
+ OPERATOR 6 <> (float8, float8) ,
+ OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
+ FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
+
+--
+--
+--
+-- timestamp ops
+--
+--
+--
+
+CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamp_ops
+DEFAULT FOR TYPE timestamp USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_ts_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
+ OPERATOR 6 <> (timestamp, timestamp) ,
+ OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
+ FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamptz_ops
+DEFAULT FOR TYPE timestamptz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_tstz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
+ OPERATOR 6 <> (timestamptz, timestamptz) ,
+ OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
+ FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
+
+--
+--
+--
+-- time ops
+--
+--
+--
+
+CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_time_ops
+DEFAULT FOR TYPE time USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_time_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
+ OPERATOR 6 <> (time, time) ,
+ OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
+ FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
+
+
+CREATE OPERATOR CLASS gist_timetz_ops
+DEFAULT FOR TYPE timetz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_timetz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
+ OPERATOR 6 <> (timetz, timetz) ;
+ -- no 'fetch' function, as the compress function is lossy.
+
+
+--
+--
+--
+-- date ops
+--
+--
+--
+
+CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_date_ops
+DEFAULT FOR TYPE date USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
+ FUNCTION 2 gbt_date_union (internal, internal),
+ FUNCTION 3 gbt_date_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_date_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_date_picksplit (internal, internal),
+ FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
+ OPERATOR 6 <> (date, date) ,
+ OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
+ FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
+
+
+--
+--
+--
+-- interval ops
+--
+--
+--
+
+CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_interval_ops
+DEFAULT FOR TYPE interval USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
+ FUNCTION 2 gbt_intv_union (internal, internal),
+ FUNCTION 3 gbt_intv_compress (internal),
+ FUNCTION 4 gbt_intv_decompress (internal),
+ FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_intv_picksplit (internal, internal),
+ FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
+ OPERATOR 6 <> (interval, interval) ,
+ OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
+ FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
+
+
+--
+--
+--
+-- cash ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cash_ops
+DEFAULT FOR TYPE money USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
+ FUNCTION 2 gbt_cash_union (internal, internal),
+ FUNCTION 3 gbt_cash_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_cash_picksplit (internal, internal),
+ FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
+ OPERATOR 6 <> (money, money) ,
+ OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
+ FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
+ FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
+
+
+--
+--
+--
+-- macaddr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr_ops
+DEFAULT FOR TYPE macaddr USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
+ FUNCTION 2 gbt_macad_union (internal, internal),
+ FUNCTION 3 gbt_macad_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
+ OPERATOR 6 <> (macaddr, macaddr) ,
+ FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
+
+
+--
+--
+--
+-- text/ bpchar ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_text_ops
+DEFAULT FOR TYPE text USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_text_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
+ OPERATOR 6 <> (text, text) ,
+ FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
+
+
+---- Create the operator class
+CREATE OPERATOR CLASS gist_bpchar_ops
+DEFAULT FOR TYPE bpchar USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_bpchar_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
+ OPERATOR 6 <> (bpchar, bpchar) ,
+ FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
+
+--
+--
+-- bytea ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bytea_ops
+DEFAULT FOR TYPE bytea USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
+ FUNCTION 2 gbt_bytea_union (internal, internal),
+ FUNCTION 3 gbt_bytea_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bytea_picksplit (internal, internal),
+ FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
+ OPERATOR 6 <> (bytea, bytea) ,
+ FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- numeric ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_numeric_ops
+DEFAULT FOR TYPE numeric USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
+ FUNCTION 2 gbt_numeric_union (internal, internal),
+ FUNCTION 3 gbt_numeric_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_numeric_picksplit (internal, internal),
+ FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
+ OPERATOR 6 <> (numeric, numeric) ,
+ FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
+
+
+--
+--
+-- bit ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bit_ops
+DEFAULT FOR TYPE bit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
+ OPERATOR 6 <> (bit, bit) ,
+ FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
+
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_vbit_ops
+DEFAULT FOR TYPE varbit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
+ OPERATOR 6 <> (varbit, varbit) ,
+ FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- inet/cidr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_inet_ops
+DEFAULT FOR TYPE inet USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cidr_ops
+DEFAULT FOR TYPE cidr USING gist
+AS
+ OPERATOR 1 < (inet, inet) ,
+ OPERATOR 2 <= (inet, inet) ,
+ OPERATOR 3 = (inet, inet) ,
+ OPERATOR 4 >= (inet, inet) ,
+ OPERATOR 5 > (inet, inet) ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+--
+--
+--
+-- uuid ops
+--
+--
+---- define the GiST support methods
+CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_uuid_ops
+DEFAULT FOR TYPE uuid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal),
+ FUNCTION 2 gbt_uuid_union (internal, internal),
+ FUNCTION 3 gbt_uuid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_uuid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_uuid_picksplit (internal, internal),
+ FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+-- These are "loose" in the opfamily for consistency with the rest of btree_gist
+ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
+ OPERATOR 6 <> (uuid, uuid) ,
+ FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ;
+
+
+-- Add support for indexing macaddr8 columns
+
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad8_consistent(internal,macaddr8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr8_ops
+DEFAULT FOR TYPE macaddr8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad8_consistent (internal, macaddr8, int2, oid, internal),
+ FUNCTION 2 gbt_macad8_union (internal, internal),
+ FUNCTION 3 gbt_macad8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad8_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD
+ OPERATOR 6 <> (macaddr8, macaddr8) ,
+ FUNCTION 9 (macaddr8, macaddr8) gbt_macad8_fetch (internal);
+
+--
+--
+--
+-- enum ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_enum_consistent(internal,anyenum,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_enum_ops
+DEFAULT FOR TYPE anyenum USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_enum_consistent (internal, anyenum, int2, oid, internal),
+ FUNCTION 2 gbt_enum_union (internal, internal),
+ FUNCTION 3 gbt_enum_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_enum_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_enum_picksplit (internal, internal),
+ FUNCTION 7 gbt_enum_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD
+ OPERATOR 6 <> (anyenum, anyenum) ,
+ FUNCTION 9 (anyenum, anyenum) gbt_enum_fetch (internal) ;
diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control
index 81c8509..9ced3bc 100644
--- a/contrib/btree_gist/btree_gist.control
+++ b/contrib/btree_gist/btree_gist.control
@@ -1,5 +1,5 @@
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
-default_version = '1.5'
+default_version = '1.6'
module_pathname = '$libdir/btree_gist'
relocatable = true
diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c
index 7674e2d..2afc343 100644
--- a/contrib/btree_gist/btree_int2.c
+++ b/contrib/btree_gist/btree_int2.c
@@ -94,20 +94,7 @@ PG_FUNCTION_INFO_V1(int2_dist);
Datum
int2_dist(PG_FUNCTION_ARGS)
{
- int16 a = PG_GETARG_INT16(0);
- int16 b = PG_GETARG_INT16(1);
- int16 r;
- int16 ra;
-
- if (pg_sub_s16_overflow(a, b, &r) ||
- r == PG_INT16_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("smallint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT16(ra);
+ return int2dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c
index 80005ab..2361ce7 100644
--- a/contrib/btree_gist/btree_int4.c
+++ b/contrib/btree_gist/btree_int4.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(int4_dist);
Datum
int4_dist(PG_FUNCTION_ARGS)
{
- int32 a = PG_GETARG_INT32(0);
- int32 b = PG_GETARG_INT32(1);
- int32 r;
- int32 ra;
-
- if (pg_sub_s32_overflow(a, b, &r) ||
- r == PG_INT32_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT32(ra);
+ return int4dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c
index b0fd3e1..182d7c4 100644
--- a/contrib/btree_gist/btree_int8.c
+++ b/contrib/btree_gist/btree_int8.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(int8_dist);
Datum
int8_dist(PG_FUNCTION_ARGS)
{
- int64 a = PG_GETARG_INT64(0);
- int64 b = PG_GETARG_INT64(1);
- int64 r;
- int64 ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT64(ra);
+ return int8dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c
index 3a527a7..c68d48e 100644
--- a/contrib/btree_gist/btree_interval.c
+++ b/contrib/btree_gist/btree_interval.c
@@ -128,11 +128,7 @@ PG_FUNCTION_INFO_V1(interval_dist);
Datum
interval_dist(PG_FUNCTION_ARGS)
{
- Datum diff = DirectFunctionCall2(interval_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+ return interval_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c
index 00e7019..c702d51 100644
--- a/contrib/btree_gist/btree_oid.c
+++ b/contrib/btree_gist/btree_oid.c
@@ -100,15 +100,7 @@ PG_FUNCTION_INFO_V1(oid_dist);
Datum
oid_dist(PG_FUNCTION_ARGS)
{
- Oid a = PG_GETARG_OID(0);
- Oid b = PG_GETARG_OID(1);
- Oid res;
-
- if (a < b)
- res = b - a;
- else
- res = a - b;
- PG_RETURN_OID(res);
+ return oiddist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c
index 90cf655..cfafd02 100644
--- a/contrib/btree_gist/btree_time.c
+++ b/contrib/btree_gist/btree_time.c
@@ -141,11 +141,7 @@ PG_FUNCTION_INFO_V1(time_dist);
Datum
time_dist(PG_FUNCTION_ARGS)
{
- Datum diff = DirectFunctionCall2(time_mi_time,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+ return time_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index 49d1849..9e62f7d 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -146,48 +146,14 @@ PG_FUNCTION_INFO_V1(ts_dist);
Datum
ts_dist(PG_FUNCTION_ARGS)
{
- Timestamp a = PG_GETARG_TIMESTAMP(0);
- Timestamp b = PG_GETARG_TIMESTAMP(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
- else
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
+ return timestamp_distance(fcinfo);
}
PG_FUNCTION_INFO_V1(tstz_dist);
Datum
tstz_dist(PG_FUNCTION_ARGS)
{
- TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
- TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
-
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
+ return timestamptz_distance(fcinfo);
}
diff --git a/doc/src/sgml/btree-gist.sgml b/doc/src/sgml/btree-gist.sgml
index 774442f..6eb4a18 100644
--- a/doc/src/sgml/btree-gist.sgml
+++ b/doc/src/sgml/btree-gist.sgml
@@ -96,6 +96,19 @@ INSERT 0 1
</sect2>
<sect2>
+ <title>Upgrade notes for version 1.6</title>
+
+ <para>
+ In version 1.6 <literal>btree_gist</literal> switched to using in-core
+ distance operators, and its own implementations were removed. References to
+ these operators in <literal>btree_gist</literal> opclasses will be updated
+ automatically during the extension upgrade, but if the user has created
+ objects referencing these operators or functions, then these objects must be
+ dropped manually before updating the extension.
+ </para>
+ </sect2>
+
+ <sect2>
<title>Authors</title>
<para>
0007-Add-regression-tests-for-kNN-btree-v07.patchtext/x-patch; name=0007-Add-regression-tests-for-kNN-btree-v07.patchDownload
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index b21298a..0cefbcc 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -250,3 +250,1109 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+---
+--- Test B-tree distance ordering
+---
+SET enable_bitmapscan = OFF;
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+ QUERY PLAN
+------------------------------
+ Sort
+ Sort Key: ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+ QUERY PLAN
+------------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((random <-> 1))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)))
+ Order By: (random <-> 4000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)) AND (random = ANY ('{1809552,1919087,2321799,2648497,3000193,3013326,4157193,4488889,5257716,5593978,NULL}'::integer[])))
+ Order By: (random <-> 3000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ seqno | random
+-------+---------
+ 5380 | 3000193
+ 6262 | 3013326
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 6448 | 4157193
+ 210 | 1809552
+ 4408 | 4488889
+ 6320 | 5257716
+ 1836 | 5593978
+(10 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+-- test parallel KNN scan
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+RESET enable_indexscan;
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Gather
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i > 8000000)
+(4 rows)
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+---------------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t1.n = t2.n)
+ Join Filter: (t1.i <> t2.i)
+ -> Subquery Scan on t1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Order By: (i <-> 4000003)
+ -> Hash
+ -> Gather
+ Workers Planned: 4
+ -> Parallel Seq Scan on bt_knn_test2 t2
+(13 rows)
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+---------------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t1.n = ((i.i * 100000) + j.j))
+ Join Filter: (t1.i <> ('{4,3,2,7,8}'::integer[])[(i.i + 1)])
+ -> Subquery Scan on t1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i = ANY ('{3,4,7,8,2}'::integer[]))
+ Order By: (i <-> 4)
+ -> Hash
+ -> Nested Loop
+ -> Function Scan on generate_series i
+ -> Function Scan on generate_series j
+(14 rows)
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+ROLLBACK;
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ QUERY PLAN
+-----------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000))
+ Order By: (thousand <-> 998)
+(3 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 1 | 9001
+ 1 | 8001
+ 1 | 7001
+ 1 | 6001
+ 1 | 5001
+ 1 | 4001
+ 1 | 3001
+ 1 | 2001
+ 1 | 1001
+ 1 | 1
+ 0 | 9000
+ 0 | 8000
+ 0 | 7000
+ 0 | 6000
+ 0 | 5000
+ 0 | 4000
+ 0 | 3000
+ 0 | 2000
+ 0 | 1000
+ 0 | 0
+ | 1
+ | 2
+ | 3
+(33 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: ((thousand > 100) AND (thousand < 800) AND (thousand = ANY ('{0,123,234,345,456,678,901,NULL}'::smallint[])))
+ Order By: (thousand <-> '300'::bigint)
+(3 rows)
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ thousand | tenthous
+----------+----------
+ 345 | 345
+ 345 | 1345
+ 345 | 2345
+ 345 | 3345
+ 345 | 4345
+ 345 | 5345
+ 345 | 6345
+ 345 | 7345
+ 345 | 8345
+ 345 | 9345
+ 234 | 234
+ 234 | 1234
+ 234 | 2234
+ 234 | 3234
+ 234 | 4234
+ 234 | 5234
+ 234 | 6234
+ 234 | 7234
+ 234 | 8234
+ 234 | 9234
+ 456 | 456
+ 456 | 1456
+ 456 | 2456
+ 456 | 3456
+ 456 | 4456
+ 456 | 5456
+ 456 | 6456
+ 456 | 7456
+ 456 | 8456
+ 456 | 9456
+ 123 | 123
+ 123 | 1123
+ 123 | 2123
+ 123 | 3123
+ 123 | 4123
+ 123 | 5123
+ 123 | 6123
+ 123 | 7123
+ 123 | 8123
+ 123 | 9123
+ 678 | 678
+ 678 | 1678
+ 678 | 2678
+ 678 | 3678
+ 678 | 4678
+ 678 | 5678
+ 678 | 6678
+ 678 | 7678
+ 678 | 8678
+ 678 | 9678
+(50 rows)
+
+DROP INDEX tenk3_idx;
+-- Test order by distance ordering on non-first column
+SET enable_sort = OFF;
+-- Ranges are not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand > 120
+ORDER BY tenthous <-> 3500;
+ QUERY PLAN
+-----------------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 3500))
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand > 120)
+(4 rows)
+
+-- Equality restriction on the first column is supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+ QUERY PLAN
+-----------------------------------------------------
+ Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = 120)
+ Order By: (tenthous <-> 3500)
+(3 rows)
+
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+ thousand | tenthous
+----------+----------
+ 120 | 3120
+ 120 | 4120
+ 120 | 2120
+ 120 | 5120
+ 120 | 1120
+ 120 | 6120
+ 120 | 120
+ 120 | 7120
+ 120 | 8120
+ 120 | 9120
+(10 rows)
+
+-- IN restriction on the first column is not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY tenthous <-> 3500;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 3500))
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
+(4 rows)
+
+-- Test kNN search using 4-column index
+CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
+-- Ordering by distance to 3rd column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+ QUERY PLAN
+----------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43))
+ Order By: (thousand <-> 600)
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 43 | 643 | 643
+ 3 | 43 | 643 | 1643
+ 3 | 43 | 643 | 2643
+ 3 | 43 | 643 | 3643
+ 3 | 43 | 643 | 4643
+ 3 | 43 | 643 | 5643
+ 3 | 43 | 643 | 6643
+ 3 | 43 | 643 | 7643
+ 3 | 43 | 643 | 8643
+ 3 | 43 | 643 | 9643
+ 3 | 43 | 543 | 9543
+ 3 | 43 | 543 | 8543
+ 3 | 43 | 543 | 7543
+ 3 | 43 | 543 | 6543
+ 3 | 43 | 543 | 5543
+ 3 | 43 | 543 | 4543
+ 3 | 43 | 543 | 3543
+ 3 | 43 | 543 | 2543
+ 3 | 43 | 543 | 1543
+ 3 | 43 | 543 | 543
+ 3 | 43 | 743 | 743
+ 3 | 43 | 743 | 1743
+ 3 | 43 | 743 | 2743
+ 3 | 43 | 743 | 3743
+ 3 | 43 | 743 | 4743
+ 3 | 43 | 743 | 5743
+ 3 | 43 | 743 | 6743
+ 3 | 43 | 743 | 7743
+ 3 | 43 | 743 | 8743
+ 3 | 43 | 743 | 9743
+ 3 | 43 | 443 | 9443
+ 3 | 43 | 443 | 8443
+ 3 | 43 | 443 | 7443
+ 3 | 43 | 443 | 6443
+ 3 | 43 | 443 | 5443
+ 3 | 43 | 443 | 4443
+ 3 | 43 | 443 | 3443
+ 3 | 43 | 443 | 2443
+ 3 | 43 | 443 | 1443
+ 3 | 43 | 443 | 443
+ 3 | 43 | 843 | 843
+ 3 | 43 | 843 | 1843
+ 3 | 43 | 843 | 2843
+ 3 | 43 | 843 | 3843
+ 3 | 43 | 843 | 4843
+ 3 | 43 | 843 | 5843
+ 3 | 43 | 843 | 6843
+ 3 | 43 | 843 | 7843
+ 3 | 43 | 843 | 8843
+ 3 | 43 | 843 | 9843
+ 3 | 43 | 343 | 9343
+ 3 | 43 | 343 | 8343
+ 3 | 43 | 343 | 7343
+ 3 | 43 | 343 | 6343
+ 3 | 43 | 343 | 5343
+ 3 | 43 | 343 | 4343
+ 3 | 43 | 343 | 3343
+ 3 | 43 | 343 | 2343
+ 3 | 43 | 343 | 1343
+ 3 | 43 | 343 | 343
+ 3 | 43 | 943 | 943
+ 3 | 43 | 943 | 1943
+ 3 | 43 | 943 | 2943
+ 3 | 43 | 943 | 3943
+ 3 | 43 | 943 | 4943
+ 3 | 43 | 943 | 5943
+ 3 | 43 | 943 | 6943
+ 3 | 43 | 943 | 7943
+ 3 | 43 | 943 | 8943
+ 3 | 43 | 943 | 9943
+ 3 | 43 | 243 | 9243
+ 3 | 43 | 243 | 8243
+ 3 | 43 | 243 | 7243
+ 3 | 43 | 243 | 6243
+ 3 | 43 | 243 | 5243
+ 3 | 43 | 243 | 4243
+ 3 | 43 | 243 | 3243
+ 3 | 43 | 243 | 2243
+ 3 | 43 | 243 | 1243
+ 3 | 43 | 243 | 243
+ 3 | 43 | 143 | 9143
+ 3 | 43 | 143 | 8143
+ 3 | 43 | 143 | 7143
+ 3 | 43 | 143 | 6143
+ 3 | 43 | 143 | 5143
+ 3 | 43 | 143 | 4143
+ 3 | 43 | 143 | 3143
+ 3 | 43 | 143 | 2143
+ 3 | 43 | 143 | 1143
+ 3 | 43 | 143 | 143
+ 3 | 43 | 43 | 9043
+ 3 | 43 | 43 | 8043
+ 3 | 43 | 43 | 7043
+ 3 | 43 | 43 | 6043
+ 3 | 43 | 43 | 5043
+ 3 | 43 | 43 | 4043
+ 3 | 43 | 43 | 3043
+ 3 | 43 | 43 | 2043
+ 3 | 43 | 43 | 1043
+ 3 | 43 | 43 | 43
+(100 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43) AND (tenthous > 3000))
+ Order By: (thousand <-> 600)
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 43 | 643 | 3643
+ 3 | 43 | 643 | 4643
+ 3 | 43 | 643 | 5643
+ 3 | 43 | 643 | 6643
+ 3 | 43 | 643 | 7643
+ 3 | 43 | 643 | 8643
+ 3 | 43 | 643 | 9643
+ 3 | 43 | 543 | 9543
+ 3 | 43 | 543 | 8543
+ 3 | 43 | 543 | 7543
+ 3 | 43 | 543 | 6543
+ 3 | 43 | 543 | 5543
+ 3 | 43 | 543 | 4543
+ 3 | 43 | 543 | 3543
+ 3 | 43 | 743 | 3743
+ 3 | 43 | 743 | 4743
+ 3 | 43 | 743 | 5743
+ 3 | 43 | 743 | 6743
+ 3 | 43 | 743 | 7743
+ 3 | 43 | 743 | 8743
+ 3 | 43 | 743 | 9743
+ 3 | 43 | 443 | 9443
+ 3 | 43 | 443 | 8443
+ 3 | 43 | 443 | 7443
+ 3 | 43 | 443 | 6443
+ 3 | 43 | 443 | 5443
+ 3 | 43 | 443 | 4443
+ 3 | 43 | 443 | 3443
+ 3 | 43 | 843 | 3843
+ 3 | 43 | 843 | 4843
+ 3 | 43 | 843 | 5843
+ 3 | 43 | 843 | 6843
+ 3 | 43 | 843 | 7843
+ 3 | 43 | 843 | 8843
+ 3 | 43 | 843 | 9843
+ 3 | 43 | 343 | 9343
+ 3 | 43 | 343 | 8343
+ 3 | 43 | 343 | 7343
+ 3 | 43 | 343 | 6343
+ 3 | 43 | 343 | 5343
+ 3 | 43 | 343 | 4343
+ 3 | 43 | 343 | 3343
+ 3 | 43 | 943 | 3943
+ 3 | 43 | 943 | 4943
+ 3 | 43 | 943 | 5943
+ 3 | 43 | 943 | 6943
+ 3 | 43 | 943 | 7943
+ 3 | 43 | 943 | 8943
+ 3 | 43 | 943 | 9943
+ 3 | 43 | 243 | 9243
+ 3 | 43 | 243 | 8243
+ 3 | 43 | 243 | 7243
+ 3 | 43 | 243 | 6243
+ 3 | 43 | 243 | 5243
+ 3 | 43 | 243 | 4243
+ 3 | 43 | 243 | 3243
+ 3 | 43 | 143 | 9143
+ 3 | 43 | 143 | 8143
+ 3 | 43 | 143 | 7143
+ 3 | 43 | 143 | 6143
+ 3 | 43 | 143 | 5143
+ 3 | 43 | 143 | 4143
+ 3 | 43 | 143 | 3143
+ 3 | 43 | 43 | 9043
+ 3 | 43 | 43 | 8043
+ 3 | 43 | 43 | 7043
+ 3 | 43 | 43 | 6043
+ 3 | 43 | 43 | 5043
+ 3 | 43 | 43 | 4043
+ 3 | 43 | 43 | 3043
+(70 rows)
+
+-- Ordering by distance to 4th column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+-------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43) AND (thousand = 643))
+ Order By: (tenthous <-> 4000)
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 43 | 643 | 3643
+ 3 | 43 | 643 | 4643
+ 3 | 43 | 643 | 2643
+ 3 | 43 | 643 | 5643
+ 3 | 43 | 643 | 1643
+ 3 | 43 | 643 | 6643
+ 3 | 43 | 643 | 643
+ 3 | 43 | 643 | 7643
+ 3 | 43 | 643 | 8643
+ 3 | 43 | 643 | 9643
+(10 rows)
+
+-- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+------------------------------------------------
+ Index Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = 643)
+ Order By: (tenthous <-> 4000)
+ Filter: (hundred = 43)
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+----------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 4000))
+ -> Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43))
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+------------------------------------------------
+ Index Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = 643)
+ Order By: (tenthous <-> 4000)
+ Filter: (ten = 3)
+(4 rows)
+
+DROP INDEX tenk1_knn_idx;
+RESET enable_sort;
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 1 | 1
+ 1 | 1001
+ 1 | 2001
+ 1 | 3001
+ 1 | 4001
+ 1 | 5001
+ 1 | 6001
+ 1 | 7001
+ 1 | 8001
+ 1 | 9001
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 0 | 0
+ 0 | 1000
+ 0 | 2000
+ 0 | 3000
+ 0 | 4000
+ 0 | 5000
+ 0 | 6000
+ 0 | 7000
+ 0 | 8000
+ 0 | 9000
+ | 3
+ | 2
+ | 1
+(33 rows)
+
+DROP INDEX tenk3_idx;
+DROP TABLE tenk3;
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+-------------------
+ Wed May 03 00:00:00 2017 | @ 2 days
+ Wed May 03 01:00:00 2017 | @ 2 days 1 hour
+ Wed May 03 02:00:00 2017 | @ 2 days 2 hours
+ Wed May 03 03:00:00 2017 | @ 2 days 3 hours
+ Wed May 03 04:00:00 2017 | @ 2 days 4 hours
+ Wed May 03 05:00:00 2017 | @ 2 days 5 hours
+ Wed May 03 06:00:00 2017 | @ 2 days 6 hours
+ Wed May 03 07:00:00 2017 | @ 2 days 7 hours
+ Wed May 03 08:00:00 2017 | @ 2 days 8 hours
+ Wed May 03 09:00:00 2017 | @ 2 days 9 hours
+ Wed May 03 10:00:00 2017 | @ 2 days 10 hours
+ Wed May 03 11:00:00 2017 | @ 2 days 11 hours
+ Wed May 03 12:00:00 2017 | @ 2 days 12 hours
+ Wed May 03 13:00:00 2017 | @ 2 days 13 hours
+ Wed May 03 14:00:00 2017 | @ 2 days 14 hours
+ Wed May 03 15:00:00 2017 | @ 2 days 15 hours
+ Wed May 03 16:00:00 2017 | @ 2 days 16 hours
+ Wed May 03 17:00:00 2017 | @ 2 days 17 hours
+ Wed May 03 18:00:00 2017 | @ 2 days 18 hours
+ Wed May 03 19:00:00 2017 | @ 2 days 19 hours
+(20 rows)
+
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+------------
+ Mon Jan 01 00:00:00 2018 | @ 0
+ Mon Jan 01 01:00:00 2018 | @ 1 hour
+ Sun Dec 31 23:00:00 2017 | @ 1 hour
+ Mon Jan 01 02:00:00 2018 | @ 2 hours
+ Sun Dec 31 22:00:00 2017 | @ 2 hours
+ Mon Jan 01 03:00:00 2018 | @ 3 hours
+ Sun Dec 31 21:00:00 2017 | @ 3 hours
+ Mon Jan 01 04:00:00 2018 | @ 4 hours
+ Sun Dec 31 20:00:00 2017 | @ 4 hours
+ Mon Jan 01 05:00:00 2018 | @ 5 hours
+ Sun Dec 31 19:00:00 2017 | @ 5 hours
+ Mon Jan 01 06:00:00 2018 | @ 6 hours
+ Sun Dec 31 18:00:00 2017 | @ 6 hours
+ Mon Jan 01 07:00:00 2018 | @ 7 hours
+ Sun Dec 31 17:00:00 2017 | @ 7 hours
+ Mon Jan 01 08:00:00 2018 | @ 8 hours
+ Sun Dec 31 16:00:00 2017 | @ 8 hours
+ Mon Jan 01 09:00:00 2018 | @ 9 hours
+ Sun Dec 31 15:00:00 2017 | @ 9 hours
+ Mon Jan 01 10:00:00 2018 | @ 10 hours
+(20 rows)
+
+DROP TABLE knn_btree_ts;
+RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 2b087be..5e3bffe 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -129,3 +129,307 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
-- Simple ALTER INDEX
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+---
+--- Test B-tree distance ordering
+---
+
+SET enable_bitmapscan = OFF;
+
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+-- test parallel KNN scan
+
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+
+RESET enable_indexscan;
+
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+
+ROLLBACK;
+
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+
+
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+DROP INDEX tenk3_idx;
+
+-- Test order by distance ordering on non-first column
+SET enable_sort = OFF;
+
+-- Ranges are not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand > 120
+ORDER BY tenthous <-> 3500;
+
+-- Equality restriction on the first column is supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+
+-- IN restriction on the first column is not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY tenthous <-> 3500;
+
+-- Test kNN search using 4-column index
+CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
+
+-- Ordering by distance to 3rd column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+
+-- Ordering by distance to 4th column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+-- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+DROP INDEX tenk1_knn_idx;
+
+RESET enable_sort;
+
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+DROP TABLE tenk3;
+
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+
+DROP TABLE knn_btree_ts;
+
+RESET enable_bitmapscan;
0008-Allow-ammatchorderby-to-return-pathkey-sublists-v07.patchtext/x-patch; name=0008-Allow-ammatchorderby-to-return-pathkey-sublists-v07.patchDownload
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 279d8ba..b88044b 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1014,8 +1014,25 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
index_clauses,
&orderbyclauses,
&orderbyclausecols);
+
if (orderbyclauses)
- useful_pathkeys = root->query_pathkeys;
+ {
+ int norderbys = list_length(orderbyclauses);
+ int npathkeys = list_length(root->query_pathkeys);
+
+ if (norderbys < npathkeys)
+ {
+ /*
+ * We do not accept pathkey sublists until we implement
+ * partial sorting.
+ */
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+ }
+ else
+ useful_pathkeys = root->query_pathkeys;
+ }
else
useful_pathkeys = NIL;
}
@@ -3286,8 +3303,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
* index column numbers (zero based) that each clause would be used with.
* NIL lists are returned if the ordering is not achievable this way.
*
- * On success, the result list is ordered by pathkeys, and in fact is
- * one-to-one with the requested pathkeys.
+ * On success, the result list is ordered by pathkeys.
*/
static void
match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
@@ -3305,8 +3321,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
ammatchorderby(index, pathkeys, index_clauses,
&orderby_clauses, &orderby_clause_columns))
{
- Assert(list_length(pathkeys) == list_length(orderby_clauses));
- Assert(list_length(pathkeys) == list_length(orderby_clause_columns));
+ Assert(list_length(orderby_clauses) <= list_length(pathkeys));
+ Assert(list_length(orderby_clauses) == list_length(orderby_clause_columns));
*orderby_clauses_p = orderby_clauses; /* success! */
*orderby_clause_columns_p = orderby_clause_columns;
0009-Add-support-of-array-ops-to-btree-kNN-v07.patchtext/x-patch; name=0009-Add-support-of-array-ops-to-btree-kNN-v07.patchDownload
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index fb413e7..3085fae 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -509,8 +509,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
if (orderbys && scan->numberOfOrderBys > 0)
memmove(scan->orderByData,
- orderbys,
- scan->numberOfOrderBys * sizeof(ScanKeyData));
+ &orderbys[scan->numberOfOrderBys - 1],
+ 1 /* scan->numberOfOrderBys */ * sizeof(ScanKeyData));
so->scanDirection = NoMovementScanDirection;
so->distanceTypeByVal = true;
@@ -1554,13 +1554,15 @@ static bool
btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
List **orderby_clauses_p, List **orderby_clausecols_p)
{
- Expr *expr;
+ Expr *expr = NULL;
ListCell *lc;
int indexcol;
int num_eq_cols = 0;
+ int nsaops = 0;
+ int last_saop_ord_col = -1;
+ bool saops[INDEX_MAX_KEYS] = {0};
- /* only one ORDER BY clause is supported */
- if (list_length(pathkeys) != 1)
+ if (list_length(pathkeys) < 1)
return false;
/*
@@ -1584,6 +1586,7 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
Expr *clause = rinfo->clause;
Oid opno;
StrategyNumber strat;
+ bool is_saop;
if (!clause)
continue;
@@ -1593,6 +1596,18 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
OpExpr *opexpr = (OpExpr *) clause;
opno = opexpr->opno;
+ is_saop = false;
+ }
+ else if (IsA(clause, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+
+ /* We only accept ANY clauses, not ALL */
+ if (!saop->useOr)
+ continue;
+
+ opno = saop->opno;
+ is_saop = true;
}
else
{
@@ -1603,19 +1618,67 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
/* Check if the operator is btree equality operator. */
strat = get_op_opfamily_strategy(opno, index->opfamily[indexcol]);
- if (strat == BTEqualStrategyNumber)
- num_eq_cols = indexcol + 1;
+ if (strat != BTEqualStrategyNumber)
+ continue;
+
+ if (is_saop && indexcol == num_eq_cols)
+ {
+ saops[indexcol] = true;
+ nsaops++;
+ }
+ else if (!is_saop && saops[indexcol])
+ {
+ saops[indexcol] = false;
+ nsaops--;
+ }
+
+ num_eq_cols = indexcol + 1;
}
}
- /*
- * If there are no equality columns try to match only the first column,
- * otherwise try all columns.
- */
- indexcol = num_eq_cols ? -1 : 0;
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = lfirst_node(PathKey, lc);
+
+ /*
+ * If there are no equality columns try to match only the first column,
+ * otherwise try all columns.
+ */
+ indexcol = num_eq_cols ? -1 : 0;
+
+ if ((expr = match_orderbyop_pathkey(index, pathkey, &indexcol)))
+ break; /* found order-by-operator pathkey */
+
+ if (!num_eq_cols)
+ return false; /* first pathkey is not order-by-operator */
- expr = match_orderbyop_pathkey(index, castNode(PathKey, linitial(pathkeys)),
- &indexcol);
+ indexcol = -1;
+
+ if (!(expr = match_pathkey_to_indexcol(index, pathkey, &indexcol)))
+ return false;
+
+ if (indexcol >= num_eq_cols)
+ return false;
+
+ if (saops[indexcol])
+ {
+ saops[indexcol] = false;
+ nsaops--;
+
+ /*
+ * ORDER BY column numbers for array ops should go in
+ * non-decreasing order.
+ */
+ if (indexcol < last_saop_ord_col)
+ return false;
+
+ last_saop_ord_col = indexcol;
+ }
+ /* else: order of equality-restricted columns is arbitrary */
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *orderby_clausecols_p = lappend_int(*orderby_clausecols_p, -indexcol - 1);
+ }
if (!expr)
return false;
@@ -1627,6 +1690,19 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
if (indexcol > num_eq_cols)
return false;
+ if (nsaops)
+ {
+ int i;
+
+ /*
+ * Check that all preceding array-op columns are included into
+ * ORDER BY clause.
+ */
+ for (i = 0; i < indexcol; i++)
+ if (saops[i])
+ return false;
+ }
+
/* Return first ORDER BY clause's expression and column. */
*orderby_clauses_p = lappend(*orderby_clauses_p, expr);
*orderby_clausecols_p = lappend_int(*orderby_clausecols_p, indexcol);
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 5b9294b..e980351 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -905,10 +905,6 @@ _bt_preprocess_keys(IndexScanDesc scan)
{
ScanKey ord = scan->orderByData;
- if (scan->numberOfOrderBys > 1)
- /* it should not happen, see btmatchorderby() */
- elog(ERROR, "only one btree ordering operator is supported");
-
Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
/* use bidirectional kNN scan by default */
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 805abd2..034e3b8 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -1644,6 +1644,27 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
InvalidOid, /* no reg proc for this */
(Datum) 0); /* constant */
}
+ else if (IsA(clause, Var))
+ {
+ /* indexkey IS NULL or indexkey IS NOT NULL */
+ Var *var = (Var *) clause;
+
+ Assert(isorderby);
+
+ if (var->varno != INDEX_VAR)
+ elog(ERROR, "Var indexqual has wrong key");
+
+ varattno = var->varattno;
+
+ ScanKeyEntryInitialize(this_scan_key,
+ SK_ORDER_BY | SK_SEARCHNOTNULL,
+ varattno, /* attribute number to scan */
+ InvalidStrategy, /* no strategy */
+ InvalidOid, /* no strategy subtype */
+ var->varcollid, /* collation FIXME */
+ InvalidOid, /* no reg proc for this */
+ (Datum) 0); /* constant */
+ }
else
elog(ERROR, "unsupported indexqual type: %d",
(int) nodeTag(clause));
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index b88044b..a1a36ad 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -2945,6 +2945,62 @@ match_rowcompare_to_indexcol(RestrictInfo *rinfo,
return NULL;
}
+/*
+ * Try to match pathkey to the specified index column (*indexcol >= 0) or
+ * to all index columns (*indexcol < 0).
+ */
+Expr *
+match_pathkey_to_indexcol(IndexOptInfo *index, PathKey *pathkey, int *indexcol)
+{
+ ListCell *lc;
+
+ /* Pathkey must request default sort order for the target opfamily */
+ if (pathkey->pk_strategy != BTLessStrategyNumber ||
+ pathkey->pk_nulls_first)
+ return NULL;
+
+ /* If eclass is volatile, no hope of using an indexscan */
+ if (pathkey->pk_eclass->ec_has_volatile)
+ return NULL;
+
+ /*
+ * Try to match eclass member expression(s) to index. Note that child
+ * EC members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could
+ * be considered to match more than one pathkey list, which is OK
+ * here. See also get_eclass_for_sort_expr.)
+ */
+ foreach(lc, pathkey->pk_eclass->ec_members)
+ {
+ EquivalenceMember *member = lfirst_node(EquivalenceMember, lc);
+ Expr *expr = member->em_expr;
+
+ /* No possibility of match if it references other relations */
+ if (!bms_equal(member->em_relids, index->rel->relids))
+ continue;
+
+ /* If *indexcol is non-negative then try to match only to it */
+ if (*indexcol >= 0)
+ {
+ if (match_index_to_operand((Node *) expr, *indexcol, index))
+ /* don't want to look at remaining members */
+ return expr;
+ }
+ else /* try to match all columns */
+ {
+ for (*indexcol = 0; *indexcol < index->nkeycolumns; ++*indexcol)
+ {
+ if (match_index_to_operand((Node *) expr, *indexcol, index))
+ /* don't want to look at remaining members */
+ return expr;
+ }
+ }
+ }
+
+ return NULL;
+}
+
/****************************************************************************
* ---- ROUTINES TO CHECK ORDERING OPERATORS ----
****************************************************************************/
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 236f506..307af39 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4555,7 +4555,11 @@ fix_indexqual_clause(PlannerInfo *root, IndexOptInfo *index, int indexcol,
*/
clause = replace_nestloop_params(root, clause);
- if (IsA(clause, OpExpr))
+ if (indexcol < 0)
+ {
+ clause = fix_indexqual_operand(clause, index, -indexcol - 1);
+ }
+ else if (IsA(clause, OpExpr))
{
OpExpr *op = (OpExpr *) clause;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 2e3fab6..2076405 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5284,10 +5284,12 @@ get_quals_from_indexclauses(List *indexclauses)
* index key expression is on the left side of binary clauses.
*/
Cost
-index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
+index_other_operands_eval_cost(PlannerInfo *root, List *indexquals,
+ List *indexcolnos)
{
Cost qual_arg_cost = 0;
ListCell *lc;
+ ListCell *indexcolno_lc = indexcolnos ? list_head(indexcolnos) : NULL;
foreach(lc, indexquals)
{
@@ -5302,6 +5304,20 @@ index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
if (IsA(clause, RestrictInfo))
clause = ((RestrictInfo *) clause)->clause;
+ if (indexcolnos)
+ {
+ int indexcol = lfirst_int(indexcolno_lc);
+
+ if (indexcol < 0)
+ {
+ /* FIXME */
+ qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
+ continue;
+ }
+
+ indexcolno_lc = lnext(indexcolno_lc);
+ }
+
if (IsA(clause, OpExpr))
{
OpExpr *op = (OpExpr *) clause;
@@ -5331,7 +5347,8 @@ index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
other_operand = NULL; /* keep compiler quiet */
}
- cost_qual_eval_node(&index_qual_cost, other_operand, root);
+ if (other_operand)
+ cost_qual_eval_node(&index_qual_cost, other_operand, root);
qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
}
return qual_arg_cost;
@@ -5346,6 +5363,7 @@ genericcostestimate(PlannerInfo *root,
IndexOptInfo *index = path->indexinfo;
List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
List *indexOrderBys = path->indexorderbys;
+ List *indexOrderByCols = path->indexorderbycols;
Cost indexStartupCost;
Cost indexTotalCost;
Selectivity indexSelectivity;
@@ -5509,8 +5527,8 @@ genericcostestimate(PlannerInfo *root,
* Detecting that that might be needed seems more expensive than it's
* worth, though, considering all the other inaccuracies here ...
*/
- qual_arg_cost = index_other_operands_eval_cost(root, indexQuals) +
- index_other_operands_eval_cost(root, indexOrderBys);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals, NIL) +
+ index_other_operands_eval_cost(root, indexOrderBys, indexOrderByCols);
qual_op_cost = cpu_operator_cost *
(list_length(indexQuals) + list_length(indexOrderBys));
@@ -6629,7 +6647,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
* Add on index qual eval costs, much as in genericcostestimate. But we
* can disregard indexorderbys, since GIN doesn't support those.
*/
- qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals, NIL);
qual_op_cost = cpu_operator_cost * list_length(indexQuals);
*indexStartupCost += qual_arg_cost;
@@ -6809,7 +6827,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
* the index costs. We can disregard indexorderbys, since BRIN doesn't
* support those.
*/
- qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals, NIL);
/*
* Compute the startup cost as the cost to read the whole revmap
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index e9f4f75..6428932 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -79,6 +79,8 @@ extern bool indexcol_is_bool_constant_for_query(IndexOptInfo *index,
extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index);
extern void check_index_predicates(PlannerInfo *root, RelOptInfo *rel);
+extern Expr *match_pathkey_to_indexcol(IndexOptInfo *index, PathKey *pathkey,
+ int *indexcol_p);
extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
int *indexcol_p);
extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index 0ce2175..636e280 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -189,7 +189,7 @@ extern void estimate_hash_bucket_stats(PlannerInfo *root,
extern List *get_quals_from_indexclauses(List *indexclauses);
extern Cost index_other_operands_eval_cost(PlannerInfo *root,
- List *indexquals);
+ List *indexquals, List *indexcolnos);
extern List *add_predicate_to_index_quals(IndexOptInfo *index,
List *indexQuals);
extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0cefbcc..724890b 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -876,11 +876,9 @@ ORDER BY tenthous <-> 3500;
120 | 9120
(10 rows)
--- IN restriction on the first column is not supported
+-- IN restriction on the first column is not supported without 'ORDER BY col1 ASC'
EXPLAIN (COSTS OFF)
-SELECT thousand, tenthous
-FROM tenk1
-WHERE thousand IN (5, 120, 3456, 23)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY tenthous <-> 3500;
QUERY PLAN
---------------------------------------------------------------------
@@ -890,6 +888,63 @@ ORDER BY tenthous <-> 3500;
Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
(4 rows)
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand DESC, tenthous <-> 3500;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Sort
+ Sort Key: thousand DESC, ((tenthous <-> 3500))
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+ QUERY PLAN
+---------------------------------------------------------------
+ Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
+ Order By: (thousand AND (tenthous <-> 3500))
+(3 rows)
+
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+ thousand | tenthous
+----------+----------
+ 5 | 3005
+ 5 | 4005
+ 5 | 2005
+ 5 | 5005
+ 5 | 1005
+ 5 | 6005
+ 5 | 5
+ 5 | 7005
+ 5 | 8005
+ 5 | 9005
+ 23 | 3023
+ 23 | 4023
+ 23 | 2023
+ 23 | 5023
+ 23 | 1023
+ 23 | 6023
+ 23 | 23
+ 23 | 7023
+ 23 | 8023
+ 23 | 9023
+ 120 | 3120
+ 120 | 4120
+ 120 | 2120
+ 120 | 5120
+ 120 | 1120
+ 120 | 6120
+ 120 | 120
+ 120 | 7120
+ 120 | 8120
+ 120 | 9120
+(30 rows)
+
-- Test kNN search using 4-column index
CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
-- Ordering by distance to 3rd column
@@ -1122,38 +1177,132 @@ WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
3 | 43 | 643 | 9643
(10 rows)
--- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+-- Array ops on non-first columns are not supported
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
- QUERY PLAN
-------------------------------------------------
- Index Scan using tenk1_thous_tenthous on tenk1
- Index Cond: (thousand = 643)
- Order By: (tenthous <-> 4000)
- Filter: (hundred = 43)
+WHERE ten IN (3, 4, 5) AND hundred IN (23, 24, 35) AND thousand IN (843, 132, 623, 243)
+ORDER BY tenthous <-> 6000;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 6000))
+ -> Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = ANY ('{23,24,35}'::integer[])) AND (thousand = ANY ('{843,132,623,243}'::integer[])))
(4 rows)
+-- All array columns should be included into ORDER BY
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
- QUERY PLAN
-----------------------------------------------------
- Sort
- Sort Key: ((tenthous <-> 4000))
- -> Index Only Scan using tenk1_knn_idx on tenk1
- Index Cond: ((ten = 3) AND (hundred = 43))
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY tenthous <-> 6000;
+ QUERY PLAN
+-------------------------------------------------------------------
+ Index Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: ((thousand = 123) AND (tenthous > 2000))
+ Order By: (tenthous <-> 6000)
+ Filter: ((hundred = 23) AND (ten = ANY ('{3,4,5}'::integer[])))
(4 rows)
+-- Eq-restricted columns can be omitted from ORDER BY
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
- QUERY PLAN
-------------------------------------------------
- Index Scan using tenk1_thous_tenthous on tenk1
- Index Cond: (thousand = 643)
- Order By: (tenthous <-> 4000)
- Filter: (ten = 3)
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 23 | 123 | 6123
+ 3 | 23 | 123 | 5123
+ 3 | 23 | 123 | 7123
+ 3 | 23 | 123 | 4123
+ 3 | 23 | 123 | 8123
+ 3 | 23 | 123 | 3123
+ 3 | 23 | 123 | 9123
+ 3 | 23 | 123 | 2123
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 23 | 123 | 6123
+ 3 | 23 | 123 | 5123
+ 3 | 23 | 123 | 7123
+ 3 | 23 | 123 | 4123
+ 3 | 23 | 123 | 8123
+ 3 | 23 | 123 | 3123
+ 3 | 23 | 123 | 9123
+ 3 | 23 | 123 | 2123
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4 ,5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 23 | 123 | 6123
+ 3 | 23 | 123 | 5123
+ 3 | 23 | 123 | 7123
+ 3 | 23 | 123 | 4123
+ 3 | 23 | 123 | 8123
+ 3 | 23 | 123 | 3123
+ 3 | 23 | 123 | 9123
+ 3 | 23 | 123 | 2123
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, thousand, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+-- Extra ORDER BY columns after order-by-op are not supported
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 ORDER BY ten, thousand <-> 6000, tenthous;
+ QUERY PLAN
+-----------------------------------------------------------------------------
+ Sort
+ Sort Key: ten, ((thousand <-> 6000)), tenthous
+ -> Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23))
(4 rows)
DROP INDEX tenk1_knn_idx;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 5e3bffe..69c890e 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -345,13 +345,22 @@ FROM tenk1
WHERE thousand = 120
ORDER BY tenthous <-> 3500;
--- IN restriction on the first column is not supported
+-- IN restriction on the first column is not supported without 'ORDER BY col1 ASC'
EXPLAIN (COSTS OFF)
-SELECT thousand, tenthous
-FROM tenk1
-WHERE thousand IN (5, 120, 3456, 23)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY tenthous <-> 3500;
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand DESC, tenthous <-> 3500;
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+
-- Test kNN search using 4-column index
CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
@@ -378,18 +387,55 @@ WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
SELECT ten, hundred, thousand, tenthous FROM tenk1
WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
--- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+-- Array ops on non-first columns are not supported
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred IN (23, 24, 35) AND thousand IN (843, 132, 623, 243)
+ORDER BY tenthous <-> 6000;
+
+-- All array columns should be included into ORDER BY
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY tenthous <-> 6000;
+
+-- Eq-restricted columns can be omitted from ORDER BY
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4 ,5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, thousand, tenthous <-> 6000;
+-- Extra ORDER BY columns after order-by-op are not supported
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
+WHERE ten IN (3, 4, 5) AND hundred = 23 ORDER BY ten, thousand <-> 6000, tenthous;
DROP INDEX tenk1_knn_idx;
On 20.02.2019 15:44, Nikita Glukhov wrote:
On 20.02.2019 7:35, Thomas Munro wrote:
On Wed, Feb 20, 2019 at 2:18 PM Nikita Glukhov<n.gluhov@postgrespro.ru> wrote:
On 04.02.2019 8:35, Michael Paquier wrote:
This patch set needs a rebase because of conflicts caused by the
recent patches for pluggable storage.Hi Nikita,
From the department of trivialities: according to cfbot the
documentation doesn't build. Looks like you have some cases of </>,
but these days you have to write out </quote> (or whatever) in full.Sorry, tags in docs were fixed. Also I fixed list of data types with built-in
distance operators and list of assumptions for btree distance operators.Attached 7th version the patches (only documentation was changed).
Sorry again. Experimental patch #9 contained a bug leading to random failures
in the 'brin' test.
Attached fixed 7th version the patches.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-Fix-get_index_column_opclass-v07.patchtext/x-patch; name=0001-Fix-get_index_column_opclass-v07.patchDownload
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index e88c45d..2760748 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3118,9 +3118,6 @@ get_index_column_opclass(Oid index_oid, int attno)
{
HeapTuple tuple;
Form_pg_index rd_index PG_USED_FOR_ASSERTS_ONLY;
- Datum datum;
- bool isnull;
- oidvector *indclass;
Oid opclass;
/* First we need to know the column's opclass. */
@@ -3134,12 +3131,23 @@ get_index_column_opclass(Oid index_oid, int attno)
/* caller is supposed to guarantee this */
Assert(attno > 0 && attno <= rd_index->indnatts);
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indclass, &isnull);
- Assert(!isnull);
+ if (attno >= 1 && attno <= rd_index->indnkeyatts)
+ {
+ oidvector *indclass;
+ bool isnull;
+ Datum datum = SysCacheGetAttr(INDEXRELID, tuple,
+ Anum_pg_index_indclass,
+ &isnull);
+
+ Assert(!isnull);
- indclass = ((oidvector *) DatumGetPointer(datum));
- opclass = indclass->values[attno - 1];
+ indclass = ((oidvector *) DatumGetPointer(datum));
+ opclass = indclass->values[attno - 1];
+ }
+ else
+ {
+ opclass = InvalidOid;
+ }
ReleaseSysCache(tuple);
0002-Introduce-ammatchorderby-function-v07.patchtext/x-patch; name=0002-Introduce-ammatchorderby-function-v07.patchDownload
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6458376..9bff793 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,7 +109,6 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BLOOM_NSTRATEGIES;
amroutine->amsupport = BLOOM_NPROC;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -143,6 +142,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 8f008dd..5cd06c7 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -88,7 +88,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -122,6 +121,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index afc2023..0f6714d 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -41,7 +41,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GINNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -75,6 +74,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index b75b3a8..77ca187 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,7 @@
#include "access/gistscan.h"
#include "catalog/pg_collation.h"
#include "miscadmin.h"
+#include "optimizer/paths.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "nodes/execnodes.h"
@@ -64,7 +65,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = GISTNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = true;
@@ -98,6 +98,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index f1f01a0..bb5c6a1 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -59,7 +59,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = HTMaxStrategyNumber;
amroutine->amsupport = HASHNProcs;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -93,6 +92,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 98917de..c56ee5e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -110,7 +110,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = BTMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
- amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
amroutine->amcanunique = true;
amroutine->amcanmulticol = true;
@@ -144,6 +143,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
+ amroutine->ammatchorderby = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 8e63c1f..37de33c 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -22,6 +22,7 @@
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/pg_amop.h"
+#include "optimizer/paths.h"
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
@@ -31,7 +32,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-
/*
* SP-GiST handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -44,7 +44,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amstrategies = 0;
amroutine->amsupport = SPGISTNProc;
amroutine->amcanorder = false;
- amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
amroutine->amcanunique = false;
amroutine->amcanmulticol = false;
@@ -78,6 +77,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->ammatchorderby = match_orderbyop_pathkeys;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5d73848..a595c79 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -1097,7 +1097,7 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
*/
IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
- if (!amroutine->amcanorderbyop)
+ if (!amroutine->ammatchorderby)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("access method \"%s\" does not support ordering operators",
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 324356e..805abd2 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -198,7 +198,7 @@ IndexNextWithReorder(IndexScanState *node)
* with just Asserting here because the system will not try to run the
* plan backwards if ExecSupportsBackwardScan() says it won't work.
* Currently, that is guaranteed because no index AMs support both
- * amcanorderbyop and amcanbackward; if any ever do,
+ * ammatchorderby and amcanbackward; if any ever do,
* ExecSupportsBackwardScan() will need to consider indexorderbys
* explicitly.
*/
@@ -1148,7 +1148,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* 5. NullTest ("indexkey IS NULL/IS NOT NULL"). We just fill in the
* ScanKey properly.
*
- * This code is also used to prepare ORDER BY expressions for amcanorderbyop
+ * This code is also used to prepare ORDER BY expressions for ammatchorderby
* indexes. The behavior is exactly the same, except that we have to look up
* the operator differently. Note that only cases 1 and 2 are currently
* possible for ORDER BY.
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 3434219..6cf0b0d 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -17,6 +17,7 @@
#include <math.h>
+#include "access/amapi.h"
#include "access/stratnum.h"
#include "access/sysattr.h"
#include "catalog/pg_am.h"
@@ -182,8 +183,9 @@ static IndexClause *expand_indexqual_rowcompare(RestrictInfo *rinfo,
Oid expr_op,
bool var_on_left);
static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses,
List **orderby_clauses_p,
- List **clause_columns_p);
+ List **orderby_clause_columns_p);
static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
int indexcol, Expr *clause, Oid pk_opfamily);
static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
@@ -1001,10 +1003,11 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
orderbyclauses = NIL;
orderbyclausecols = NIL;
}
- else if (index->amcanorderbyop && pathkeys_possibly_useful)
+ else if (index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
+ index_clauses,
&orderbyclauses,
&orderbyclausecols);
if (orderbyclauses)
@@ -2927,6 +2930,113 @@ match_rowcompare_to_indexcol(RestrictInfo *rinfo,
return NULL;
}
+/****************************************************************************
+ * ---- ROUTINES TO CHECK ORDERING OPERATORS ----
+ ****************************************************************************/
+
+/*
+ * Try to match order-by-operator pathkey to the specified index column
+ * (*indexcol_p >= 0) or to all index columns (*indexcol_p < 0).
+ *
+ * Returned matched index clause exression.
+ * Number of matched index column is returned in *indexcol_p.
+ */
+Expr *
+match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey, int *indexcol_p)
+{
+ ListCell *lc;
+
+ /* Pathkey must request default sort order for the target opfamily */
+ if (pathkey->pk_strategy != BTLessStrategyNumber ||
+ pathkey->pk_nulls_first)
+ return NULL;
+
+ /* If eclass is volatile, no hope of using an indexscan */
+ if (pathkey->pk_eclass->ec_has_volatile)
+ return NULL;
+
+ /*
+ * Try to match eclass member expression(s) to index. Note that child EC
+ * members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could be
+ * considered to match more than one pathkey list, which is OK here. See
+ * also get_eclass_for_sort_expr.)
+ */
+ foreach(lc, pathkey->pk_eclass->ec_members)
+ {
+ EquivalenceMember *member = lfirst_node(EquivalenceMember, lc);
+ Expr *expr;
+
+ /* No possibility of match if it references other relations. */
+ if (!bms_equal(member->em_relids, index->rel->relids))
+ continue;
+
+ /* If *indexcol_p is non-negative then try to match only to it. */
+ if (*indexcol_p >= 0)
+ {
+ expr = match_clause_to_ordering_op(index, *indexcol_p,
+ member->em_expr,
+ pathkey->pk_opfamily);
+
+ if (expr)
+ return expr; /* don't want to look at remaining members */
+ }
+ else
+ {
+ int indexcol;
+
+ /*
+ * We allow any column of this index to match each pathkey; they
+ * don't have to match left-to-right as you might expect.
+ */
+ for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
+ {
+ expr = match_clause_to_ordering_op(index, indexcol,
+ member->em_expr,
+ pathkey->pk_opfamily);
+ if (expr)
+ {
+ *indexcol_p = indexcol;
+ return expr; /* don't want to look at remaining members */
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* Try to match order-by-operator pathkeys to any index columns. */
+bool
+match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses, List **orderby_clauses_p,
+ List **orderby_clausecols_p)
+{
+ ListCell *lc;
+
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = lfirst_node(PathKey, lc);
+ Expr *expr;
+ int indexcol = -1; /* match all index columns */
+
+ expr = match_orderbyop_pathkey(index, pathkey, &indexcol);
+
+ /*
+ * Note: for any failure to match, we just return NIL immediately.
+ * There is no value in matching just some of the pathkeys.
+ */
+ if (!expr)
+ return false;
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *orderby_clausecols_p = lappend_int(*orderby_clausecols_p, indexcol);
+ }
+
+ return true; /* success */
+}
+
/*
* expand_indexqual_rowcompare --- expand a single indexqual condition
* that is a RowCompareExpr
@@ -3183,92 +3293,31 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
*/
static void
match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses,
List **orderby_clauses_p,
- List **clause_columns_p)
+ List **orderby_clause_columns_p)
{
List *orderby_clauses = NIL;
- List *clause_columns = NIL;
- ListCell *lc1;
-
- *orderby_clauses_p = NIL; /* set default results */
- *clause_columns_p = NIL;
+ List *orderby_clause_columns = NIL;
+ ammatchorderby_function ammatchorderby =
+ (ammatchorderby_function) index->ammatchorderby;
- /* Only indexes with the amcanorderbyop property are interesting here */
- if (!index->amcanorderbyop)
- return;
-
- foreach(lc1, pathkeys)
+ /* Only indexes with the ammatchorderby function are interesting here */
+ if (ammatchorderby &&
+ ammatchorderby(index, pathkeys, index_clauses,
+ &orderby_clauses, &orderby_clause_columns))
{
- PathKey *pathkey = (PathKey *) lfirst(lc1);
- bool found = false;
- ListCell *lc2;
-
- /*
- * Note: for any failure to match, we just return NIL immediately.
- * There is no value in matching just some of the pathkeys.
- */
-
- /* Pathkey must request default sort order for the target opfamily */
- if (pathkey->pk_strategy != BTLessStrategyNumber ||
- pathkey->pk_nulls_first)
- return;
-
- /* If eclass is volatile, no hope of using an indexscan */
- if (pathkey->pk_eclass->ec_has_volatile)
- return;
-
- /*
- * Try to match eclass member expression(s) to index. Note that child
- * EC members are considered, but only when they belong to the target
- * relation. (Unlike regular members, the same expression could be a
- * child member of more than one EC. Therefore, the same index could
- * be considered to match more than one pathkey list, which is OK
- * here. See also get_eclass_for_sort_expr.)
- */
- foreach(lc2, pathkey->pk_eclass->ec_members)
- {
- EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
- int indexcol;
-
- /* No possibility of match if it references other relations */
- if (!bms_equal(member->em_relids, index->rel->relids))
- continue;
+ Assert(list_length(pathkeys) == list_length(orderby_clauses));
+ Assert(list_length(pathkeys) == list_length(orderby_clause_columns));
- /*
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, and it doesn't matter for SP-GiST because
- * that doesn't handle multiple columns anyway, and no other
- * existing AMs support amcanorderbyop. We might need different
- * logic in future for other implementations.
- */
- for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
- {
- Expr *expr;
-
- expr = match_clause_to_ordering_op(index,
- indexcol,
- member->em_expr,
- pathkey->pk_opfamily);
- if (expr)
- {
- orderby_clauses = lappend(orderby_clauses, expr);
- clause_columns = lappend_int(clause_columns, indexcol);
- found = true;
- break;
- }
- }
-
- if (found) /* don't want to look at remaining members */
- break;
- }
-
- if (!found) /* fail if no match for this pathkey */
- return;
+ *orderby_clauses_p = orderby_clauses; /* success! */
+ *orderby_clause_columns_p = orderby_clause_columns;
+ }
+ else
+ {
+ *orderby_clauses_p = NIL; /* set default results */
+ *orderby_clause_columns_p = NIL;
}
-
- *orderby_clauses_p = orderby_clauses; /* success! */
- *clause_columns_p = clause_columns;
}
/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index d6dc83c..2d81fd1 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -267,7 +267,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
/* We copy just the fields we need, not all of rd_indam */
amroutine = indexRelation->rd_indam;
- info->amcanorderbyop = amroutine->amcanorderbyop;
+ info->ammatchorderby = amroutine->ammatchorderby;
info->amoptionalkey = amroutine->amoptionalkey;
info->amsearcharray = amroutine->amsearcharray;
info->amsearchnulls = amroutine->amsearchnulls;
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 060ffe5..3907065 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -298,7 +298,7 @@ indexam_property(FunctionCallInfo fcinfo,
* a nonkey column, and null otherwise (meaning we don't
* know).
*/
- if (!iskey || !routine->amcanorderbyop)
+ if (!iskey || !routine->ammatchorderby)
{
res = false;
isnull = false;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 653ddc9..d8fb7d3 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -21,6 +21,9 @@
*/
struct PlannerInfo;
struct IndexPath;
+struct IndexOptInfo;
+struct PathKey;
+struct Expr;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
@@ -140,6 +143,13 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* does AM support ORDER BY result of an operator on indexed column? */
+typedef bool (*ammatchorderby_function) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List *index_clauses,
+ List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -170,8 +180,6 @@ typedef struct IndexAmRoutine
uint16 amsupport;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
- /* does AM support ORDER BY result of an operator on indexed column? */
- bool amcanorderbyop;
/* does AM support backward scanning? */
bool amcanbackward;
/* does AM support UNIQUE indexes? */
@@ -221,6 +229,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ ammatchorderby_function ammatchorderby; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a008ae0..4680cb8 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -814,7 +814,6 @@ struct IndexOptInfo
bool hypothetical; /* true if index doesn't really exist */
/* Remaining fields are copied from the index AM's API struct: */
- bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
@@ -823,6 +822,12 @@ struct IndexOptInfo
bool amcanparallel; /* does AM support parallel scan? */
/* Rather than include amapi.h here, we declare amcostestimate like this */
void (*amcostestimate) (); /* AM's cost estimator */
+ /* AM order-by match function */
+ bool (*ammatchorderby) (struct IndexOptInfo *index,
+ List *pathkeys,
+ List *index_clause_columns,
+ List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
};
/*
@@ -1133,7 +1138,7 @@ typedef struct Path
* An empty list implies a full index scan.
*
* 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
- * been found to be usable as ordering operators for an amcanorderbyop index.
+ * been found to be usable as ordering operators for an ammatchorderby index.
* The list must match the path's pathkeys, ie, one expression per pathkey
* in the same order. These are not RestrictInfos, just bare expressions,
* since they generally won't yield booleans. It's guaranteed that each
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6d087c2..bd6ce97 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -388,7 +388,7 @@ typedef struct SampleScan
* indexorderbyops is a list of the OIDs of the operators used to sort the
* ORDER BY expressions. This is used together with indexorderbyorig to
* recheck ordering at run time. (Note that indexorderby, indexorderbyorig,
- * and indexorderbyops are used for amcanorderbyop cases, not amcanorder.)
+ * and indexorderbyops are used for ammatchorderby cases, not amcanorder.)
*
* indexorderdir specifies the scan ordering, for indexscans on amcanorder
* indexes (for other indexes it should be "don't care").
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 040335a..e9f4f75 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -79,6 +79,11 @@ extern bool indexcol_is_bool_constant_for_query(IndexOptInfo *index,
extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index);
extern void check_index_predicates(PlannerInfo *root, RelOptInfo *rel);
+extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
+ int *indexcol_p);
+extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses, List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
/*
* tidpath.h
0003-Extract-structure-BTScanState-v07.patchtext/x-patch; name=0003-Extract-structure-BTScanState-v07.patchDownload
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index c56ee5e..bf6a6c6 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -214,6 +214,7 @@ bool
btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
bool res;
/* btree indexes are never lossy */
@@ -224,7 +225,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(so->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
@@ -241,7 +242,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(so->currPos))
+ if (!BTScanPosIsValid(state->currPos))
res = _bt_first(scan, dir);
else
{
@@ -259,11 +260,11 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* trying to optimize that, so we don't detect it, but instead
* just forget any excess entries.
*/
- if (so->killedItems == NULL)
- so->killedItems = (int *)
+ if (state->killedItems == NULL)
+ state->killedItems = (int *)
palloc(MaxIndexTuplesPerPage * sizeof(int));
- if (so->numKilled < MaxIndexTuplesPerPage)
- so->killedItems[so->numKilled++] = so->currPos.itemIndex;
+ if (state->numKilled < MaxIndexTuplesPerPage)
+ state->killedItems[so->state.numKilled++] = state->currPos.itemIndex;
}
/*
@@ -288,6 +289,7 @@ int64
btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
int64 ntids = 0;
ItemPointer heapTid;
@@ -320,7 +322,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
* Advance to next tuple within page. This is the same as the
* easy case in _bt_next().
*/
- if (++so->currPos.itemIndex > so->currPos.lastItem)
+ if (++currPos->itemIndex > currPos->lastItem)
{
/* let _bt_next do the heavy lifting */
if (!_bt_next(scan, ForwardScanDirection))
@@ -328,7 +330,7 @@ btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
}
/* Save tuple ID, and continue scanning */
- heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
+ heapTid = &currPos->items[currPos->itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
}
@@ -356,8 +358,8 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
/* allocate private workspace */
so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
- BTScanPosInvalidate(so->currPos);
- BTScanPosInvalidate(so->markPos);
+ BTScanPosInvalidate(so->state.currPos);
+ BTScanPosInvalidate(so->state.markPos);
if (scan->numberOfKeys > 0)
so->keyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
else
@@ -368,15 +370,15 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
so->arrayKeys = NULL;
so->arrayContext = NULL;
- so->killedItems = NULL; /* until needed */
- so->numKilled = 0;
+ so->state.killedItems = NULL; /* until needed */
+ so->state.numKilled = 0;
/*
* We don't know yet whether the scan will be index-only, so we do not
* allocate the tuple workspace arrays until btrescan. However, we set up
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
- so->currTuples = so->markTuples = NULL;
+ so->state.currTuples = so->state.markTuples = NULL;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -385,6 +387,45 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
return scan;
}
+static void
+_bt_release_current_position(BTScanState state, Relation indexRelation,
+ bool invalidate)
+{
+ /* we aren't holding any read locks, but gotta drop the pins */
+ if (BTScanPosIsValid(state->currPos))
+ {
+ /* Before leaving current page, deal with any killed items */
+ if (state->numKilled > 0)
+ _bt_killitems(state, indexRelation);
+
+ BTScanPosUnpinIfPinned(state->currPos);
+
+ if (invalidate)
+ BTScanPosInvalidate(state->currPos);
+ }
+}
+
+static void
+_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
+{
+ /* No need to invalidate positions, if the RAM is about to be freed. */
+ _bt_release_current_position(state, scan->indexRelation, !free);
+
+ state->markItemIndex = -1;
+ BTScanPosUnpinIfPinned(state->markPos);
+
+ if (free)
+ {
+ if (state->killedItems != NULL)
+ pfree(state->killedItems);
+ if (state->currTuples != NULL)
+ pfree(state->currTuples);
+ /* markTuples should not be pfree'd (_bt_allocate_tuple_workspaces) */
+ }
+ else
+ BTScanPosInvalidate(state->markPos);
+}
+
/*
* btrescan() -- rescan an index relation
*/
@@ -393,21 +434,11 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
ScanKey orderbys, int norderbys)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState state = &so->state;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
- }
+ _bt_release_scan_state(scan, state, false);
- so->markItemIndex = -1;
so->arrayKeyCount = 0;
- BTScanPosUnpinIfPinned(so->markPos);
- BTScanPosInvalidate(so->markPos);
/*
* Allocate tuple workspace arrays, if needed for an index-only scan and
@@ -425,11 +456,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
* a SIGSEGV is not possible. Yeah, this is ugly as sin, but it beats
* adding special-case treatment for name_ops elsewhere.
*/
- if (scan->xs_want_itup && so->currTuples == NULL)
- {
- so->currTuples = (char *) palloc(BLCKSZ * 2);
- so->markTuples = so->currTuples + BLCKSZ;
- }
+ if (scan->xs_want_itup && state->currTuples == NULL)
+ _bt_allocate_tuple_workspaces(state);
/*
* Reset the scan keys. Note that keys ordering stuff moved to _bt_first.
@@ -453,19 +481,7 @@ btendscan(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* we aren't holding any read locks, but gotta drop the pins */
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
-
- so->markItemIndex = -1;
- BTScanPosUnpinIfPinned(so->markPos);
-
- /* No need to invalidate positions, the RAM is about to be freed. */
+ _bt_release_scan_state(scan, &so->state, true);
/* Release storage */
if (so->keyData != NULL)
@@ -473,24 +489,15 @@ btendscan(IndexScanDesc scan)
/* so->arrayKeyData and so->arrayKeys are in arrayContext */
if (so->arrayContext != NULL)
MemoryContextDelete(so->arrayContext);
- if (so->killedItems != NULL)
- pfree(so->killedItems);
- if (so->currTuples != NULL)
- pfree(so->currTuples);
- /* so->markTuples should not be pfree'd, see btrescan */
+
pfree(so);
}
-/*
- * btmarkpos() -- save current scan position
- */
-void
-btmarkpos(IndexScanDesc scan)
+static void
+_bt_mark_current_position(BTScanState state)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
-
/* There may be an old mark with a pin (but no lock). */
- BTScanPosUnpinIfPinned(so->markPos);
+ BTScanPosUnpinIfPinned(state->markPos);
/*
* Just record the current itemIndex. If we later step to next page
@@ -498,32 +505,34 @@ btmarkpos(IndexScanDesc scan)
* the currPos struct in markPos. If (as often happens) the mark is moved
* before we leave the page, we don't have to do that work.
*/
- if (BTScanPosIsValid(so->currPos))
- so->markItemIndex = so->currPos.itemIndex;
+ if (BTScanPosIsValid(state->currPos))
+ state->markItemIndex = state->currPos.itemIndex;
else
{
- BTScanPosInvalidate(so->markPos);
- so->markItemIndex = -1;
+ BTScanPosInvalidate(state->markPos);
+ state->markItemIndex = -1;
}
-
- /* Also record the current positions of any array keys */
- if (so->numArrayKeys)
- _bt_mark_array_keys(scan);
}
/*
- * btrestrpos() -- restore scan to last saved position
+ * btmarkpos() -- save current scan position
*/
void
-btrestrpos(IndexScanDesc scan)
+btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- /* Restore the marked positions of any array keys */
+ _bt_mark_current_position(&so->state);
+
+ /* Also record the current positions of any array keys */
if (so->numArrayKeys)
- _bt_restore_array_keys(scan);
+ _bt_mark_array_keys(scan);
+}
- if (so->markItemIndex >= 0)
+static void
+_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
+{
+ if (state->markItemIndex >= 0)
{
/*
* The scan has never moved to a new page since the last mark. Just
@@ -532,7 +541,7 @@ btrestrpos(IndexScanDesc scan)
* NB: In this case we can't count on anything in so->markPos to be
* accurate.
*/
- so->currPos.itemIndex = so->markItemIndex;
+ state->currPos.itemIndex = state->markItemIndex;
}
else
{
@@ -542,28 +551,21 @@ btrestrpos(IndexScanDesc scan)
* locks, but if we're still holding the pin for the current position,
* we must drop it.
*/
- if (BTScanPosIsValid(so->currPos))
- {
- /* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
- BTScanPosUnpinIfPinned(so->currPos);
- }
+ _bt_release_current_position(state, scan->indexRelation,
+ !BTScanPosIsValid(state->markPos));
- if (BTScanPosIsValid(so->markPos))
+ if (BTScanPosIsValid(state->markPos))
{
/* bump pin on mark buffer for assignment to current buffer */
- if (BTScanPosIsPinned(so->markPos))
- IncrBufferRefCount(so->markPos.buf);
- memcpy(&so->currPos, &so->markPos,
+ if (BTScanPosIsPinned(state->markPos))
+ IncrBufferRefCount(state->markPos.buf);
+ memcpy(&state->currPos, &state->markPos,
offsetof(BTScanPosData, items[1]) +
- so->markPos.lastItem * sizeof(BTScanPosItem));
- if (so->currTuples)
- memcpy(so->currTuples, so->markTuples,
- so->markPos.nextTupleOffset);
+ state->markPos.lastItem * sizeof(BTScanPosItem));
+ if (state->currTuples)
+ memcpy(state->currTuples, state->markTuples,
+ state->markPos.nextTupleOffset);
}
- else
- BTScanPosInvalidate(so->currPos);
}
}
@@ -779,9 +781,10 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
}
/*
- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
- * btbulkdelete() wasn't called.
- */
+- * _bt_vacuum_needs_cleanup() -- Checks if index needs cleanup assuming that
+- * btbulkdelete() wasn't called.
++ * btrestrpos() -- restore scan to last saved position
+ */
static bool
_bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
{
@@ -844,6 +847,21 @@ _bt_vacuum_needs_cleanup(IndexVacuumInfo *info)
}
/*
+ * btrestrpos() -- restore scan to last saved position
+ */
+void
+btrestrpos(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
+ /* Restore the marked positions of any array keys */
+ if (so->numArrayKeys)
+ _bt_restore_array_keys(scan);
+
+ _bt_restore_marked_position(scan, &so->state);
+}
+
+/*
* Bulk deletion of all index entries pointing to a set of heap tuples.
* The set of target tuples is specified via a callback routine that tells
* whether any given heap tuple (identified by ItemPointer) is being deleted.
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 9283223..cbd72bd 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -24,18 +24,19 @@
#include "utils/rel.h"
-static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir,
+static bool _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
OffsetNumber offnum);
-static void _bt_saveitem(BTScanOpaque so, int itemIndex,
+static void _bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup);
-static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
-static bool _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir);
+static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
+static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
-static inline void _bt_initialize_more_data(BTScanOpaque so, ScanDirection dir);
+static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
/*
@@ -544,6 +545,58 @@ _bt_compare(Relation rel,
}
/*
+ * _bt_return_current_item() -- Prepare current scan state item for return.
+ *
+ * This function is used only in "return _bt_return_current_item();" statements
+ * and always returns true.
+ */
+static inline bool
+_bt_return_current_item(IndexScanDesc scan, BTScanState state)
+{
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+
+ scan->xs_ctup.t_self = currItem->heapTid;
+
+ if (scan->xs_want_itup)
+ scan->xs_itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+
+ return true;
+}
+
+/*
+ * _bt_load_first_page() -- Load data from the first page of the scan.
+ *
+ * Caller must have pinned and read-locked state->currPos.buf.
+ *
+ * On success exit, state->currPos is updated to contain data from the next
+ * interesting page. For success on a scan using a non-MVCC snapshot we hold
+ * a pin, but not a read lock, on that page. If we do not hold the pin, we
+ * set state->currPos.buf to InvalidBuffer. We return true to indicate success.
+ *
+ * If there are no more matching records in the given direction at all,
+ * we drop all locks and pins, set state->currPos.buf to InvalidBuffer,
+ * and return false.
+ */
+static bool
+_bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
+{
+ if (!_bt_readpage(scan, state, dir, offnum))
+ {
+ /*
+ * There's no actually-matching data on this page. Try to advance to
+ * the next page. Return false if there's no matching data at all.
+ */
+ LockBuffer(state->currPos.buf, BUFFER_LOCK_UNLOCK);
+ return _bt_steppage(scan, state, dir);
+ }
+
+ /* Drop the lock, and maybe the pin, on the current page */
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
+ return true;
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -568,6 +621,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
BTStack stack;
OffsetNumber offnum;
@@ -581,10 +635,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
int i;
bool status = true;
StrategyNumber strat_total;
- BTScanPosItem *currItem;
BlockNumber blkno;
- Assert(!BTScanPosIsValid(so->currPos));
+ Assert(!BTScanPosIsValid(*currPos));
pgstat_count_index_scan(rel);
@@ -1075,7 +1128,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* their scan
*/
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1083,7 +1136,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
PredicateLockPage(rel, BufferGetBlockNumber(buf),
scan->xs_snapshot);
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
/* position to the precise item on the page */
offnum = _bt_binsrch(rel, buf, keysCount, scankeys, nextkey);
@@ -1110,36 +1163,36 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
offnum = OffsetNumberPrev(offnum);
/* remember which buffer we have pinned, if any */
- Assert(!BTScanPosIsValid(so->currPos));
- so->currPos.buf = buf;
+ Assert(!BTScanPosIsValid(*currPos));
+ currPos->buf = buf;
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, offnum))
+ if (!_bt_load_first_page(scan, &so->state, dir, offnum))
+ return false;
+
+readcomplete:
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
+}
+
+/*
+ * Advance to next tuple on current page; or if there's no more,
+ * try to step to the next page with data.
+ */
+static bool
+_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
+{
+ if (ScanDirectionIsForward(dir))
{
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
+ if (++state->currPos.itemIndex <= state->currPos.lastItem)
+ return true;
}
else
{
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ if (--state->currPos.itemIndex >= state->currPos.firstItem)
+ return true;
}
-readcomplete:
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_steppage(scan, state, dir);
}
/*
@@ -1160,44 +1213,20 @@ bool
_bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- BTScanPosItem *currItem;
- /*
- * Advance to next tuple on current page; or if there's no more, try to
- * step to the next page with data.
- */
- if (ScanDirectionIsForward(dir))
- {
- if (++so->currPos.itemIndex > so->currPos.lastItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
- else
- {
- if (--so->currPos.itemIndex < so->currPos.firstItem)
- {
- if (!_bt_steppage(scan, dir))
- return false;
- }
- }
+ if (!_bt_next_item(scan, &so->state, dir))
+ return false;
/* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
-
- return true;
+ return _bt_return_current_item(scan, &so->state);
}
/*
* _bt_readpage() -- Load data from current index page into so->currPos
*
- * Caller must have pinned and read-locked so->currPos.buf; the buffer's state
- * is not changed here. Also, currPos.moreLeft and moreRight must be valid;
- * they are updated as appropriate. All other fields of so->currPos are
+ * Caller must have pinned and read-locked pos->buf; the buffer's state
+ * is not changed here. Also, pos->moreLeft and moreRight must be valid;
+ * they are updated as appropriate. All other fields of pos are
* initialized from scratch here.
*
* We scan the current page starting at offnum and moving in the indicated
@@ -1212,9 +1241,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
* Returns true if any matching items found on the page, false if none.
*/
static bool
-_bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
+_bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
+ OffsetNumber offnum)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
@@ -1227,9 +1257,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We must have the buffer pinned and locked, but the usual macro can't be
* used here; this function is what makes it good for currPos.
*/
- Assert(BufferIsValid(so->currPos.buf));
+ Assert(BufferIsValid(pos->buf));
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* allow next page be processed by parallel worker */
@@ -1238,7 +1268,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (ScanDirectionIsForward(dir))
_bt_parallel_release(scan, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1248,30 +1278,30 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
* We note the buffer's block number so that we can release the pin later.
* This allows us to re-read the buffer if it is needed again for hinting.
*/
- so->currPos.currPage = BufferGetBlockNumber(so->currPos.buf);
+ pos->currPage = BufferGetBlockNumber(pos->buf);
/*
* We save the LSN of the page as we read it, so that we know whether it
* safe to apply LP_DEAD hints to the page later. This allows us to drop
* the pin for MVCC scans, which allows vacuum to avoid blocking.
*/
- so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf);
+ pos->lsn = BufferGetLSNAtomic(pos->buf);
/*
* we must save the page's right-link while scanning it; this tells us
* where to step right to after we're done with these items. There is no
* corresponding need for the left-link, since splits always go right.
*/
- so->currPos.nextPage = opaque->btpo_next;
+ pos->nextPage = opaque->btpo_next;
/* initialize tuple workspace to empty */
- so->currPos.nextTupleOffset = 0;
+ pos->nextTupleOffset = 0;
/*
* Now that the current page has been made consistent, the macro should be
* good.
*/
- Assert(BTScanPosIsPinned(so->currPos));
+ Assert(BTScanPosIsPinned(*pos));
if (ScanDirectionIsForward(dir))
{
@@ -1286,13 +1316,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
if (itup != NULL)
{
/* tuple passes all scan key conditions, so remember it */
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
itemIndex++;
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreRight = false;
+ pos->moreRight = false;
break;
}
@@ -1300,9 +1330,9 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex <= MaxIndexTuplesPerPage);
- so->currPos.firstItem = 0;
- so->currPos.lastItem = itemIndex - 1;
- so->currPos.itemIndex = 0;
+ pos->firstItem = 0;
+ pos->lastItem = itemIndex - 1;
+ pos->itemIndex = 0;
}
else
{
@@ -1318,12 +1348,12 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
{
/* tuple passes all scan key conditions, so remember it */
itemIndex--;
- _bt_saveitem(so, itemIndex, offnum, itup);
+ _bt_saveitem(state, itemIndex, offnum, itup);
}
if (!continuescan)
{
/* there can't be any more matches, so stop */
- so->currPos.moreLeft = false;
+ pos->moreLeft = false;
break;
}
@@ -1331,30 +1361,31 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
}
Assert(itemIndex >= 0);
- so->currPos.firstItem = itemIndex;
- so->currPos.lastItem = MaxIndexTuplesPerPage - 1;
- so->currPos.itemIndex = MaxIndexTuplesPerPage - 1;
+ pos->firstItem = itemIndex;
+ pos->lastItem = MaxIndexTuplesPerPage - 1;
+ pos->itemIndex = MaxIndexTuplesPerPage - 1;
}
- return (so->currPos.firstItem <= so->currPos.lastItem);
+ return (pos->firstItem <= pos->lastItem);
}
/* Save an index item into so->currPos.items[itemIndex] */
static void
-_bt_saveitem(BTScanOpaque so, int itemIndex,
+_bt_saveitem(BTScanState state, int itemIndex,
OffsetNumber offnum, IndexTuple itup)
{
- BTScanPosItem *currItem = &so->currPos.items[itemIndex];
+ BTScanPosItem *currItem = &state->currPos.items[itemIndex];
currItem->heapTid = itup->t_tid;
currItem->indexOffset = offnum;
- if (so->currTuples)
+ if (state->currTuples)
{
Size itupsz = IndexTupleSize(itup);
- currItem->tupleOffset = so->currPos.nextTupleOffset;
- memcpy(so->currTuples + so->currPos.nextTupleOffset, itup, itupsz);
- so->currPos.nextTupleOffset += MAXALIGN(itupsz);
+ currItem->tupleOffset = state->currPos.nextTupleOffset;
+ memcpy(state->currTuples + state->currPos.nextTupleOffset,
+ itup, itupsz);
+ state->currPos.nextTupleOffset += MAXALIGN(itupsz);
}
}
@@ -1370,35 +1401,36 @@ _bt_saveitem(BTScanOpaque so, int itemIndex,
* to InvalidBuffer. We return true to indicate success.
*/
static bool
-_bt_steppage(IndexScanDesc scan, ScanDirection dir)
+_bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
+ Relation rel = scan->indexRelation;
BlockNumber blkno = InvalidBlockNumber;
bool status = true;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(*currPos));
/* Before leaving current page, deal with any killed items */
- if (so->numKilled > 0)
- _bt_killitems(scan);
+ if (state->numKilled > 0)
+ _bt_killitems(state, rel);
/*
* Before we modify currPos, make a copy of the page data if there was a
* mark position that needs it.
*/
- if (so->markItemIndex >= 0)
+ if (state->markItemIndex >= 0)
{
/* bump pin on current buffer for assignment to mark buffer */
- if (BTScanPosIsPinned(so->currPos))
- IncrBufferRefCount(so->currPos.buf);
- memcpy(&so->markPos, &so->currPos,
+ if (BTScanPosIsPinned(*currPos))
+ IncrBufferRefCount(currPos->buf);
+ memcpy(&state->markPos, currPos,
offsetof(BTScanPosData, items[1]) +
- so->currPos.lastItem * sizeof(BTScanPosItem));
- if (so->markTuples)
- memcpy(so->markTuples, so->currTuples,
- so->currPos.nextTupleOffset);
- so->markPos.itemIndex = so->markItemIndex;
- so->markItemIndex = -1;
+ currPos->lastItem * sizeof(BTScanPosItem));
+ if (state->markTuples)
+ memcpy(state->markTuples, state->currTuples,
+ currPos->nextTupleOffset);
+ state->markPos.itemIndex = state->markItemIndex;
+ state->markItemIndex = -1;
}
if (ScanDirectionIsForward(dir))
@@ -1414,27 +1446,27 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
if (!status)
{
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so use the previously-saved nextPage link. */
- blkno = so->currPos.nextPage;
+ blkno = currPos->nextPage;
}
/* Remember we left a page with data */
- so->currPos.moreLeft = true;
+ currPos->moreLeft = true;
/* release the previous buffer, if pinned */
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
}
else
{
/* Remember we left a page with data */
- so->currPos.moreRight = true;
+ currPos->moreRight = true;
if (scan->parallel_scan != NULL)
{
@@ -1443,25 +1475,25 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* ended already, bail out.
*/
status = _bt_parallel_seize(scan, &blkno);
- BTScanPosUnpinIfPinned(so->currPos);
+ BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
/* Not parallel, so just use our own notion of the current page */
- blkno = so->currPos.currPage;
+ blkno = currPos->currPage;
}
}
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, currPos);
return true;
}
@@ -1477,9 +1509,10 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
* locks and pins, set so->currPos.buf to InvalidBuffer, and return false.
*/
static bool
-_bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &state->currPos;
Relation rel;
Page page;
BTPageOpaque opaque;
@@ -1495,17 +1528,17 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* if we're at end of scan, give up and mark parallel scan as
* done, so that all the workers can finish their scan
*/
- if (blkno == P_NONE || !so->currPos.moreRight)
+ if (blkno == P_NONE || !currPos->moreRight)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* check for interrupts while we're not holding any buffer lock */
CHECK_FOR_INTERRUPTS();
/* step right one page */
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
- page = BufferGetPage(so->currPos.buf);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
/* check for deleted page */
@@ -1514,7 +1547,7 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
PredicateLockPage(rel, blkno, scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreRight if we can stop */
- if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque)))
+ if (_bt_readpage(scan, state, dir, P_FIRSTDATAKEY(opaque)))
break;
}
else if (scan->parallel_scan != NULL)
@@ -1526,18 +1559,18 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
}
else
{
blkno = opaque->btpo_next;
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
}
}
}
@@ -1547,10 +1580,10 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* Should only happen in parallel cases, when some other backend
* advanced the scan.
*/
- if (so->currPos.currPage != blkno)
+ if (currPos->currPage != blkno)
{
- BTScanPosUnpinIfPinned(so->currPos);
- so->currPos.currPage = blkno;
+ BTScanPosUnpinIfPinned(*currPos);
+ currPos->currPage = blkno;
}
/*
@@ -1575,31 +1608,30 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* is MVCC the page cannot move past the half-dead state to fully
* deleted.
*/
- if (BTScanPosIsPinned(so->currPos))
- LockBuffer(so->currPos.buf, BT_READ);
+ if (BTScanPosIsPinned(*currPos))
+ LockBuffer(currPos->buf, BT_READ);
else
- so->currPos.buf = _bt_getbuf(rel, so->currPos.currPage, BT_READ);
+ currPos->buf = _bt_getbuf(rel, currPos->currPage, BT_READ);
for (;;)
{
/* Done if we know there are no matching keys to the left */
- if (!so->currPos.moreLeft)
+ if (!currPos->moreLeft)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
/* Step to next physical page */
- so->currPos.buf = _bt_walk_left(rel, so->currPos.buf,
- scan->xs_snapshot);
+ currPos->buf = _bt_walk_left(rel, currPos->buf, scan->xs_snapshot);
/* if we're physically at end of index, return failure */
- if (so->currPos.buf == InvalidBuffer)
+ if (currPos->buf == InvalidBuffer)
{
_bt_parallel_done(scan);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1608,21 +1640,21 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
* it's not half-dead and contains matching tuples. Else loop back
* and do it all again.
*/
- page = BufferGetPage(so->currPos.buf);
+ page = BufferGetPage(currPos->buf);
TestForOldSnapshot(scan->xs_snapshot, rel, page);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
{
- PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
+ PredicateLockPage(rel, BufferGetBlockNumber(currPos->buf), scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreLeft if we can stop */
- if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page)))
+ if (_bt_readpage(scan, state, dir, PageGetMaxOffsetNumber(page)))
break;
}
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(so->currPos.buf));
+ _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1633,14 +1665,14 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- _bt_relbuf(rel, so->currPos.buf);
+ _bt_relbuf(rel, currPos->buf);
status = _bt_parallel_seize(scan, &blkno);
if (!status)
{
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
- so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
+ currPos->buf = _bt_getbuf(rel, blkno, BT_READ);
}
}
}
@@ -1659,13 +1691,13 @@ _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- if (!_bt_readnextpage(scan, blkno, dir))
+ if (!_bt_readnextpage(scan, &so->state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
return true;
}
@@ -1890,11 +1922,11 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
{
Relation rel = scan->indexRelation;
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos currPos = &so->state.currPos;
Buffer buf;
Page page;
BTPageOpaque opaque;
OffsetNumber start;
- BTScanPosItem *currItem;
/*
* Scan down to the leftmost or rightmost leaf page. This is a simplified
@@ -1910,7 +1942,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* exists.
*/
PredicateLockRelation(rel, scan->xs_snapshot);
- BTScanPosInvalidate(so->currPos);
+ BTScanPosInvalidate(*currPos);
return false;
}
@@ -1939,36 +1971,15 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
}
/* remember which buffer we have pinned */
- so->currPos.buf = buf;
+ currPos->buf = buf;
- _bt_initialize_more_data(so, dir);
+ _bt_initialize_more_data(&so->state, dir);
- /*
- * Now load data from the first page of the scan.
- */
- if (!_bt_readpage(scan, dir, start))
- {
- /*
- * There's no actually-matching data on this page. Try to advance to
- * the next page. Return false if there's no matching data at all.
- */
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
- if (!_bt_steppage(scan, dir))
- return false;
- }
- else
- {
- /* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->currPos);
- }
-
- /* OK, itemIndex says what to return */
- currItem = &so->currPos.items[so->currPos.itemIndex];
- scan->xs_ctup.t_self = currItem->heapTid;
- if (scan->xs_want_itup)
- scan->xs_itup = (IndexTuple) (so->currTuples + currItem->tupleOffset);
+ if (!_bt_load_first_page(scan, &so->state, dir, start))
+ return false;
- return true;
+ /* OK, currPos->itemIndex says what to return */
+ return _bt_return_current_item(scan, &so->state);
}
/*
@@ -1976,19 +1987,19 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* for scan direction
*/
static inline void
-_bt_initialize_more_data(BTScanOpaque so, ScanDirection dir)
+_bt_initialize_more_data(BTScanState state, ScanDirection dir)
{
/* initialize moreLeft/moreRight appropriately for scan direction */
if (ScanDirectionIsForward(dir))
{
- so->currPos.moreLeft = false;
- so->currPos.moreRight = true;
+ state->currPos.moreLeft = false;
+ state->currPos.moreRight = true;
}
else
{
- so->currPos.moreLeft = true;
- so->currPos.moreRight = false;
+ state->currPos.moreLeft = true;
+ state->currPos.moreRight = false;
}
- so->numKilled = 0; /* just paranoia */
- so->markItemIndex = -1; /* ditto */
+ state->numKilled = 0; /* just paranoia */
+ state->markItemIndex = -1; /* ditto */
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 2c05fb5..e548354 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1741,26 +1741,26 @@ _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
* away and the TID was re-used by a completely different heap tuple.
*/
void
-_bt_killitems(IndexScanDesc scan)
+_bt_killitems(BTScanState state, Relation indexRelation)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPos pos = &state->currPos;
Page page;
BTPageOpaque opaque;
OffsetNumber minoff;
OffsetNumber maxoff;
int i;
- int numKilled = so->numKilled;
+ int numKilled = state->numKilled;
bool killedsomething = false;
- Assert(BTScanPosIsValid(so->currPos));
+ Assert(BTScanPosIsValid(state->currPos));
/*
* Always reset the scan state, so we don't look for same items on other
* pages.
*/
- so->numKilled = 0;
+ state->numKilled = 0;
- if (BTScanPosIsPinned(so->currPos))
+ if (BTScanPosIsPinned(*pos))
{
/*
* We have held the pin on this page since we read the index tuples,
@@ -1768,44 +1768,42 @@ _bt_killitems(IndexScanDesc scan)
* re-use of any TID on the page, so there is no need to check the
* LSN.
*/
- LockBuffer(so->currPos.buf, BT_READ);
-
- page = BufferGetPage(so->currPos.buf);
+ LockBuffer(pos->buf, BT_READ);
}
else
{
Buffer buf;
/* Attempt to re-read the buffer, getting pin and lock. */
- buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, BT_READ);
+ buf = _bt_getbuf(indexRelation, pos->currPage, BT_READ);
/* It might not exist anymore; in which case we can't hint it. */
if (!BufferIsValid(buf))
return;
- page = BufferGetPage(buf);
- if (BufferGetLSNAtomic(buf) == so->currPos.lsn)
- so->currPos.buf = buf;
+ if (BufferGetLSNAtomic(buf) == pos->lsn)
+ pos->buf = buf;
else
{
/* Modified while not pinned means hinting is not safe. */
- _bt_relbuf(scan->indexRelation, buf);
+ _bt_relbuf(indexRelation, buf);
return;
}
}
+ page = BufferGetPage(pos->buf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
minoff = P_FIRSTDATAKEY(opaque);
maxoff = PageGetMaxOffsetNumber(page);
for (i = 0; i < numKilled; i++)
{
- int itemIndex = so->killedItems[i];
- BTScanPosItem *kitem = &so->currPos.items[itemIndex];
+ int itemIndex = state->killedItems[i];
+ BTScanPosItem *kitem = &pos->items[itemIndex];
OffsetNumber offnum = kitem->indexOffset;
- Assert(itemIndex >= so->currPos.firstItem &&
- itemIndex <= so->currPos.lastItem);
+ Assert(itemIndex >= pos->firstItem &&
+ itemIndex <= pos->lastItem);
if (offnum < minoff)
continue; /* pure paranoia */
while (offnum <= maxoff)
@@ -1833,10 +1831,10 @@ _bt_killitems(IndexScanDesc scan)
if (killedsomething)
{
opaque->btpo_flags |= BTP_HAS_GARBAGE;
- MarkBufferDirtyHint(so->currPos.buf, true);
+ MarkBufferDirtyHint(pos->buf, true);
}
- LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(pos->buf, BUFFER_LOCK_UNLOCK);
}
@@ -2214,3 +2212,14 @@ _bt_check_natts(Relation rel, Page page, OffsetNumber offnum)
}
}
+
+/*
+ * _bt_allocate_tuple_workspaces() -- Allocate buffers for saving index tuples
+ * in index-only scans.
+ */
+void
+_bt_allocate_tuple_workspaces(BTScanState state)
+{
+ state->currTuples = (char *) palloc(BLCKSZ * 2);
+ state->markTuples = state->currTuples + BLCKSZ;
+}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 4fb92d6..d78df29 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -433,22 +433,8 @@ typedef struct BTArrayKeyInfo
Datum *elem_values; /* array of num_elems Datums */
} BTArrayKeyInfo;
-typedef struct BTScanOpaqueData
+typedef struct BTScanStateData
{
- /* these fields are set by _bt_preprocess_keys(): */
- bool qual_ok; /* false if qual can never be satisfied */
- int numberOfKeys; /* number of preprocessed scan keys */
- ScanKey keyData; /* array of preprocessed scan keys */
-
- /* workspace for SK_SEARCHARRAY support */
- ScanKey arrayKeyData; /* modified copy of scan->keyData */
- int numArrayKeys; /* number of equality-type array keys (-1 if
- * there are any unsatisfiable array keys) */
- int arrayKeyCount; /* count indicating number of array scan keys
- * processed */
- BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
- MemoryContext arrayContext; /* scan-lifespan context for array data */
-
/* info about killed items if any (killedItems is NULL if never used) */
int *killedItems; /* currPos.items indexes of killed items */
int numKilled; /* number of currently stored items */
@@ -473,6 +459,27 @@ typedef struct BTScanOpaqueData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+} BTScanStateData;
+
+typedef BTScanStateData *BTScanState;
+
+typedef struct BTScanOpaqueData
+{
+ /* these fields are set by _bt_preprocess_keys(): */
+ bool qual_ok; /* false if qual can never be satisfied */
+ int numberOfKeys; /* number of preprocessed scan keys */
+ ScanKey keyData; /* array of preprocessed scan keys */
+
+ /* workspace for SK_SEARCHARRAY support */
+ ScanKey arrayKeyData; /* modified copy of scan->keyData */
+ int numArrayKeys; /* number of equality-type array keys (-1 if
+ * there are any unsatisfiable array keys) */
+ int arrayKeyCount; /* count indicating number of array scan keys
+ * processed */
+ BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
+ MemoryContext arrayContext; /* scan-lifespan context for array data */
+
+ BTScanStateData state;
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -589,7 +596,7 @@ extern void _bt_preprocess_keys(IndexScanDesc scan);
extern IndexTuple _bt_checkkeys(IndexScanDesc scan,
Page page, OffsetNumber offnum,
ScanDirection dir, bool *continuescan);
-extern void _bt_killitems(IndexScanDesc scan);
+extern void _bt_killitems(BTScanState state, Relation indexRelation);
extern BTCycleId _bt_vacuum_cycleid(Relation rel);
extern BTCycleId _bt_start_vacuum(Relation rel);
extern void _bt_end_vacuum(Relation rel);
@@ -602,6 +609,7 @@ extern bool btproperty(Oid index_oid, int attno,
bool *res, bool *isnull);
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
+extern void _bt_allocate_tuple_workspaces(BTScanState state);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 3d3c76d..5c065a5 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -181,6 +181,8 @@ BTScanOpaqueData
BTScanPos
BTScanPosData
BTScanPosItem
+BTScanState
+BTScanStateData
BTShared
BTSortArrayContext
BTSpool
0004-Add-kNN-support-to-btree-v07.patchtext/x-patch; name=0004-Add-kNN-support-to-btree-v07.patchDownload
diff --git a/doc/src/sgml/btree.sgml b/doc/src/sgml/btree.sgml
index 996932e..b94be7a 100644
--- a/doc/src/sgml/btree.sgml
+++ b/doc/src/sgml/btree.sgml
@@ -200,6 +200,53 @@
planner relies on them for optimization purposes.
</para>
+ <para>
+ To implement the distance ordered (nearest-neighbor) search, we only need
+ to define a distance operator (usually it called
+ <literal><-></literal>) with a correpsonding operator family for
+ distance comparison in the index's operator class. These operators must
+ satisfy the following assumptions for all non-null values
+ <replaceable>A</replaceable>, <replaceable>B</replaceable>,
+ <replaceable>C</replaceable> of the datatype:
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <replaceable>A</replaceable> <literal><-></literal>
+ <replaceable>B</replaceable> <literal>=</literal>
+ <replaceable>B</replaceable> <literal><-></literal>
+ <replaceable>A</replaceable>
+ (<firstterm>symmetric law</firstterm>)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if <replaceable>A</replaceable> <literal>=</literal>
+ <replaceable>B</replaceable>, then <replaceable>A</replaceable>
+ <literal><-></literal> <replaceable>C</replaceable>
+ <literal>=</literal> <replaceable>B</replaceable>
+ <literal><-></literal> <replaceable>C</replaceable>
+ (<firstterm>distance equivalence</firstterm>)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ if (<replaceable>A</replaceable> <literal><=</literal>
+ <replaceable>B</replaceable> and <replaceable>B</replaceable>
+ <literal><=</literal> <replaceable>C</replaceable>) or
+ (<replaceable>A</replaceable> <literal>>=</literal>
+ <replaceable>B</replaceable> and <replaceable>B</replaceable>
+ <literal>>=</literal> <replaceable>C</replaceable>),
+ then <replaceable>A</replaceable> <literal><-></literal>
+ <replaceable>B</replaceable> <literal><=</literal>
+ <replaceable>A</replaceable> <literal><-></literal>
+ <replaceable>C</replaceable>
+ (<firstterm>monotonicity</firstterm>)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
</sect1>
<sect1 id="btree-support-funcs">
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 46f427b..1998171 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -175,6 +175,17 @@ CREATE INDEX test1_id_index ON test1 (id);
</para>
<para>
+ B-tree indexes are also capable of optimizing <quote>nearest-neighbor</quote>
+ searches, such as
+<programlisting><![CDATA[
+SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
+]]>
+</programlisting>
+ which finds the ten events closest to a given target date. The ability
+ to do this is again dependent on the particular operator class being used.
+ </para>
+
+ <para>
<indexterm>
<primary>index</primary>
<secondary>hash</secondary>
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..93094bc 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -1242,7 +1242,8 @@ SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING)
<title>Ordering Operators</title>
<para>
- Some index access methods (currently, only GiST and SP-GiST) support the concept of
+ Some index access methods (currently, only B-tree, GiST and SP-GiST)
+ support the concept of
<firstterm>ordering operators</firstterm>. What we have been discussing so far
are <firstterm>search operators</firstterm>. A search operator is one for which
the index can be searched to find all rows satisfying
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
index 3680e69..3f7e1b1 100644
--- a/src/backend/access/nbtree/README
+++ b/src/backend/access/nbtree/README
@@ -659,3 +659,20 @@ routines must treat it accordingly. The actual key stored in the
item is irrelevant, and need not be stored at all. This arrangement
corresponds to the fact that an L&Y non-leaf page has one more pointer
than key.
+
+Nearest-neighbor search
+-----------------------
+
+There is a special scan strategy for nearest-neighbor (kNN) search,
+that is used in queries with ORDER BY distance clauses like this:
+SELECT * FROM tab WHERE col > const1 ORDER BY col <-> const2 LIMIT k.
+But, unlike GiST, B-tree supports only a one ordering operator on the
+first index column.
+
+At the beginning of kNN scan, we need to determine which strategy we
+will use --- a special bidirectional or a ordinary unidirectional.
+If the point from which we measure the distance falls into the scan range,
+we use bidirectional scan starting from this point, else we use simple
+unidirectional scan in the right direction. Algorithm of a bidirectional
+scan is very simple: at each step we advancing scan in that direction,
+which has the nearest point.
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index bf6a6c6..fb413e7 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -25,6 +25,9 @@
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
+#include "nodes/pathnodes.h"
+#include "nodes/primnodes.h"
+#include "optimizer/paths.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "storage/condition_variable.h"
@@ -33,7 +36,9 @@
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/index_selfuncs.h"
+#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -79,6 +84,7 @@ typedef enum
typedef struct BTParallelScanDescData
{
BlockNumber btps_scanPage; /* latest or next page to be scanned */
+ BlockNumber btps_knnScanPage; /* secondary kNN page to be scanned */
BTPS_State btps_pageStatus; /* indicates whether next page is
* available for scan. see above for
* possible states of parallel scan. */
@@ -97,6 +103,10 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
+static bool btmatchorderby(IndexOptInfo *index, List *pathkeys,
+ List *index_clauses, List **orderby_clauses_p,
+ List **orderby_clause_columns_p);
+
/*
* Btree handler function: return IndexAmRoutine with access method parameters
@@ -107,7 +117,7 @@ bthandler(PG_FUNCTION_ARGS)
{
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
- amroutine->amstrategies = BTMaxStrategyNumber;
+ amroutine->amstrategies = BtreeMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
amroutine->amcanorder = true;
amroutine->amcanbackward = true;
@@ -143,7 +153,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
- amroutine->ammatchorderby = NULL;
+ amroutine->ammatchorderby = btmatchorderby;
PG_RETURN_POINTER(amroutine);
}
@@ -215,23 +225,30 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTScanState state = &so->state;
+ ScanDirection arraydir =
+ scan->numberOfOrderBys > 0 ? ForwardScanDirection : dir;
bool res;
/* btree indexes are never lossy */
scan->xs_recheck = false;
+ scan->xs_recheckorderby = false;
+
+ if (so->scanDirection != NoMovementScanDirection)
+ dir = so->scanDirection;
/*
* If we have any array keys, initialize them during first call for a
* scan. We can't do this in btrescan because we don't know the scan
* direction at that time.
*/
- if (so->numArrayKeys && !BTScanPosIsValid(state->currPos))
+ if (so->numArrayKeys && !BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
return false;
- _bt_start_array_keys(scan, dir);
+ _bt_start_array_keys(scan, arraydir);
}
/* This loop handles advancing to the next array elements, if any */
@@ -242,7 +259,8 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
* the appropriate direction. If we haven't done so yet, we call
* _bt_first() to get the first item in the scan.
*/
- if (!BTScanPosIsValid(state->currPos))
+ if (!BTScanPosIsValid(state->currPos) &&
+ (!so->knnState || !BTScanPosIsValid(so->knnState->currPos)))
res = _bt_first(scan, dir);
else
{
@@ -277,7 +295,7 @@ btgettuple(IndexScanDesc scan, ScanDirection dir)
if (res)
break;
/* ... otherwise see if we have more array keys to deal with */
- } while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
+ } while (so->numArrayKeys && _bt_advance_array_keys(scan, arraydir));
return res;
}
@@ -350,9 +368,6 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
IndexScanDesc scan;
BTScanOpaque so;
- /* no order by operators allowed */
- Assert(norderbys == 0);
-
/* get the scan */
scan = RelationGetIndexScan(rel, nkeys, norderbys);
@@ -379,6 +394,9 @@ btbeginscan(Relation rel, int nkeys, int norderbys)
* scan->xs_itupdesc whether we'll need it or not, since that's so cheap.
*/
so->state.currTuples = so->state.markTuples = NULL;
+ so->knnState = NULL;
+ so->distanceTypeByVal = true;
+ so->scanDirection = NoMovementScanDirection;
scan->xs_itupdesc = RelationGetDescr(rel);
@@ -408,6 +426,8 @@ _bt_release_current_position(BTScanState state, Relation indexRelation,
static void
_bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
/* No need to invalidate positions, if the RAM is about to be freed. */
_bt_release_current_position(state, scan->indexRelation, !free);
@@ -424,6 +444,17 @@ _bt_release_scan_state(IndexScanDesc scan, BTScanState state, bool free)
}
else
BTScanPosInvalidate(state->markPos);
+
+ if (!so->distanceTypeByVal)
+ {
+ if (DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+ state->currDistance = PointerGetDatum(NULL);
+
+ if (DatumGetPointer(state->markDistance))
+ pfree(DatumGetPointer(state->markDistance));
+ state->markDistance = PointerGetDatum(NULL);
+ }
}
/*
@@ -438,6 +469,13 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
_bt_release_scan_state(scan, state, false);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ so->knnState = NULL;
+ }
+
so->arrayKeyCount = 0;
/*
@@ -469,6 +507,14 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
scan->numberOfKeys * sizeof(ScanKeyData));
so->numberOfKeys = 0; /* until _bt_preprocess_keys sets it */
+ if (orderbys && scan->numberOfOrderBys > 0)
+ memmove(scan->orderByData,
+ orderbys,
+ scan->numberOfOrderBys * sizeof(ScanKeyData));
+
+ so->scanDirection = NoMovementScanDirection;
+ so->distanceTypeByVal = true;
+
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
_bt_preprocess_array_keys(scan);
}
@@ -483,6 +529,12 @@ btendscan(IndexScanDesc scan)
_bt_release_scan_state(scan, &so->state, true);
+ if (so->knnState)
+ {
+ _bt_release_scan_state(scan, so->knnState, true);
+ pfree(so->knnState);
+ }
+
/* Release storage */
if (so->keyData != NULL)
pfree(so->keyData);
@@ -494,7 +546,7 @@ btendscan(IndexScanDesc scan)
}
static void
-_bt_mark_current_position(BTScanState state)
+_bt_mark_current_position(BTScanOpaque so, BTScanState state)
{
/* There may be an old mark with a pin (but no lock). */
BTScanPosUnpinIfPinned(state->markPos);
@@ -512,6 +564,21 @@ _bt_mark_current_position(BTScanState state)
BTScanPosInvalidate(state->markPos);
state->markItemIndex = -1;
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->markDistance));
+
+ state->markIsNull = !BTScanPosIsValid(state->currPos) ||
+ state->currIsNull;
+
+ state->markDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->currDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -522,7 +589,13 @@ btmarkpos(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
- _bt_mark_current_position(&so->state);
+ _bt_mark_current_position(so, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_mark_current_position(so, so->knnState);
+ so->markRightIsNearest = so->currRightIsNearest;
+ }
/* Also record the current positions of any array keys */
if (so->numArrayKeys)
@@ -532,6 +605,8 @@ btmarkpos(IndexScanDesc scan)
static void
_bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+
if (state->markItemIndex >= 0)
{
/*
@@ -567,6 +642,19 @@ _bt_restore_marked_position(IndexScanDesc scan, BTScanState state)
state->markPos.nextTupleOffset);
}
}
+
+ if (so->knnState)
+ {
+ if (!so->distanceTypeByVal)
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currIsNull = state->markIsNull;
+ state->currDistance =
+ state->markIsNull ? PointerGetDatum(NULL)
+ : datumCopy(state->markDistance,
+ so->distanceTypeByVal,
+ so->distanceTypeLen);
+ }
}
/*
@@ -588,6 +676,7 @@ btinitparallelscan(void *target)
SpinLockInit(&bt_target->btps_mutex);
bt_target->btps_scanPage = InvalidBlockNumber;
+ bt_target->btps_knnScanPage = InvalidBlockNumber;
bt_target->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
bt_target->btps_arrayKeyCount = 0;
ConditionVariableInit(&bt_target->btps_cv);
@@ -614,6 +703,7 @@ btparallelrescan(IndexScanDesc scan)
*/
SpinLockAcquire(&btscan->btps_mutex);
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount = 0;
SpinLockRelease(&btscan->btps_mutex);
@@ -638,7 +728,7 @@ btparallelrescan(IndexScanDesc scan)
* Callers should ignore the value of pageno if the return value is false.
*/
bool
-_bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
+_bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
BTPS_State pageStatus;
@@ -646,12 +736,17 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
bool status = true;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
*pageno = P_NONE;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ scanPage = state == &so->state
+ ? &btscan->btps_scanPage
+ : &btscan->btps_knnScanPage;
+
while (1)
{
SpinLockAcquire(&btscan->btps_mutex);
@@ -677,7 +772,7 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* of advancing it to a new page!
*/
btscan->btps_pageStatus = BTPARALLEL_ADVANCING;
- *pageno = btscan->btps_scanPage;
+ *pageno = *scanPage;
exit_loop = true;
}
SpinLockRelease(&btscan->btps_mutex);
@@ -696,19 +791,42 @@ _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno)
* can now begin advancing the scan.
*/
void
-_bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
+_bt_parallel_release(IndexScanDesc scan, BTScanState state,
+ BlockNumber scan_page)
{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
+ bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
SpinLockAcquire(&btscan->btps_mutex);
- btscan->btps_scanPage = scan_page;
- btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ *scanPage = scan_page;
+ /* switch to idle state only if both KNN pages are initialized */
+ if (!knnScan || *otherScanPage != InvalidBlockNumber)
+ {
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ status_changed = true;
+ }
SpinLockRelease(&btscan->btps_mutex);
- ConditionVariableSignal(&btscan->btps_cv);
+
+ if (status_changed)
+ ConditionVariableSignal(&btscan->btps_cv);
}
/*
@@ -719,12 +837,15 @@ _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page)
* advance to the next page.
*/
void
-_bt_parallel_done(IndexScanDesc scan)
+_bt_parallel_done(IndexScanDesc scan, BTScanState state)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
ParallelIndexScanDesc parallel_scan = scan->parallel_scan;
BTParallelScanDesc btscan;
+ BlockNumber *scanPage;
+ BlockNumber *otherScanPage;
bool status_changed = false;
+ bool knnScan = so->knnState != NULL;
/* Do nothing, for non-parallel scans */
if (parallel_scan == NULL)
@@ -733,18 +854,41 @@ _bt_parallel_done(IndexScanDesc scan)
btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan,
parallel_scan->ps_offset);
+ if (!state || state == &so->state)
+ {
+ scanPage = &btscan->btps_scanPage;
+ otherScanPage = &btscan->btps_knnScanPage;
+ }
+ else
+ {
+ scanPage = &btscan->btps_knnScanPage;
+ otherScanPage = &btscan->btps_scanPage;
+ }
+
/*
* Mark the parallel scan as done for this combination of scan keys,
* unless some other process already did so. See also
* _bt_advance_array_keys.
*/
SpinLockAcquire(&btscan->btps_mutex);
- if (so->arrayKeyCount >= btscan->btps_arrayKeyCount &&
- btscan->btps_pageStatus != BTPARALLEL_DONE)
+
+ Assert(btscan->btps_pageStatus == BTPARALLEL_ADVANCING);
+
+ if (so->arrayKeyCount >= btscan->btps_arrayKeyCount)
{
- btscan->btps_pageStatus = BTPARALLEL_DONE;
+ *scanPage = P_NONE;
status_changed = true;
+
+ /* switch to "done" state only if both KNN scans are done */
+ if (!knnScan || *otherScanPage == P_NONE)
+ btscan->btps_pageStatus = BTPARALLEL_DONE;
+ /* else switch to "idle" state only if both KNN scans are initialized */
+ else if (*otherScanPage != InvalidBlockNumber)
+ btscan->btps_pageStatus = BTPARALLEL_IDLE;
+ else
+ status_changed = false;
}
+
SpinLockRelease(&btscan->btps_mutex);
/* wake up all the workers associated with this parallel scan */
@@ -774,6 +918,7 @@ _bt_parallel_advance_array_keys(IndexScanDesc scan)
if (btscan->btps_pageStatus == BTPARALLEL_DONE)
{
btscan->btps_scanPage = InvalidBlockNumber;
+ btscan->btps_knnScanPage = InvalidBlockNumber;
btscan->btps_pageStatus = BTPARALLEL_NOT_INITIALIZED;
btscan->btps_arrayKeyCount++;
}
@@ -859,6 +1004,12 @@ btrestrpos(IndexScanDesc scan)
_bt_restore_array_keys(scan);
_bt_restore_marked_position(scan, &so->state);
+
+ if (so->knnState)
+ {
+ _bt_restore_marked_position(scan, so->knnState);
+ so->currRightIsNearest = so->markRightIsNearest;
+ }
}
/*
@@ -1394,3 +1545,91 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+/*
+ * btmatchorderby() -- Check whether KNN-search strategy is applicable to
+ * the given ORDER BY distance operator.
+ */
+static bool
+btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
+ List **orderby_clauses_p, List **orderby_clausecols_p)
+{
+ Expr *expr;
+ ListCell *lc;
+ int indexcol;
+ int num_eq_cols = 0;
+
+ /* only one ORDER BY clause is supported */
+ if (list_length(pathkeys) != 1)
+ return false;
+
+ /*
+ * Compute a number of leading consequent index columns with equality
+ * restriction clauses.
+ */
+ foreach(lc, index_clauses)
+ {
+ IndexClause *iclause = lfirst_node(IndexClause, lc);
+ ListCell *lcq;
+
+ indexcol = iclause->indexcol;
+
+ if (indexcol > num_eq_cols)
+ /* Sequence of equality-restricted columns is broken. */
+ break;
+
+ foreach(lcq, iclause->indexquals)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcq);
+ Expr *clause = rinfo->clause;
+ Oid opno;
+ StrategyNumber strat;
+
+ if (!clause)
+ continue;
+
+ if (IsA(clause, OpExpr))
+ {
+ OpExpr *opexpr = (OpExpr *) clause;
+
+ opno = opexpr->opno;
+ }
+ else
+ {
+ /* Skip unsupported expression */
+ continue;
+ }
+
+ /* Check if the operator is btree equality operator. */
+ strat = get_op_opfamily_strategy(opno, index->opfamily[indexcol]);
+
+ if (strat == BTEqualStrategyNumber)
+ num_eq_cols = indexcol + 1;
+ }
+ }
+
+ /*
+ * If there are no equality columns try to match only the first column,
+ * otherwise try all columns.
+ */
+ indexcol = num_eq_cols ? -1 : 0;
+
+ expr = match_orderbyop_pathkey(index, castNode(PathKey, linitial(pathkeys)),
+ &indexcol);
+
+ if (!expr)
+ return false;
+
+ /*
+ * ORDER BY distance is supported only for the first index column or if
+ * all previous columns have equality restrictions.
+ */
+ if (indexcol > num_eq_cols)
+ return false;
+
+ /* Return first ORDER BY clause's expression and column. */
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *orderby_clausecols_p = lappend_int(*orderby_clausecols_p, indexcol);
+
+ return true;
+}
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index cbd72bd..28f94e7 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -31,12 +31,14 @@ static void _bt_saveitem(BTScanState state, int itemIndex,
static bool _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir);
static bool _bt_readnextpage(IndexScanDesc scan, BTScanState state,
BlockNumber blkno, ScanDirection dir);
-static bool _bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno,
- ScanDirection dir);
+static bool _bt_parallel_readpage(IndexScanDesc scan, BTScanState state,
+ BlockNumber blkno, ScanDirection dir);
static Buffer _bt_walk_left(Relation rel, Buffer buf, Snapshot snapshot);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp);
static inline void _bt_initialize_more_data(BTScanState state, ScanDirection dir);
+static BTScanState _bt_alloc_knn_scan(IndexScanDesc scan);
+static bool _bt_start_knn_scan(IndexScanDesc scan, bool left, bool right);
/*
@@ -597,6 +599,157 @@ _bt_load_first_page(IndexScanDesc scan, BTScanState state, ScanDirection dir,
}
/*
+ * _bt_calc_current_dist() -- Calculate distance from the current item
+ * of the scan state to the target order-by ScanKey argument.
+ */
+static void
+_bt_calc_current_dist(IndexScanDesc scan, BTScanState state)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanPosItem *currItem = &state->currPos.items[state->currPos.itemIndex];
+ IndexTuple itup = (IndexTuple) (state->currTuples + currItem->tupleOffset);
+ ScanKey scankey = &scan->orderByData[0];
+ Datum value;
+
+ value = index_getattr(itup, scankey->sk_attno, scan->xs_itupdesc,
+ &state->currIsNull);
+
+ if (state->currIsNull)
+ return; /* NULL distance */
+
+ value = FunctionCall2Coll(&scankey->sk_func,
+ scankey->sk_collation,
+ value,
+ scankey->sk_argument);
+
+ /* free previous distance value for by-ref types */
+ if (!so->distanceTypeByVal && DatumGetPointer(state->currDistance))
+ pfree(DatumGetPointer(state->currDistance));
+
+ state->currDistance = value;
+}
+
+/*
+ * _bt_compare_current_dist() -- Compare current distances of the left and right scan states.
+ *
+ * NULL distances are considered to be greater than any non-NULL distances.
+ *
+ * Returns true if right distance is lesser than left, otherwise false.
+ */
+static bool
+_bt_compare_current_dist(BTScanOpaque so, BTScanState rstate, BTScanState lstate)
+{
+ if (lstate->currIsNull)
+ return true; /* non-NULL < NULL */
+
+ if (rstate->currIsNull)
+ return false; /* NULL > non-NULL */
+
+ return DatumGetBool(FunctionCall2Coll(&so->distanceCmpProc,
+ InvalidOid, /* XXX collation for distance comparison */
+ rstate->currDistance,
+ lstate->currDistance));
+}
+
+/*
+ * _bt_alloc_knn_scan() -- Allocate additional backward scan state for KNN.
+ */
+static BTScanState
+_bt_alloc_knn_scan(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState lstate = (BTScanState) palloc(sizeof(BTScanStateData));
+
+ _bt_allocate_tuple_workspaces(lstate);
+
+ if (!scan->xs_want_itup)
+ {
+ /* We need to request index tuples for distance comparison. */
+ scan->xs_want_itup = true;
+ _bt_allocate_tuple_workspaces(&so->state);
+ }
+
+ BTScanPosInvalidate(lstate->currPos);
+ lstate->currPos.moreLeft = false;
+ lstate->currPos.moreRight = false;
+ BTScanPosInvalidate(lstate->markPos);
+ lstate->markItemIndex = -1;
+ lstate->killedItems = NULL;
+ lstate->numKilled = 0;
+ lstate->currDistance = PointerGetDatum(NULL);
+ lstate->markDistance = PointerGetDatum(NULL);
+
+ return so->knnState = lstate;
+}
+
+static bool
+_bt_start_knn_scan(IndexScanDesc scan, bool left, bool right)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+
+ if (!left && !right)
+ return false; /* empty result */
+
+ rstate = &so->state;
+ lstate = so->knnState;
+
+ if (left && right)
+ {
+ /*
+ * We have found items in both scan directions,
+ * determine nearest item to return.
+ */
+ _bt_calc_current_dist(scan, rstate);
+ _bt_calc_current_dist(scan, lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+
+ /* Reset right flag if the left item is nearer. */
+ right = so->currRightIsNearest;
+ }
+
+ /* Return current item of the selected scan direction. */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
+ * _bt_init_knn_scan() -- Init additional scan state for KNN search.
+ *
+ * Caller must pin and read-lock scan->state.currPos.buf buffer.
+ *
+ * If empty result was found returned false.
+ * Otherwise prepared current item, and returned true.
+ */
+static bool
+_bt_init_knn_scan(IndexScanDesc scan, OffsetNumber offnum)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state; /* right (forward) main scan state */
+ BTScanState lstate; /* additional left (backward) KNN scan state */
+ Buffer buf = rstate->currPos.buf;
+ bool left,
+ right;
+
+ lstate = _bt_alloc_knn_scan(scan);
+
+ /* Bump pin and lock count before BTScanPosData copying. */
+ IncrBufferRefCount(buf);
+ LockBuffer(buf, BT_READ);
+
+ memcpy(&lstate->currPos, &rstate->currPos, sizeof(BTScanPosData));
+ lstate->currPos.moreLeft = true;
+ lstate->currPos.moreRight = false;
+
+ /* Load first pages from the both scans. */
+ right = _bt_load_first_page(scan, rstate, ForwardScanDirection, offnum);
+ left = _bt_load_first_page(scan, lstate, BackwardScanDirection,
+ OffsetNumberPrev(offnum));
+
+ return _bt_start_knn_scan(scan, left, right);
+}
+
+/*
* _bt_first() -- Find the first item in a scan.
*
* We need to be clever about the direction of scan, the search
@@ -654,6 +807,15 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!so->qual_ok)
return false;
+ if (scan->numberOfOrderBys > 0)
+ {
+ if (so->useBidirectionalKnnScan)
+ _bt_init_distance_comparison(scan);
+ else if (so->scanDirection != NoMovementScanDirection)
+ /* use selected KNN scan direction */
+ dir = so->scanDirection;
+ }
+
/*
* For parallel scans, get the starting page from shared state. If the
* scan has not started, proceed to find out first leaf page in the usual
@@ -662,19 +824,50 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*/
if (scan->parallel_scan != NULL)
{
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, &so->state, &blkno);
if (!status)
return false;
- else if (blkno == P_NONE)
- {
- _bt_parallel_done(scan);
- return false;
- }
else if (blkno != InvalidBlockNumber)
{
- if (!_bt_parallel_readpage(scan, blkno, dir))
- return false;
- goto readcomplete;
+ bool knn = so->useBidirectionalKnnScan;
+ bool right;
+ bool left;
+
+ if (knn)
+ _bt_alloc_knn_scan(scan);
+
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, &so->state);
+ right = false;
+ }
+ else
+ right = _bt_parallel_readpage(scan, &so->state, blkno,
+ knn ? ForwardScanDirection : dir);
+
+ if (!knn)
+ return right && _bt_return_current_item(scan, &so->state);
+
+ /* seize additional backward KNN scan */
+ left = _bt_parallel_seize(scan, so->knnState, &blkno);
+
+ if (left)
+ {
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, so->knnState);
+ left = false;
+ }
+ else
+ {
+ /* backward scan should be already initialized */
+ Assert(blkno != InvalidBlockNumber);
+ left = _bt_parallel_readpage(scan, so->knnState, blkno,
+ BackwardScanDirection);
+ }
+ }
+
+ return _bt_start_knn_scan(scan, left, right);
}
}
@@ -724,7 +917,9 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* storing their addresses into the local startKeys[] array.
*----------
*/
+
strat_total = BTEqualStrategyNumber;
+
if (so->numberOfKeys > 0)
{
AttrNumber curattr;
@@ -749,6 +944,10 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*/
for (cur = so->keyData, i = 0;; cur++, i++)
{
+ if (so->useBidirectionalKnnScan &&
+ curattr >= scan->orderByData->sk_attno)
+ break;
+
if (i >= so->numberOfKeys || cur->sk_attno != curattr)
{
/*
@@ -851,6 +1050,16 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
}
}
+ if (so->useBidirectionalKnnScan)
+ {
+ Assert(strat_total == BTEqualStrategyNumber);
+ strat_total = BtreeKNNSearchStrategyNumber;
+
+ (void) _bt_init_knn_start_keys(scan, &startKeys[keysCount],
+ ¬nullkeys[keysCount]);
+ keysCount++;
+ }
+
/*
* If we found no usable boundary keys, we have to start from one end of
* the tree. Walk down that edge to the first or last key, and scan from
@@ -865,7 +1074,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
if (!match)
{
/* No match, so mark (parallel) scan finished */
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
}
return match;
@@ -900,7 +1109,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(subkey->sk_flags & SK_ROW_MEMBER);
if (subkey->sk_flags & SK_ISNULL)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
return false;
}
memcpy(scankeys + i, subkey, sizeof(ScanKeyData));
@@ -1080,6 +1289,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
break;
case BTGreaterEqualStrategyNumber:
+ case BtreeKNNSearchStrategyNumber:
/*
* Find first item >= scankey. (This is only used for forward
@@ -1127,7 +1337,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* mark parallel scan as done, so that all the workers can finish
* their scan
*/
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, NULL);
BTScanPosInvalidate(*currPos);
return false;
@@ -1166,17 +1376,21 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
Assert(!BTScanPosIsValid(*currPos));
currPos->buf = buf;
+ if (strat_total == BtreeKNNSearchStrategyNumber)
+ return _bt_init_knn_scan(scan, offnum);
+
if (!_bt_load_first_page(scan, &so->state, dir, offnum))
- return false;
+ return false; /* empty result */
-readcomplete:
/* OK, currPos->itemIndex says what to return */
return _bt_return_current_item(scan, &so->state);
}
/*
- * Advance to next tuple on current page; or if there's no more,
- * try to step to the next page with data.
+ * _bt_next_item() -- Advance to next tuple on current page;
+ * or if there's no more, try to step to the next page with data.
+ *
+ * If there are no more matching records in the given direction
*/
static bool
_bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
@@ -1196,6 +1410,51 @@ _bt_next_item(IndexScanDesc scan, BTScanState state, ScanDirection dir)
}
/*
+ * _bt_next_nearest() -- Return next nearest item from bidirectional KNN scan.
+ */
+static bool
+_bt_next_nearest(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ BTScanState rstate = &so->state;
+ BTScanState lstate = so->knnState;
+ bool right = BTScanPosIsValid(rstate->currPos);
+ bool left = BTScanPosIsValid(lstate->currPos);
+ bool advanceRight;
+
+ if (right && left)
+ advanceRight = so->currRightIsNearest;
+ else if (right)
+ advanceRight = true;
+ else if (left)
+ advanceRight = false;
+ else
+ return false; /* end of the scan */
+
+ if (advanceRight)
+ right = _bt_next_item(scan, rstate, ForwardScanDirection);
+ else
+ left = _bt_next_item(scan, lstate, BackwardScanDirection);
+
+ if (!left && !right)
+ return false; /* end of the scan */
+
+ if (left && right)
+ {
+ /*
+ * If there are items in both scans we must recalculate distance
+ * in the advanced scan.
+ */
+ _bt_calc_current_dist(scan, advanceRight ? rstate : lstate);
+ so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
+ right = so->currRightIsNearest;
+ }
+
+ /* return nearest item */
+ return _bt_return_current_item(scan, right ? rstate : lstate);
+}
+
+/*
* _bt_next() -- Get the next item in a scan.
*
* On entry, so->currPos describes the current page, which may be pinned
@@ -1214,6 +1473,10 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ if (so->knnState)
+ /* return next neareset item from KNN scan */
+ return _bt_next_nearest(scan);
+
if (!_bt_next_item(scan, &so->state, dir))
return false;
@@ -1266,9 +1529,9 @@ _bt_readpage(IndexScanDesc scan, BTScanState state, ScanDirection dir,
if (scan->parallel_scan)
{
if (ScanDirectionIsForward(dir))
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
else
- _bt_parallel_release(scan, BufferGetBlockNumber(pos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(pos->buf));
}
minoff = P_FIRSTDATAKEY(opaque);
@@ -1442,7 +1705,7 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the next block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
/* release the previous buffer, if pinned */
@@ -1474,13 +1737,19 @@ _bt_steppage(IndexScanDesc scan, BTScanState state, ScanDirection dir)
* Seize the scan to get the current block number; if the scan has
* ended already, bail out.
*/
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
BTScanPosUnpinIfPinned(*currPos);
if (!status)
{
BTScanPosInvalidate(*currPos);
return false;
}
+ if (blkno == P_NONE)
+ {
+ _bt_parallel_done(scan, state);
+ BTScanPosInvalidate(*currPos);
+ return false;
+ }
}
else
{
@@ -1530,7 +1799,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
*/
if (blkno == P_NONE || !currPos->moreRight)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1553,14 +1822,14 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, opaque->btpo_next);
+ _bt_parallel_release(scan, state, opaque->btpo_next);
}
/* nope, keep going */
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1619,7 +1888,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (!currPos->moreLeft)
{
_bt_relbuf(rel, currPos->buf);
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1630,7 +1899,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
/* if we're physically at end of index, return failure */
if (currPos->buf == InvalidBuffer)
{
- _bt_parallel_done(scan);
+ _bt_parallel_done(scan, state);
BTScanPosInvalidate(*currPos);
return false;
}
@@ -1654,7 +1923,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
else if (scan->parallel_scan != NULL)
{
/* allow next page be processed by parallel worker */
- _bt_parallel_release(scan, BufferGetBlockNumber(currPos->buf));
+ _bt_parallel_release(scan, state, BufferGetBlockNumber(currPos->buf));
}
/*
@@ -1666,7 +1935,7 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
if (scan->parallel_scan != NULL)
{
_bt_relbuf(rel, currPos->buf);
- status = _bt_parallel_seize(scan, &blkno);
+ status = _bt_parallel_seize(scan, state, &blkno);
if (!status)
{
BTScanPosInvalidate(*currPos);
@@ -1687,17 +1956,16 @@ _bt_readnextpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
* indicate success.
*/
static bool
-_bt_parallel_readpage(IndexScanDesc scan, BlockNumber blkno, ScanDirection dir)
+_bt_parallel_readpage(IndexScanDesc scan, BTScanState state, BlockNumber blkno,
+ ScanDirection dir)
{
- BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ _bt_initialize_more_data(state, dir);
- _bt_initialize_more_data(&so->state, dir);
-
- if (!_bt_readnextpage(scan, &so->state, blkno, dir))
+ if (!_bt_readnextpage(scan, state, blkno, dir))
return false;
/* Drop the lock, and maybe the pin, on the current page */
- _bt_drop_lock_and_maybe_pin(scan, &so->state.currPos);
+ _bt_drop_lock_and_maybe_pin(scan, &state->currPos);
return true;
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index e548354..5b9294b 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -20,16 +20,21 @@
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "catalog/pg_amop.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
typedef struct BTSortArrayContext
{
FmgrInfo flinfo;
+ FmgrInfo distflinfo;
+ FmgrInfo distcmpflinfo;
+ ScanKey distkey;
Oid collation;
bool reverse;
} BTSortArrayContext;
@@ -49,6 +54,11 @@ static void _bt_mark_scankey_required(ScanKey skey);
static bool _bt_check_rowcompare(ScanKey skey,
IndexTuple tuple, TupleDesc tupdesc,
ScanDirection dir, bool *continuescan);
+static inline StrategyNumber _bt_select_knn_strategy_for_key(IndexScanDesc scan,
+ ScanKey cond);
+static void _bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval);
+
/*
@@ -445,6 +455,7 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
{
Relation rel = scan->indexRelation;
Oid elemtype;
+ Oid opfamily;
RegProcedure cmp_proc;
BTSortArrayContext cxt;
int last_non_dup;
@@ -462,6 +473,54 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
if (elemtype == InvalidOid)
elemtype = rel->rd_opcintype[skey->sk_attno - 1];
+ opfamily = rel->rd_opfamily[skey->sk_attno - 1];
+
+ if (scan->numberOfOrderBys <= 0 ||
+ scan->orderByData[0].sk_attno != skey->sk_attno)
+ {
+ cxt.distkey = NULL;
+ cxt.reverse = reverse;
+ }
+ else
+ {
+ /* Init procedures for distance calculation and comparison. */
+ ScanKey distkey = &scan->orderByData[0];
+ ScanKeyData distkey2;
+ Oid disttype = distkey->sk_subtype;
+ Oid distopr;
+ RegProcedure distproc;
+
+ if (!OidIsValid(disttype))
+ disttype = rel->rd_opcintype[skey->sk_attno - 1];
+
+ /* Lookup distance operator in index column's operator family. */
+ distopr = get_opfamily_member(opfamily,
+ elemtype,
+ disttype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(distopr))
+ elog(ERROR, "missing operator (%u,%u) for strategy %d in opfamily %u",
+ elemtype, disttype, BtreeKNNSearchStrategyNumber, opfamily);
+
+ distproc = get_opcode(distopr);
+
+ if (!RegProcedureIsValid(distproc))
+ elog(ERROR, "missing code for operator %u", distopr);
+
+ fmgr_info(distproc, &cxt.distflinfo);
+
+ distkey2 = *distkey;
+ fmgr_info_copy(&distkey2.sk_func, &cxt.distflinfo, CurrentMemoryContext);
+ distkey2.sk_subtype = disttype;
+
+ _bt_get_distance_cmp_proc(&distkey2, opfamily, elemtype,
+ &cxt.distcmpflinfo, NULL, NULL);
+
+ cxt.distkey = distkey;
+ cxt.reverse = false; /* supported only ascending ordering */
+ }
+
/*
* Look up the appropriate comparison function in the opfamily.
*
@@ -470,19 +529,17 @@ _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
* non-cross-type support functions for any datatype that it supports at
* all.
*/
- cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
+ cmp_proc = get_opfamily_proc(opfamily,
elemtype,
elemtype,
BTORDER_PROC);
if (!RegProcedureIsValid(cmp_proc))
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
- BTORDER_PROC, elemtype, elemtype,
- rel->rd_opfamily[skey->sk_attno - 1]);
+ BTORDER_PROC, elemtype, elemtype, opfamily);
/* Sort the array elements */
fmgr_info(cmp_proc, &cxt.flinfo);
cxt.collation = skey->sk_collation;
- cxt.reverse = reverse;
qsort_arg((void *) elems, nelems, sizeof(Datum),
_bt_compare_array_elements, (void *) &cxt);
@@ -514,6 +571,23 @@ _bt_compare_array_elements(const void *a, const void *b, void *arg)
BTSortArrayContext *cxt = (BTSortArrayContext *) arg;
int32 compare;
+ if (cxt->distkey)
+ {
+ Datum dista = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ da,
+ cxt->distkey->sk_argument);
+ Datum distb = FunctionCall2Coll(&cxt->distflinfo,
+ cxt->collation,
+ db,
+ cxt->distkey->sk_argument);
+ bool cmp = DatumGetBool(FunctionCall2Coll(&cxt->distcmpflinfo,
+ cxt->collation,
+ dista,
+ distb));
+ return cmp ? -1 : 1;
+ }
+
compare = DatumGetInt32(FunctionCall2Coll(&cxt->flinfo,
cxt->collation,
da, db));
@@ -667,6 +741,66 @@ _bt_restore_array_keys(IndexScanDesc scan)
}
}
+/*
+ * _bt_emit_scan_key() -- Emit one prepared scan key
+ *
+ * Push the scan key into the so->keyData[] array, and then mark it if it is
+ * required. Also update selected kNN strategy.
+ */
+static void
+_bt_emit_scan_key(IndexScanDesc scan, ScanKey skey, int numberOfEqualCols)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ ScanKey outkey = &so->keyData[so->numberOfKeys++];
+
+ memcpy(outkey, skey, sizeof(ScanKeyData));
+
+ /*
+ * We can mark the qual as required (possibly only in one direction) if all
+ * attrs before this one had "=".
+ */
+ if (outkey->sk_attno - 1 == numberOfEqualCols)
+ _bt_mark_scankey_required(outkey);
+
+ /* Update kNN strategy if it is not already selected. */
+ if (so->useBidirectionalKnnScan)
+ {
+ switch (_bt_select_knn_strategy_for_key(scan, outkey))
+ {
+ case BTLessStrategyNumber:
+ case BTLessEqualStrategyNumber:
+ /*
+ * Ordering key argument is greater than all values in scan
+ * range, select backward scan direction.
+ */
+ so->scanDirection = BackwardScanDirection;
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BTEqualStrategyNumber:
+ /* Use default unidirectional scan direction. */
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BTGreaterEqualStrategyNumber:
+ case BTGreaterStrategyNumber:
+ /*
+ * Ordering key argument is lesser than all values in scan
+ * range, select forward scan direction.
+ */
+ so->scanDirection = ForwardScanDirection;
+ so->useBidirectionalKnnScan = false;
+ break;
+
+ case BtreeKNNSearchStrategyNumber:
+ /*
+ * Ordering key argument falls into scan range,
+ * keep using bidirectional scan.
+ */
+ break;
+ }
+ }
+}
/*
* _bt_preprocess_keys() -- Preprocess scan keys
@@ -758,10 +892,8 @@ _bt_preprocess_keys(IndexScanDesc scan)
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int numberOfKeys = scan->numberOfKeys;
int16 *indoption = scan->indexRelation->rd_indoption;
- int new_numberOfKeys;
int numberOfEqualCols;
ScanKey inkeys;
- ScanKey outkeys;
ScanKey cur;
ScanKey xform[BTMaxStrategyNumber];
bool test_result;
@@ -769,6 +901,24 @@ _bt_preprocess_keys(IndexScanDesc scan)
j;
AttrNumber attno;
+ if (scan->numberOfOrderBys > 0)
+ {
+ ScanKey ord = scan->orderByData;
+
+ if (scan->numberOfOrderBys > 1)
+ /* it should not happen, see btmatchorderby() */
+ elog(ERROR, "only one btree ordering operator is supported");
+
+ Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
+
+ /* use bidirectional kNN scan by default */
+ so->useBidirectionalKnnScan = true;
+ }
+ else
+ {
+ so->useBidirectionalKnnScan = false;
+ }
+
/* initialize result variables */
so->qual_ok = true;
so->numberOfKeys = 0;
@@ -784,7 +934,6 @@ _bt_preprocess_keys(IndexScanDesc scan)
else
inkeys = scan->keyData;
- outkeys = so->keyData;
cur = &inkeys[0];
/* we check that input keys are correctly ordered */
if (cur->sk_attno < 1)
@@ -796,18 +945,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
/* Apply indoption to scankey (might change sk_strategy!) */
if (!_bt_fix_scankey_strategy(cur, indoption))
so->qual_ok = false;
- memcpy(outkeys, cur, sizeof(ScanKeyData));
- so->numberOfKeys = 1;
- /* We can mark the qual as required if it's for first index col */
- if (cur->sk_attno == 1)
- _bt_mark_scankey_required(outkeys);
+
+ _bt_emit_scan_key(scan, cur, 0);
return;
}
/*
* Otherwise, do the full set of pushups.
*/
- new_numberOfKeys = 0;
numberOfEqualCols = 0;
/*
@@ -931,20 +1076,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
}
/*
- * Emit the cleaned-up keys into the outkeys[] array, and then
+ * Emit the cleaned-up keys into the so->keyData[] array, and then
* mark them if they are required. They are required (possibly
* only in one direction) if all attrs before this one had "=".
*/
for (j = BTMaxStrategyNumber; --j >= 0;)
{
if (xform[j])
- {
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, xform[j], sizeof(ScanKeyData));
- if (priorNumberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
- }
+ _bt_emit_scan_key(scan, xform[j], priorNumberOfEqualCols);
}
/*
@@ -964,17 +1103,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
/* if row comparison, push it directly to the output array */
if (cur->sk_flags & SK_ROW_HEADER)
{
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, cur, sizeof(ScanKeyData));
- if (numberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
+ _bt_emit_scan_key(scan, cur, numberOfEqualCols);
/*
* We don't support RowCompare using equality; such a qual would
* mess up the numberOfEqualCols tracking.
*/
Assert(j != (BTEqualStrategyNumber - 1));
+
continue;
}
@@ -1007,16 +1143,10 @@ _bt_preprocess_keys(IndexScanDesc scan)
* previous one in xform[j] and push this one directly to the
* output array.
*/
- ScanKey outkey = &outkeys[new_numberOfKeys++];
-
- memcpy(outkey, cur, sizeof(ScanKeyData));
- if (numberOfEqualCols == attno - 1)
- _bt_mark_scankey_required(outkey);
+ _bt_emit_scan_key(scan, cur, numberOfEqualCols);
}
}
}
-
- so->numberOfKeys = new_numberOfKeys;
}
/*
@@ -2075,6 +2205,39 @@ btproperty(Oid index_oid, int attno,
*res = true;
return true;
+ case AMPROP_DISTANCE_ORDERABLE:
+ {
+ Oid opclass,
+ opfamily,
+ opcindtype;
+
+ /* answer only for columns, not AM or whole index */
+ if (attno == 0)
+ return false;
+
+ opclass = get_index_column_opclass(index_oid, attno);
+
+ if (!OidIsValid(opclass))
+ {
+ *res = false; /* non-key attribute */
+ return true;
+ }
+
+ if (!get_opclass_opfamily_and_input_type(opclass,
+ &opfamily, &opcindtype))
+ {
+ *isnull = true;
+ return true;
+ }
+
+ *res = SearchSysCacheExists(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(opcindtype),
+ ObjectIdGetDatum(opcindtype),
+ Int16GetDatum(BtreeKNNSearchStrategyNumber));
+ return true;
+ }
+
default:
return false; /* punt to generic code */
}
@@ -2223,3 +2386,212 @@ _bt_allocate_tuple_workspaces(BTScanState state)
state->currTuples = (char *) palloc(BLCKSZ * 2);
state->markTuples = state->currTuples + BLCKSZ;
}
+
+static bool
+_bt_compare_row_key_with_ordering_key(ScanKey row, ScanKey ord, bool *result)
+{
+ ScanKey subkey = (ScanKey) DatumGetPointer(row->sk_argument);
+ int32 cmpresult;
+
+ Assert(subkey->sk_attno == 1);
+ Assert(subkey->sk_flags & SK_ROW_MEMBER);
+
+ if (subkey->sk_flags & SK_ISNULL)
+ return false;
+
+ /* Perform the test --- three-way comparison not bool operator */
+ cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
+ subkey->sk_collation,
+ ord->sk_argument,
+ subkey->sk_argument));
+
+ if (subkey->sk_flags & SK_BT_DESC)
+ cmpresult = -cmpresult;
+
+ /*
+ * At this point cmpresult indicates the overall result of the row
+ * comparison, and subkey points to the deciding column (or the last
+ * column if the result is "=").
+ */
+ switch (subkey->sk_strategy)
+ {
+ /* EQ and NE cases aren't allowed here */
+ case BTLessStrategyNumber:
+ *result = cmpresult < 0;
+ break;
+ case BTLessEqualStrategyNumber:
+ *result = cmpresult <= 0;
+ break;
+ case BTGreaterEqualStrategyNumber:
+ *result = cmpresult >= 0;
+ break;
+ case BTGreaterStrategyNumber:
+ *result = cmpresult > 0;
+ break;
+ default:
+ elog(ERROR, "unrecognized RowCompareType: %d",
+ (int) subkey->sk_strategy);
+ *result = false; /* keep compiler quiet */
+ }
+
+ return true;
+}
+
+/*
+ * _bt_select_knn_strategy_for_key() -- Determine which kNN scan strategy to use:
+ * bidirectional or unidirectional. We are checking here if the
+ * ordering scankey argument falls into the scan range: if it falls
+ * we must use bidirectional scan, otherwise we use unidirectional.
+ *
+ * Returns BtreeKNNSearchStrategyNumber for bidirectional scan or
+ * strategy number of non-matched scankey for unidirectional.
+ */
+static inline StrategyNumber
+_bt_select_knn_strategy_for_key(IndexScanDesc scan, ScanKey cond)
+{
+ ScanKey ord = scan->orderByData;
+ bool result;
+
+ /* only interesting in the index attribute that is ordered by a distance */
+ if (cond->sk_attno != ord->sk_attno)
+ return BtreeKNNSearchStrategyNumber;
+
+ if (cond->sk_strategy == BTEqualStrategyNumber)
+ /* always use simple unidirectional scan for equals operators */
+ return BTEqualStrategyNumber;
+
+ if (cond->sk_flags & SK_ROW_HEADER)
+ {
+ if (!_bt_compare_row_key_with_ordering_key(cond, ord, &result))
+ return BTEqualStrategyNumber; /* ROW(fist_index_attr, ...) IS NULL */
+ }
+ else
+ {
+ if (!_bt_compare_scankey_args(scan, cond, ord, cond, &result))
+ elog(ERROR, "could not compare ordering key");
+ }
+
+ if (!result)
+ /*
+ * Ordering scankey argument is out of scan range,
+ * use unidirectional scan.
+ */
+ return cond->sk_strategy;
+
+ return BtreeKNNSearchStrategyNumber;
+}
+
+int
+_bt_init_knn_start_keys(IndexScanDesc scan, ScanKey *startKeys, ScanKey bufKeys)
+{
+ ScanKey ord = scan->orderByData;
+ int indopt = scan->indexRelation->rd_indoption[ord->sk_attno - 1];
+ int flags = (indopt << SK_BT_INDOPTION_SHIFT) |
+ SK_ORDER_BY |
+ SK_SEARCHNULL; /* only for invalid procedure oid, see
+ * assert in ScanKeyEntryInitialize() */
+ int keysCount = 0;
+
+ /* Init btree search key with ordering key argument. */
+ ScanKeyEntryInitialize(&bufKeys[0],
+ flags,
+ ord->sk_attno,
+ BtreeKNNSearchStrategyNumber,
+ ord->sk_subtype,
+ ord->sk_collation,
+ InvalidOid,
+ ord->sk_argument);
+
+ startKeys[keysCount++] = &bufKeys[0];
+
+ return keysCount;
+}
+
+static Oid
+_bt_get_sortfamily_for_opfamily_op(Oid opfamily, Oid lefttype, Oid righttype,
+ StrategyNumber strategy)
+{
+ HeapTuple tp;
+ Form_pg_amop amop_tup;
+ Oid sortfamily;
+
+ tp = SearchSysCache4(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamily),
+ ObjectIdGetDatum(lefttype),
+ ObjectIdGetDatum(righttype),
+ Int16GetDatum(strategy));
+ if (!HeapTupleIsValid(tp))
+ return InvalidOid;
+ amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+ sortfamily = amop_tup->amopsortfamily;
+ ReleaseSysCache(tp);
+
+ return sortfamily;
+}
+
+/*
+ * _bt_get_distance_cmp_proc() -- Init procedure for comparsion of distances
+ * between "leftargtype" and "distkey".
+ */
+static void
+_bt_get_distance_cmp_proc(ScanKey distkey, Oid opfamily, Oid leftargtype,
+ FmgrInfo *finfo, int16 *typlen, bool *typbyval)
+{
+ RegProcedure opcode;
+ Oid sortfamily;
+ Oid opno;
+ Oid distanceType;
+
+ distanceType = get_func_rettype(distkey->sk_func.fn_oid);
+
+ sortfamily = _bt_get_sortfamily_for_opfamily_op(opfamily, leftargtype,
+ distkey->sk_subtype,
+ distkey->sk_strategy);
+
+ if (!OidIsValid(sortfamily))
+ elog(ERROR, "could not find sort family for btree ordering operator");
+
+ opno = get_opfamily_member(sortfamily,
+ distanceType,
+ distanceType,
+ BTLessEqualStrategyNumber);
+
+ if (!OidIsValid(opno))
+ elog(ERROR, "could not find operator for btree distance comparison");
+
+ opcode = get_opcode(opno);
+
+ if (!RegProcedureIsValid(opcode))
+ elog(ERROR,
+ "could not find procedure for btree distance comparison operator");
+
+ fmgr_info(opcode, finfo);
+
+ if (typlen)
+ get_typlenbyval(distanceType, typlen, typbyval);
+}
+
+/*
+ * _bt_init_distance_comparison() -- Init distance typlen/typbyval and its
+ * comparison procedure.
+ */
+void
+_bt_init_distance_comparison(IndexScanDesc scan)
+{
+ BTScanOpaque so = (BTScanOpaque) scan->opaque;
+ Relation rel = scan->indexRelation;
+ ScanKey ord = scan->orderByData;
+
+ _bt_get_distance_cmp_proc(ord,
+ rel->rd_opfamily[ord->sk_attno - 1],
+ rel->rd_opcintype[ord->sk_attno - 1],
+ &so->distanceCmpProc,
+ &so->distanceTypeLen,
+ &so->distanceTypeByVal);
+
+ if (!so->distanceTypeByVal)
+ {
+ so->state.currDistance = PointerGetDatum(NULL);
+ so->state.markDistance = PointerGetDatum(NULL);
+ }
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 0148ea7..4558fd3 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -22,9 +22,17 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
+#define BTRequiredOperatorSet \
+ ((1 << BTLessStrategyNumber) | \
+ (1 << BTLessEqualStrategyNumber) | \
+ (1 << BTEqualStrategyNumber) | \
+ (1 << BTGreaterEqualStrategyNumber) | \
+ (1 << BTGreaterStrategyNumber))
+
/*
* Validator for a btree opclass.
@@ -132,10 +140,11 @@ btvalidate(Oid opclassoid)
{
HeapTuple oprtup = &oprlist->members[i]->tuple;
Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ Oid op_rettype;
/* Check that only allowed strategy numbers exist */
if (oprform->amopstrategy < 1 ||
- oprform->amopstrategy > BTMaxStrategyNumber)
+ oprform->amopstrategy > BtreeMaxStrategyNumber)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -146,20 +155,29 @@ btvalidate(Oid opclassoid)
result = false;
}
- /* btree doesn't support ORDER BY operators */
- if (oprform->amoppurpose != AMOP_SEARCH ||
- OidIsValid(oprform->amopsortfamily))
+ /* btree supports ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH)
{
- ereport(INFO,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
- opfamilyname, "btree",
- format_operator(oprform->amopopr))));
- result = false;
+ /* ... and operator result must match the claimed btree opfamily */
+ op_rettype = get_op_rettype(oprform->amopopr);
+ if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
+ {
+ ereport(INFO,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
+ opfamilyname, "btree",
+ format_operator(oprform->amopopr))));
+ result = false;
+ }
+ }
+ else
+ {
+ /* Search operators must always return bool */
+ op_rettype = BOOLOID;
}
/* Check operator signature --- same for all btree strategies */
- if (!check_amop_signature(oprform->amopopr, BOOLOID,
+ if (!check_amop_signature(oprform->amopopr, op_rettype,
oprform->amoplefttype,
oprform->amoprighttype))
{
@@ -214,12 +232,8 @@ btvalidate(Oid opclassoid)
* or support functions for this datatype pair. The only things
* considered optional are the sortsupport and in_range functions.
*/
- if (thisgroup->operatorset !=
- ((1 << BTLessStrategyNumber) |
- (1 << BTLessEqualStrategyNumber) |
- (1 << BTEqualStrategyNumber) |
- (1 << BTGreaterEqualStrategyNumber) |
- (1 << BTGreaterStrategyNumber)))
+ if ((thisgroup->operatorset & BTRequiredOperatorSet) !=
+ BTRequiredOperatorSet)
{
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6cf0b0d..279d8ba 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -990,6 +990,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
* if we are only trying to build bitmap indexscans, nor if we have to
* assume the scan is unordered.
*/
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
!found_lower_saop_clause &&
has_useful_pathkeys(root, rel));
@@ -1000,10 +1004,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
ForwardScanDirection);
useful_pathkeys = truncate_useless_pathkeys(root, rel,
index_pathkeys);
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
}
- else if (index->ammatchorderby && pathkeys_possibly_useful)
+
+ if (useful_pathkeys == NIL &&
+ index->ammatchorderby && pathkeys_possibly_useful)
{
/* see if we can generate ordering operators for query_pathkeys */
match_pathkeys_to_index(index, root->query_pathkeys,
@@ -1015,12 +1019,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
else
useful_pathkeys = NIL;
}
- else
- {
- useful_pathkeys = NIL;
- orderbyclauses = NIL;
- orderbyclausecols = NIL;
- }
/*
* 3. Check if an index-only scan is possible. If we're not building
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index d78df29..4f98e91 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -459,6 +459,12 @@ typedef struct BTScanStateData
/* keep these last in struct for efficiency */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
+
+ /* KNN-search fields: */
+ Datum currDistance; /* current distance */
+ Datum markDistance; /* marked distance */
+ bool currIsNull; /* current item is NULL */
+ bool markIsNull; /* marked item is NULL */
} BTScanStateData;
typedef BTScanStateData *BTScanState;
@@ -479,7 +485,18 @@ typedef struct BTScanOpaqueData
BTArrayKeyInfo *arrayKeys; /* info about each equality-type array key */
MemoryContext arrayContext; /* scan-lifespan context for array data */
- BTScanStateData state;
+ BTScanStateData state; /* main scan state */
+
+ /* kNN-search fields: */
+ BTScanState knnState; /* optional scan state for kNN search */
+ bool useBidirectionalKnnScan; /* use bidirectional kNN scan? */
+ ScanDirection scanDirection; /* selected scan direction for
+ * unidirectional kNN scan */
+ FmgrInfo distanceCmpProc; /* distance comparison procedure */
+ int16 distanceTypeLen; /* distance typlen */
+ bool distanceTypeByVal; /* distance typebyval */
+ bool currRightIsNearest; /* current right item is nearest */
+ bool markRightIsNearest; /* marked right item is nearest */
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
@@ -525,11 +542,12 @@ extern bool btcanreturn(Relation index, int attno);
/*
* prototypes for internal functions in nbtree.c
*/
-extern bool _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno);
-extern void _bt_parallel_release(IndexScanDesc scan, BlockNumber scan_page);
-extern void _bt_parallel_done(IndexScanDesc scan);
+extern bool _bt_parallel_seize(IndexScanDesc scan, BTScanState state, BlockNumber *pageno);
+extern void _bt_parallel_release(IndexScanDesc scan, BTScanState state, BlockNumber scan_page);
+extern void _bt_parallel_done(IndexScanDesc scan, BTScanState state);
extern void _bt_parallel_advance_array_keys(IndexScanDesc scan);
+
/*
* prototypes for functions in nbtinsert.c
*/
@@ -610,6 +628,9 @@ extern bool btproperty(Oid index_oid, int attno,
extern IndexTuple _bt_nonkey_truncate(Relation rel, IndexTuple itup);
extern bool _bt_check_natts(Relation rel, Page page, OffsetNumber offnum);
extern void _bt_allocate_tuple_workspaces(BTScanState state);
+extern void _bt_init_distance_comparison(IndexScanDesc scan);
+extern int _bt_init_knn_start_keys(IndexScanDesc scan, ScanKey *startKeys,
+ ScanKey bufKeys);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/include/access/stratnum.h b/src/include/access/stratnum.h
index 8fdba28..8087ffe 100644
--- a/src/include/access/stratnum.h
+++ b/src/include/access/stratnum.h
@@ -32,7 +32,10 @@ typedef uint16 StrategyNumber;
#define BTGreaterEqualStrategyNumber 4
#define BTGreaterStrategyNumber 5
-#define BTMaxStrategyNumber 5
+#define BTMaxStrategyNumber 5 /* number of canonical B-tree strategies */
+
+#define BtreeKNNSearchStrategyNumber 6 /* for <-> (distance) */
+#define BtreeMaxStrategyNumber 6 /* number of extended B-tree strategies */
/*
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 6faa9d7..c75ef39 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -347,10 +347,10 @@ ROLLBACK;
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
ERROR: access method "invalid_index_method" does not exist
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 6, must be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
-ERROR: invalid operator number 0, must be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 7, must be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
+ERROR: invalid operator number 0, must be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -397,11 +397,12 @@ DROP OPERATOR FAMILY alt_opf8 USING btree;
CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
-ERROR: access method "btree" does not support ordering operators
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
ALTER OPERATOR FAMILY alt_opf11 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 84fd900..73e6e206 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -295,8 +295,8 @@ ROLLBACK;
-- Should fail. Invalid values for ALTER OPERATOR FAMILY .. ADD / DROP
CREATE OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD OPERATOR 1 < (int4, int2); -- invalid indexing_method
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 7 < (int4, int2); -- operator number should be between 1 and 6
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 6
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
@@ -340,10 +340,12 @@ CREATE OPERATOR FAMILY alt_opf9 USING gist;
ALTER OPERATOR FAMILY alt_opf9 USING gist ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf9 USING gist;
--- Should fail. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+-- Should work. Ensure correct ordering methods in ALTER OPERATOR FAMILY ... ADD OPERATOR .. FOR ORDER BY
+BEGIN TRANSACTION;
CREATE OPERATOR FAMILY alt_opf10 USING btree;
ALTER OPERATOR FAMILY alt_opf10 USING btree ADD OPERATOR 1 < (int4, int4) FOR ORDER BY float_ops;
DROP OPERATOR FAMILY alt_opf10 USING btree;
+ROLLBACK;
-- Should work. Textbook case of ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR ORDER BY
CREATE OPERATOR FAMILY alt_opf11 USING gist;
0005-Add-btree-distance-operators-v07.patchtext/x-patch; name=0005-Add-btree-distance-operators-v07.patchDownload
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 1998171..9203497 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -183,6 +183,24 @@ SELECT * FROM events ORDER BY event_date <-> date '2017-05-05' LIMIT 10;
</programlisting>
which finds the ten events closest to a given target date. The ability
to do this is again dependent on the particular operator class being used.
+ Built-in B-tree operator classes support distance ordering for the following
+ data types:
+ <simplelist>
+ <member><type>int2</type></member>
+ <member><type>int4</type></member>
+ <member><type>int8</type></member>
+ <member><type>float4</type></member>
+ <member><type>float8</type></member>
+ <member><type>numeric</type></member>
+ <member><type>timestamp with time zone</type></member>
+ <member><type>timestamp without time zone</type></member>
+ <member><type>time with time zone</type></member>
+ <member><type>time without time zone</type></member>
+ <member><type>date</type></member>
+ <member><type>interval</type></member>
+ <member><type>oid</type></member>
+ <member><type>money</type></member>
+ </simplelist>
</para>
<para>
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index c92e9d5..83073c4 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -30,6 +30,7 @@
#include "utils/numeric.h"
#include "utils/pg_locale.h"
+#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
/*************************************************************************
* Private routines
@@ -1157,3 +1158,22 @@ int8_cash(PG_FUNCTION_ARGS)
PG_RETURN_CASH(result);
}
+
+Datum
+cash_distance(PG_FUNCTION_ARGS)
+{
+ Cash a = PG_GETARG_CASH(0);
+ Cash b = PG_GETARG_CASH(1);
+ Cash r;
+ Cash ra;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("money out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_CASH(ra);
+}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index cf5a1c6..4492f69 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -563,6 +563,17 @@ date_mii(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(result);
}
+Datum
+date_distance(PG_FUNCTION_ARGS)
+{
+ /* we assume the difference can't overflow */
+ Datum diff = DirectFunctionCall2(date_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+}
+
/*
* Internal routines for promoting date to timestamp and timestamp with
* time zone
@@ -760,6 +771,29 @@ date_cmp_timestamp(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
+ Timestamp dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
date_eq_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
@@ -844,6 +878,30 @@ date_cmp_timestamptz(PG_FUNCTION_ARGS)
}
Datum
+date_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ DateADT dateVal = PG_GETARG_DATEADT(0);
+ TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz dt1;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt2))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt1 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
+
+
+Datum
timestamp_eq_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
@@ -928,6 +986,29 @@ timestamp_cmp_date(PG_FUNCTION_ARGS)
}
Datum
+timestamp_dist_date(PG_FUNCTION_ARGS)
+{
+ Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ Timestamp dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamp(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(dt1, dt2));
+}
+
+Datum
timestamptz_eq_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
@@ -1039,6 +1120,28 @@ in_range_date_interval(PG_FUNCTION_ARGS)
BoolGetDatum(less));
}
+Datum
+timestamptz_dist_date(PG_FUNCTION_ARGS)
+{
+ TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
+ DateADT dateVal = PG_GETARG_DATEADT(1);
+ TimestampTz dt2;
+
+ if (DATE_NOT_FINITE(dateVal) || TIMESTAMP_NOT_FINITE(dt1))
+ {
+ Interval *r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ PG_RETURN_INTERVAL_P(r);
+ }
+
+ dt2 = date2timestamptz(dateVal);
+
+ PG_RETURN_INTERVAL_P(timestamptz_dist_internal(dt1, dt2));
+}
/* Add an interval to a date, giving a new date.
* Must handle both positive and negative intervals.
@@ -1957,6 +2060,16 @@ time_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+Datum
+time_distance(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(time_mi_time,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
+
/*****************************************************************************
* Time With Time Zone ADT
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 37c202d..d72fbef 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -3726,6 +3726,47 @@ width_bucket_float8(PG_FUNCTION_ARGS)
PG_RETURN_INT32(result);
}
+Datum
+float4dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float4 r = float4_mi(a, b);
+
+ PG_RETURN_FLOAT4(Abs(r));
+}
+
+Datum
+float8dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+
+Datum
+float48dist(PG_FUNCTION_ARGS)
+{
+ float4 a = PG_GETARG_FLOAT4(0);
+ float8 b = PG_GETARG_FLOAT8(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
+Datum
+float84dist(PG_FUNCTION_ARGS)
+{
+ float8 a = PG_GETARG_FLOAT8(0);
+ float4 b = PG_GETARG_FLOAT4(1);
+ float8 r = float8_mi(a, b);
+
+ PG_RETURN_FLOAT8(Abs(r));
+}
+
/* ========== PRIVATE ROUTINES ========== */
#ifndef HAVE_CBRT
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 04825fc..76e92ec 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -1501,3 +1501,54 @@ generate_series_int4_support(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(ret);
}
+
+Datum
+int2dist(PG_FUNCTION_ARGS)
+{
+ int16 a = PG_GETARG_INT16(0);
+ int16 b = PG_GETARG_INT16(1);
+ int16 r;
+ int16 ra;
+
+ if (pg_sub_s16_overflow(a, b, &r) ||
+ r == PG_INT16_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("smallint out of range")));
+
+ ra = Abs(r);
+
+ PG_RETURN_INT16(ra);
+}
+
+static int32
+int44_dist(int32 a, int32 b)
+{
+ int32 r;
+
+ if (pg_sub_s32_overflow(a, b, &r) ||
+ r == PG_INT32_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ return Abs(r);
+}
+
+Datum
+int4dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int24dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT16(0), PG_GETARG_INT32(1)));
+}
+
+Datum
+int42dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(int44_dist(PG_GETARG_INT32(0), PG_GETARG_INT16(1)));
+}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 0ff9394..2ceb16b 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1446,3 +1446,47 @@ generate_series_int8_support(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(ret);
}
+
+static int64
+int88_dist(int64 a, int64 b)
+{
+ int64 r;
+
+ if (pg_sub_s64_overflow(a, b, &r) ||
+ r == PG_INT64_MIN)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+
+ return Abs(r);
+}
+
+Datum
+int8dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int82dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT16(1)));
+}
+
+Datum
+int84dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist(PG_GETARG_INT64(0), (int64) PG_GETARG_INT32(1)));
+}
+
+Datum
+int28dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT16(0), PG_GETARG_INT64(1)));
+}
+
+Datum
+int48dist(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT64(int88_dist((int64) PG_GETARG_INT32(0), PG_GETARG_INT64(1)));
+}
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
index bb67e01..b8f48d5 100644
--- a/src/backend/utils/adt/oid.c
+++ b/src/backend/utils/adt/oid.c
@@ -469,3 +469,17 @@ oidvectorgt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+
+Datum
+oiddist(PG_FUNCTION_ARGS)
+{
+ Oid a = PG_GETARG_OID(0);
+ Oid b = PG_GETARG_OID(1);
+ Oid res;
+
+ if (a < b)
+ res = b - a;
+ else
+ res = a - b;
+ PG_RETURN_OID(res);
+}
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index e0ef2f7..c5d1042 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2694,6 +2694,86 @@ timestamp_mi(PG_FUNCTION_ARGS)
PG_RETURN_INTERVAL_P(result);
}
+Datum
+timestamp_distance(PG_FUNCTION_ARGS)
+{
+ Timestamp a = PG_GETARG_TIMESTAMP(0);
+ Timestamp b = PG_GETARG_TIMESTAMP(1);
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ Interval *p = palloc(sizeof(Interval));
+
+ p->day = INT_MAX;
+ p->month = INT_MAX;
+ p->time = PG_INT64_MAX;
+ PG_RETURN_INTERVAL_P(p);
+ }
+ else
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+ PG_RETURN_INTERVAL_P(abs_interval(r));
+}
+
+Interval *
+timestamp_dist_internal(Timestamp a, Timestamp b)
+{
+ Interval *r;
+
+ if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
+ {
+ r = palloc(sizeof(Interval));
+
+ r->day = INT_MAX;
+ r->month = INT_MAX;
+ r->time = PG_INT64_MAX;
+
+ return r;
+ }
+
+ r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
+ TimestampGetDatum(a),
+ TimestampGetDatum(b)));
+
+ return abs_interval(r);
+}
+
+Datum
+timestamptz_distance(PG_FUNCTION_ARGS)
+{
+ TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
+ TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(a, b));
+}
+
+Datum
+timestamp_dist_timestamptz(PG_FUNCTION_ARGS)
+{
+ Timestamp ts1 = PG_GETARG_TIMESTAMP(0);
+ TimestampTz tstz2 = PG_GETARG_TIMESTAMPTZ(1);
+ TimestampTz tstz1;
+
+ tstz1 = timestamp2timestamptz(ts1);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+Datum
+timestamptz_dist_timestamp(PG_FUNCTION_ARGS)
+{
+ TimestampTz tstz1 = PG_GETARG_TIMESTAMPTZ(0);
+ Timestamp ts2 = PG_GETARG_TIMESTAMP(1);
+ TimestampTz tstz2;
+
+ tstz2 = timestamp2timestamptz(ts2);
+
+ PG_RETURN_INTERVAL_P(timestamp_dist_internal(tstz1, tstz2));
+}
+
+
/*
* interval_justify_interval()
*
@@ -3563,6 +3643,29 @@ interval_avg(PG_FUNCTION_ARGS)
Float8GetDatum((double) N.time));
}
+Interval *
+abs_interval(Interval *a)
+{
+ static Interval zero = {0, 0, 0};
+
+ if (DatumGetBool(DirectFunctionCall2(interval_lt,
+ IntervalPGetDatum(a),
+ IntervalPGetDatum(&zero))))
+ a = DatumGetIntervalP(DirectFunctionCall1(interval_um,
+ IntervalPGetDatum(a)));
+
+ return a;
+}
+
+Datum
+interval_distance(PG_FUNCTION_ARGS)
+{
+ Datum diff = DirectFunctionCall2(interval_mi,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1));
+
+ PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+}
/* timestamp_age()
* Calculate time difference while retaining year/month fields.
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 0ab95d8..0e06b04 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -30,6 +30,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int2,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int24
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -47,6 +51,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int2,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int28
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
@@ -64,6 +72,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int2,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int2',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int2,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int4
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -81,6 +93,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int4,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int42
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -98,6 +114,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int4,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int48
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
@@ -115,6 +135,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int4,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int4',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int4,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# default operators int8
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -132,6 +156,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int8', amopstrategy => '5', amopopr => '>(int8,int8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int82
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -149,6 +177,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int2', amopstrategy => '5', amopopr => '>(int8,int2)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int2', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int2)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators int84
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
@@ -166,6 +198,10 @@
{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
amoprighttype => 'int4', amopstrategy => '5', amopopr => '>(int8,int4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/integer_ops', amoplefttype => 'int8',
+ amoprighttype => 'int4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(int8,int4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# btree oid_ops
@@ -179,6 +215,10 @@
amopstrategy => '4', amopopr => '>=(oid,oid)', amopmethod => 'btree' },
{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid', amoprighttype => 'oid',
amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'btree' },
+{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid',
+ amoprighttype => 'oid', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(oid,oid)', amopmethod => 'btree',
+ amopsortfamily => 'btree/oid_ops' },
# btree tid_ops
@@ -229,6 +269,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float4,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float48
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
@@ -246,6 +290,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float4,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float4',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float4,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# default operators float8
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -263,6 +311,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float8', amopstrategy => '5', amopopr => '>(float8,float8)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float8', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float8)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# crosstype operators float84
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
@@ -280,6 +332,10 @@
{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
amoprighttype => 'float4', amopstrategy => '5', amopopr => '>(float8,float4)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/float_ops', amoplefttype => 'float8',
+ amoprighttype => 'float4', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(float8,float4)', amopmethod => 'btree',
+ amopsortfamily => 'btree/float_ops' },
# btree char_ops
@@ -416,6 +472,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(date,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/integer_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -433,6 +493,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(date,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
@@ -450,6 +514,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(date,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'date',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(date,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -467,6 +535,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamp,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -484,6 +556,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'date', amopstrategy => '5', amopopr => '>(timestamp,date)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
@@ -501,6 +577,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamp,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamp',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamp,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# default operators timestamptz
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -518,6 +598,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamptz', amopstrategy => '5',
amopopr => '>(timestamptz,timestamptz)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamptz', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamptz)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs date
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -535,6 +619,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'date', amopstrategy => '5',
amopopr => '>(timestamptz,date)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'date', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,date)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# crosstype operators vs timestamp
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
@@ -552,6 +640,10 @@
{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
amoprighttype => 'timestamp', amopstrategy => '5',
amopopr => '>(timestamptz,timestamp)', amopmethod => 'btree' },
+{ amopfamily => 'btree/datetime_ops', amoplefttype => 'timestamptz',
+ amoprighttype => 'timestamp', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(timestamptz,timestamp)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree time_ops
@@ -570,6 +662,10 @@
{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
amoprighttype => 'time', amopstrategy => '5', amopopr => '>(time,time)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/time_ops', amoplefttype => 'time',
+ amoprighttype => 'time', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(time,time)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree timetz_ops
@@ -606,6 +702,10 @@
{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
amoprighttype => 'interval', amopstrategy => '5',
amopopr => '>(interval,interval)', amopmethod => 'btree' },
+{ amopfamily => 'btree/interval_ops', amoplefttype => 'interval',
+ amoprighttype => 'interval', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(interval,interval)', amopmethod => 'btree',
+ amopsortfamily => 'btree/interval_ops' },
# btree macaddr
@@ -781,6 +881,10 @@
{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
amoprighttype => 'money', amopstrategy => '5', amopopr => '>(money,money)',
amopmethod => 'btree' },
+{ amopfamily => 'btree/money_ops', amoplefttype => 'money',
+ amoprighttype => 'money', amopstrategy => '6', amoppurpose => 'o',
+ amopopr => '<->(money,money)', amopmethod => 'btree',
+ amopsortfamily => 'btree/money_ops' },
# btree array_ops
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 06aec07..914ca76 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -2851,6 +2851,114 @@
oprname => '-', oprleft => 'pg_lsn', oprright => 'pg_lsn',
oprresult => 'numeric', oprcode => 'pg_lsn_mi' },
+# distance operators
+{ oid => '4217', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int2', oprresult => 'int2', oprcom => '<->(int2,int2)',
+ oprcode => 'int2dist'},
+{ oid => '4218', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int4)',
+ oprcode => 'int4dist'},
+{ oid => '4219', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int8)',
+ oprcode => 'int8dist'},
+{ oid => '4220', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'oid',
+ oprright => 'oid', oprresult => 'oid', oprcom => '<->(oid,oid)',
+ oprcode => 'oiddist'},
+{ oid => '4221', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float4', oprresult => 'float4', oprcom => '<->(float4,float4)',
+ oprcode => 'float4dist'},
+{ oid => '4222', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float8)',
+ oprcode => 'float8dist'},
+{ oid => '4223', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'money',
+ oprright => 'money', oprresult => 'money', oprcom => '<->(money,money)',
+ oprcode => 'cash_distance'},
+{ oid => '4224', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'date', oprresult => 'int4', oprcom => '<->(date,date)',
+ oprcode => 'date_distance'},
+{ oid => '4225', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'time',
+ oprright => 'time', oprresult => 'interval', oprcom => '<->(time,time)',
+ oprcode => 'time_distance'},
+{ oid => '4226', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamp)',
+ oprcode => 'timestamp_distance'},
+{ oid => '4227', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamptz)',
+ oprcode => 'timestamptz_distance'},
+{ oid => '4228', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'interval',
+ oprright => 'interval', oprresult => 'interval', oprcom => '<->(interval,interval)',
+ oprcode => 'interval_distance'},
+
+# cross-type distance operators
+{ oid => '4229', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int4', oprresult => 'int4', oprcom => '<->(int4,int2)',
+ oprcode => 'int24dist'},
+{ oid => '4230', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int2', oprresult => 'int4', oprcom => '<->(int2,int4)',
+ oprcode => 'int42dist'},
+{ oid => '4231', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int2',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int2)',
+ oprcode => 'int28dist'},
+{ oid => '4232', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int2', oprresult => 'int8', oprcom => '<->(int2,int8)',
+ oprcode => 'int82dist'},
+{ oid => '4233', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int4',
+ oprright => 'int8', oprresult => 'int8', oprcom => '<->(int8,int4)',
+ oprcode => 'int48dist'},
+{ oid => '4234', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'int8',
+ oprright => 'int4', oprresult => 'int8', oprcom => '<->(int4,int8)',
+ oprcode => 'int84dist'},
+{ oid => '4235', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float4',
+ oprright => 'float8', oprresult => 'float8', oprcom => '<->(float8,float4)',
+ oprcode => 'float48dist'},
+{ oid => '4236', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'float8',
+ oprright => 'float4', oprresult => 'float8', oprcom => '<->(float4,float8)',
+ oprcode => 'float84dist'},
+{ oid => '4237', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,date)',
+ oprcode => 'date_dist_timestamp'},
+{ oid => '4238', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamp)',
+ oprcode => 'timestamp_dist_date'},
+{ oid => '4239', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'date',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,date)',
+ oprcode => 'date_dist_timestamptz'},
+{ oid => '4240', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'date', oprresult => 'interval', oprcom => '<->(date,timestamptz)',
+ oprcode => 'timestamptz_dist_date'},
+{ oid => '4241', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamp',
+ oprright => 'timestamptz', oprresult => 'interval', oprcom => '<->(timestamptz,timestamp)',
+ oprcode => 'timestamp_dist_timestamptz'},
+{ oid => '4242', descr => 'distance between',
+ oprname => '<->', oprcanmerge => 'f', oprcanhash => 'f', oprleft => 'timestamptz',
+ oprright => 'timestamp', oprresult => 'interval', oprcom => '<->(timestamp,timestamptz)',
+ oprcode => 'timestamptz_dist_timestamp'},
+
# enum operators
{ oid => '3516', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anyenum',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a4e173b..96d5db0 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10534,4 +10534,90 @@
proname => 'pg_partition_root', prorettype => 'regclass',
proargtypes => 'regclass', prosrc => 'pg_partition_root' },
+# distance functions
+{ oid => '4243',
+ proname => 'int2dist', prorettype => 'int2',
+ proargtypes => 'int2 int2', prosrc => 'int2dist' },
+{ oid => '4244',
+ proname => 'int4dist', prorettype => 'int4',
+ proargtypes => 'int4 int4', prosrc => 'int4dist' },
+{ oid => '4245',
+ proname => 'int8dist', prorettype => 'int8',
+ proargtypes => 'int8 int8', prosrc => 'int8dist' },
+{ oid => '4246',
+ proname => 'oiddist', proleakproof => 't', prorettype => 'oid',
+ proargtypes => 'oid oid', prosrc => 'oiddist' },
+{ oid => '4247',
+ proname => 'float4dist', prorettype => 'float4',
+ proargtypes => 'float4 float4', prosrc => 'float4dist' },
+{ oid => '4248',
+ proname => 'float8dist', prorettype => 'float8',
+ proargtypes => 'float8 float8', prosrc => 'float8dist' },
+{ oid => '4249',
+ proname => 'cash_distance', prorettype => 'money',
+ proargtypes => 'money money', prosrc => 'cash_distance' },
+{ oid => '4250',
+ proname => 'date_distance', prorettype => 'int4',
+ proargtypes => 'date date', prosrc => 'date_distance' },
+{ oid => '4251',
+ proname => 'time_distance', prorettype => 'interval',
+ proargtypes => 'time time', prosrc => 'time_distance' },
+{ oid => '4252',
+ proname => 'timestamp_distance', prorettype => 'interval',
+ proargtypes => 'timestamp timestamp', prosrc => 'timestamp_distance' },
+{ oid => '4253',
+ proname => 'timestamptz_distance', prorettype => 'interval',
+ proargtypes => 'timestamptz timestamptz', prosrc => 'timestamptz_distance' },
+{ oid => '4254',
+ proname => 'interval_distance', prorettype => 'interval',
+ proargtypes => 'interval interval', prosrc => 'interval_distance' },
+
+# cross-type distance functions
+{ oid => '4255',
+ proname => 'int24dist', prorettype => 'int4',
+ proargtypes => 'int2 int4', prosrc => 'int24dist' },
+{ oid => '4256',
+ proname => 'int28dist', prorettype => 'int8',
+ proargtypes => 'int2 int8', prosrc => 'int28dist' },
+{ oid => '4257',
+ proname => 'int42dist', prorettype => 'int4',
+ proargtypes => 'int4 int2', prosrc => 'int42dist' },
+{ oid => '4258',
+ proname => 'int48dist', prorettype => 'int8',
+ proargtypes => 'int4 int8', prosrc => 'int48dist' },
+{ oid => '4259',
+ proname => 'int82dist', prorettype => 'int8',
+ proargtypes => 'int8 int2', prosrc => 'int82dist' },
+{ oid => '4260',
+ proname => 'int84dist', prorettype => 'int8',
+ proargtypes => 'int8 int4', prosrc => 'int84dist' },
+{ oid => '4261',
+ proname => 'float48dist', prorettype => 'float8',
+ proargtypes => 'float4 float8', prosrc => 'float48dist' },
+{ oid => '4262',
+ proname => 'float84dist', prorettype => 'float8',
+ proargtypes => 'float8 float4', prosrc => 'float84dist' },
+{ oid => '4263',
+ proname => 'date_dist_timestamp', prorettype => 'interval',
+ proargtypes => 'date timestamp', prosrc => 'date_dist_timestamp' },
+{ oid => '4264',
+ proname => 'date_dist_timestamptz', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'date timestamptz',
+ prosrc => 'date_dist_timestamptz' },
+{ oid => '4265',
+ proname => 'timestamp_dist_date', prorettype => 'interval',
+ proargtypes => 'timestamp date', prosrc => 'timestamp_dist_date' },
+{ oid => '4266',
+ proname => 'timestamp_dist_timestamptz', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamp timestamptz',
+ prosrc => 'timestamp_dist_timestamptz' },
+{ oid => '4267',
+ proname => 'timestamptz_dist_date', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamptz date',
+ prosrc => 'timestamptz_dist_date' },
+{ oid => '4268',
+ proname => 'timestamptz_dist_timestamp', provolatile => 's',
+ prorettype => 'interval', proargtypes => 'timestamptz timestamp',
+ prosrc => 'timestamptz_dist_timestamp' },
+
]
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 87f819e..01d2284 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -338,4 +338,6 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
+extern Interval *abs_interval(Interval *a);
+
#endif /* DATETIME_H */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index aeb89dc..438a5eb 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -93,9 +93,11 @@ extern Timestamp SetEpochTimestamp(void);
extern void GetEpochTime(struct pg_tm *tm);
extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
+extern Interval *timestamp_dist_internal(Timestamp a, Timestamp b);
-/* timestamp comparison works for timestamptz also */
+/* timestamp comparison and distance works for timestamptz also */
#define timestamptz_cmp_internal(dt1,dt2) timestamp_cmp_internal(dt1, dt2)
+#define timestamptz_dist_internal(dt1,dt2) timestamp_dist_internal(dt1, dt2)
extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday);
diff --git a/src/test/regress/expected/amutils.out b/src/test/regress/expected/amutils.out
index 4570a39..630dc6b 100644
--- a/src/test/regress/expected/amutils.out
+++ b/src/test/regress/expected/amutils.out
@@ -24,7 +24,7 @@ select prop,
nulls_first | | | f
nulls_last | | | t
orderable | | | t
- distance_orderable | | | f
+ distance_orderable | | | t
returnable | | | t
search_array | | | t
search_nulls | | | t
@@ -100,7 +100,7 @@ select prop,
nulls_first | f | f | f | f | f | f | f
nulls_last | t | f | f | f | f | f | f
orderable | t | f | f | f | f | f | f
- distance_orderable | f | f | t | f | t | f | f
+ distance_orderable | t | f | t | f | t | f | f
returnable | t | f | f | t | t | f | f
search_array | t | f | f | f | f | f | f
search_nulls | t | f | t | t | t | f | t
@@ -231,7 +231,7 @@ select col, prop, pg_index_column_has_property(o, col, prop)
1 | desc | f
1 | nulls_first | f
1 | nulls_last | t
- 1 | distance_orderable | f
+ 1 | distance_orderable | t
1 | returnable | t
1 | bogus |
2 | orderable | f
diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out
index 1bcc946..b9b819c 100644
--- a/src/test/regress/expected/date.out
+++ b/src/test/regress/expected/date.out
@@ -1477,3 +1477,64 @@ select make_time(10, 55, 100.1);
ERROR: time field value out of range: 10:55:100.1
select make_time(24, 0, 2.1);
ERROR: time field value out of range: 24:00:2.1
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+----------
+ | 16006
+ | 15941
+ | 1802
+ | 1801
+ | 1800
+ | 1799
+ | 1436
+ | 1435
+ | 1434
+ | 308
+ | 307
+ | 306
+ | 13578
+ | 13944
+ | 14311
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16006 days 1 hour 23 mins 45 secs
+ | @ 15941 days 1 hour 23 mins 45 secs
+ | @ 1802 days 1 hour 23 mins 45 secs
+ | @ 1801 days 1 hour 23 mins 45 secs
+ | @ 1800 days 1 hour 23 mins 45 secs
+ | @ 1799 days 1 hour 23 mins 45 secs
+ | @ 1436 days 1 hour 23 mins 45 secs
+ | @ 1435 days 1 hour 23 mins 45 secs
+ | @ 1434 days 1 hour 23 mins 45 secs
+ | @ 308 days 1 hour 23 mins 45 secs
+ | @ 307 days 1 hour 23 mins 45 secs
+ | @ 306 days 1 hour 23 mins 45 secs
+ | @ 13577 days 22 hours 36 mins 15 secs
+ | @ 13943 days 22 hours 36 mins 15 secs
+ | @ 14310 days 22 hours 36 mins 15 secs
+(15 rows)
+
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
+ Fifteen | Distance
+---------+---------------------------------------
+ | @ 16005 days 14 hours 23 mins 45 secs
+ | @ 15940 days 14 hours 23 mins 45 secs
+ | @ 1801 days 14 hours 23 mins 45 secs
+ | @ 1800 days 14 hours 23 mins 45 secs
+ | @ 1799 days 14 hours 23 mins 45 secs
+ | @ 1798 days 14 hours 23 mins 45 secs
+ | @ 1435 days 14 hours 23 mins 45 secs
+ | @ 1434 days 14 hours 23 mins 45 secs
+ | @ 1433 days 14 hours 23 mins 45 secs
+ | @ 307 days 14 hours 23 mins 45 secs
+ | @ 306 days 14 hours 23 mins 45 secs
+ | @ 305 days 15 hours 23 mins 45 secs
+ | @ 13578 days 8 hours 36 mins 15 secs
+ | @ 13944 days 8 hours 36 mins 15 secs
+ | @ 14311 days 8 hours 36 mins 15 secs
+(15 rows)
+
diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out
index cf78277..ce29924e 100644
--- a/src/test/regress/expected/float4.out
+++ b/src/test/regress/expected/float4.out
@@ -273,6 +273,26 @@ SELECT '' AS five, * FROM FLOAT4_TBL;
| -1.2345679e-20
(5 rows)
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+----------------+---------------
+ | 0 | 1004.3
+ | -34.84 | 1039.14
+ | -1004.3 | 2008.6
+ | -1.2345679e+20 | 1.2345679e+20
+ | -1.2345679e-20 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+ five | f1 | dist
+------+----------------+------------------------
+ | 0 | 1004.3
+ | -34.84 | 1039.1400001525878
+ | -1004.3 | 2008.5999877929687
+ | -1.2345679e+20 | 1.2345678955701443e+20
+ | -1.2345679e-20 | 1004.3
+(5 rows)
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
int2
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index c3a6f53..5485c19 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -413,6 +413,27 @@ SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
| 1.2345678901234e-200 | 2.3112042409018e-67
(5 rows)
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.3
+ | 1004.3 | 0
+ | -34.84 | 1039.14
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.3
+(5 rows)
+
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
+ five | f1 | dist
+------+----------------------+----------------------
+ | 0 | 1004.29998779297
+ | 1004.3 | 1.22070312045253e-05
+ | -34.84 | 1039.13998779297
+ | 1.2345678901234e+200 | 1.2345678901234e+200
+ | 1.2345678901234e-200 | 1004.29998779297
+(5 rows)
+
SELECT '' AS five, * FROM FLOAT8_TBL;
five | f1
------+----------------------
diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out
index 8c255b9..0edc57e 100644
--- a/src/test/regress/expected/int2.out
+++ b/src/test/regress/expected/int2.out
@@ -242,6 +242,39 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
| -32767 | -16383
(5 rows)
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+ERROR: smallint out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+ four | f1 | x
+------+-------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+ five | f1 | x
+------+--------+-------
+ | 0 | 2
+ | 1234 | 1232
+ | -1234 | 1236
+ | 32767 | 32765
+ | -32767 | 32769
+(5 rows)
+
-- corner cases
SELECT (-1::int2<<15)::text;
text
diff --git a/src/test/regress/expected/int4.out b/src/test/regress/expected/int4.out
index bda7a8d..3735dbc 100644
--- a/src/test/regress/expected/int4.out
+++ b/src/test/regress/expected/int4.out
@@ -247,6 +247,38 @@ SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
| -2147483647 | -1073741823
(5 rows)
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+ERROR: integer out of range
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+ four | f1 | x
+------+------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+(4 rows)
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+ five | f1 | x
+------+-------------+------------
+ | 0 | 2
+ | 123456 | 123454
+ | -123456 | 123458
+ | 2147483647 | 2147483645
+ | -2147483647 | 2147483649
+(5 rows)
+
--
-- more complex expressions
--
diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out
index 8447a28..d56886a 100644
--- a/src/test/regress/expected/int8.out
+++ b/src/test/regress/expected/int8.out
@@ -432,6 +432,37 @@ SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 A
4567890123457035 | -4567890123456543 | 1123700970370370094 | 0
(5 rows)
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+ five | q2 | dist
+------+-------------------+------------------
+ | 456 | 333
+ | 4567890123456789 | 4567890123456666
+ | 123 | 0
+ | 4567890123456789 | 4567890123456666
+ | -4567890123456789 | 4567890123456912
+(5 rows)
+
SELECT q2, abs(q2) FROM INT8_TBL;
q2 | abs
-------------------+------------------
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index f88f345..cb95adf 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -207,6 +207,21 @@ SELECT '' AS fortyfive, r1.*, r2.*
| 34 years | 6 years
(45 rows)
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+ ten | ?column?
+-----+----------------------------
+ | 2 days 02:59:00
+ | 2 days -02:00:00
+ | 8 days -03:00:00
+ | 34 years -2 days -03:00:00
+ | 3 mons -2 days -03:00:00
+ | 2 days 03:00:14
+ | 1 day 00:56:56
+ | 6 years -2 days -03:00:00
+ | 5 mons -2 days -03:00:00
+ | 5 mons -2 days +09:00:00
+(10 rows)
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out
index ab86595..fb2a489 100644
--- a/src/test/regress/expected/money.out
+++ b/src/test/regress/expected/money.out
@@ -123,6 +123,12 @@ SELECT m / 2::float4 FROM money_data;
$61.50
(1 row)
+SELECT m <-> '$123.45' FROM money_data;
+ ?column?
+----------
+ $0.45
+(1 row)
+
-- All true
SELECT m = '$123.00' FROM money_data;
?column?
diff --git a/src/test/regress/expected/oid.out b/src/test/regress/expected/oid.out
index 1eab9cc..5339a48 100644
--- a/src/test/regress/expected/oid.out
+++ b/src/test/regress/expected/oid.out
@@ -119,4 +119,17 @@ SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
| 99999999
(3 rows)
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+ eight | f1 | ?column?
+-------+------------+------------
+ | 1234 | 1111
+ | 1235 | 1112
+ | 987 | 864
+ | 4294966256 | 4294966133
+ | 99999999 | 99999876
+ | 5 | 118
+ | 10 | 113
+ | 15 | 108
+(8 rows)
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index ce25ee0..e637420 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -750,6 +750,7 @@ macaddr8_gt(macaddr8,macaddr8)
macaddr8_ge(macaddr8,macaddr8)
macaddr8_ne(macaddr8,macaddr8)
macaddr8_cmp(macaddr8,macaddr8)
+oiddist(oid,oid)
-- restore normal output mode
\a\t
-- List of functions used by libpq's fe-lobj.c
@@ -1332,7 +1333,7 @@ WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND
ao.amoprighttype = ap.amprocrighttype AND
ap.amprocnum = 1 AND
(pp.provolatile != po.provolatile OR
- pp.proleakproof != po.proleakproof)
+ (NOT pp.proleakproof AND po.proleakproof))
ORDER BY 1;
proc | vp | lp | opr | vo | lo
-------------------------------------------+----+----+-------------------------------+----+----
@@ -1862,6 +1863,7 @@ ORDER BY 1, 2, 3;
403 | 5 | *>
403 | 5 | >
403 | 5 | ~>~
+ 403 | 6 | <->
405 | 1 | =
783 | 1 | <<
783 | 1 | @@
@@ -1971,7 +1973,7 @@ ORDER BY 1, 2, 3;
4000 | 26 | >>
4000 | 27 | >>=
4000 | 28 | ^@
-(123 rows)
+(124 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/expected/time.out b/src/test/regress/expected/time.out
index 8e0afe6..ee74faa 100644
--- a/src/test/regress/expected/time.out
+++ b/src/test/regress/expected/time.out
@@ -86,3 +86,19 @@ ERROR: operator is not unique: time without time zone + time without time zone
LINE 1: SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
+ Ten | Distance
+-------------+-------------------------------
+ 00:00:00 | @ 1 hour 23 mins 45 secs
+ 01:00:00 | @ 23 mins 45 secs
+ 02:03:00 | @ 39 mins 15 secs
+ 11:59:00 | @ 10 hours 35 mins 15 secs
+ 12:00:00 | @ 10 hours 36 mins 15 secs
+ 12:01:00 | @ 10 hours 37 mins 15 secs
+ 23:59:00 | @ 22 hours 35 mins 15 secs
+ 23:59:59.99 | @ 22 hours 36 mins 14.99 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+ 15:36:39 | @ 14 hours 12 mins 54 secs
+(10 rows)
+
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index 4a2fabd..dcb4205 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -1604,3 +1604,214 @@ SELECT make_timestamp(2014,12,28,6,30,45.887);
Sun Dec 28 06:30:45.887 2014
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 231 days 18 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 10 hours 45 mins 58 secs
+ | @ 324 days 11 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 21 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 5 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 1 hour 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 231 days 16 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 12 hours 9 mins 43 secs
+ | @ 324 days 13 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 324 days 23 hours 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 6 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(63 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 14 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 1 hour 9 mins 43 secs
+ | @ 324 days 2 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 12 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(63 rows)
+
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 8a4c719..0a05e37 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -2544,3 +2544,217 @@ select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
Tue Jan 17 16:00:00 2017 PST
(1 row)
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 8 hours
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 58 secs
+ | @ 1453 days 6 hours 27 mins 58.6 secs
+ | @ 1453 days 6 hours 27 mins 58.5 secs
+ | @ 1453 days 6 hours 27 mins 58.4 secs
+ | @ 1493 days
+ | @ 1492 days 20 hours 55 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1333 days 7 hours 27 mins 59 secs
+ | @ 231 days 17 hours 19 mins 20 secs
+ | @ 324 days 15 hours 45 mins 59 secs
+ | @ 324 days 19 hours 45 mins 58 secs
+ | @ 324 days 21 hours 45 mins 57 secs
+ | @ 324 days 20 hours 45 mins 56 secs
+ | @ 324 days 22 hours 45 mins 55 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 28 mins
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 14 hours 27 mins 59 secs
+ | @ 1453 days 9 hours 27 mins 59 secs
+ | @ 1303 days 10 hours 27 mins 59 secs
+ | @ 1333 days 6 hours 27 mins 59 secs
+ | @ 1453 days 6 hours 27 mins 59 secs
+ | @ 1452 days 6 hours 27 mins 59 secs
+ | @ 1451 days 6 hours 27 mins 59 secs
+ | @ 1450 days 6 hours 27 mins 59 secs
+ | @ 1449 days 6 hours 27 mins 59 secs
+ | @ 1448 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 765901 days 6 hours 27 mins 59 secs
+ | @ 695407 days 6 hours 27 mins 59 secs
+ | @ 512786 days 6 hours 27 mins 59 secs
+ | @ 330165 days 6 hours 27 mins 59 secs
+ | @ 111019 days 6 hours 27 mins 59 secs
+ | @ 74495 days 6 hours 27 mins 59 secs
+ | @ 37971 days 6 hours 27 mins 59 secs
+ | @ 1447 days 6 hours 27 mins 59 secs
+ | @ 35077 days 17 hours 32 mins 1 sec
+ | @ 1801 days 6 hours 27 mins 59 secs
+ | @ 1800 days 6 hours 27 mins 59 secs
+ | @ 1799 days 6 hours 27 mins 59 secs
+ | @ 1495 days 6 hours 27 mins 59 secs
+ | @ 1494 days 6 hours 27 mins 59 secs
+ | @ 1493 days 6 hours 27 mins 59 secs
+ | @ 1435 days 6 hours 27 mins 59 secs
+ | @ 1434 days 6 hours 27 mins 59 secs
+ | @ 1130 days 6 hours 27 mins 59 secs
+ | @ 1129 days 6 hours 27 mins 59 secs
+ | @ 399 days 6 hours 27 mins 59 secs
+ | @ 398 days 6 hours 27 mins 59 secs
+ | @ 33 days 6 hours 27 mins 59 secs
+ | @ 32 days 6 hours 27 mins 59 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+---------------------------------------
+ | @ 11356 days 9 hours 23 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 43 secs
+ | @ 1453 days 7 hours 51 mins 43.6 secs
+ | @ 1453 days 7 hours 51 mins 43.5 secs
+ | @ 1453 days 7 hours 51 mins 43.4 secs
+ | @ 1493 days 1 hour 23 mins 45 secs
+ | @ 1492 days 22 hours 19 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1333 days 8 hours 51 mins 44 secs
+ | @ 231 days 15 hours 55 mins 35 secs
+ | @ 324 days 17 hours 9 mins 44 secs
+ | @ 324 days 21 hours 9 mins 43 secs
+ | @ 324 days 23 hours 9 mins 42 secs
+ | @ 324 days 22 hours 9 mins 41 secs
+ | @ 325 days 9 mins 40 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 45 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 15 hours 51 mins 44 secs
+ | @ 1453 days 10 hours 51 mins 44 secs
+ | @ 1303 days 11 hours 51 mins 44 secs
+ | @ 1333 days 7 hours 51 mins 44 secs
+ | @ 1453 days 7 hours 51 mins 44 secs
+ | @ 1452 days 7 hours 51 mins 44 secs
+ | @ 1451 days 7 hours 51 mins 44 secs
+ | @ 1450 days 7 hours 51 mins 44 secs
+ | @ 1449 days 7 hours 51 mins 44 secs
+ | @ 1448 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 765901 days 7 hours 51 mins 44 secs
+ | @ 695407 days 7 hours 51 mins 44 secs
+ | @ 512786 days 7 hours 51 mins 44 secs
+ | @ 330165 days 7 hours 51 mins 44 secs
+ | @ 111019 days 7 hours 51 mins 44 secs
+ | @ 74495 days 7 hours 51 mins 44 secs
+ | @ 37971 days 7 hours 51 mins 44 secs
+ | @ 1447 days 7 hours 51 mins 44 secs
+ | @ 35077 days 16 hours 8 mins 16 secs
+ | @ 1801 days 7 hours 51 mins 44 secs
+ | @ 1800 days 7 hours 51 mins 44 secs
+ | @ 1799 days 7 hours 51 mins 44 secs
+ | @ 1495 days 7 hours 51 mins 44 secs
+ | @ 1494 days 7 hours 51 mins 44 secs
+ | @ 1493 days 7 hours 51 mins 44 secs
+ | @ 1435 days 7 hours 51 mins 44 secs
+ | @ 1434 days 7 hours 51 mins 44 secs
+ | @ 1130 days 7 hours 51 mins 44 secs
+ | @ 1129 days 7 hours 51 mins 44 secs
+ | @ 399 days 7 hours 51 mins 44 secs
+ | @ 398 days 7 hours 51 mins 44 secs
+ | @ 33 days 7 hours 51 mins 44 secs
+ | @ 32 days 7 hours 51 mins 44 secs
+(64 rows)
+
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+ERROR: interval out of range
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+ 63 | Distance
+----+----------------------------------------
+ | @ 11355 days 22 hours 23 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 43 secs
+ | @ 1452 days 20 hours 51 mins 43.6 secs
+ | @ 1452 days 20 hours 51 mins 43.5 secs
+ | @ 1452 days 20 hours 51 mins 43.4 secs
+ | @ 1492 days 14 hours 23 mins 45 secs
+ | @ 1492 days 11 hours 19 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1332 days 21 hours 51 mins 44 secs
+ | @ 232 days 2 hours 55 mins 35 secs
+ | @ 324 days 6 hours 9 mins 44 secs
+ | @ 324 days 10 hours 9 mins 43 secs
+ | @ 324 days 12 hours 9 mins 42 secs
+ | @ 324 days 11 hours 9 mins 41 secs
+ | @ 324 days 13 hours 9 mins 40 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 45 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1453 days 4 hours 51 mins 44 secs
+ | @ 1452 days 23 hours 51 mins 44 secs
+ | @ 1303 days 51 mins 44 secs
+ | @ 1332 days 20 hours 51 mins 44 secs
+ | @ 1452 days 20 hours 51 mins 44 secs
+ | @ 1451 days 20 hours 51 mins 44 secs
+ | @ 1450 days 20 hours 51 mins 44 secs
+ | @ 1449 days 20 hours 51 mins 44 secs
+ | @ 1448 days 20 hours 51 mins 44 secs
+ | @ 1447 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 765900 days 20 hours 51 mins 44 secs
+ | @ 695406 days 20 hours 51 mins 44 secs
+ | @ 512785 days 20 hours 51 mins 44 secs
+ | @ 330164 days 20 hours 51 mins 44 secs
+ | @ 111018 days 20 hours 51 mins 44 secs
+ | @ 74494 days 20 hours 51 mins 44 secs
+ | @ 37970 days 20 hours 51 mins 44 secs
+ | @ 1446 days 20 hours 51 mins 44 secs
+ | @ 35078 days 3 hours 8 mins 16 secs
+ | @ 1800 days 20 hours 51 mins 44 secs
+ | @ 1799 days 20 hours 51 mins 44 secs
+ | @ 1798 days 20 hours 51 mins 44 secs
+ | @ 1494 days 20 hours 51 mins 44 secs
+ | @ 1493 days 20 hours 51 mins 44 secs
+ | @ 1492 days 20 hours 51 mins 44 secs
+ | @ 1434 days 20 hours 51 mins 44 secs
+ | @ 1433 days 20 hours 51 mins 44 secs
+ | @ 1129 days 20 hours 51 mins 44 secs
+ | @ 1128 days 20 hours 51 mins 44 secs
+ | @ 398 days 20 hours 51 mins 44 secs
+ | @ 397 days 20 hours 51 mins 44 secs
+ | @ 32 days 20 hours 51 mins 44 secs
+ | @ 31 days 20 hours 51 mins 44 secs
+(64 rows)
+
diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql
index 22f80f2..24be476 100644
--- a/src/test/regress/sql/date.sql
+++ b/src/test/regress/sql/date.sql
@@ -346,3 +346,8 @@ select make_date(2013, 13, 1);
select make_date(2013, 11, -1);
select make_time(10, 55, 100.1);
select make_time(24, 0, 2.1);
+
+-- distance operators
+SELECT '' AS "Fifteen", f1 <-> date '2001-02-03' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM DATE_TBL;
+SELECT '' AS "Fifteen", f1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM DATE_TBL;
diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql
index 646027f..35b5942 100644
--- a/src/test/regress/sql/float4.sql
+++ b/src/test/regress/sql/float4.sql
@@ -87,6 +87,9 @@ UPDATE FLOAT4_TBL
SELECT '' AS five, * FROM FLOAT4_TBL;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3' AS dist FROM FLOAT4_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT4_TBL f;
+
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
SELECT '32767.6'::float4::int2;
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index a333218..7fe9bca 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -131,6 +131,9 @@ SELECT ||/ float8 '27' AS three;
SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
+-- distance
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float8 AS dist FROM FLOAT8_TBL f;
+SELECT '' AS five, f.f1, f.f1 <-> '1004.3'::float4 AS dist FROM FLOAT8_TBL f;
SELECT '' AS five, * FROM FLOAT8_TBL;
diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql
index 7dbafb6..16dd5d8 100644
--- a/src/test/regress/sql/int2.sql
+++ b/src/test/regress/sql/int2.sql
@@ -84,6 +84,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT2_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT2_TBL i;
+-- distance
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT2_TBL i
+WHERE f1 > -32767;
+
+SELECT '' AS five, i.f1, i.f1 <-> int4 '2' AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT2_TBL i;
+
-- corner cases
SELECT (-1::int2<<15)::text;
SELECT ((-1::int2<<15)+1::int2)::text;
diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql
index f014cb2..cff32946 100644
--- a/src/test/regress/sql/int4.sql
+++ b/src/test/regress/sql/int4.sql
@@ -93,6 +93,16 @@ SELECT '' AS five, i.f1, i.f1 / int2 '2' AS x FROM INT4_TBL i;
SELECT '' AS five, i.f1, i.f1 / int4 '2' AS x FROM INT4_TBL i;
+SELECT '' AS five, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i;
+
+SELECT '' AS four, i.f1, i.f1 <-> int2 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS four, i.f1, i.f1 <-> int4 '2' AS x FROM INT4_TBL i
+WHERE f1 > -2147483647;
+
+SELECT '' AS five, i.f1, i.f1 <-> int8 '2' AS x FROM INT4_TBL i;
+
--
-- more complex expressions
--
diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql
index e890452..d7f5bde 100644
--- a/src/test/regress/sql/int8.sql
+++ b/src/test/regress/sql/int8.sql
@@ -89,6 +89,11 @@ SELECT q1 + 42::int2 AS "8plus2", q1 - 42::int2 AS "8minus2", q1 * 42::int2 AS "
-- int2 op int8
SELECT 246::int2 + q1 AS "2plus8", 246::int2 - q1 AS "2minus8", 246::int2 * q1 AS "2mul8", 246::int2 / q1 AS "2div8" FROM INT8_TBL;
+-- distance
+SELECT '' AS five, q2, q2 <-> int2 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int4 '123' AS dist FROM INT8_TBL i;
+SELECT '' AS five, q2, q2 <-> int8 '123' AS dist FROM INT8_TBL i;
+
SELECT q2, abs(q2) FROM INT8_TBL;
SELECT min(q1), min(q2) FROM INT8_TBL;
SELECT max(q1), max(q2) FROM INT8_TBL;
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index bc5537d..d51c866 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -59,6 +59,8 @@ SELECT '' AS fortyfive, r1.*, r2.*
WHERE r1.f1 > r2.f1
ORDER BY r1.f1, r2.f1;
+SELECT '' AS ten, f1 <-> interval '@ 2 day 3 hours' FROM INTERVAL_TBL;
+
-- Test intervals that are large enough to overflow 64 bits in comparisons
CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval);
INSERT INTO INTERVAL_TBL_OF (f1) VALUES
diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql
index 37b9ecc..8428d59 100644
--- a/src/test/regress/sql/money.sql
+++ b/src/test/regress/sql/money.sql
@@ -25,6 +25,7 @@ SELECT m / 2::float8 FROM money_data;
SELECT m * 2::float4 FROM money_data;
SELECT 2::float4 * m FROM money_data;
SELECT m / 2::float4 FROM money_data;
+SELECT m <-> '$123.45' FROM money_data;
-- All true
SELECT m = '$123.00' FROM money_data;
diff --git a/src/test/regress/sql/oid.sql b/src/test/regress/sql/oid.sql
index 4a09689..9f54f92 100644
--- a/src/test/regress/sql/oid.sql
+++ b/src/test/regress/sql/oid.sql
@@ -40,4 +40,6 @@ SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 >= '1234';
SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 > '1234';
+SELECT '' AS eight, f1, f1 <-> oid '123' FROM OID_TBL;
+
DROP TABLE OID_TBL;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index e2014fc..959928b 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -818,7 +818,7 @@ WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND
ao.amoprighttype = ap.amprocrighttype AND
ap.amprocnum = 1 AND
(pp.provolatile != po.provolatile OR
- pp.proleakproof != po.proleakproof)
+ (NOT pp.proleakproof AND po.proleakproof))
ORDER BY 1;
diff --git a/src/test/regress/sql/time.sql b/src/test/regress/sql/time.sql
index 99a1562..31f0330 100644
--- a/src/test/regress/sql/time.sql
+++ b/src/test/regress/sql/time.sql
@@ -40,3 +40,6 @@ SELECT f1 AS "Eight" FROM TIME_TBL WHERE f1 >= '00:00';
-- where we do mixed-type arithmetic. - thomas 2000-12-02
SELECT f1 + time '00:01' AS "Illegal" FROM TIME_TBL;
+
+-- distance
+SELECT f1 AS "Ten", f1 <-> time '01:23:45' AS "Distance" FROM TIME_TBL;
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index b7957cb..5d023dd 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -230,3 +230,11 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
-- timestamp numeric fields constructor
SELECT make_timestamp(2014,12,28,6,30,45.887);
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMP_TBL WHERE isfinite(d1);
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index c3bd46c..7f0525d 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -464,3 +464,11 @@ insert into tmptz values ('2017-01-18 00:00+00');
explain (costs off)
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
+
+-- distance operators
+SELECT '' AS "0", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> date '2001-02-03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamp '2001-02-03 01:23:45' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
+SELECT '' AS "0", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL;
+SELECT '' AS "63", d1 <-> timestamptz '2001-02-03 01:23:45+03' AS "Distance" FROM TIMESTAMPTZ_TBL WHERE isfinite(d1);
0006-Remove-distance-operators-from-btree_gist-v07.patchtext/x-patch; name=0006-Remove-distance-operators-from-btree_gist-v07.patchDownload
diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile
index af65120..46ab241 100644
--- a/contrib/btree_gist/Makefile
+++ b/contrib/btree_gist/Makefile
@@ -11,8 +11,9 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
EXTENSION = btree_gist
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
- btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \
- btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql
+ btree_gist--1.1--1.2.sql btree_gist--1.2--1.3.sql \
+ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \
+ btree_gist--1.5--1.6.sql btree_gist--1.6.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c
index 894d0a2..1b0e317 100644
--- a/contrib/btree_gist/btree_cash.c
+++ b/contrib/btree_gist/btree_cash.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(cash_dist);
Datum
cash_dist(PG_FUNCTION_ARGS)
{
- Cash a = PG_GETARG_CASH(0);
- Cash b = PG_GETARG_CASH(1);
- Cash r;
- Cash ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("money out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_CASH(ra);
+ return cash_distance(fcinfo);
}
/**************************************************
diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c
index 992ce57..f3f0fa1 100644
--- a/contrib/btree_gist/btree_date.c
+++ b/contrib/btree_gist/btree_date.c
@@ -113,12 +113,7 @@ PG_FUNCTION_INFO_V1(date_dist);
Datum
date_dist(PG_FUNCTION_ARGS)
{
- /* we assume the difference can't overflow */
- Datum diff = DirectFunctionCall2(date_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INT32(Abs(DatumGetInt32(diff)));
+ return date_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c
index 6b20f44..0a9148d 100644
--- a/contrib/btree_gist/btree_float4.c
+++ b/contrib/btree_gist/btree_float4.c
@@ -93,14 +93,7 @@ PG_FUNCTION_INFO_V1(float4_dist);
Datum
float4_dist(PG_FUNCTION_ARGS)
{
- float4 a = PG_GETARG_FLOAT4(0);
- float4 b = PG_GETARG_FLOAT4(1);
- float4 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT4(Abs(r));
+ return float4dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c
index ee114cb..8b73b57 100644
--- a/contrib/btree_gist/btree_float8.c
+++ b/contrib/btree_gist/btree_float8.c
@@ -101,14 +101,7 @@ PG_FUNCTION_INFO_V1(float8_dist);
Datum
float8_dist(PG_FUNCTION_ARGS)
{
- float8 a = PG_GETARG_FLOAT8(0);
- float8 b = PG_GETARG_FLOAT8(1);
- float8 r;
-
- r = a - b;
- CHECKFLOATVAL(r, isinf(a) || isinf(b), true);
-
- PG_RETURN_FLOAT8(Abs(r));
+ return float8dist(fcinfo);
}
/**************************************************
diff --git a/contrib/btree_gist/btree_gist--1.2.sql b/contrib/btree_gist/btree_gist--1.2.sql
deleted file mode 100644
index 1efe753..0000000
--- a/contrib/btree_gist/btree_gist--1.2.sql
+++ /dev/null
@@ -1,1570 +0,0 @@
-/* contrib/btree_gist/btree_gist--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
-
-CREATE FUNCTION gbtreekey4_in(cstring)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey4_out(gbtreekey4)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey4 (
- INTERNALLENGTH = 4,
- INPUT = gbtreekey4_in,
- OUTPUT = gbtreekey4_out
-);
-
-CREATE FUNCTION gbtreekey8_in(cstring)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey8_out(gbtreekey8)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey8 (
- INTERNALLENGTH = 8,
- INPUT = gbtreekey8_in,
- OUTPUT = gbtreekey8_out
-);
-
-CREATE FUNCTION gbtreekey16_in(cstring)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey16_out(gbtreekey16)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey16 (
- INTERNALLENGTH = 16,
- INPUT = gbtreekey16_in,
- OUTPUT = gbtreekey16_out
-);
-
-CREATE FUNCTION gbtreekey32_in(cstring)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey32_out(gbtreekey32)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey32 (
- INTERNALLENGTH = 32,
- INPUT = gbtreekey32_in,
- OUTPUT = gbtreekey32_out
-);
-
-CREATE FUNCTION gbtreekey_var_in(cstring)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME', 'gbtreekey_in'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
-RETURNS cstring
-AS 'MODULE_PATHNAME', 'gbtreekey_out'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE gbtreekey_var (
- INTERNALLENGTH = VARIABLE,
- INPUT = gbtreekey_var_in,
- OUTPUT = gbtreekey_var_out,
- STORAGE = EXTENDED
-);
-
---distance operators
-
-CREATE FUNCTION cash_dist(money, money)
-RETURNS money
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = money,
- RIGHTARG = money,
- PROCEDURE = cash_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION date_dist(date, date)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = date,
- RIGHTARG = date,
- PROCEDURE = date_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float4_dist(float4, float4)
-RETURNS float4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float4,
- RIGHTARG = float4,
- PROCEDURE = float4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION float8_dist(float8, float8)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = float8,
- RIGHTARG = float8,
- PROCEDURE = float8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int2_dist(int2, int2)
-RETURNS int2
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int2,
- RIGHTARG = int2,
- PROCEDURE = int2_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int4_dist(int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int4,
- RIGHTARG = int4,
- PROCEDURE = int4_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION int8_dist(int8, int8)
-RETURNS int8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = int8,
- RIGHTARG = int8,
- PROCEDURE = int8_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION interval_dist(interval, interval)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = interval,
- RIGHTARG = interval,
- PROCEDURE = interval_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION oid_dist(oid, oid)
-RETURNS oid
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = oid,
- RIGHTARG = oid,
- PROCEDURE = oid_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION time_dist(time, time)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = time,
- RIGHTARG = time,
- PROCEDURE = time_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION ts_dist(timestamp, timestamp)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamp,
- RIGHTARG = timestamp,
- PROCEDURE = ts_dist,
- COMMUTATOR = '<->'
-);
-
-CREATE FUNCTION tstz_dist(timestamptz, timestamptz)
-RETURNS interval
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE OPERATOR <-> (
- LEFTARG = timestamptz,
- RIGHTARG = timestamptz,
- PROCEDURE = tstz_dist,
- COMMUTATOR = '<->'
-);
-
-
---
---
---
--- oid ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_var_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_oid_ops
-DEFAULT FOR TYPE oid USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
- FUNCTION 2 gbt_oid_union (internal, internal),
- FUNCTION 3 gbt_oid_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
- FUNCTION 6 gbt_oid_picksplit (internal, internal),
- FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
--- Add operators that are new in 9.1. We do it like this, leaving them
--- "loose" in the operator family rather than bound into the opclass, because
--- that's the only state that can be reproduced during an upgrade from 9.0.
-ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
- OPERATOR 6 <> (oid, oid) ,
- OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
- FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
- -- Also add support function for index-only-scans, added in 9.5.
- FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
-
-
---
---
---
--- int2 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_union(internal, internal)
-RETURNS gbtreekey4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int2_ops
-DEFAULT FOR TYPE int2 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
- FUNCTION 2 gbt_int2_union (internal, internal),
- FUNCTION 3 gbt_int2_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int2_picksplit (internal, internal),
- FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
- STORAGE gbtreekey4;
-
-ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
- OPERATOR 6 <> (int2, int2) ,
- OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
- FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
-
---
---
---
--- int4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int4_ops
-DEFAULT FOR TYPE int4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
- FUNCTION 2 gbt_int4_union (internal, internal),
- FUNCTION 3 gbt_int4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int4_picksplit (internal, internal),
- FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
- OPERATOR 6 <> (int4, int4) ,
- OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
- FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
-
-
---
---
---
--- int8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_int8_ops
-DEFAULT FOR TYPE int8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
- FUNCTION 2 gbt_int8_union (internal, internal),
- FUNCTION 3 gbt_int8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_int8_picksplit (internal, internal),
- FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
- OPERATOR 6 <> (int8, int8) ,
- OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
- FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
-
---
---
---
--- float4 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float4_ops
-DEFAULT FOR TYPE float4 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
- FUNCTION 2 gbt_float4_union (internal, internal),
- FUNCTION 3 gbt_float4_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float4_picksplit (internal, internal),
- FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
- OPERATOR 6 <> (float4, float4) ,
- OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
- FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
-
---
---
---
--- float8 ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_float8_ops
-DEFAULT FOR TYPE float8 USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
- FUNCTION 2 gbt_float8_union (internal, internal),
- FUNCTION 3 gbt_float8_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
- FUNCTION 6 gbt_float8_picksplit (internal, internal),
- FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
- OPERATOR 6 <> (float8, float8) ,
- OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
- FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
- FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
-
---
---
---
--- timestamp ops
---
---
---
-
-CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_tstz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamp_ops
-DEFAULT FOR TYPE timestamp USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_ts_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
- OPERATOR 6 <> (timestamp, timestamp) ,
- OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
- FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_timestamptz_ops
-DEFAULT FOR TYPE timestamptz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
- FUNCTION 2 gbt_ts_union (internal, internal),
- FUNCTION 3 gbt_tstz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
- FUNCTION 6 gbt_ts_picksplit (internal, internal),
- FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
- OPERATOR 6 <> (timestamptz, timestamptz) ,
- OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
- FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
-
---
---
---
--- time ops
---
---
---
-
-CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_timetz_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_time_ops
-DEFAULT FOR TYPE time USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_time_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
- OPERATOR 6 <> (time, time) ,
- OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
- FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
-
-
-CREATE OPERATOR CLASS gist_timetz_ops
-DEFAULT FOR TYPE timetz USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
- FUNCTION 2 gbt_time_union (internal, internal),
- FUNCTION 3 gbt_timetz_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_time_penalty (internal, internal, internal),
- FUNCTION 6 gbt_time_picksplit (internal, internal),
- FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
- OPERATOR 6 <> (timetz, timetz) ;
- -- no 'fetch' function, as the compress function is lossy.
-
-
---
---
---
--- date ops
---
---
---
-
-CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_union(internal, internal)
-RETURNS gbtreekey8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_date_ops
-DEFAULT FOR TYPE date USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
- FUNCTION 2 gbt_date_union (internal, internal),
- FUNCTION 3 gbt_date_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_date_penalty (internal, internal, internal),
- FUNCTION 6 gbt_date_picksplit (internal, internal),
- FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
- STORAGE gbtreekey8;
-
-ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
- OPERATOR 6 <> (date, date) ,
- OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
- FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
- FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
-
-
---
---
---
--- interval ops
---
---
---
-
-CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_union(internal, internal)
-RETURNS gbtreekey32
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_interval_ops
-DEFAULT FOR TYPE interval USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
- FUNCTION 2 gbt_intv_union (internal, internal),
- FUNCTION 3 gbt_intv_compress (internal),
- FUNCTION 4 gbt_intv_decompress (internal),
- FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
- FUNCTION 6 gbt_intv_picksplit (internal, internal),
- FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
- STORAGE gbtreekey32;
-
-ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
- OPERATOR 6 <> (interval, interval) ,
- OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
- FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
- FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
-
-
---
---
---
--- cash ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cash_ops
-DEFAULT FOR TYPE money USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
- FUNCTION 2 gbt_cash_union (internal, internal),
- FUNCTION 3 gbt_cash_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
- FUNCTION 6 gbt_cash_picksplit (internal, internal),
- FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
- OPERATOR 6 <> (money, money) ,
- OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
- FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
- FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
-
-
---
---
---
--- macaddr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_fetch(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_macaddr_ops
-DEFAULT FOR TYPE macaddr USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
- FUNCTION 2 gbt_macad_union (internal, internal),
- FUNCTION 3 gbt_macad_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
- FUNCTION 6 gbt_macad_picksplit (internal, internal),
- FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
- OPERATOR 6 <> (macaddr, macaddr) ,
- FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
-
-
---
---
---
--- text/ bpchar ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bpchar_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_text_ops
-DEFAULT FOR TYPE text USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_text_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
- OPERATOR 6 <> (text, text) ,
- FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
-
-
----- Create the operator class
-CREATE OPERATOR CLASS gist_bpchar_ops
-DEFAULT FOR TYPE bpchar USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
- FUNCTION 2 gbt_text_union (internal, internal),
- FUNCTION 3 gbt_bpchar_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_text_penalty (internal, internal, internal),
- FUNCTION 6 gbt_text_picksplit (internal, internal),
- FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
- OPERATOR 6 <> (bpchar, bpchar) ,
- FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
-
---
---
--- bytea ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bytea_ops
-DEFAULT FOR TYPE bytea USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
- FUNCTION 2 gbt_bytea_union (internal, internal),
- FUNCTION 3 gbt_bytea_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bytea_picksplit (internal, internal),
- FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
- OPERATOR 6 <> (bytea, bytea) ,
- FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
-
-
---
---
---
--- numeric ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_numeric_ops
-DEFAULT FOR TYPE numeric USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
- FUNCTION 2 gbt_numeric_union (internal, internal),
- FUNCTION 3 gbt_numeric_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
- FUNCTION 6 gbt_numeric_picksplit (internal, internal),
- FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
- OPERATOR 6 <> (numeric, numeric) ,
- FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
-
-
---
---
--- bit ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_union(internal, internal)
-RETURNS gbtreekey_var
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_bit_ops
-DEFAULT FOR TYPE bit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
- OPERATOR 6 <> (bit, bit) ,
- FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
-
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_vbit_ops
-DEFAULT FOR TYPE varbit USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
- FUNCTION 2 gbt_bit_union (internal, internal),
- FUNCTION 3 gbt_bit_compress (internal),
- FUNCTION 4 gbt_var_decompress (internal),
- FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
- FUNCTION 6 gbt_bit_picksplit (internal, internal),
- FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
- STORAGE gbtreekey_var;
-
-ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
- OPERATOR 6 <> (varbit, varbit) ,
- FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
-
-
---
---
---
--- inet/cidr ops
---
---
---
--- define the GiST support methods
-CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_union(internal, internal)
-RETURNS gbtreekey16
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_inet_ops
-DEFAULT FOR TYPE inet USING gist
-AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
-
--- Create the operator class
-CREATE OPERATOR CLASS gist_cidr_ops
-DEFAULT FOR TYPE cidr USING gist
-AS
- OPERATOR 1 < (inet, inet) ,
- OPERATOR 2 <= (inet, inet) ,
- OPERATOR 3 = (inet, inet) ,
- OPERATOR 4 >= (inet, inet) ,
- OPERATOR 5 > (inet, inet) ,
- FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
- FUNCTION 2 gbt_inet_union (internal, internal),
- FUNCTION 3 gbt_inet_compress (internal),
- FUNCTION 4 gbt_decompress (internal),
- FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
- FUNCTION 6 gbt_inet_picksplit (internal, internal),
- FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
- STORAGE gbtreekey16;
-
-ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
- OPERATOR 6 <> (inet, inet) ;
- -- no fetch support, the compress function is lossy
diff --git a/contrib/btree_gist/btree_gist--1.5--1.6.sql b/contrib/btree_gist/btree_gist--1.5--1.6.sql
new file mode 100644
index 0000000..ef4424e
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.5--1.6.sql
@@ -0,0 +1,99 @@
+/* contrib/btree_gist/btree_gist--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.6'" to load this file. \quit
+
+-- drop btree_gist distance operators from opfamilies
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist DROP OPERATOR 15 (int2, int2);
+ALTER OPERATOR FAMILY gist_int4_ops USING gist DROP OPERATOR 15 (int4, int4);
+ALTER OPERATOR FAMILY gist_int8_ops USING gist DROP OPERATOR 15 (int8, int8);
+ALTER OPERATOR FAMILY gist_float4_ops USING gist DROP OPERATOR 15 (float4, float4);
+ALTER OPERATOR FAMILY gist_float8_ops USING gist DROP OPERATOR 15 (float8, float8);
+ALTER OPERATOR FAMILY gist_oid_ops USING gist DROP OPERATOR 15 (oid, oid);
+ALTER OPERATOR FAMILY gist_cash_ops USING gist DROP OPERATOR 15 (money, money);
+ALTER OPERATOR FAMILY gist_date_ops USING gist DROP OPERATOR 15 (date, date);
+ALTER OPERATOR FAMILY gist_time_ops USING gist DROP OPERATOR 15 (time, time);
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist DROP OPERATOR 15 (timestamp, timestamp);
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist DROP OPERATOR 15 (timestamptz, timestamptz);
+ALTER OPERATOR FAMILY gist_interval_ops USING gist DROP OPERATOR 15 (interval, interval);
+
+-- add pg_catalog distance operators to opfamilies
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops;
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops;
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops;
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops;
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops;
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops;
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops;
+
+-- disable implicit pg_catalog search
+
+DO
+$$
+BEGIN
+ EXECUTE 'SET LOCAL search_path TO ' || current_schema() || ', pg_catalog';
+END
+$$;
+
+-- drop distance operators
+
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int2, int2);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int4, int4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (int8, int8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float4, float4);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (float8, float8);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (oid, oid);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (money, money);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (date, date);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (time, time);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP OPERATOR <-> (interval, interval);
+
+DROP OPERATOR <-> (int2, int2);
+DROP OPERATOR <-> (int4, int4);
+DROP OPERATOR <-> (int8, int8);
+DROP OPERATOR <-> (float4, float4);
+DROP OPERATOR <-> (float8, float8);
+DROP OPERATOR <-> (oid, oid);
+DROP OPERATOR <-> (money, money);
+DROP OPERATOR <-> (date, date);
+DROP OPERATOR <-> (time, time);
+DROP OPERATOR <-> (timestamp, timestamp);
+DROP OPERATOR <-> (timestamptz, timestamptz);
+DROP OPERATOR <-> (interval, interval);
+
+-- drop distance functions
+
+ALTER EXTENSION btree_gist DROP FUNCTION int2_dist(int2, int2);
+ALTER EXTENSION btree_gist DROP FUNCTION int4_dist(int4, int4);
+ALTER EXTENSION btree_gist DROP FUNCTION int8_dist(int8, int8);
+ALTER EXTENSION btree_gist DROP FUNCTION float4_dist(float4, float4);
+ALTER EXTENSION btree_gist DROP FUNCTION float8_dist(float8, float8);
+ALTER EXTENSION btree_gist DROP FUNCTION oid_dist(oid, oid);
+ALTER EXTENSION btree_gist DROP FUNCTION cash_dist(money, money);
+ALTER EXTENSION btree_gist DROP FUNCTION date_dist(date, date);
+ALTER EXTENSION btree_gist DROP FUNCTION time_dist(time, time);
+ALTER EXTENSION btree_gist DROP FUNCTION ts_dist(timestamp, timestamp);
+ALTER EXTENSION btree_gist DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+ALTER EXTENSION btree_gist DROP FUNCTION interval_dist(interval, interval);
+
+DROP FUNCTION int2_dist(int2, int2);
+DROP FUNCTION int4_dist(int4, int4);
+DROP FUNCTION int8_dist(int8, int8);
+DROP FUNCTION float4_dist(float4, float4);
+DROP FUNCTION float8_dist(float8, float8);
+DROP FUNCTION oid_dist(oid, oid);
+DROP FUNCTION cash_dist(money, money);
+DROP FUNCTION date_dist(date, date);
+DROP FUNCTION time_dist(time, time);
+DROP FUNCTION ts_dist(timestamp, timestamp);
+DROP FUNCTION tstz_dist(timestamptz, timestamptz);
+DROP FUNCTION interval_dist(interval, interval);
diff --git a/contrib/btree_gist/btree_gist--1.6.sql b/contrib/btree_gist/btree_gist--1.6.sql
new file mode 100644
index 0000000..8ff8eb5
--- /dev/null
+++ b/contrib/btree_gist/btree_gist--1.6.sql
@@ -0,0 +1,1615 @@
+/* contrib/btree_gist/btree_gist--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION btree_gist" to load this file. \quit
+
+CREATE FUNCTION gbtreekey4_in(cstring)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey4_out(gbtreekey4)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey4 (
+ INTERNALLENGTH = 4,
+ INPUT = gbtreekey4_in,
+ OUTPUT = gbtreekey4_out
+);
+
+CREATE FUNCTION gbtreekey8_in(cstring)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey8_out(gbtreekey8)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey8 (
+ INTERNALLENGTH = 8,
+ INPUT = gbtreekey8_in,
+ OUTPUT = gbtreekey8_out
+);
+
+CREATE FUNCTION gbtreekey16_in(cstring)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey16_out(gbtreekey16)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey16 (
+ INTERNALLENGTH = 16,
+ INPUT = gbtreekey16_in,
+ OUTPUT = gbtreekey16_out
+);
+
+CREATE FUNCTION gbtreekey32_in(cstring)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey32_out(gbtreekey32)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey32 (
+ INTERNALLENGTH = 32,
+ INPUT = gbtreekey32_in,
+ OUTPUT = gbtreekey32_out
+);
+
+CREATE FUNCTION gbtreekey_var_in(cstring)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME', 'gbtreekey_in'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbtreekey_var_out(gbtreekey_var)
+RETURNS cstring
+AS 'MODULE_PATHNAME', 'gbtreekey_out'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE gbtreekey_var (
+ INTERNALLENGTH = VARIABLE,
+ INPUT = gbtreekey_var_in,
+ OUTPUT = gbtreekey_var_out,
+ STORAGE = EXTENDED
+);
+
+
+--
+--
+--
+-- oid ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_distance(internal,oid,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_var_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_oid_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_oid_ops
+DEFAULT FOR TYPE oid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_oid_consistent (internal, oid, int2, oid, internal),
+ FUNCTION 2 gbt_oid_union (internal, internal),
+ FUNCTION 3 gbt_oid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_oid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_oid_picksplit (internal, internal),
+ FUNCTION 7 gbt_oid_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+-- Add operators that are new in 9.1. We do it like this, leaving them
+-- "loose" in the operator family rather than bound into the opclass, because
+-- that's the only state that can be reproduced during an upgrade from 9.0.
+ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
+ OPERATOR 6 <> (oid, oid) ,
+ OPERATOR 15 <-> (oid, oid) FOR ORDER BY pg_catalog.oid_ops ,
+ FUNCTION 8 (oid, oid) gbt_oid_distance (internal, oid, int2, oid, internal) ,
+ -- Also add support function for index-only-scans, added in 9.5.
+ FUNCTION 9 (oid, oid) gbt_oid_fetch (internal) ;
+
+
+--
+--
+--
+-- int2 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_distance(internal,int2,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_union(internal, internal)
+RETURNS gbtreekey4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int2_same(gbtreekey4, gbtreekey4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int2_ops
+DEFAULT FOR TYPE int2 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int2_consistent (internal, int2, int2, oid, internal),
+ FUNCTION 2 gbt_int2_union (internal, internal),
+ FUNCTION 3 gbt_int2_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int2_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int2_picksplit (internal, internal),
+ FUNCTION 7 gbt_int2_same (gbtreekey4, gbtreekey4, internal),
+ STORAGE gbtreekey4;
+
+ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
+ OPERATOR 6 <> (int2, int2) ,
+ OPERATOR 15 <-> (int2, int2) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int2, int2) gbt_int2_distance (internal, int2, int2, oid, internal) ,
+ FUNCTION 9 (int2, int2) gbt_int2_fetch (internal) ;
+
+--
+--
+--
+-- int4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_distance(internal,int4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int4_ops
+DEFAULT FOR TYPE int4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int4_consistent (internal, int4, int2, oid, internal),
+ FUNCTION 2 gbt_int4_union (internal, internal),
+ FUNCTION 3 gbt_int4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int4_picksplit (internal, internal),
+ FUNCTION 7 gbt_int4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
+ OPERATOR 6 <> (int4, int4) ,
+ OPERATOR 15 <-> (int4, int4) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int4, int4) gbt_int4_distance (internal, int4, int2, oid, internal) ,
+ FUNCTION 9 (int4, int4) gbt_int4_fetch (internal) ;
+
+
+--
+--
+--
+-- int8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_distance(internal,int8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_int8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_int8_ops
+DEFAULT FOR TYPE int8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_int8_consistent (internal, int8, int2, oid, internal),
+ FUNCTION 2 gbt_int8_union (internal, internal),
+ FUNCTION 3 gbt_int8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_int8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_int8_picksplit (internal, internal),
+ FUNCTION 7 gbt_int8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
+ OPERATOR 6 <> (int8, int8) ,
+ OPERATOR 15 <-> (int8, int8) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (int8, int8) gbt_int8_distance (internal, int8, int2, oid, internal) ,
+ FUNCTION 9 (int8, int8) gbt_int8_fetch (internal) ;
+
+--
+--
+--
+-- float4 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_distance(internal,float4,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float4_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float4_ops
+DEFAULT FOR TYPE float4 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float4_consistent (internal, float4, int2, oid, internal),
+ FUNCTION 2 gbt_float4_union (internal, internal),
+ FUNCTION 3 gbt_float4_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float4_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float4_picksplit (internal, internal),
+ FUNCTION 7 gbt_float4_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
+ OPERATOR 6 <> (float4, float4) ,
+ OPERATOR 15 <-> (float4, float4) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float4, float4) gbt_float4_distance (internal, float4, int2, oid, internal) ,
+ FUNCTION 9 (float4, float4) gbt_float4_fetch (internal) ;
+
+--
+--
+--
+-- float8 ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_distance(internal,float8,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_float8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_float8_ops
+DEFAULT FOR TYPE float8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_float8_consistent (internal, float8, int2, oid, internal),
+ FUNCTION 2 gbt_float8_union (internal, internal),
+ FUNCTION 3 gbt_float8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_float8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_float8_picksplit (internal, internal),
+ FUNCTION 7 gbt_float8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
+ OPERATOR 6 <> (float8, float8) ,
+ OPERATOR 15 <-> (float8, float8) FOR ORDER BY pg_catalog.float_ops ,
+ FUNCTION 8 (float8, float8) gbt_float8_distance (internal, float8, int2, oid, internal) ,
+ FUNCTION 9 (float8, float8) gbt_float8_fetch (internal) ;
+
+--
+--
+--
+-- timestamp ops
+--
+--
+--
+
+CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_distance(internal,timestamp,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_distance(internal,timestamptz,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_tstz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_ts_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamp_ops
+DEFAULT FOR TYPE timestamp USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_ts_consistent (internal, timestamp, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_ts_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
+ OPERATOR 6 <> (timestamp, timestamp) ,
+ OPERATOR 15 <-> (timestamp, timestamp) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamp, timestamp) gbt_ts_distance (internal, timestamp, int2, oid, internal) ,
+ FUNCTION 9 (timestamp, timestamp) gbt_ts_fetch (internal) ;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_timestamptz_ops
+DEFAULT FOR TYPE timestamptz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_tstz_consistent (internal, timestamptz, int2, oid, internal),
+ FUNCTION 2 gbt_ts_union (internal, internal),
+ FUNCTION 3 gbt_tstz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_ts_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_ts_picksplit (internal, internal),
+ FUNCTION 7 gbt_ts_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
+ OPERATOR 6 <> (timestamptz, timestamptz) ,
+ OPERATOR 15 <-> (timestamptz, timestamptz) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (timestamptz, timestamptz) gbt_tstz_distance (internal, timestamptz, int2, oid, internal) ,
+ FUNCTION 9 (timestamptz, timestamptz) gbt_ts_fetch (internal) ;
+
+--
+--
+--
+-- time ops
+--
+--
+--
+
+CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_distance(internal,time,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_timetz_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_time_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_time_ops
+DEFAULT FOR TYPE time USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_time_consistent (internal, time, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_time_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
+ OPERATOR 6 <> (time, time) ,
+ OPERATOR 15 <-> (time, time) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (time, time) gbt_time_distance (internal, time, int2, oid, internal) ,
+ FUNCTION 9 (time, time) gbt_time_fetch (internal) ;
+
+
+CREATE OPERATOR CLASS gist_timetz_ops
+DEFAULT FOR TYPE timetz USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_timetz_consistent (internal, timetz, int2, oid, internal),
+ FUNCTION 2 gbt_time_union (internal, internal),
+ FUNCTION 3 gbt_timetz_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_time_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_time_picksplit (internal, internal),
+ FUNCTION 7 gbt_time_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
+ OPERATOR 6 <> (timetz, timetz) ;
+ -- no 'fetch' function, as the compress function is lossy.
+
+
+--
+--
+--
+-- date ops
+--
+--
+--
+
+CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_distance(internal,date,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_date_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_date_ops
+DEFAULT FOR TYPE date USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_date_consistent (internal, date, int2, oid, internal),
+ FUNCTION 2 gbt_date_union (internal, internal),
+ FUNCTION 3 gbt_date_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_date_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_date_picksplit (internal, internal),
+ FUNCTION 7 gbt_date_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
+ OPERATOR 6 <> (date, date) ,
+ OPERATOR 15 <-> (date, date) FOR ORDER BY pg_catalog.integer_ops ,
+ FUNCTION 8 (date, date) gbt_date_distance (internal, date, int2, oid, internal) ,
+ FUNCTION 9 (date, date) gbt_date_fetch (internal) ;
+
+
+--
+--
+--
+-- interval ops
+--
+--
+--
+
+CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_distance(internal,interval,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_intv_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_interval_ops
+DEFAULT FOR TYPE interval USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_intv_consistent (internal, interval, int2, oid, internal),
+ FUNCTION 2 gbt_intv_union (internal, internal),
+ FUNCTION 3 gbt_intv_compress (internal),
+ FUNCTION 4 gbt_intv_decompress (internal),
+ FUNCTION 5 gbt_intv_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_intv_picksplit (internal, internal),
+ FUNCTION 7 gbt_intv_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
+ OPERATOR 6 <> (interval, interval) ,
+ OPERATOR 15 <-> (interval, interval) FOR ORDER BY pg_catalog.interval_ops ,
+ FUNCTION 8 (interval, interval) gbt_intv_distance (internal, interval, int2, oid, internal) ,
+ FUNCTION 9 (interval, interval) gbt_intv_fetch (internal) ;
+
+
+--
+--
+--
+-- cash ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_distance(internal,money,int2,oid,internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_cash_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cash_ops
+DEFAULT FOR TYPE money USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_cash_consistent (internal, money, int2, oid, internal),
+ FUNCTION 2 gbt_cash_union (internal, internal),
+ FUNCTION 3 gbt_cash_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_cash_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_cash_picksplit (internal, internal),
+ FUNCTION 7 gbt_cash_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
+ OPERATOR 6 <> (money, money) ,
+ OPERATOR 15 <-> (money, money) FOR ORDER BY pg_catalog.money_ops ,
+ FUNCTION 8 (money, money) gbt_cash_distance (internal, money, int2, oid, internal) ,
+ FUNCTION 9 (money, money) gbt_cash_fetch (internal) ;
+
+
+--
+--
+--
+-- macaddr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr_ops
+DEFAULT FOR TYPE macaddr USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad_consistent (internal, macaddr, int2, oid, internal),
+ FUNCTION 2 gbt_macad_union (internal, internal),
+ FUNCTION 3 gbt_macad_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
+ OPERATOR 6 <> (macaddr, macaddr) ,
+ FUNCTION 9 (macaddr, macaddr) gbt_macad_fetch (internal);
+
+
+--
+--
+--
+-- text/ bpchar ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bpchar_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_text_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_text_ops
+DEFAULT FOR TYPE text USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_text_consistent (internal, text, int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_text_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
+ OPERATOR 6 <> (text, text) ,
+ FUNCTION 9 (text, text) gbt_var_fetch (internal) ;
+
+
+---- Create the operator class
+CREATE OPERATOR CLASS gist_bpchar_ops
+DEFAULT FOR TYPE bpchar USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bpchar_consistent (internal, bpchar , int2, oid, internal),
+ FUNCTION 2 gbt_text_union (internal, internal),
+ FUNCTION 3 gbt_bpchar_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_text_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_text_picksplit (internal, internal),
+ FUNCTION 7 gbt_text_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
+ OPERATOR 6 <> (bpchar, bpchar) ,
+ FUNCTION 9 (bpchar, bpchar) gbt_var_fetch (internal) ;
+
+--
+--
+-- bytea ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bytea_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bytea_ops
+DEFAULT FOR TYPE bytea USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bytea_consistent (internal, bytea, int2, oid, internal),
+ FUNCTION 2 gbt_bytea_union (internal, internal),
+ FUNCTION 3 gbt_bytea_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bytea_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bytea_picksplit (internal, internal),
+ FUNCTION 7 gbt_bytea_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
+ OPERATOR 6 <> (bytea, bytea) ,
+ FUNCTION 9 (bytea, bytea) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- numeric ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_numeric_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_numeric_ops
+DEFAULT FOR TYPE numeric USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_numeric_consistent (internal, numeric, int2, oid, internal),
+ FUNCTION 2 gbt_numeric_union (internal, internal),
+ FUNCTION 3 gbt_numeric_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_numeric_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_numeric_picksplit (internal, internal),
+ FUNCTION 7 gbt_numeric_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
+ OPERATOR 6 <> (numeric, numeric) ,
+ FUNCTION 9 (numeric, numeric) gbt_var_fetch (internal) ;
+
+
+--
+--
+-- bit ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_union(internal, internal)
+RETURNS gbtreekey_var
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_bit_same(gbtreekey_var, gbtreekey_var, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_bit_ops
+DEFAULT FOR TYPE bit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
+ OPERATOR 6 <> (bit, bit) ,
+ FUNCTION 9 (bit, bit) gbt_var_fetch (internal) ;
+
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_vbit_ops
+DEFAULT FOR TYPE varbit USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_bit_consistent (internal, bit, int2, oid, internal),
+ FUNCTION 2 gbt_bit_union (internal, internal),
+ FUNCTION 3 gbt_bit_compress (internal),
+ FUNCTION 4 gbt_var_decompress (internal),
+ FUNCTION 5 gbt_bit_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_bit_picksplit (internal, internal),
+ FUNCTION 7 gbt_bit_same (gbtreekey_var, gbtreekey_var, internal),
+ STORAGE gbtreekey_var;
+
+ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
+ OPERATOR 6 <> (varbit, varbit) ,
+ FUNCTION 9 (varbit, varbit) gbt_var_fetch (internal) ;
+
+
+--
+--
+--
+-- inet/cidr ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_inet_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_inet_ops
+DEFAULT FOR TYPE inet USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_cidr_ops
+DEFAULT FOR TYPE cidr USING gist
+AS
+ OPERATOR 1 < (inet, inet) ,
+ OPERATOR 2 <= (inet, inet) ,
+ OPERATOR 3 = (inet, inet) ,
+ OPERATOR 4 >= (inet, inet) ,
+ OPERATOR 5 > (inet, inet) ,
+ FUNCTION 1 gbt_inet_consistent (internal, inet, int2, oid, internal),
+ FUNCTION 2 gbt_inet_union (internal, internal),
+ FUNCTION 3 gbt_inet_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_inet_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_inet_picksplit (internal, internal),
+ FUNCTION 7 gbt_inet_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
+ OPERATOR 6 <> (inet, inet) ;
+ -- no fetch support, the compress function is lossy
+
+--
+--
+--
+-- uuid ops
+--
+--
+---- define the GiST support methods
+CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_union(internal, internal)
+RETURNS gbtreekey32
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_uuid_ops
+DEFAULT FOR TYPE uuid USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal),
+ FUNCTION 2 gbt_uuid_union (internal, internal),
+ FUNCTION 3 gbt_uuid_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_uuid_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_uuid_picksplit (internal, internal),
+ FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal),
+ STORAGE gbtreekey32;
+
+-- These are "loose" in the opfamily for consistency with the rest of btree_gist
+ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
+ OPERATOR 6 <> (uuid, uuid) ,
+ FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ;
+
+
+-- Add support for indexing macaddr8 columns
+
+-- define the GiST support methods
+CREATE FUNCTION gbt_macad8_consistent(internal,macaddr8,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_union(internal, internal)
+RETURNS gbtreekey16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_macad8_same(gbtreekey16, gbtreekey16, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_macaddr8_ops
+DEFAULT FOR TYPE macaddr8 USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_macad8_consistent (internal, macaddr8, int2, oid, internal),
+ FUNCTION 2 gbt_macad8_union (internal, internal),
+ FUNCTION 3 gbt_macad8_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_macad8_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_macad8_picksplit (internal, internal),
+ FUNCTION 7 gbt_macad8_same (gbtreekey16, gbtreekey16, internal),
+ STORAGE gbtreekey16;
+
+ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD
+ OPERATOR 6 <> (macaddr8, macaddr8) ,
+ FUNCTION 9 (macaddr8, macaddr8) gbt_macad8_fetch (internal);
+
+--
+--
+--
+-- enum ops
+--
+--
+--
+-- define the GiST support methods
+CREATE FUNCTION gbt_enum_consistent(internal,anyenum,int2,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_union(internal, internal)
+RETURNS gbtreekey8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION gbt_enum_same(gbtreekey8, gbtreekey8, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator class
+CREATE OPERATOR CLASS gist_enum_ops
+DEFAULT FOR TYPE anyenum USING gist
+AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 gbt_enum_consistent (internal, anyenum, int2, oid, internal),
+ FUNCTION 2 gbt_enum_union (internal, internal),
+ FUNCTION 3 gbt_enum_compress (internal),
+ FUNCTION 4 gbt_decompress (internal),
+ FUNCTION 5 gbt_enum_penalty (internal, internal, internal),
+ FUNCTION 6 gbt_enum_picksplit (internal, internal),
+ FUNCTION 7 gbt_enum_same (gbtreekey8, gbtreekey8, internal),
+ STORAGE gbtreekey8;
+
+ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD
+ OPERATOR 6 <> (anyenum, anyenum) ,
+ FUNCTION 9 (anyenum, anyenum) gbt_enum_fetch (internal) ;
diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control
index 81c8509..9ced3bc 100644
--- a/contrib/btree_gist/btree_gist.control
+++ b/contrib/btree_gist/btree_gist.control
@@ -1,5 +1,5 @@
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
-default_version = '1.5'
+default_version = '1.6'
module_pathname = '$libdir/btree_gist'
relocatable = true
diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c
index 7674e2d..2afc343 100644
--- a/contrib/btree_gist/btree_int2.c
+++ b/contrib/btree_gist/btree_int2.c
@@ -94,20 +94,7 @@ PG_FUNCTION_INFO_V1(int2_dist);
Datum
int2_dist(PG_FUNCTION_ARGS)
{
- int16 a = PG_GETARG_INT16(0);
- int16 b = PG_GETARG_INT16(1);
- int16 r;
- int16 ra;
-
- if (pg_sub_s16_overflow(a, b, &r) ||
- r == PG_INT16_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("smallint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT16(ra);
+ return int2dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c
index 80005ab..2361ce7 100644
--- a/contrib/btree_gist/btree_int4.c
+++ b/contrib/btree_gist/btree_int4.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(int4_dist);
Datum
int4_dist(PG_FUNCTION_ARGS)
{
- int32 a = PG_GETARG_INT32(0);
- int32 b = PG_GETARG_INT32(1);
- int32 r;
- int32 ra;
-
- if (pg_sub_s32_overflow(a, b, &r) ||
- r == PG_INT32_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT32(ra);
+ return int4dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c
index b0fd3e1..182d7c4 100644
--- a/contrib/btree_gist/btree_int8.c
+++ b/contrib/btree_gist/btree_int8.c
@@ -95,20 +95,7 @@ PG_FUNCTION_INFO_V1(int8_dist);
Datum
int8_dist(PG_FUNCTION_ARGS)
{
- int64 a = PG_GETARG_INT64(0);
- int64 b = PG_GETARG_INT64(1);
- int64 r;
- int64 ra;
-
- if (pg_sub_s64_overflow(a, b, &r) ||
- r == PG_INT64_MIN)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
-
- ra = Abs(r);
-
- PG_RETURN_INT64(ra);
+ return int8dist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c
index 3a527a7..c68d48e 100644
--- a/contrib/btree_gist/btree_interval.c
+++ b/contrib/btree_gist/btree_interval.c
@@ -128,11 +128,7 @@ PG_FUNCTION_INFO_V1(interval_dist);
Datum
interval_dist(PG_FUNCTION_ARGS)
{
- Datum diff = DirectFunctionCall2(interval_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+ return interval_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c
index 00e7019..c702d51 100644
--- a/contrib/btree_gist/btree_oid.c
+++ b/contrib/btree_gist/btree_oid.c
@@ -100,15 +100,7 @@ PG_FUNCTION_INFO_V1(oid_dist);
Datum
oid_dist(PG_FUNCTION_ARGS)
{
- Oid a = PG_GETARG_OID(0);
- Oid b = PG_GETARG_OID(1);
- Oid res;
-
- if (a < b)
- res = b - a;
- else
- res = a - b;
- PG_RETURN_OID(res);
+ return oiddist(fcinfo);
}
diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c
index 90cf655..cfafd02 100644
--- a/contrib/btree_gist/btree_time.c
+++ b/contrib/btree_gist/btree_time.c
@@ -141,11 +141,7 @@ PG_FUNCTION_INFO_V1(time_dist);
Datum
time_dist(PG_FUNCTION_ARGS)
{
- Datum diff = DirectFunctionCall2(time_mi_time,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1));
-
- PG_RETURN_INTERVAL_P(abs_interval(DatumGetIntervalP(diff)));
+ return time_distance(fcinfo);
}
diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c
index 49d1849..9e62f7d 100644
--- a/contrib/btree_gist/btree_ts.c
+++ b/contrib/btree_gist/btree_ts.c
@@ -146,48 +146,14 @@ PG_FUNCTION_INFO_V1(ts_dist);
Datum
ts_dist(PG_FUNCTION_ARGS)
{
- Timestamp a = PG_GETARG_TIMESTAMP(0);
- Timestamp b = PG_GETARG_TIMESTAMP(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
- else
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
+ return timestamp_distance(fcinfo);
}
PG_FUNCTION_INFO_V1(tstz_dist);
Datum
tstz_dist(PG_FUNCTION_ARGS)
{
- TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
- TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
- Interval *r;
-
- if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
- {
- Interval *p = palloc(sizeof(Interval));
-
- p->day = INT_MAX;
- p->month = INT_MAX;
- p->time = PG_INT64_MAX;
- PG_RETURN_INTERVAL_P(p);
- }
-
- r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
- PG_GETARG_DATUM(0),
- PG_GETARG_DATUM(1)));
- PG_RETURN_INTERVAL_P(abs_interval(r));
+ return timestamptz_distance(fcinfo);
}
diff --git a/doc/src/sgml/btree-gist.sgml b/doc/src/sgml/btree-gist.sgml
index 774442f..6eb4a18 100644
--- a/doc/src/sgml/btree-gist.sgml
+++ b/doc/src/sgml/btree-gist.sgml
@@ -96,6 +96,19 @@ INSERT 0 1
</sect2>
<sect2>
+ <title>Upgrade notes for version 1.6</title>
+
+ <para>
+ In version 1.6 <literal>btree_gist</literal> switched to using in-core
+ distance operators, and its own implementations were removed. References to
+ these operators in <literal>btree_gist</literal> opclasses will be updated
+ automatically during the extension upgrade, but if the user has created
+ objects referencing these operators or functions, then these objects must be
+ dropped manually before updating the extension.
+ </para>
+ </sect2>
+
+ <sect2>
<title>Authors</title>
<para>
0007-Add-regression-tests-for-kNN-btree-v07.patchtext/x-patch; name=0007-Add-regression-tests-for-kNN-btree-v07.patchDownload
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index b21298a..0cefbcc 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -250,3 +250,1109 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+---
+--- Test B-tree distance ordering
+---
+SET enable_bitmapscan = OFF;
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+ QUERY PLAN
+------------------------------
+ Sort
+ Sort Key: ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((seqno <-> 0))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+ QUERY PLAN
+------------------------------------------------
+ Sort
+ Sort Key: ((random <-> 0)), ((random <-> 1))
+ -> Seq Scan on bt_i4_heap
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)))
+ Order By: (random <-> 4000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using bt_i4_heap_random_idx on bt_i4_heap
+ Index Cond: ((random > 1000000) AND (ROW(random, seqno) < ROW(6000000, 0)) AND (random = ANY ('{1809552,1919087,2321799,2648497,3000193,3013326,4157193,4488889,5257716,5593978,NULL}'::integer[])))
+ Order By: (random <-> 3000000)
+(3 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+ seqno | random
+-------+---------
+ 5380 | 3000193
+ 6262 | 3013326
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 6448 | 4157193
+ 210 | 1809552
+ 4408 | 4488889
+ 6320 | 5257716
+ 1836 | 5593978
+(10 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+ seqno | random
+-------+---------
+ 6448 | 4157193
+ 9004 | 3783884
+ 4408 | 4488889
+ 8391 | 4825069
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2859 | 5224694
+ 6320 | 5257716
+ 2126 | 2648497
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+ seqno | random
+-------+---------
+ 1836 | 5593978
+ 6862 | 5556001
+ 8729 | 5450460
+ 6320 | 5257716
+ 2859 | 5224694
+ 8391 | 4825069
+ 4408 | 4488889
+ 6448 | 4157193
+ 9004 | 3783884
+ 8984 | 3148979
+ 1829 | 3053937
+ 6262 | 3013326
+ 5380 | 3000193
+ 9142 | 2847247
+ 8411 | 2809541
+ 2126 | 2648497
+ 2681 | 2321799
+ 2893 | 1919087
+ 210 | 1809552
+(19 rows)
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+ seqno | random
+-------+---------
+ 210 | 1809552
+ 2893 | 1919087
+ 2681 | 2321799
+ 2126 | 2648497
+ 8411 | 2809541
+ 9142 | 2847247
+ 5380 | 3000193
+ 6262 | 3013326
+ 1829 | 3053937
+ 8984 | 3148979
+ 9004 | 3783884
+ 6448 | 4157193
+ 4408 | 4488889
+ 8391 | 4825069
+ 2859 | 5224694
+ 6320 | 5257716
+ 8729 | 5450460
+ 6862 | 5556001
+ 1836 | 5593978
+(19 rows)
+
+DROP INDEX bt_i4_heap_random_idx;
+-- test parallel KNN scan
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+RESET enable_indexscan;
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Gather
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i > 8000000)
+(4 rows)
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+---------------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t1.n = t2.n)
+ Join Filter: (t1.i <> t2.i)
+ -> Subquery Scan on t1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Order By: (i <-> 4000003)
+ -> Hash
+ -> Gather
+ Workers Planned: 4
+ -> Parallel Seq Scan on bt_knn_test2 t2
+(13 rows)
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ QUERY PLAN
+---------------------------------------------------------------------------------------
+ Hash Join
+ Hash Cond: (t1.n = ((i.i * 100000) + j.j))
+ Join Filter: (t1.i <> ('{4,3,2,7,8}'::integer[])[(i.i + 1)])
+ -> Subquery Scan on t1
+ -> WindowAgg
+ -> Gather Merge
+ Workers Planned: 4
+ -> Parallel Index Only Scan using bt_knn_test_idx on bt_knn_test
+ Index Cond: (i = ANY ('{3,4,7,8,2}'::integer[]))
+ Order By: (i <-> 4)
+ -> Hash
+ -> Nested Loop
+ -> Function Scan on generate_series i
+ -> Function Scan on generate_series j
+(14 rows)
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+ n | i | i
+---+---+---
+(0 rows)
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+ROLLBACK;
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ QUERY PLAN
+-----------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000))
+ Order By: (thousand <-> 998)
+(3 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 1 | 9001
+ 1 | 8001
+ 1 | 7001
+ 1 | 6001
+ 1 | 5001
+ 1 | 4001
+ 1 | 3001
+ 1 | 2001
+ 1 | 1001
+ 1 | 1
+ 0 | 9000
+ 0 | 8000
+ 0 | 7000
+ 0 | 6000
+ 0 | 5000
+ 0 | 4000
+ 0 | 3000
+ 0 | 2000
+ 0 | 1000
+ 0 | 0
+ | 1
+ | 2
+ | 3
+(33 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk3_idx on tenk3
+ Index Cond: ((thousand > 100) AND (thousand < 800) AND (thousand = ANY ('{0,123,234,345,456,678,901,NULL}'::smallint[])))
+ Order By: (thousand <-> '300'::bigint)
+(3 rows)
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+ thousand | tenthous
+----------+----------
+ 345 | 345
+ 345 | 1345
+ 345 | 2345
+ 345 | 3345
+ 345 | 4345
+ 345 | 5345
+ 345 | 6345
+ 345 | 7345
+ 345 | 8345
+ 345 | 9345
+ 234 | 234
+ 234 | 1234
+ 234 | 2234
+ 234 | 3234
+ 234 | 4234
+ 234 | 5234
+ 234 | 6234
+ 234 | 7234
+ 234 | 8234
+ 234 | 9234
+ 456 | 456
+ 456 | 1456
+ 456 | 2456
+ 456 | 3456
+ 456 | 4456
+ 456 | 5456
+ 456 | 6456
+ 456 | 7456
+ 456 | 8456
+ 456 | 9456
+ 123 | 123
+ 123 | 1123
+ 123 | 2123
+ 123 | 3123
+ 123 | 4123
+ 123 | 5123
+ 123 | 6123
+ 123 | 7123
+ 123 | 8123
+ 123 | 9123
+ 678 | 678
+ 678 | 1678
+ 678 | 2678
+ 678 | 3678
+ 678 | 4678
+ 678 | 5678
+ 678 | 6678
+ 678 | 7678
+ 678 | 8678
+ 678 | 9678
+(50 rows)
+
+DROP INDEX tenk3_idx;
+-- Test order by distance ordering on non-first column
+SET enable_sort = OFF;
+-- Ranges are not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand > 120
+ORDER BY tenthous <-> 3500;
+ QUERY PLAN
+-----------------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 3500))
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand > 120)
+(4 rows)
+
+-- Equality restriction on the first column is supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+ QUERY PLAN
+-----------------------------------------------------
+ Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = 120)
+ Order By: (tenthous <-> 3500)
+(3 rows)
+
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+ thousand | tenthous
+----------+----------
+ 120 | 3120
+ 120 | 4120
+ 120 | 2120
+ 120 | 5120
+ 120 | 1120
+ 120 | 6120
+ 120 | 120
+ 120 | 7120
+ 120 | 8120
+ 120 | 9120
+(10 rows)
+
+-- IN restriction on the first column is not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY tenthous <-> 3500;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 3500))
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
+(4 rows)
+
+-- Test kNN search using 4-column index
+CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
+-- Ordering by distance to 3rd column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+ QUERY PLAN
+----------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43))
+ Order By: (thousand <-> 600)
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 43 | 643 | 643
+ 3 | 43 | 643 | 1643
+ 3 | 43 | 643 | 2643
+ 3 | 43 | 643 | 3643
+ 3 | 43 | 643 | 4643
+ 3 | 43 | 643 | 5643
+ 3 | 43 | 643 | 6643
+ 3 | 43 | 643 | 7643
+ 3 | 43 | 643 | 8643
+ 3 | 43 | 643 | 9643
+ 3 | 43 | 543 | 9543
+ 3 | 43 | 543 | 8543
+ 3 | 43 | 543 | 7543
+ 3 | 43 | 543 | 6543
+ 3 | 43 | 543 | 5543
+ 3 | 43 | 543 | 4543
+ 3 | 43 | 543 | 3543
+ 3 | 43 | 543 | 2543
+ 3 | 43 | 543 | 1543
+ 3 | 43 | 543 | 543
+ 3 | 43 | 743 | 743
+ 3 | 43 | 743 | 1743
+ 3 | 43 | 743 | 2743
+ 3 | 43 | 743 | 3743
+ 3 | 43 | 743 | 4743
+ 3 | 43 | 743 | 5743
+ 3 | 43 | 743 | 6743
+ 3 | 43 | 743 | 7743
+ 3 | 43 | 743 | 8743
+ 3 | 43 | 743 | 9743
+ 3 | 43 | 443 | 9443
+ 3 | 43 | 443 | 8443
+ 3 | 43 | 443 | 7443
+ 3 | 43 | 443 | 6443
+ 3 | 43 | 443 | 5443
+ 3 | 43 | 443 | 4443
+ 3 | 43 | 443 | 3443
+ 3 | 43 | 443 | 2443
+ 3 | 43 | 443 | 1443
+ 3 | 43 | 443 | 443
+ 3 | 43 | 843 | 843
+ 3 | 43 | 843 | 1843
+ 3 | 43 | 843 | 2843
+ 3 | 43 | 843 | 3843
+ 3 | 43 | 843 | 4843
+ 3 | 43 | 843 | 5843
+ 3 | 43 | 843 | 6843
+ 3 | 43 | 843 | 7843
+ 3 | 43 | 843 | 8843
+ 3 | 43 | 843 | 9843
+ 3 | 43 | 343 | 9343
+ 3 | 43 | 343 | 8343
+ 3 | 43 | 343 | 7343
+ 3 | 43 | 343 | 6343
+ 3 | 43 | 343 | 5343
+ 3 | 43 | 343 | 4343
+ 3 | 43 | 343 | 3343
+ 3 | 43 | 343 | 2343
+ 3 | 43 | 343 | 1343
+ 3 | 43 | 343 | 343
+ 3 | 43 | 943 | 943
+ 3 | 43 | 943 | 1943
+ 3 | 43 | 943 | 2943
+ 3 | 43 | 943 | 3943
+ 3 | 43 | 943 | 4943
+ 3 | 43 | 943 | 5943
+ 3 | 43 | 943 | 6943
+ 3 | 43 | 943 | 7943
+ 3 | 43 | 943 | 8943
+ 3 | 43 | 943 | 9943
+ 3 | 43 | 243 | 9243
+ 3 | 43 | 243 | 8243
+ 3 | 43 | 243 | 7243
+ 3 | 43 | 243 | 6243
+ 3 | 43 | 243 | 5243
+ 3 | 43 | 243 | 4243
+ 3 | 43 | 243 | 3243
+ 3 | 43 | 243 | 2243
+ 3 | 43 | 243 | 1243
+ 3 | 43 | 243 | 243
+ 3 | 43 | 143 | 9143
+ 3 | 43 | 143 | 8143
+ 3 | 43 | 143 | 7143
+ 3 | 43 | 143 | 6143
+ 3 | 43 | 143 | 5143
+ 3 | 43 | 143 | 4143
+ 3 | 43 | 143 | 3143
+ 3 | 43 | 143 | 2143
+ 3 | 43 | 143 | 1143
+ 3 | 43 | 143 | 143
+ 3 | 43 | 43 | 9043
+ 3 | 43 | 43 | 8043
+ 3 | 43 | 43 | 7043
+ 3 | 43 | 43 | 6043
+ 3 | 43 | 43 | 5043
+ 3 | 43 | 43 | 4043
+ 3 | 43 | 43 | 3043
+ 3 | 43 | 43 | 2043
+ 3 | 43 | 43 | 1043
+ 3 | 43 | 43 | 43
+(100 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43) AND (tenthous > 3000))
+ Order By: (thousand <-> 600)
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 43 | 643 | 3643
+ 3 | 43 | 643 | 4643
+ 3 | 43 | 643 | 5643
+ 3 | 43 | 643 | 6643
+ 3 | 43 | 643 | 7643
+ 3 | 43 | 643 | 8643
+ 3 | 43 | 643 | 9643
+ 3 | 43 | 543 | 9543
+ 3 | 43 | 543 | 8543
+ 3 | 43 | 543 | 7543
+ 3 | 43 | 543 | 6543
+ 3 | 43 | 543 | 5543
+ 3 | 43 | 543 | 4543
+ 3 | 43 | 543 | 3543
+ 3 | 43 | 743 | 3743
+ 3 | 43 | 743 | 4743
+ 3 | 43 | 743 | 5743
+ 3 | 43 | 743 | 6743
+ 3 | 43 | 743 | 7743
+ 3 | 43 | 743 | 8743
+ 3 | 43 | 743 | 9743
+ 3 | 43 | 443 | 9443
+ 3 | 43 | 443 | 8443
+ 3 | 43 | 443 | 7443
+ 3 | 43 | 443 | 6443
+ 3 | 43 | 443 | 5443
+ 3 | 43 | 443 | 4443
+ 3 | 43 | 443 | 3443
+ 3 | 43 | 843 | 3843
+ 3 | 43 | 843 | 4843
+ 3 | 43 | 843 | 5843
+ 3 | 43 | 843 | 6843
+ 3 | 43 | 843 | 7843
+ 3 | 43 | 843 | 8843
+ 3 | 43 | 843 | 9843
+ 3 | 43 | 343 | 9343
+ 3 | 43 | 343 | 8343
+ 3 | 43 | 343 | 7343
+ 3 | 43 | 343 | 6343
+ 3 | 43 | 343 | 5343
+ 3 | 43 | 343 | 4343
+ 3 | 43 | 343 | 3343
+ 3 | 43 | 943 | 3943
+ 3 | 43 | 943 | 4943
+ 3 | 43 | 943 | 5943
+ 3 | 43 | 943 | 6943
+ 3 | 43 | 943 | 7943
+ 3 | 43 | 943 | 8943
+ 3 | 43 | 943 | 9943
+ 3 | 43 | 243 | 9243
+ 3 | 43 | 243 | 8243
+ 3 | 43 | 243 | 7243
+ 3 | 43 | 243 | 6243
+ 3 | 43 | 243 | 5243
+ 3 | 43 | 243 | 4243
+ 3 | 43 | 243 | 3243
+ 3 | 43 | 143 | 9143
+ 3 | 43 | 143 | 8143
+ 3 | 43 | 143 | 7143
+ 3 | 43 | 143 | 6143
+ 3 | 43 | 143 | 5143
+ 3 | 43 | 143 | 4143
+ 3 | 43 | 143 | 3143
+ 3 | 43 | 43 | 9043
+ 3 | 43 | 43 | 8043
+ 3 | 43 | 43 | 7043
+ 3 | 43 | 43 | 6043
+ 3 | 43 | 43 | 5043
+ 3 | 43 | 43 | 4043
+ 3 | 43 | 43 | 3043
+(70 rows)
+
+-- Ordering by distance to 4th column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+-------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43) AND (thousand = 643))
+ Order By: (tenthous <-> 4000)
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 43 | 643 | 3643
+ 3 | 43 | 643 | 4643
+ 3 | 43 | 643 | 2643
+ 3 | 43 | 643 | 5643
+ 3 | 43 | 643 | 1643
+ 3 | 43 | 643 | 6643
+ 3 | 43 | 643 | 643
+ 3 | 43 | 643 | 7643
+ 3 | 43 | 643 | 8643
+ 3 | 43 | 643 | 9643
+(10 rows)
+
+-- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+------------------------------------------------
+ Index Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = 643)
+ Order By: (tenthous <-> 4000)
+ Filter: (hundred = 43)
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+----------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 4000))
+ -> Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = 3) AND (hundred = 43))
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
+ QUERY PLAN
+------------------------------------------------
+ Index Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = 643)
+ Order By: (tenthous <-> 4000)
+ Filter: (ten = 3)
+(4 rows)
+
+DROP INDEX tenk1_knn_idx;
+RESET enable_sort;
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+ thousand | tenthous
+----------+----------
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+ thousand | tenthous
+----------+----------
+ 997 | 9997
+ 997 | 8997
+ 997 | 7997
+ 997 | 6997
+ 997 | 5997
+ 998 | 9998
+ 998 | 8998
+ 998 | 7998
+ 998 | 6998
+ 998 | 5998
+ 998 | 4998
+ 998 | 3998
+ 998 | 2998
+ 998 | 1998
+ 998 | 998
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+ thousand | tenthous
+----------+----------
+ 999 | 999
+ 999 | 1999
+ 999 | 2999
+ 999 | 3999
+ 999 | 4999
+ 999 | 5999
+ 999 | 6999
+ 999 | 7999
+ 999 | 8999
+ 999 | 9999
+ 998 | 998
+ 998 | 1998
+ 998 | 2998
+ 998 | 3998
+ 998 | 4998
+ 998 | 5998
+ 998 | 6998
+ 998 | 7998
+ 998 | 8998
+ 998 | 9998
+ 997 | 5997
+ 997 | 6997
+ 997 | 7997
+ 997 | 8997
+ 997 | 9997
+(25 rows)
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+ thousand | tenthous
+----------+----------
+ 1 | 1
+ 1 | 1001
+ 1 | 2001
+ 1 | 3001
+ 1 | 4001
+ 1 | 5001
+ 1 | 6001
+ 1 | 7001
+ 1 | 8001
+ 1 | 9001
+ 999 | 9999
+ 999 | 8999
+ 999 | 7999
+ 999 | 6999
+ 999 | 5999
+ 999 | 4999
+ 999 | 3999
+ 999 | 2999
+ 999 | 1999
+ 999 | 999
+ 0 | 0
+ 0 | 1000
+ 0 | 2000
+ 0 | 3000
+ 0 | 4000
+ 0 | 5000
+ 0 | 6000
+ 0 | 7000
+ 0 | 8000
+ 0 | 9000
+ | 3
+ | 2
+ | 1
+(33 rows)
+
+DROP INDEX tenk3_idx;
+DROP TABLE tenk3;
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+-------------------
+ Wed May 03 00:00:00 2017 | @ 2 days
+ Wed May 03 01:00:00 2017 | @ 2 days 1 hour
+ Wed May 03 02:00:00 2017 | @ 2 days 2 hours
+ Wed May 03 03:00:00 2017 | @ 2 days 3 hours
+ Wed May 03 04:00:00 2017 | @ 2 days 4 hours
+ Wed May 03 05:00:00 2017 | @ 2 days 5 hours
+ Wed May 03 06:00:00 2017 | @ 2 days 6 hours
+ Wed May 03 07:00:00 2017 | @ 2 days 7 hours
+ Wed May 03 08:00:00 2017 | @ 2 days 8 hours
+ Wed May 03 09:00:00 2017 | @ 2 days 9 hours
+ Wed May 03 10:00:00 2017 | @ 2 days 10 hours
+ Wed May 03 11:00:00 2017 | @ 2 days 11 hours
+ Wed May 03 12:00:00 2017 | @ 2 days 12 hours
+ Wed May 03 13:00:00 2017 | @ 2 days 13 hours
+ Wed May 03 14:00:00 2017 | @ 2 days 14 hours
+ Wed May 03 15:00:00 2017 | @ 2 days 15 hours
+ Wed May 03 16:00:00 2017 | @ 2 days 16 hours
+ Wed May 03 17:00:00 2017 | @ 2 days 17 hours
+ Wed May 03 18:00:00 2017 | @ 2 days 18 hours
+ Wed May 03 19:00:00 2017 | @ 2 days 19 hours
+(20 rows)
+
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+ ts | ?column?
+--------------------------+------------
+ Mon Jan 01 00:00:00 2018 | @ 0
+ Mon Jan 01 01:00:00 2018 | @ 1 hour
+ Sun Dec 31 23:00:00 2017 | @ 1 hour
+ Mon Jan 01 02:00:00 2018 | @ 2 hours
+ Sun Dec 31 22:00:00 2017 | @ 2 hours
+ Mon Jan 01 03:00:00 2018 | @ 3 hours
+ Sun Dec 31 21:00:00 2017 | @ 3 hours
+ Mon Jan 01 04:00:00 2018 | @ 4 hours
+ Sun Dec 31 20:00:00 2017 | @ 4 hours
+ Mon Jan 01 05:00:00 2018 | @ 5 hours
+ Sun Dec 31 19:00:00 2017 | @ 5 hours
+ Mon Jan 01 06:00:00 2018 | @ 6 hours
+ Sun Dec 31 18:00:00 2017 | @ 6 hours
+ Mon Jan 01 07:00:00 2018 | @ 7 hours
+ Sun Dec 31 17:00:00 2017 | @ 7 hours
+ Mon Jan 01 08:00:00 2018 | @ 8 hours
+ Sun Dec 31 16:00:00 2017 | @ 8 hours
+ Mon Jan 01 09:00:00 2018 | @ 9 hours
+ Sun Dec 31 15:00:00 2017 | @ 9 hours
+ Mon Jan 01 10:00:00 2018 | @ 10 hours
+(20 rows)
+
+DROP TABLE knn_btree_ts;
+RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 2b087be..5e3bffe 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -129,3 +129,307 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
-- Simple ALTER INDEX
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+---
+--- Test B-tree distance ordering
+---
+
+SET enable_bitmapscan = OFF;
+
+-- temporarily disable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = false WHERE indexrelid = 'bt_i4_index'::regclass;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random, seqno);
+
+-- test unsupported orderings (by non-first index attribute or by more than one order keys)
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, seqno <-> 0;
+EXPLAIN (COSTS OFF) SELECT * FROM bt_i4_heap ORDER BY random <-> 0, random <-> 1;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+SELECT * FROM bt_i4_heap
+WHERE
+ random > 1000000 AND (random, seqno) < (6000000, 0) AND
+ random IN (1809552, 1919087, 2321799, 2648497, 3000193, 3013326, 4157193, 4488889, 5257716, 5593978, NULL)
+ORDER BY random <-> 3000000;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+CREATE INDEX bt_i4_heap_random_idx ON bt_i4_heap USING btree(random DESC, seqno);
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 4000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 10000000;
+
+SELECT * FROM bt_i4_heap
+WHERE random > 1000000 AND (random, seqno) < (6000000, 0)
+ORDER BY random <-> 0;
+
+DROP INDEX bt_i4_heap_random_idx;
+
+-- test parallel KNN scan
+
+-- Serializable isolation would disable parallel query, so explicitly use an
+-- arbitrary other level.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+
+SET parallel_setup_cost = 0;
+SET parallel_tuple_cost = 0;
+SET min_parallel_table_scan_size = 0;
+SET max_parallel_workers = 4;
+SET max_parallel_workers_per_gather = 4;
+SET cpu_operator_cost = 0;
+
+RESET enable_indexscan;
+
+CREATE TABLE bt_knn_test AS SELECT i * 10 AS i FROM generate_series(1, 1000000) i;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+SELECT i FROM bt_knn_test WHERE i > 8000000;
+
+CREATE TABLE bt_knn_test2 AS
+ SELECT row_number() OVER (ORDER BY i * 10 <-> 4000003) AS n, i * 10 AS i
+ FROM generate_series(1, 1000000) i;
+
+EXPLAIN (COSTS OFF)
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH bt_knn_test1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ ORDER BY i <-> 4000003
+)
+SELECT * FROM bt_knn_test1 t1 JOIN bt_knn_test2 t2 USING (n) WHERE t1.i <> t2.i;
+
+DROP TABLE bt_knn_test;
+CREATE TABLE bt_knn_test AS SELECT i FROM generate_series(1, 10) i, generate_series(1, 100000) j;
+CREATE INDEX bt_knn_test_idx ON bt_knn_test (i);
+ALTER TABLE bt_knn_test SET (parallel_workers = 4);
+ANALYZE bt_knn_test;
+
+EXPLAIN (COSTS OFF)
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+WITH
+t1 AS (
+ SELECT row_number() OVER () AS n, i
+ FROM bt_knn_test
+ WHERE i IN (3, 4, 7, 8, 2)
+ ORDER BY i <-> 4
+),
+t2 AS (
+ SELECT i * 100000 + j AS n, (ARRAY[4, 3, 2, 7, 8])[i + 1] AS i
+ FROM generate_series(0, 4) i, generate_series(1, 100000) j
+)
+SELECT * FROM t1 JOIN t2 USING (n) WHERE t1.i <> t2.i;
+
+RESET parallel_setup_cost;
+RESET parallel_tuple_cost;
+RESET min_parallel_table_scan_size;
+RESET max_parallel_workers;
+RESET max_parallel_workers_per_gather;
+RESET cpu_operator_cost;
+
+ROLLBACK;
+
+-- enable bt_i4_index index on bt_i4_heap(seqno)
+UPDATE pg_index SET indisvalid = true WHERE indexrelid = 'bt_i4_index'::regclass;
+
+
+CREATE TABLE tenk3 AS SELECT thousand, tenthous FROM tenk1;
+
+INSERT INTO tenk3 VALUES (NULL, 1), (NULL, 2), (NULL, 3);
+
+-- Test distance ordering by ASC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand, tenthous);
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+SELECT * FROM tenk3
+WHERE thousand > 100 AND thousand < 800 AND
+ thousand = ANY(ARRAY[0, 123, 234, 345, 456, 678, 901, NULL]::int2[])
+ORDER BY thousand <-> 300::int8;
+
+DROP INDEX tenk3_idx;
+
+-- Test order by distance ordering on non-first column
+SET enable_sort = OFF;
+
+-- Ranges are not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand > 120
+ORDER BY tenthous <-> 3500;
+
+-- Equality restriction on the first column is supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand = 120
+ORDER BY tenthous <-> 3500;
+
+-- IN restriction on the first column is not supported
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous
+FROM tenk1
+WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY tenthous <-> 3500;
+
+-- Test kNN search using 4-column index
+CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
+
+-- Ordering by distance to 3rd column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY thousand <-> 600;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND tenthous > 3000 ORDER BY thousand <-> 600;
+
+-- Ordering by distance to 4th column
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+-- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
+
+DROP INDEX tenk1_knn_idx;
+
+RESET enable_sort;
+
+-- Test distance ordering by DESC index
+CREATE INDEX tenk3_idx ON tenk3 USING btree(thousand DESC, tenthous);
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 998;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000)
+ORDER BY thousand <-> 0;
+
+SELECT thousand, tenthous FROM tenk3
+WHERE (thousand, tenthous) >= (997, 5000) AND thousand < 1000
+ORDER BY thousand <-> 10000;
+
+SELECT thousand, tenthous FROM tenk3
+ORDER BY thousand <-> 500
+OFFSET 9970;
+
+DROP INDEX tenk3_idx;
+
+DROP TABLE tenk3;
+
+-- Test distance ordering on by-ref types
+CREATE TABLE knn_btree_ts (ts timestamp);
+
+INSERT INTO knn_btree_ts
+SELECT timestamp '2017-05-03 00:00:00' + tenthous * interval '1 hour'
+FROM tenk1;
+
+CREATE INDEX knn_btree_ts_idx ON knn_btree_ts USING btree(ts);
+
+SELECT ts, ts <-> timestamp '2017-05-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+SELECT ts, ts <-> timestamp '2018-01-01 00:00:00' FROM knn_btree_ts ORDER BY 2 LIMIT 20;
+
+DROP TABLE knn_btree_ts;
+
+RESET enable_bitmapscan;
0008-Allow-ammatchorderby-to-return-pathkey-sublists-v07.patchtext/x-patch; name=0008-Allow-ammatchorderby-to-return-pathkey-sublists-v07.patchDownload
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 279d8ba..b88044b 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1014,8 +1014,25 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
index_clauses,
&orderbyclauses,
&orderbyclausecols);
+
if (orderbyclauses)
- useful_pathkeys = root->query_pathkeys;
+ {
+ int norderbys = list_length(orderbyclauses);
+ int npathkeys = list_length(root->query_pathkeys);
+
+ if (norderbys < npathkeys)
+ {
+ /*
+ * We do not accept pathkey sublists until we implement
+ * partial sorting.
+ */
+ useful_pathkeys = NIL;
+ orderbyclauses = NIL;
+ orderbyclausecols = NIL;
+ }
+ else
+ useful_pathkeys = root->query_pathkeys;
+ }
else
useful_pathkeys = NIL;
}
@@ -3286,8 +3303,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
* index column numbers (zero based) that each clause would be used with.
* NIL lists are returned if the ordering is not achievable this way.
*
- * On success, the result list is ordered by pathkeys, and in fact is
- * one-to-one with the requested pathkeys.
+ * On success, the result list is ordered by pathkeys.
*/
static void
match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
@@ -3305,8 +3321,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
ammatchorderby(index, pathkeys, index_clauses,
&orderby_clauses, &orderby_clause_columns))
{
- Assert(list_length(pathkeys) == list_length(orderby_clauses));
- Assert(list_length(pathkeys) == list_length(orderby_clause_columns));
+ Assert(list_length(orderby_clauses) <= list_length(pathkeys));
+ Assert(list_length(orderby_clauses) == list_length(orderby_clause_columns));
*orderby_clauses_p = orderby_clauses; /* success! */
*orderby_clause_columns_p = orderby_clause_columns;
0009-Add-support-of-array-ops-to-btree-kNN-v07.patchtext/x-patch; name=0009-Add-support-of-array-ops-to-btree-kNN-v07.patchDownload
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index fb413e7..3085fae 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -509,8 +509,8 @@ btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
if (orderbys && scan->numberOfOrderBys > 0)
memmove(scan->orderByData,
- orderbys,
- scan->numberOfOrderBys * sizeof(ScanKeyData));
+ &orderbys[scan->numberOfOrderBys - 1],
+ 1 /* scan->numberOfOrderBys */ * sizeof(ScanKeyData));
so->scanDirection = NoMovementScanDirection;
so->distanceTypeByVal = true;
@@ -1554,13 +1554,15 @@ static bool
btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
List **orderby_clauses_p, List **orderby_clausecols_p)
{
- Expr *expr;
+ Expr *expr = NULL;
ListCell *lc;
int indexcol;
int num_eq_cols = 0;
+ int nsaops = 0;
+ int last_saop_ord_col = -1;
+ bool saops[INDEX_MAX_KEYS] = {0};
- /* only one ORDER BY clause is supported */
- if (list_length(pathkeys) != 1)
+ if (list_length(pathkeys) < 1)
return false;
/*
@@ -1584,6 +1586,7 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
Expr *clause = rinfo->clause;
Oid opno;
StrategyNumber strat;
+ bool is_saop;
if (!clause)
continue;
@@ -1593,6 +1596,18 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
OpExpr *opexpr = (OpExpr *) clause;
opno = opexpr->opno;
+ is_saop = false;
+ }
+ else if (IsA(clause, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+
+ /* We only accept ANY clauses, not ALL */
+ if (!saop->useOr)
+ continue;
+
+ opno = saop->opno;
+ is_saop = true;
}
else
{
@@ -1603,19 +1618,67 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
/* Check if the operator is btree equality operator. */
strat = get_op_opfamily_strategy(opno, index->opfamily[indexcol]);
- if (strat == BTEqualStrategyNumber)
- num_eq_cols = indexcol + 1;
+ if (strat != BTEqualStrategyNumber)
+ continue;
+
+ if (is_saop && indexcol == num_eq_cols)
+ {
+ saops[indexcol] = true;
+ nsaops++;
+ }
+ else if (!is_saop && saops[indexcol])
+ {
+ saops[indexcol] = false;
+ nsaops--;
+ }
+
+ num_eq_cols = indexcol + 1;
}
}
- /*
- * If there are no equality columns try to match only the first column,
- * otherwise try all columns.
- */
- indexcol = num_eq_cols ? -1 : 0;
+ foreach(lc, pathkeys)
+ {
+ PathKey *pathkey = lfirst_node(PathKey, lc);
+
+ /*
+ * If there are no equality columns try to match only the first column,
+ * otherwise try all columns.
+ */
+ indexcol = num_eq_cols ? -1 : 0;
+
+ if ((expr = match_orderbyop_pathkey(index, pathkey, &indexcol)))
+ break; /* found order-by-operator pathkey */
+
+ if (!num_eq_cols)
+ return false; /* first pathkey is not order-by-operator */
- expr = match_orderbyop_pathkey(index, castNode(PathKey, linitial(pathkeys)),
- &indexcol);
+ indexcol = -1;
+
+ if (!(expr = match_pathkey_to_indexcol(index, pathkey, &indexcol)))
+ return false;
+
+ if (indexcol >= num_eq_cols)
+ return false;
+
+ if (saops[indexcol])
+ {
+ saops[indexcol] = false;
+ nsaops--;
+
+ /*
+ * ORDER BY column numbers for array ops should go in
+ * non-decreasing order.
+ */
+ if (indexcol < last_saop_ord_col)
+ return false;
+
+ last_saop_ord_col = indexcol;
+ }
+ /* else: order of equality-restricted columns is arbitrary */
+
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *orderby_clausecols_p = lappend_int(*orderby_clausecols_p, -indexcol - 1);
+ }
if (!expr)
return false;
@@ -1627,6 +1690,19 @@ btmatchorderby(IndexOptInfo *index, List *pathkeys, List *index_clauses,
if (indexcol > num_eq_cols)
return false;
+ if (nsaops)
+ {
+ int i;
+
+ /*
+ * Check that all preceding array-op columns are included into
+ * ORDER BY clause.
+ */
+ for (i = 0; i < indexcol; i++)
+ if (saops[i])
+ return false;
+ }
+
/* Return first ORDER BY clause's expression and column. */
*orderby_clauses_p = lappend(*orderby_clauses_p, expr);
*orderby_clausecols_p = lappend_int(*orderby_clausecols_p, indexcol);
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 5b9294b..e980351 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -905,10 +905,6 @@ _bt_preprocess_keys(IndexScanDesc scan)
{
ScanKey ord = scan->orderByData;
- if (scan->numberOfOrderBys > 1)
- /* it should not happen, see btmatchorderby() */
- elog(ERROR, "only one btree ordering operator is supported");
-
Assert(ord->sk_strategy == BtreeKNNSearchStrategyNumber);
/* use bidirectional kNN scan by default */
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 805abd2..034e3b8 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -1644,6 +1644,27 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
InvalidOid, /* no reg proc for this */
(Datum) 0); /* constant */
}
+ else if (IsA(clause, Var))
+ {
+ /* indexkey IS NULL or indexkey IS NOT NULL */
+ Var *var = (Var *) clause;
+
+ Assert(isorderby);
+
+ if (var->varno != INDEX_VAR)
+ elog(ERROR, "Var indexqual has wrong key");
+
+ varattno = var->varattno;
+
+ ScanKeyEntryInitialize(this_scan_key,
+ SK_ORDER_BY | SK_SEARCHNOTNULL,
+ varattno, /* attribute number to scan */
+ InvalidStrategy, /* no strategy */
+ InvalidOid, /* no strategy subtype */
+ var->varcollid, /* collation FIXME */
+ InvalidOid, /* no reg proc for this */
+ (Datum) 0); /* constant */
+ }
else
elog(ERROR, "unsupported indexqual type: %d",
(int) nodeTag(clause));
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index b88044b..a1a36ad 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -2945,6 +2945,62 @@ match_rowcompare_to_indexcol(RestrictInfo *rinfo,
return NULL;
}
+/*
+ * Try to match pathkey to the specified index column (*indexcol >= 0) or
+ * to all index columns (*indexcol < 0).
+ */
+Expr *
+match_pathkey_to_indexcol(IndexOptInfo *index, PathKey *pathkey, int *indexcol)
+{
+ ListCell *lc;
+
+ /* Pathkey must request default sort order for the target opfamily */
+ if (pathkey->pk_strategy != BTLessStrategyNumber ||
+ pathkey->pk_nulls_first)
+ return NULL;
+
+ /* If eclass is volatile, no hope of using an indexscan */
+ if (pathkey->pk_eclass->ec_has_volatile)
+ return NULL;
+
+ /*
+ * Try to match eclass member expression(s) to index. Note that child
+ * EC members are considered, but only when they belong to the target
+ * relation. (Unlike regular members, the same expression could be a
+ * child member of more than one EC. Therefore, the same index could
+ * be considered to match more than one pathkey list, which is OK
+ * here. See also get_eclass_for_sort_expr.)
+ */
+ foreach(lc, pathkey->pk_eclass->ec_members)
+ {
+ EquivalenceMember *member = lfirst_node(EquivalenceMember, lc);
+ Expr *expr = member->em_expr;
+
+ /* No possibility of match if it references other relations */
+ if (!bms_equal(member->em_relids, index->rel->relids))
+ continue;
+
+ /* If *indexcol is non-negative then try to match only to it */
+ if (*indexcol >= 0)
+ {
+ if (match_index_to_operand((Node *) expr, *indexcol, index))
+ /* don't want to look at remaining members */
+ return expr;
+ }
+ else /* try to match all columns */
+ {
+ for (*indexcol = 0; *indexcol < index->nkeycolumns; ++*indexcol)
+ {
+ if (match_index_to_operand((Node *) expr, *indexcol, index))
+ /* don't want to look at remaining members */
+ return expr;
+ }
+ }
+ }
+
+ return NULL;
+}
+
/****************************************************************************
* ---- ROUTINES TO CHECK ORDERING OPERATORS ----
****************************************************************************/
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 236f506..307af39 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4555,7 +4555,11 @@ fix_indexqual_clause(PlannerInfo *root, IndexOptInfo *index, int indexcol,
*/
clause = replace_nestloop_params(root, clause);
- if (IsA(clause, OpExpr))
+ if (indexcol < 0)
+ {
+ clause = fix_indexqual_operand(clause, index, -indexcol - 1);
+ }
+ else if (IsA(clause, OpExpr))
{
OpExpr *op = (OpExpr *) clause;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 2e3fab6..ab52b93 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5284,10 +5284,12 @@ get_quals_from_indexclauses(List *indexclauses)
* index key expression is on the left side of binary clauses.
*/
Cost
-index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
+index_other_operands_eval_cost(PlannerInfo *root, List *indexquals,
+ List *indexcolnos)
{
Cost qual_arg_cost = 0;
ListCell *lc;
+ ListCell *indexcolno_lc = indexcolnos ? list_head(indexcolnos) : NULL;
foreach(lc, indexquals)
{
@@ -5302,6 +5304,19 @@ index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
if (IsA(clause, RestrictInfo))
clause = ((RestrictInfo *) clause)->clause;
+ if (indexcolnos)
+ {
+ int indexcol = lfirst_int(indexcolno_lc);
+
+ if (indexcol < 0)
+ {
+ /* FIXME */
+ continue;
+ }
+
+ indexcolno_lc = lnext(indexcolno_lc);
+ }
+
if (IsA(clause, OpExpr))
{
OpExpr *op = (OpExpr *) clause;
@@ -5346,6 +5361,7 @@ genericcostestimate(PlannerInfo *root,
IndexOptInfo *index = path->indexinfo;
List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
List *indexOrderBys = path->indexorderbys;
+ List *indexOrderByCols = path->indexorderbycols;
Cost indexStartupCost;
Cost indexTotalCost;
Selectivity indexSelectivity;
@@ -5509,8 +5525,8 @@ genericcostestimate(PlannerInfo *root,
* Detecting that that might be needed seems more expensive than it's
* worth, though, considering all the other inaccuracies here ...
*/
- qual_arg_cost = index_other_operands_eval_cost(root, indexQuals) +
- index_other_operands_eval_cost(root, indexOrderBys);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals, NIL) +
+ index_other_operands_eval_cost(root, indexOrderBys, indexOrderByCols);
qual_op_cost = cpu_operator_cost *
(list_length(indexQuals) + list_length(indexOrderBys));
@@ -6629,7 +6645,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
* Add on index qual eval costs, much as in genericcostestimate. But we
* can disregard indexorderbys, since GIN doesn't support those.
*/
- qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals, NIL);
qual_op_cost = cpu_operator_cost * list_length(indexQuals);
*indexStartupCost += qual_arg_cost;
@@ -6809,7 +6825,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
* the index costs. We can disregard indexorderbys, since BRIN doesn't
* support those.
*/
- qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals, NIL);
/*
* Compute the startup cost as the cost to read the whole revmap
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index e9f4f75..6428932 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -79,6 +79,8 @@ extern bool indexcol_is_bool_constant_for_query(IndexOptInfo *index,
extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index);
extern void check_index_predicates(PlannerInfo *root, RelOptInfo *rel);
+extern Expr *match_pathkey_to_indexcol(IndexOptInfo *index, PathKey *pathkey,
+ int *indexcol_p);
extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
int *indexcol_p);
extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index 0ce2175..636e280 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -189,7 +189,7 @@ extern void estimate_hash_bucket_stats(PlannerInfo *root,
extern List *get_quals_from_indexclauses(List *indexclauses);
extern Cost index_other_operands_eval_cost(PlannerInfo *root,
- List *indexquals);
+ List *indexquals, List *indexcolnos);
extern List *add_predicate_to_index_quals(IndexOptInfo *index,
List *indexQuals);
extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0cefbcc..724890b 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -876,11 +876,9 @@ ORDER BY tenthous <-> 3500;
120 | 9120
(10 rows)
--- IN restriction on the first column is not supported
+-- IN restriction on the first column is not supported without 'ORDER BY col1 ASC'
EXPLAIN (COSTS OFF)
-SELECT thousand, tenthous
-FROM tenk1
-WHERE thousand IN (5, 120, 3456, 23)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY tenthous <-> 3500;
QUERY PLAN
---------------------------------------------------------------------
@@ -890,6 +888,63 @@ ORDER BY tenthous <-> 3500;
Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
(4 rows)
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand DESC, tenthous <-> 3500;
+ QUERY PLAN
+---------------------------------------------------------------------
+ Sort
+ Sort Key: thousand DESC, ((tenthous <-> 3500))
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+ QUERY PLAN
+---------------------------------------------------------------
+ Index Only Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
+ Order By: (thousand AND (tenthous <-> 3500))
+(3 rows)
+
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+ thousand | tenthous
+----------+----------
+ 5 | 3005
+ 5 | 4005
+ 5 | 2005
+ 5 | 5005
+ 5 | 1005
+ 5 | 6005
+ 5 | 5
+ 5 | 7005
+ 5 | 8005
+ 5 | 9005
+ 23 | 3023
+ 23 | 4023
+ 23 | 2023
+ 23 | 5023
+ 23 | 1023
+ 23 | 6023
+ 23 | 23
+ 23 | 7023
+ 23 | 8023
+ 23 | 9023
+ 120 | 3120
+ 120 | 4120
+ 120 | 2120
+ 120 | 5120
+ 120 | 1120
+ 120 | 6120
+ 120 | 120
+ 120 | 7120
+ 120 | 8120
+ 120 | 9120
+(30 rows)
+
-- Test kNN search using 4-column index
CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
-- Ordering by distance to 3rd column
@@ -1122,38 +1177,132 @@ WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
3 | 43 | 643 | 9643
(10 rows)
--- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+-- Array ops on non-first columns are not supported
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
- QUERY PLAN
-------------------------------------------------
- Index Scan using tenk1_thous_tenthous on tenk1
- Index Cond: (thousand = 643)
- Order By: (tenthous <-> 4000)
- Filter: (hundred = 43)
+WHERE ten IN (3, 4, 5) AND hundred IN (23, 24, 35) AND thousand IN (843, 132, 623, 243)
+ORDER BY tenthous <-> 6000;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Sort Key: ((tenthous <-> 6000))
+ -> Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = ANY ('{23,24,35}'::integer[])) AND (thousand = ANY ('{843,132,623,243}'::integer[])))
(4 rows)
+-- All array columns should be included into ORDER BY
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
- QUERY PLAN
-----------------------------------------------------
- Sort
- Sort Key: ((tenthous <-> 4000))
- -> Index Only Scan using tenk1_knn_idx on tenk1
- Index Cond: ((ten = 3) AND (hundred = 43))
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY tenthous <-> 6000;
+ QUERY PLAN
+-------------------------------------------------------------------
+ Index Scan using tenk1_thous_tenthous on tenk1
+ Index Cond: ((thousand = 123) AND (tenthous > 2000))
+ Order By: (tenthous <-> 6000)
+ Filter: ((hundred = 23) AND (ten = ANY ('{3,4,5}'::integer[])))
(4 rows)
+-- Eq-restricted columns can be omitted from ORDER BY
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
- QUERY PLAN
-------------------------------------------------
- Index Scan using tenk1_thous_tenthous on tenk1
- Index Cond: (thousand = 643)
- Order By: (tenthous <-> 4000)
- Filter: (ten = 3)
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 23 | 123 | 6123
+ 3 | 23 | 123 | 5123
+ 3 | 23 | 123 | 7123
+ 3 | 23 | 123 | 4123
+ 3 | 23 | 123 | 8123
+ 3 | 23 | 123 | 3123
+ 3 | 23 | 123 | 9123
+ 3 | 23 | 123 | 2123
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 23 | 123 | 6123
+ 3 | 23 | 123 | 5123
+ 3 | 23 | 123 | 7123
+ 3 | 23 | 123 | 4123
+ 3 | 23 | 123 | 8123
+ 3 | 23 | 123 | 3123
+ 3 | 23 | 123 | 9123
+ 3 | 23 | 123 | 2123
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4 ,5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
+ ten | hundred | thousand | tenthous
+-----+---------+----------+----------
+ 3 | 23 | 123 | 6123
+ 3 | 23 | 123 | 5123
+ 3 | 23 | 123 | 7123
+ 3 | 23 | 123 | 4123
+ 3 | 23 | 123 | 8123
+ 3 | 23 | 123 | 3123
+ 3 | 23 | 123 | 9123
+ 3 | 23 | 123 | 2123
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, thousand, tenthous <-> 6000;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23) AND (thousand = 123) AND (tenthous > 2000))
+ Order By: (ten AND (tenthous <-> 6000))
+(3 rows)
+
+-- Extra ORDER BY columns after order-by-op are not supported
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 ORDER BY ten, thousand <-> 6000, tenthous;
+ QUERY PLAN
+-----------------------------------------------------------------------------
+ Sort
+ Sort Key: ten, ((thousand <-> 6000)), tenthous
+ -> Index Only Scan using tenk1_knn_idx on tenk1
+ Index Cond: ((ten = ANY ('{3,4,5}'::integer[])) AND (hundred = 23))
(4 rows)
DROP INDEX tenk1_knn_idx;
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 5e3bffe..69c890e 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -345,13 +345,22 @@ FROM tenk1
WHERE thousand = 120
ORDER BY tenthous <-> 3500;
--- IN restriction on the first column is not supported
+-- IN restriction on the first column is not supported without 'ORDER BY col1 ASC'
EXPLAIN (COSTS OFF)
-SELECT thousand, tenthous
-FROM tenk1
-WHERE thousand IN (5, 120, 3456, 23)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY tenthous <-> 3500;
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand DESC, tenthous <-> 3500;
+
+EXPLAIN (COSTS OFF)
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+
+SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
+ORDER BY thousand, tenthous <-> 3500;
+
-- Test kNN search using 4-column index
CREATE INDEX tenk1_knn_idx ON tenk1(ten, hundred, thousand, tenthous);
@@ -378,18 +387,55 @@ WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
SELECT ten, hundred, thousand, tenthous FROM tenk1
WHERE ten = 3 AND hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
--- Not supported by tenk1_knn_idx (not all previous columns are eq-restricted)
+-- Array ops on non-first columns are not supported
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred IN (23, 24, 35) AND thousand IN (843, 132, 623, 243)
+ORDER BY tenthous <-> 6000;
+
+-- All array columns should be included into ORDER BY
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY tenthous <-> 6000;
+
+-- Eq-restricted columns can be omitted from ORDER BY
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE hundred = 43 AND thousand = 643 ORDER BY tenthous <-> 4000;
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, tenthous <-> 6000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, tenthous <-> 6000;
+
+EXPLAIN (COSTS OFF)
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4 ,5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
+
+SELECT ten, hundred, thousand, tenthous FROM tenk1
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, thousand, tenthous <-> 6000;
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND hundred = 43 ORDER BY tenthous <-> 4000;
+WHERE ten IN (3, 4, 5) AND hundred = 23 AND thousand = 123 AND tenthous > 2000
+ORDER BY ten, hundred, thousand, tenthous <-> 6000;
+-- Extra ORDER BY columns after order-by-op are not supported
EXPLAIN (COSTS OFF)
SELECT ten, hundred, thousand, tenthous FROM tenk1
-WHERE ten = 3 AND thousand = 643 ORDER BY tenthous <-> 4000;
+WHERE ten IN (3, 4, 5) AND hundred = 23 ORDER BY ten, thousand <-> 6000, tenthous;
DROP INDEX tenk1_knn_idx;
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: tested, passed
Hi,
thank you for your work on this patch.
Patch #1 is ready for commit.
It only fixes lack of refactoring after INCLUDE index patch.
Patches 2-7 are ready for committer's review.
I found no significant problems in algorithm or implementation.
Here are few suggestions to improve readability:
1) patch 0002.
* Returned matched index clause exression.
* Number of matched index column is returned in *indexcol_p.
Typos in comment:
exPression
index columnS
2) patch 0002.
+ /*
+ * We allow any column of this index to match each pathkey; they
+ * don't have to match left-to-right as you might expect.
+ */
Before refactoring this comment had a line about gist and sp-gist specific:
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, and it doesn't matter for SP-GiST because
- * that doesn't handle multiple columns anyway, and no other
- * existing AMs support amcanorderbyop. We might need different
- * logic in future for other implementations.
Now, when the code was moved to a separate function, it is not clear why the same logic is ok for b-tree and other index methods.
If I got it right, it doesn't affect the correctness of the algorithm, because b-tree specific checks are performed in another function.
Still it would be better to explain it in the comment for future readers.
3) patch 0004
if (!so->distanceTypeByVal)
{
so->state.currDistance = PointerGetDatum(NULL);
so->state.markDistance = PointerGetDatum(NULL);
}
Why do we reset these fields only for !distanceTypeByVal?
4) patch 0004
+ * _bt_next_item() -- Advance to next tuple on current page;
+ * or if there's no more, try to step to the next page with data.
+ *
+ * If there are no more matching records in the given direction
*/
Looks like the last sentence of the comment is unfinished.
5) patch 0004
_bt_start_knn_scan()
so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
/* Reset right flag if the left item is nearer. */
right = so->currRightIsNearest;
If this comment relates to the string above it?
6) patch 0004
_bt_parallel_seize()
+ scanPage = state == &so->state
+ ? &btscan->btps_scanPage
+ : &btscan->btps_knnScanPage;
This code looks a bit tricke to me. Why do we need to pass BTScanState state to _bt_parallel_seize() at all?
Won't it be enough to choose the page before function call and pass it?
7) patch 0006
+ <title>Upgrade notes for version 1.6</title>
+
+ <para>
+ In version 1.6 <literal>btree_gist</literal> switched to using in-core
+ distance operators, and its own implementations were removed. References to
+ these operators in <literal>btree_gist</literal> opclasses will be updated
+ automatically during the extension upgrade, but if the user has created
+ objects referencing these operators or functions, then these objects must be
+ dropped manually before updating the extension.
Is this comment still relevant after the latest changes?
Functions are not removed, they're still in contrib.
Patches #8 and #9 need more review and tests.
I'll try to give a feedback on them in the week.
P.S. many thanks for splitting the code into separate patches. It made review a lot easier.
The new status of this patch is: Waiting on Author
Attached 9th version of the patches.
On 03.03.2019 12:46, Anastasia Lubennikova wrote:
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: tested, passedHi,
thank you for your work on this patch.
Thank you for you review.
Patch #1 is ready for commit.
It only fixes lack of refactoring after INCLUDE index patch.Patches 2-7 are ready for committer's review.
I found no significant problems in algorithm or implementation.
Here are few suggestions to improve readability:1) patch 0002.
* Returned matched index clause exression.
* Number of matched index column is returned in *indexcol_p.Typos in comment:
exPression
index columnS
"exPression" is fixed.
But there should be "column" because only single index column is matched.
2) patch 0002. + /* + * We allow any column of this index to match each pathkey; they + * don't have to match left-to-right as you might expect. + */Before refactoring this comment had a line about gist and sp-gist specific:
- * We allow any column of the index to match each pathkey; they
- * don't have to match left-to-right as you might expect. This is
- * correct for GiST, and it doesn't matter for SP-GiST because
- * that doesn't handle multiple columns anyway, and no other
- * existing AMs support amcanorderbyop. We might need different
- * logic in future for other implementations.Now, when the code was moved to a separate function, it is not clear why the
same logic is ok for b-tree and other index methods. If I got it right, it
doesn't affect the correctness of the algorithm, because b-tree specific
checks are performed in another function. Still it would be better to
explain it in the comment for future readers.
It seems that match_orderbyop_pathkey() is simply the wrong place for this
comment. I moved it into match_orderbyop_pathkeys() which is implementation of
ammatchorderby() for GiST an SP-GiST. Also I added old sentence about its
correctness for GiST and SP-GiST.
3) patch 0004
if (!so->distanceTypeByVal)
{
so->state.currDistance = PointerGetDatum(NULL);
so->state.markDistance = PointerGetDatum(NULL);
}Why do we reset these fields only for !distanceTypeByVal?
These fields should be initialized (it is initialization, not reset) only for
by-ref types because before writing a new distance values to these fields,
the previous by-ref values are pfreed. The corresponding comment was added.
4) patch 0004 + * _bt_next_item() -- Advance to next tuple on current page; + * or if there's no more, try to step to the next page with data. + * + * If there are no more matching records in the given direction */Looks like the last sentence of the comment is unfinished.
Yes, "false is returned" is missing. Fixed.
5) patch 0004
_bt_start_knn_scan()so->currRightIsNearest = _bt_compare_current_dist(so, rstate, lstate);
/* Reset right flag if the left item is nearer. */
right = so->currRightIsNearest;If this comment relates to the string above it?
No, it relates only to string below. 'right' flag determines later the selected
scan direction, so 'currRightIsNearest' should be assigned to it. This comment
was rewritten.
6) patch 0004
_bt_parallel_seize()+ scanPage = state == &so->state + ? &btscan->btps_scanPage + : &btscan->btps_knnScanPage;This code looks a bit tricke to me. Why do we need to pass BTScanState state
to _bt_parallel_seize() at all? Won't it be enough to choose the page before
function call and pass it?
If we will pass page, then we will have to pass it through the whole function
tree:
_bt_parallel_seize()
_bt_steppage()
_bt_next_item()
_bt_next_nearest()
_bt_load_first_page()
_bt_init_knn_scan()
_bt_readnextpage()
_bt_parallel_readpage()
_bt_first()
I decided simply to add flag 'isKnn' to BtScanState, so the code now looks like
this:
scanPage = state->isKnn
? &btscan->btps_scanPage
: &btscan->btps_knnScanPage;
I also can offer to add 'id' (0/1) to BtScanState instead, then the code will
look like this:
scanPage = &btscan->btps_scanPages[state->id];
7) patch 0006 + <title>Upgrade notes for version 1.6</title> + + <para> + In version 1.6 <literal>btree_gist</literal> switched to using in-core + distance operators, and its own implementations were removed. References to + these operators in <literal>btree_gist</literal> opclasses will be updated + automatically during the extension upgrade, but if the user has created + objects referencing these operators or functions, then these objects must be + dropped manually before updating the extension.Is this comment still relevant after the latest changes?
Functions are not removed, they're still in contrib.
Yes, comment is still relevant. SQL functions and operators are dropped,
but C functions remain (see [1]/messages/by-id/CAPpHfdstf812dYObwMeu54P5HijHgURNdoJRc3jKxRj2LsQJRg@mail.gmail.com).
Patches #8 and #9 need more review and tests.
I'll try to give a feedback on them in the week.P.S. many thanks for splitting the code into separate patches. It made review a lot easier.
The new status of this patch is: Waiting on Author
[1]: /messages/by-id/CAPpHfdstf812dYObwMeu54P5HijHgURNdoJRc3jKxRj2LsQJRg@mail.gmail.com
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
Hi!
I've some questions regarding this patchset.
1) This comment needs to be revised. Now, B-tree supports both
ammatchorderby and amcanbackward. How do we guarantee that kNN is not
backwards scan?
/*
* Only forward scan is supported with reordering. Note: we can get away
* with just Asserting here because the system will not try to run the
* plan backwards if ExecSupportsBackwardScan() says it won't work.
* Currently, that is guaranteed because no index AMs support both
* ammatchorderby and amcanbackward; if any ever do,
* ExecSupportsBackwardScan() will need to consider indexorderbys
* explicitly.
*/
2) Why this should be so?
EXPLAIN (COSTS OFF)
SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY thousand DESC, tenthous <-> 3500;
QUERY PLAN
---------------------------------------------------------------------
Sort
Sort Key: thousand DESC, ((tenthous <-> 3500))
-> Index Only Scan using tenk1_thous_tenthous on tenk1
Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
(4 rows)
EXPLAIN (COSTS OFF)
SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY thousand, tenthous <-> 3500;
QUERY PLAN
---------------------------------------------------------------
Index Only Scan using tenk1_thous_tenthous on tenk1
Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
Order By: (thousand AND (tenthous <-> 3500))
(3 rows)
It seems that we restart bidirectional scan for each value specified
in IN clause. Then why does it matter whether it is forward or
backward scan?
3) What is idea of 8th patch? It doesn't seem to be really needed for
subsequent 9th patch, because we anyway ignore partial match pathkeys.
Then why bother producing them? Is it stub for further incremental
sort?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attached 10th versions of the patches.
Fixed two bugs in patches 3 and 10 (see below).
Patch 3 was extracted from the main patch 5 (patch 4 in v9).
On 11.03.2019 20:42, Alexander Korotkov wrote:
Hi!
I've some questions regarding this patchset.
1) This comment needs to be revised. Now, B-tree supports both
ammatchorderby and amcanbackward. How do we guarantee that kNN is not
backwards scan?/*
* Only forward scan is supported with reordering. Note: we can get away
* with just Asserting here because the system will not try to run the
* plan backwards if ExecSupportsBackwardScan() says it won't work.
* Currently, that is guaranteed because no index AMs support both
* ammatchorderby and amcanbackward; if any ever do,
* ExecSupportsBackwardScan() will need to consider indexorderbys
* explicitly.
*/
Yes, there was problem with backward kNN scans: they were not disabled in
ExecSupportsBackwardScan(). This can lead to incorrect results for backward
fetches from cursors. Corresponding regression test is included into patch #8.
And the comment was also fixed.
2) Why this should be so?
EXPLAIN (COSTS OFF)
SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY thousand DESC, tenthous <-> 3500;
QUERY PLAN
---------------------------------------------------------------------
Sort
Sort Key: thousand DESC, ((tenthous <-> 3500))
-> Index Only Scan using tenk1_thous_tenthous on tenk1
Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
(4 rows)EXPLAIN (COSTS OFF)
SELECT thousand, tenthous FROM tenk1 WHERE thousand IN (5, 120, 3456, 23)
ORDER BY thousand, tenthous <-> 3500;
QUERY PLAN
---------------------------------------------------------------
Index Only Scan using tenk1_thous_tenthous on tenk1
Index Cond: (thousand = ANY ('{5,120,3456,23}'::integer[]))
Order By: (thousand AND (tenthous <-> 3500))
(3 rows)It seems that we restart bidirectional scan for each value specified
in IN clause. Then why does it matter whether it is forward or
backward scan?
kNN scans now can only be forward, and in forward btree scans array iteration
order matches the index sort order. We could determine array iteration order
by ScanKey strategy, but ASC/DESC info flag is not passed now to the place of
ScanKeys initialization (see ExecIndexBuildScanKeys()). ASC/DESC passing needs
refactoring of whole passing of orderbyclauses/orderbyclausecols.
There also was a problem in btmmatchorderby()/match_patchkey_to_indexcol():
array keys were incorrectly matched to DESC index columns.
3) What is idea of 8th patch? It doesn't seem to be really needed for
subsequent 9th patch, because we anyway ignore partial match pathkeys.
Then why bother producing them? Is it stub for further incremental
sort?
Yes, this is a kind of stub for incremental sort. But also this simplifies
a bit ammatchorderby() functions, because they should not care about the length
of returned pathkey list, they simply return after the first unsupported
pathkey. I event think that ammacthorderby() should not depend on whether we
support incremental sorting or not.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
On 3/15/19 2:11 AM, Nikita Glukhov wrote:
Attached 10th versions of the patches.
Fixed two bugs in patches 3 and 10 (see below).
Patch 3 was extracted from the main patch 5 (patch 4 in v9).
This patch no longer applies so marking Waiting on Author.
Regards,
--
-David
david@pgmasters.net
On 25.03.2019 11:17, David Steele wrote:
On 3/15/19 2:11 AM, Nikita Glukhov wrote:
Attached 10th versions of the patches.
Fixed two bugs in patches 3 and 10 (see below).
Patch 3 was extracted from the main patch 5 (patch 4 in v9).This patch no longer applies so marking Waiting on Author.
Attached 11th version of the patches rebased onto current master.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
On Tue, Mar 26, 2019 at 4:30 AM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
Fixed two bugs in patches 3 and 10 (see below).
Patch 3 was extracted from the main patch 5 (patch 4 in v9).This patch no longer applies so marking Waiting on Author.
Attached 11th version of the patches rebased onto current master.
Hi Nikita,
Since a new Commitfest is here and this doesn't apply, could we please
have a fresh rebase?
Thanks,
--
Thomas Munro
https://enterprisedb.com
On 01.07.2019 13:41, Thomas Munro wrote:
On Tue, Mar 26, 2019 at 4:30 AM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
Fixed two bugs in patches 3 and 10 (see below).
Patch 3 was extracted from the main patch 5 (patch 4 in v9).This patch no longer applies so marking Waiting on Author.
Attached 11th version of the patches rebased onto current master.
Hi Nikita,
Since a new Commitfest is here and this doesn't apply, could we please
have a fresh rebase?
Attached 12th version of the patches rebased onto the current master.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0004-Extract-structure-BTScanState-v12.patch.gzapplication/gzip; name=0004-Extract-structure-BTScanState-v12.patch.gzDownload
��E] 0004-Extract-structure-BTScanState-v12.patch �\ms����,�
�|h$�T�f[�O����4M2q������"!��4�KRv|����/ R�,;���imK��b�������J�rp0����h~8X,���G������t1O���%M��\�����b4�^���mt��)^_.�k�}�������4/.2������O�^��|��\1�Z�@dx?���O�3{����eP<����?�`�d8�S������y���b�I���y�'����<oO�Y�d��2 ��A ��I2/2)��~ ��_f%��S����K?�@�/1����u:;�[Q�#G 19D��DI�CY��_��v��G��q�du%�L�'��
p���(/LL����b��`�'2t��p �$�Y�I�uz��p�K�������b!<�"*��d1�wh���~���O�~�?s�91'\������6�?o4<t����|1���yq!�b��e��B�z%�@���+�c��V�@De�=�yOtX����[K������_���w����=�c�,(2�|&��S��>� I����=���}A��,����k��8��[����5��8������'����/��R����XF9�Nl�c1��������]&�
4�������_�#�]�>=|��u���������,�o�����;���>�����8
�U��2��� ��'���,:�f(��:)P�),�k)��V���/�|���A������q��������I������rC)�+0��,B�0r�E93���$�r��p����cEd6/f�(��nO� N�.}%�B^�r���s'\���+��lS�����%C<}�0�zG�����Y��C�3:�������F�N?]��E����.��(n(�"q�|]�,��^��i�D
����0�Y$��nG�+�2�c���a����_���q���c��@�I��K����DZ:�R��eH��/�'�G�`�^f������.�O���=���Dt��{�\��tv�?�)Kq��vD�dc�;�6�?������h�=<���V4�x�� �C�� ���r
����W������z*������`���������&�:���Bl4��P�Sxa)���(D����Fh��#�&�g:j<��$���� l �����1$b���#9����F��64��*�s�cd�R�����Qo�iE�b?/p���q��;�{�/�.4�g�Y� �1L���(��[G��� 5�#3��4����b�z�����x�k��Y=Gw�+p���� �H�l��-�S)�V([��u6��g�(�S�Pn�������-L`����������*
���O5&�v�=IvS`:�S�E��K �8���d�3��_E��Z��f����yOC(2��O��^���4��W>h5�l�>U6T�J�D1>�� &���t�]nks�g���imS1;�,i
��dk�4�e�nA��!�P�[��\p�H�����i��j�� ����
�!�q���~��s�AY��N��_���f5�@���������`� *
y{T�YS�SJ��P�K�����+P ��YJ�2��a`O��5�|/M����p
&d�)ClT�$�+5\������9��^�`�;���"���\���1O��H3d ���
�z�#o���k�g���?j�������F�R�k�P��?Z�~������3.pz��}<=v'_���,�YB��h��3
�u�{� NN�pF�g�4��zw#�s
������`�C������>�C��\e���4��#0� �B8\�d/�vuQ��
&���6�n��tp4���+�?��8��9}@��� �a(�P�5c<�?8Qv�]%��@��aS��� L�l�>J���j��V$��4����v�/H�umQf���y7�4-���r ���� N�|
�.�~Z��AD��SXQh� �}��V�S��~�*�xD����p����Lx���i����b�i����2��Xf�=Z�b�,+�N�l��A�����:��� ���PtQ���2P�{��X�������d �H���m�!�<�R(���hzhY�)'r����������B[N�>��R�K}�]
�um�6-�������&�k1Y���*�}����b`2V���n8pn�#5�P�R������V���f�s�voB�L1�E����r����V�<7����QS� V��?���DH�?QhB����A�8?�����~C{� �d�c\�G�KW�8:] ;��Q)!4~�+B!�o��A������_\�����}g�*h0 Me���F07>�k�1���#�2kD����������/�������k���Qp��Z���
lg��ZO��5=� s���E�E��G,Vr�����X/�*�foi��}V��wx�2��Z&a��<�����Y���bnv�*,�;�=����,�����"��.�ZiF��':R��a�1H���O@���&���g8�m|�$�i�A-��D��D�n*OK���Wi����S:�����)#���H+����W�� xU:V@���~�W&ey��
�T{*���hx����C�3`z_P�9���v����J�[���!��kH<V��� ���,�q�&�Q��F�!�hK��,K���z���;`�
����#�]�vZ�&�g� ��� W&�V�6��`��G�9�� o�m���K���P7�b����/N����_�d�{���8�k`bs��7l:9?�F����I�N ^p6�����N+�h [�9v�����mg����6pi��V��sP��&��/��?�a?4h��������G��g�$t�=��{hF�BC�����O�d$`�� ��GX��^��4�
������G>�i��`M�E�N�����R��_B�} �r4���u�r�vU�N�Q>�!e���������Q�+�4�)'P��5�]���
�]}������J�G���0��1�9�j�����Sq�pCQ��.�(��:���.��Z����"�����j������m�:m&��f�>h8,kF�L��Dp0�L��2��8Uv<��_`V]�v�)PkRkWQ�]�[$,�
���Y��G���|+��9%�Eo��dM�$�Z
����U��4�y�;)r����_�haS���b��2
�_$����P
,1[#6�+%��$�^P�rAI�U��5��W���k����D�e�\���6v�������F����U�����rs� �0jqY���K ��3�G���`9<iW��'�@��gn-�pU�ImlD���1��N��r��I���d&���?v��4�N���M��BO�AU�nk����u�u4���9����
%n����`+���u�Gm���"�~�^_�0D�g��d�bd�r)���y��55� n���Y� @�h5_��!�=q�����������m�uv�q �(���������'g�"��$���7�P!�tr�������{�dP����B��M|�� �����f���#Mb����C������T��=oq�TN��QR��B�N����s��5�g�O����V�/"�u�
�I�[Y��*Z�8���>�G9]D���D7�zo&7�V5X��Kd�w���E������j�N�r�������S9�G)���C5�i�U��Ko1���'�h�v��������s9��%���
����6�-���y� ��Z�>�jzA��������~�hp��V� ;Uv�{�F������G��m]>'1��5�n�mtk<��s���V��k��v)���x��$uLb�.��uC�."T7��<�L�f�r���}��D�n�[��:#Q����*�A]�2B�m�
�C����,����+���/Sp���&��������DI�&�(u���$�pF�� ����q����be�4�L���1�����{0��#H�`�dU|�����L�H��P��Lb�j��k��Fd����� �/� 3���983:������u��� ��D�Z��K��GN�@}�w�r%��Q�O��T�}�C=�8L����J�T�9�<�}A%���wZ����rb9�
������T]�Xc��
���@aZ�I+E��!���h��B��/��X����
����u
E��K�g`(e�����M���>�������n�m�Ox�����l��%����]�������D�PJ���=�`n�}����H������/���{;i�z��8O��b~y��"-Bj>x��O"�SXx�A�dn
'�"H6}|��Z?>{
#��P3)�wF��I�l�
44��������%"���_��.B�1��8��s��x4����k��b��_j����u-��N�j��k/ev�Q*1uE����`�T�O�Nc5���b
�����IS���,�[
9�]EHk�n����$�U��2���k��b�w7������?~��y�����o�'N,m�K]���#�W�F����0y��iRM���z�������6;�
V��W��\��=�pW��7����.M�
q:&�U��������";Ga{� &O��Q��}��j7
�1����=�{o$��t:� j�b6:.����s�8`������I �Ca���5����[^i�9/�8��V1���Y�~\-q�xl�I�r
�y���]w$Q��-��fx��@�gtN3���z�J�ptH�K����C�E[�(��t;�VS���������d�������Al��YL����'0�����-^�Z��'YX���8� �+v4 oE���Akmd��M��U&
;�J�+p�����%�~Q�gu����|���X��Sw|��<����� ��� ��d2���An�Q��g*�J5�6*������}���#��g�L�������;�:��9����F��cS�*i��ugo���H=����S�U�X�a�){�w?������5��������K�6����E1��|��v�'xO��8`G~�_t�P��(.D+������u������\���b��.�!=�F@��6@�3��6PKwAi�uo���x��V������v���F�R�Nnt�������8P���~
'�y�:��m�m3���������=�u* �o��i�)1�Vn�Ul���b��� |U������(��O��d�k2��A�Vs��o|'�6h9��2���S����f��RU��4o�T��5����5����
������������-�D��m��
��w�����h��R~���dK�q[j����}RU�wB{��'�LF>������t%�:���F���A��E��>|�.�� ������RFL���~WQ�EJ���H�!o[�b�{�8W
�6N��c��y��9��J�JP�T�B!/}d�d����LDrQB����2J������kU��=t��/���`�p2X%0��t+�<&��a\����<�C4��n �������'�������^G�m9��r����T���%a<�/$���!��jS�\M]E p��� ��`�'�e����T������&�7$�EN�c�uQ �G������g�\���pD`j�?��/����|R[!s
A����c���3L���b��K�X��TeG���ZcCC
��W�u�\������9_�T�']�W�
�����v�*P�����������L^}~��pr`��������c�RV9E�X�dr�w�y��6��W��d**���_D�W�G)��_����HfF���;������c�%i!��m�3�����:T�.��M7�H������fx�����3{WcaAN��"��*_rU���Lp1�wml[8�O�
wu��W�m1��Go���T���7��#~����ZF�E�}1����<y|+�������+�mnr�H�E)��Avy_K����&,J�������4$�,�z�J6�[60��Ez�"m��"K]�����@�2�A^%��l�<+*���<��"D�:�E�i������I~%#r0����$T�JX�ii�/�M��@'I�NV�[�u;�*��\4\�<t�8�)}�0NF��)�]b���0i ���yx��Y�T�����^���Q����x�f.%������ )\UR��X]3�����Om,�o:��l��*Olv�!� =���
F�o���*���hS�R��I'��"W��l�|�g�V9^�xI����Gq��Z6]
S9��0o���Qm���3Ey���l�
�Z���q��8u���%(�#{r��]������`��x|e\��h�G�P����6�m����@�!�A����3�����GR�Mg:�-�)�%�8l�����{�(�u'����� ��������I~�x����4����=�s#�L�r�5�������h*h&#�A���E����0hf�(/�xza�w���ne�?D�y�9�k��`�v ��=<<�Z��E)g�9�UAH���u�ak��uj_�G������Wi�lb����l>��e����`@�������I@
�� ����
P��J>��3d8p�?2?����F q��q���F ��Lg���A����e! v�D�-R�����!h�W����H����sc��|V��?���XYure�����"]�b�}#�D�u���_�3=0pa_�&
o\��Pp,E&/N0��Sv�VV�]�4���[����W�������)��u���/��u�� Y�u���0��v���N@���y"�%�[����H+�1X��%r��4����gX67��3C������3���MqJ��������Q�^���]�<�^�P�������a6��2�Z��+��f��I�#*yX � ��B�3��'%���p��(J3GX�u��E���v/���E�-�nGXm���[W����.�~d����U35��KW�G�oy|:C�3�Z!<��J�nl:�����[���l3����I���������Y�&�����/����^=���w���Y@6��
����i�#��t������*n2��"���OV,z��2� �L��9�|�$����%3ns;<�k������P.�u�arm\}&�F���ra�>$���3B4�!GBnr��%��la-�|~(}:����S�����������������I����L��Z[�c�=��2r��l���V����0�a2r0�stT�����Z���B�x43�( d8��I0[�LY�v�u:�L����\GU8q�Rb�����'J�\�9[ �������O�Ly~�#��2��&�RI�
nL���a��U��M=��,����Gd�n�H���`��2���:���N�cd�T�I|���������+H`y<�]��T����a��)=:mU2��f�$B��f��TF�s��ZSQ� o%�L% <xf ,9S�U������N/��2D�A`(!c�����t��w@D�x�2����C����E��o��;QMQ.�n��M��UX�J4�����.��<]�I2����J�h��0��7��o.^������yu{q}���7�����A3,��:��1��R�!�x����__�^����z�h6v�u�8}�m��2����P�1��W���s�L#wm���s���$ eZ�������P��2/�,�D�C0=S�&&Kz����MSE������I-�P�~9@~��G*� N�?^^�������^��OR�u9w�0���g=#C'�h��Z������D����O����3�,\[Y�����$�/~�f1�br�,�U�SY�YJiz�&O��TS�K������S4�`��c� 7���a��n��]��S���8����{0��b
U�� 'X3���X�an�Vo��X|4�2�����&�\�9 r�.�KYM����kEpN,f��vrw6cBabv�
Cb������ (C�>�����G�6���&�5w�(�r�csc���>s�-�5z3%����Y�lX���56�)���v���3���XA�m���E�L�GANm� �a�flQ�hS��;���h���+�����_/��2)agj�-�8(�:��g��,�[{�zm$�!���U|�4�T5��j���"������W����f������M�������1�c�3�#���F�Eic(����1�0�&3as�.'��{�3����u�������!�b>�/__���P���a�Y�����j��0}u����x&4���_�T�*�Iq7��R���- 1j���r?����; ����z`��Q~<�����U����lV��T�����f��l�j8>�.;yE6�]�����gg�f���'Y���C��yL�#��s��-�W�M2���W@o`��O�D�L�����0[b�n�?�j)n.s���|���<m X�Qtt�#�����o/���|�������c_��v�G:�����++��S�����B��ELY�V��U�UO������Y�r��M��%K���G�|�����: �\���k�W�`�X�����z�Qt|B���YB� ���"*��
�q�����N���,�����J?��>�2xJ&k����t�#r����Ner��$}/Z������P�f��W�C�t���2�Q��t�6 �����#����q�w<O^'�S!'( �p���gX�.j�*D�|��U:�,7�<�f\���5���v�1�v��Q��'��OdS:�l��#`��������g�����#U��WBMI���`������"�yn/_aZ�"9\qX����N�l�{�.H��Jd�����Bv�RP�,S��$�q���VD����\�� ���m�H����N�����g=�Pi;�����`�t�i��
�Nf3J�X��� �
uRW0� NV����{o:Y�T����{n-����~�h�};�I�������>H9S�$�8"fC��`��2Ao@R'd�,Z�S���h%9%��+��O7W�d��E0+���N���E7��v�i��^������|LR*� t���$��3Q*��_��2�������R|�e*�l�p ��0���gp�O��]����u
���h�9�0��16�K�� �����eI��pp���d�,�tSosff�i����d,��-��9.���x��|<��hl���z'ZGu���6�����c�zF[Ep��
�Mp��&�����|��0?z��a�Z�~�m�%��v����qI��� �������K?�A6{kp��F����������/Qc6�q��������>CO�sOB�_��$����-�9W���hCb�n?gc�&�:��ttb$6m�v���B��Vv�h\� ��������]�����E�N��$8��J�o�Y�a�
R_s��p; -�� sv'_[r���~����O:�QY�b`C�$�e�����S51��9��I��@���]��!k(H!��+����
A�X���$��t��P��IB=��W�����}�]� !�G������\h�/�~�ds��7�]�����_1�
���a�-~d^ �,R^���}�jB���?�|{�w�Q����->�=�����PXr��0����=�d9W���['��(j4�89w��_�����k+���Q���jM��d�����j�
�R�4�X6��t� �� �$�_��*Y�SLEV�A)TF..l�$G��
�i�a�� �^�m����I#�X�D��2O }S-���h�'���x��}�8����_1C`��w��0//��� ������~�cq�r �S�Ox@�>`g�YxW�>�����V�Z��Rt��s��~��w}��9X����"u�����D�@��u2u����T5r��L`�����,�������'�c5��c�Y3�,_���h�b#���N�����z�N��^��Al}?�ZKI,�������%��/8E
��g@@��:�1]S�Y�f���~�RF!��-���7��\!��q�B���H>Q�?��"� �����QZ�z�9��J!��Vic���I��M������$�XNz<��a���h<'���^����/��Tg�dA��{#��.�x�YZ&�����"�������S���������p{=�u�����Qz,|���c�.�X�������pWz,T!P%dA��`/{t2m���7���S,S��I�%��T���Tn�x"�����l����jbL��X&���P�V`��8��8�������u��%�N~��qp��/n_�����"�� �4Z�J�=x �w���^6�s��J�6��p7�W�=5}`���&��������
{�)�����d%�Mu�,�u3^�\=��36��������G��a�GK�
�@f�4Y_&o�|>��d�>g�]6&����������n�6����u<�z5��h����*�a������Z�"0~:W����1���G2�/�q���#��0������� B}+"� On Tue, Jul 2, 2019 at 5:47 AM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
Attached 12th version of the patches rebased onto the current master.
Hi Nikita,
make check-world fails for me, and in tmp_install/log/install.log I see:
btree_int2.c:97:9: error: implicit declaration of function 'int2dist'
is invalid in C99 [-Werror,-Wimplicit-function-declaration]
return int2dist(fcinfo);
^
btree_int2.c:97:9: note: did you mean 'int2_dist'?
btree_int2.c:95:1: note: 'int2_dist' declared here
int2_dist(PG_FUNCTION_ARGS)
^
1 error generated.
--
Thomas Munro
https://enterprisedb.com
On Mon, Jul 1, 2019 at 8:47 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
On 01.07.2019 13:41, Thomas Munro wrote:
On Tue, Mar 26, 2019 at 4:30 AM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
Fixed two bugs in patches 3 and 10 (see below).
Patch 3 was extracted from the main patch 5 (patch 4 in v9).This patch no longer applies so marking Waiting on Author.
Attached 11th version of the patches rebased onto current master.
Hi Nikita,
Since a new Commitfest is here and this doesn't apply, could we please
have a fresh rebase?Attached 12th version of the patches rebased onto the current master.
I have more thoughts about planner/executor infrastructure. It
appears that supporting "ORDER BY col1, col2 <-> val" is too complex
for initial version of patch. Handling of "ORDER BY col" and "ORDER
BY col <-> val" cases uses different code paths in optimizer.
So, I'd like to limit first version of this patch to support just most
simple "ORDER BY col <-> val" case. We would probably be able to
enhance it even in v13 release cycle, but let's start with just simple
case. I also think we can evade replacing amcanorderbyop flag with
method, but introduce just new boolean flag indicating knn supports
only first column.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attached 13th version of the patches.
On 08.07.2019 21:09, Alexander Korotkov wrote:
I have more thoughts about planner/executor infrastructure. It
appears that supporting "ORDER BY col1, col2 <-> val" is too complex
for initial version of patch. Handling of "ORDER BY col" and "ORDER
BY col <-> val" cases uses different code paths in optimizer.So, I'd like to limit first version of this patch to support just most
simple "ORDER BY col <-> val" case. We would probably be able to
enhance it even in v13 release cycle, but let's start with just simple
case. I also think we can evade replacing amcanorderbyop flag with
method, but introduce just new boolean flag indicating knn supports
only first column.
Now patches 1-8 implement only "ORDER BY col <-> const" case.
ammatchorderby() is replaced with amorderbyopfirstcol flag.
Patches 9-12 contain ammatchorderby() and other features, so they may
not be reviewed right now.
On 08.07.2019 11:07, Thomas Munro wrote:
make check-world fails for me, and in tmp_install/log/install.log I see:
btree_int2.c:97:9: error: implicit declaration of function 'int2dist'
is invalid in C99 [-Werror,-Wimplicit-function-declaration]
return int2dist(fcinfo);
^
btree_int2.c:97:9: note: did you mean 'int2_dist'?
btree_int2.c:95:1: note: 'int2_dist' declared here
int2_dist(PG_FUNCTION_ARGS)
^
1 error generated.
Fixed.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
On Sat, Jul 13, 2019 at 5:32 AM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
Attached 13th version of the patches.
While moving this to the next CF, I noticed that this needs to be
adjusted for the new pg_list.h API.
--
Thomas Munro
https://enterprisedb.com
On 2019-Jul-12, Nikita Glukhov wrote:
Attached 13th version of the patches.
Hello Nikita,
Can you please rebase this again?
Thanks,
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Mon, Sep 2, 2019 at 9:11 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2019-Jul-12, Nikita Glukhov wrote:
Attached 13th version of the patches.
Hello Nikita,
Can you please rebase this again?
Nikita is on vacation now. Rebased patchset is attached.
I think patches 0001-0008 are very clear and extends our index-AM
infrastructure in query straightforward way. I'm going to propose
them for commit after some further polishing.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0005-Add-kNN-support-to-btree-v14.patch.gzapplication/x-gzip; name=0005-Add-kNN-support-to-btree-v14.patch.gzDownload
���m] 0005-Add-kNN-support-to-btree-v14.patch �}iwG��g�W�4�e@8� H���$sL�Z������+ ��B�*�����7����A�������dUfVfdd��vN�po�;v������{���^g2i�L&��d<�F���?���a ���h��f��O�����[�����������]�Q���~��,������ac�8�:qb��x;�j�����a�V_�����W�=Qm�4�[���o�(~%~�pxs��hv�[�_��x,�/.D����y,�P���nm���-1G����v�o���*���!DgWT��T7/{#7*��Z-��� z��
?�����������x��w���&�}uzx��4�)���^��1�{��\��W_�U���|t���C�t�Vk�U#-b���\��p����}t|o�"���P��`�/�n�[�.�;M
�T�(�;q��f���uu�������xm��f��.L������
��7j����l'�W���� O�����������9��;�3��v�D�<�� *W+xn����]~R�lm���D���^,���S0,z�E�+�{{-�����q���v�;�f�����N�X��[��K�����:P�ZOT�GwG���[����
1w}�
4&�s�b�E8������R�s $p,�����3w��VU��_�B�����|w�1"�^;���|����(���%���n����"�k�Z��\w��]�/p�c�3sY`f�E�p|h��/������:��?������_o�G���w0�(���Y� n��g��x�lIqNa}4�E�N}��#���,����#1]D1����#u���>����b:#�!X�"D�`���[�- ��;#������m��Z��Q�m��q��'�KN���3��V�� �`���0�}E�F1>�?�-�g�tG�����~k~75o���z����M������h�yx�m^T$L�
P�[6������ ��@P��
�Y�2X-E�t�7E������������������_s�p���czv�������X�G���"��1��[���b
��������u���c�����{a���7C���ib�o��Q-���5�*����$2��x_�,�Q��`�x������������N���������x��m�;��D�I��#�$���������?@|�'� ��������� [o<� ]^�Q���}���1��dw�?�,|�w�M��-� @x�y�L2��D+�(���_��{��^o�s��!7���5y��������)mmp{���/�'�7��lU�O�O�o�K�����p?�������+q�3�=@�]������v��.��q~��� ��U��W�>,3�)����3Yl�������(���[�����1}��"�=8�4�u!��E���: U��((4��) ��7Z��<%e���p\D������Yo �:$[|�fso���.��\���B9,|�����M��{;�Q��������p V��� ���p��i�@���{��r�A�,���a��z�?����w��������P�>\����]��'0/������'xP!�� �s��:��R�����
�
�:�zN%�V�Hw! Iy� ;�������b���{���P�n=����l0.�Z��|��?Ba�h���%����?�91�'w�G0p���"����3�1���#���QE��0pI�#�%���� ���@��(��$�a>DJ��Y%�z��c���D��x�n������wwo�o����$����}e3D��n��<�h7���!�<����q�v�FP�b@��]�BZ3��B���+��,$��j��S�S �_�L���: G����;SZ�B/�}�����w�#�����$�9pL��AG�[��#� l�@���p�(!�l��#���yl�v�br������ ����JE4sG���q�,�-��"�{D���Bk�[U ��$5��p6`��<k�b�c������{����4O��������S��/�"��C�SdN��-9�= �h;�hd<�5�l����8�+�3,Y�V���:A����C�
C���8a�&@���Od���vf��0��g �5������@H �r���8�"�%����pB)1A�0�0u�h1w��� �4� ����0v��l���H�<�I_ �K_� ���cD���jr�<�4�o����H���p�1E:��oU�o$>��x��L�!`�3��3��<"r�?�X_��P��,$���;m�]F|T#I�Z��;��&�^���v��v���uI�n��
���� ��%+�H��x��
���?��7��������,�=\x>�@�/��� ��
6��a�>��M���-����G���-@�yi�N�,�[�/8J��G7@��}����?q���;[��[�t������t���0�E�������K�;(�"k�b�����S�~��U�r��8�� '���$ ������u�,�(��\D4i!8!8� �������@�������4���I��~�g���d
l���y�Lv� ������o��j�Ox4D5��y�����.�o�./�W��+�3����UD��3��_���s�^�c��l�Z�V��[���$��*����w>]KB��
cYD��>"�G�s����pe� 4�@�.w?=O�~���
�(�H� ��J��.�?Y]=7��I��3/i������r�3d�o��� H�� O�3@M�@�y{���,��T�)����|s9sp ��e�Q���B�k_w�s��]^Da���@h%?�r�#|��q�a���{��j Mq�����rBJ��c�ES�x!�%F<���sjWQ�J���O��.�j�
��&����Lo�_}�X�,��p��p@���?��P���������S4�y���{���$�XM��p x={#.��@7P�M,�����tCP���?@`@Q��<��X��� �x���_h� 9��*t�����ab$~rQY�&6�~@ �X�������>�(���
6H�<��n�^n�iU� 8��~S��gd�Fg����,��Pq����O����g�ya����ZX_�3T�C�3[���%�X���7�m`���k������b(�A
]�I~k�c��|�Y@�[�FE$���cX��C x�Z"K|�B)q>����@�� �Q��v����v����'�#���9g6������aK���X�nL>"�a@]���~E*!��qE� ��vd-�[����/E1"�p����=H�"����5��~���'m�����]�~_"z��!
D�H&
��:m!:i\�r��I0������3�+d�s�Q�p��C�Pa�)����,�wwj}��{]�x z�Tb������|��HJc(�
�J"O0�z?�l�TH���� a���YZ��#X(���7o�P����X�8�d�� g������S��/�n��/e���6i+�V��|:$� 6���J�~p�A9*�K�!���Cv����6�Ng��,II
<7���b��x�����������^�}$��}*�=�pp���#QK�2�B��h��CbcG��)z:�nA��aj�H��1'r�h: -������g F�U����H|��VbT���z���o�'& &�$Y�T����E�M3^����Z�D����dbPks��q�Qc)�����I��g�
��.�����2��2!����P�f�;����Y.>"���D'��&}`C���NdGf[�Ng��<�e��\7?��H9}#���>4JE4�M{�dA
H���>?=��w����Z��}�_@�$-�E5:�����&�R������Z>���F���:�jb<z^@�>�����d��!��oP�(�v��w��Z���`B�FR�#�| ��pR��9�pI�o�'HB�������.H�hY#FG!��c/��y"��bM��s�N�@����k�P�������V�����a%�a"y��6�u������W��^]�L�F,f,���):)BC-8�����}:{=��6iTp�qBTV�c���/;���x@!��=�<������k
(��m.��.��]�tV����o��<$`k�p �������i�!��HS�������U�O�@*(� ��V�:*9�49�|��r�eR�n�Kh������k���u�!�����G`��Ro�B[]�[��������WA)��E�x<�>�tzg�����}fK K��q���Y8g�yR����_.��79w�����$W������Vqu� W� |�}T�b�5���X�e��*�W<N>���W������3��p������s��L���q�H���K
����+�-;t�{�=e�Y�+9*9� ��,~-!���-(���a����w���o+_6q� �$u
���{9� Sg���O�p_EE�.��4m�O��HJ�}k��xE����,)�"�R��}��M���I���)V�vs��#���+^rt�B��(����A���~Y? ��t��h/�o� !�������;X���>���&��>^����..ogg7g��g���$�k�Q:�����9���W=�(e�^�4����45��6�(��~_����������x
�Z�����a#�Z�r��(e��;�����47�#h,��h��]� ��
B�B�)���uA�i��^��F����e�Z��A��N�O�,��K���o���5����To�����}�� �{2��K�����B��U����g����N<��O�Z���T��KuX�����? �^��S��@K�[��nB�$�$�Z�B��&���A������C'�:[i�E��������6+D�.=-����!���?���p�/�vrH�L��j����:r�d�m���CdO~<�8>�x��%k;� �hd#�p?y���+I���,����h��Z���P/K%DEt%����I������s�����dc������2IIC?E@��B������6��lZ[_����JeD�&�)��_Ag�];_mJ��g��R)�LS;�mPJ�^��T�_��kH�BIck�Q:;9?�����Jqx^<�#��XE�q�/���(���yl�4�)2���Mal\)����������ke\���xV�
�� f9~�=��Z�K|��
FI(-��6 ,{;@�����r���'BA������rhu�f���w*�"
��u���xb�s����#�,7�5+k7��Nm��n��ie��p3�y��fJ^�B����^&��N�qO���7
�C/`g?H��H����|���0��"�H8�0:�G1�M�BLxq9��;����X�u9��V%������|��J�E��Y�����R!j*#d
�~� ���n0M�,�L43OB�aj����`�+}��������GfJhB\t}�&�����s��k�����`������u���L^y?
)���p=�/���s�������>��{��8Q�0�q��6��Ig<��K��~���q����c��z���F*���6��=�l��nV��^QjJ��0G{��2?/�@(G3[�s�����o�g)e3�����}<YW� ����
{�*]�jE.�j'����x8�G�~������h��t;�Q�����me2&���J�[q��;C4[W{�HN ��Vfpd�(?�I������[����Z2�3.�rft2I�*�C�'~��BZs�
�G��Z���~���8e*�*pf��u�_�6 p�2 ��w6#x���R$��@�G
0�k/8y����k7'�� !�$b�G�r6&���%�d�!BO�J��L����mcp�n�#����������ZF��5Y����3�H��H���B`V%$N�:,��r9���_"���tH�G���L�����U_P�%�����6-�`��kn{�9k�Bu0�<�(�zh��RZ=�8 ���[*�1$K���y�{}�����Z�;j������yi+F�S�ETa �����46���\^�(������l��p ��&&��EIi���HT��J�����B"(N�a��i�����X1M�o��|��f�%B������4�_q0��bJB�_7�*�v�����P_���`N������8�Q* ��&}����yY�a��-�^`
�2N�&Z*���u�%,���l���>q������
n�5�����=������/��@>�`��aPB��F�Z���4nQ�fr07�� �����@�$yv���swBQ��e���H��5��/��RxI� S�D7�,�}n�:"U�������Gt>��]e��Fx\\���o�����2�%��+�����&"�����n ���n.� x8f�&�Iz����Z�KC1��_m=�m��x�/; Lc����f��S@�ZK�`��:Hf,9�l�P�]�;V�Og�����J���5��?�����,%�Qrh����� 3�<sS_����F���������^��*:a��Ax����H�����*�E�/b*6&�>�<�l�/� u����' ��h��f�'���E��3���.�d���]��.�3/B���rCnsvp;�u�TS�!�Nl��L�)��5P�?G�eY�����m7Z�A+��d?K0������v�z�����c��3,
��?�����3HIR�&�^��|�����I���'\4�"�X��:�\S$s�do� O++�P��Lku^�)���%�cw:�1�t����S������ ��������I P�����`LB6���Q�V�����U!IU�a����C�����f�k���9�K��V�B�W!F���m�z|C��FL|�����E�O��S��gx�L/2=T�!�*f&g�h.i��m)P�����XU0QII%���rCh"�Jp�W����)F�Q�����FY{�exH��)���B���h'���Fh(;\L��3q�f��I��<8�Dg^*���o�R�t����8i��&'!;"�+��@�#c����D����_M�Jlz+�pK�$�0�Q�kM��e2���4��`��Kd����lc���eZ'�>g�C{�%���
��@k+�cz��l��ls|J����gB����k�H@�e�p�u�9=Fs��;!F I��N��L����VO���������"%���=O?I�r�UR���z
�8m��3��58�G�u4�A%�$d�<{s.�������P>�X)��O��u ���&�����R�.��$��X3��������8t��T��/�WrxlAD��
�=7�"+���<�[)@���h=r�D��^H�d�+����c���$k�o�_������y�rM����'��?@��}�c%!l���r��q�xZ�1d�.�����X������Q�r���gEM�0k�K����?����.����U"�[D��]}�{����[�&�Y
�f�I��S����aG���������^X�*��?�J�K������DB�;b�,��i*�P�H*��=�j�m�J�b��|S�UV��Gx�w���j���n��`%��2�dvz1��D��C��#/�uV�b����uOQ2�R�(��T���e���K<�R6L�W=o��� �5jA �P_�N���Qj��G��o��|�t�r����n,��uH����Ce7��K�m�>As��)-�u��������-S%�s)���$x$mL�Nb�$'B��\
t���K{f �J)�d�b-$<�QN���68M{$��8M��H0�I��8���;��U6x�L��-�]�~����`���9Z��/����R�OE�-9c��,��$�rp��/�W��,a�� ���l�`��=Y'�I$�j>��`Q�Xxq�k�[F��������9�����*R��A�T����~��i���)n"Y���#�T�`Rex��fz�X�+������ c@
��k���%]U��(^��Z������x��vQ�Gwa��gX-�s���T�������[M8��;(C��'��.�k�e��JlWe�?��q��������q-D���Ui07�]Sr���������:t�&�oC�e�J��"�������Q�:��- (�x��*��sW_ji���%�����:)7��o=}��~E������m$^`������O��^�d���6=���[�@�|�k,=#���d�tpP��� �F��W��0R~u ���K�ZXz���S���9�y����#Cq)�j������E�:����t�Fy�A).��;_(����� �R���f�����}�Za"���q�t���T��9[���7W5iU��eh�I��>/���><�,J(��z�v�����'[XF��&)����,v����W�m11UO�I�qX�{9���!Y+?a�{�\�N��|����+�����&4uL���G����!�
�Z&����cv��e��$[���������`���d��C]�F�9&L����#
C�H2�v��K�;���������`4���o�
��y�25�R��>*c|� ��Q���p��E�����U�q���Voa�A�_cx���eT���$�W�1xfW��7V:�h'l�Z�7�(e��'����n(���@�|��Obd�@�l���#�G^� ����W�b ��V���i�[�������*#�nY�M����G�Jf�ZZNl{����L�w�2�g7��s����8\�en�]/^�
wy�"e���v���A�^�|�w�&��]�g���\O����o���+"b����y����L��I�0�"~k��c�����z�?�2������4�um���gY�K�VmKFJ���S��=�: x��{����:��L���������D��%mU9��8H�L�� ���&�����T�W|���ij����+k�`�0�.�Q�^��k5���O���;qPJ5w���z�R��=���aT�� $����0�Zw)b���6�}�sU�5�:V���:������z����Cd��9���7�M+|\�H,e\�Xn�A!C��SG����.&���R��W^ ��f�/�'k��Kvh��]d��L���b����b�b[����.��Vm7�}sl��Z���g��Y;�M�*����
�3��~��x������{mY��+���\+Q����{�t����)�>����z
_{:cSF[�����.(��!*�V��n����o
��I��)y"�5�U��8�����:{����%��9�e��/?z�2��$��7+�uA����w����O�t�(�k�9Pz���W�Z���h�'1��Qd���;�Z{;
,���\7���S���J�9 ��.��������s4���O��v�Wk��6X��eUD���g�M�v��B���������������I�[Bk$���X}n��Y �
K���=�W����z�X �d�<����{��:�C�wy(�����^���� z�� ze���%�u:�����p:����k������Ef6,7�����oOq�QHHr�6��}gE��_l�r4|���j4��,F����u��Q�;l4��~������
�����E��>�� �l�}�v�W��m�XCH�����=Y9��{�t��v{v;p��,�uN����6��[�uJ��z0�x���~s�����R���������Glt�^^�������K�#��e���1]?q1�!��������IX��x��z�m��G��y������������]� ����/���9gR�V��gOVZ�8���^��<|��z-"0�����cp��Ml{a��ML���A��q�*�c*G��b��QE�������'<0�@`�.F�Y�jd������b@.C���`���L�* d�Vw���J��kG�Og�h5<�~��&h�g����W�A08�[�KK��x9A�H� �,�Z���b���� H�
�tvv�BAK�B}9��Mw�}k���j#}]�4q��A[WC�P�D-~�F���}w�����=M��!J�"V��(3�`0^���X�&_���u;_�HbWj��v��M�B\����p6����_"]�#��P�E�Wv���'z���N�8�(7Y���������uSz4���kg#��!1�J������d%�� �[�0s�61�f/�t�6�Y���4�[VF5�d6��M/�������-�
����+ &P_0
�;�������+j�������y�/fN��]t+�4�~1
���+�7��4��G`x�������j����^V���"��m������F�����*�[������q��(�M���-����)�i.����Q�d��jH�����l���{i�{��p@Z�^$.@�>1b>@2���W�? �)#j�% Z��`&g��j�P�����1k0�]�%ee��,O�<7�����|��?n ^�C0a����M�^���N#[�i��,��+�t���kH8��9������/x���Ld����������Q�V��k]��Tf#���hFQ��A.MO��*F��rFM(�(�l '���W���%�]i2A[���\:��4FV4h��������������1��
���BE���S��Ek2��������L��:P����C��I��(�0:�Uw
s��l����<��`:�����-��^��E�w����4:��8�OG�UZ�g/�������T+*����k~��Z[h�C�� W���K�&�?�5�|~[��_�g�u+���%����F��&`�h�/��B�B�9�����Zo��$q �Az�S�M2['������o�����7�����0�U���P�������������>@i��Y}+�-�J�L.��wk�g��V3�� ��1��@��.z�s�w�����L���b���%.��7�b��uX�S;�Gs�t�r�{]���"�3Q|C���.V)h���?��pH���L��<�NC��������*[�#���VX3���,�o���e��>EO
��
H�����t�Y��k�
����_���T��RR�T]����<H�AY�7V�U��O
�l�#a� ��E��e��w�X<���a���q�q�j�H�2X����qL�x����(By�*yT�D��i�DY�����#Cxv���21������$�u~����h�K%^�[�^q��"��;T���vqrz�9���Ix�rnM1%�\���]�^X�F�/ m*!XfqUmp�o���C��`�E���O�!1���fERY����]�s�
��fO���D_
���u�����eZ�\4��P���A�����}�U�m�"��]���y 1����P�kKwz_]�����5��!�����7��1���,��C���C��K/�S%�:J��nC����a�3��}M���V��<J��qR�,��3���� s����w>%��W�b�A>pz����NyF{��Z�����o8���M�h��4f�����e*�:���0��X������fP��������-���������{C��$�'4R������f��<�\��*-\��Z�?�����D+�-��5�]��h�XJ'y^�U��QSuYu)
27����Z�r)��USB�A~��I�*h$����� T�������j�
*m��*<����.)
�\,���_��B��������y}l�`�1g�s��9��e���u��B�����8 yxM��t6<0�����JE�$uG�_�Rf�F$,�zV��o��?�>i�P�����l�Im������#��I��R�f����5 ��� -u�d\Y�o����e�pT�L�IE�%RZY�#�d=y�k����P$����M��yL���Ac[�"c�K� !���p ������o���I����7�;�����N
��]'p�����[+�LY��Mf��T/UI
�2��N������UK�Q�q��l���k��(�3�]�A��
�4��z�����U����y:�kH?��~)U�����y��^(yt���;V��>k���R*<(fv�_���w�\����=�"b}���r �����(�?
��H��PkH�"��!����^Y������=\N(i>9d �o�(�K6���5�g�w��6�>>�{�R�����q�/�����/}�Q�^����t����i�JY�7���-AC�5�t���9C���b�i����Y)�k��0w����h�Bs"`36�IR��7=��w
�TFD?�4l�U����x��*Y�L�i9bA�
e_N�nv�Z�v���>_��^@@��2��}2�7f3#����/1����>�I�JT�����089��9�8>�K������2�K���|'��%;��������� � z�:����������`X���.�])�(E�2����T�W�R�����t�1X�+��l���Jt�t���QI��� 5>]���B��2e���J�b}M:��3�E�q�$s����?83���� <3]/��f���'&�'u����
O?yQ��_~���:�9}�srB��������1�&��������S[��
C7[�T���Y�+Ie��}VTe������� ��GQ��A�]<�pT���� �w
o�����lH�I� t�9�M�����������.H�t��0U�����]=�\� ] }`���2�S^
ha\��\���tlhB���p�Q%sW�u�)��t��9�}�VI3K��mPTM��--�V��7��9rF�]
�:`�t|Xp�
$q mP!"���O��c�f.�g�-rBj��q�zV���fprz}L0�WSO���%��">�vaz�t�F~t1�DYWd1@�|Q$��EcFJ��"�c�u"�TY�0S
�f+9��H��Q�8��3�Z�.S$D���Es�8�:�|)���6��Ea�����R���5�4c��Wz���zS8�*�E�p+�[{��@�g�� k\�T����x�+�����WD����&b8:��<�~DA76qY��H_����G.�}K���Lm^D�+���|'�W
��
��d��N��H���Ik�2��9L�{��&��?��(�tO�+���|��K�R�e��Us�85_�����=h0�U��!1��2��^�0������{y�f���P��+�����R*�� 0�8�g�S�&)���<��D�%l��$,�y��*�����D��Pmz�e�U�R�Mxp���SX�65���u���d����)����tBt���O� ��R�����F�Qg� �*^�+3n��T�� �SU(����&g���#��Q�B��&%��$a�e-��>�m�j�X��� fl;W�']����]����MTW�(�F��!S�y��|i�{V����78���d��:��_�^;T/�a\~�l��{�Z
Ug'�n�./���������W�y���������;��(���k�@L�� �}� �S)�X��4���g���<�<�{��vS,J�`�!_bE~y*Y�y�e�E�_�M����Q����"�A������9�X��WYYZ$����N��[S"]��Z��^f��3���'o�T�gh6�Xi0@���������Z��QZZ&�=�f��x2�s�_��(���L���mM���/-�el
�|s�X���m�V���^��(i;�kf���^�2��Da\�
�EO�
��xwzK�����B�X���o �a�V{P`�����%��ii4?�\Hgh[��mP�v����`)�����������T��2��ya��S�3������W$2'��8�� _��3�_�����49�^�u�rg����$@��"���q�
\�`"����-m�L�J[@�l��
�0(�<^���q$*,a[�f�j�$.lh���P7.X.�i�BMn�^e�
�f�d#L�q�w�)�%�p��lhy�)%q��2�`25�s51�R�pn|�h� ��U9v�N��������-C���y�m5�/� n O���W��P���}������%VnU�$���oav&�'+a�7Wy���/��<����
��?z��1����-u�����Uu��M\�����c}�N�� �����Ed��L����l2��e(���#�:p���@e����Wl�`�ZJ��������?oP�CZ]����u>����������F����M�;���x�:��k���Zs�,��ns�a�������S�9L��������~F��1������x�r�%�jT�c�N�\tts%����^���/<�ru�<�.�p�F�m��S��Z�����m��1t��g�#o��t�����l���m
jnu��V��y��P��kP��Q$���E�����~�"@���k��|e��N���|.K$l�b%��t-�2�:�� q�d\i�O�#�������3���j��Bt)� ��A^`[q
; �s����]P)�]��T�\ew>'9D�����������g'����<=Fw����34-����{`����������&6R�pl�?B7�#h�l!�~N���9 �-�����7�Ci�P�M�k6�R�����5��Au�XAQI��7�:w�e�����%��z������
�tUL��.��Eh=� t�������7A�o����������)�m� J�k���*�MB�
&� �4H7X�� aM]�#��e���M�K�"�.C'�y���V�23�O�(�u ��$�lK�f�J��qr��h��+]�_��Z�+�d�~�]���m����������6����K��X�z���G����g'�j�P��F�m q(�D�BfP���<SK��i�3�E�=Bf�55����������
����1QIrC�S�Sk�A|n���<c������u=����9��9`�wEr�_�8�������eeu����������5�����-\5@fz��H�r����WM�v��Y*2�6MKL�'E+|�/-Vp����o�"���R��N��}�[�GL��;���9i�A�5{�Vw��7��E�������0��v����T�����Bn{@��c<��IjN�� N�
F2ZWq��L�"#�y��a�q�8l��;�����:�[2��9������t�L<�G��
���2�o�C��vk[�����l,��@g������[a"�E��q(�����}�� ��0�/"�$k��o ���{�oW+L�/��"�.U����aNQf��F���
����}d��#���9��9C�l���*����p�<��h�5����\(���Y�����tR3�N4��#]�V/�#�����<�2��Z�u_�Zv|������3�;���Li�x$k����^����q���DF1WER����8�x
<)W�#g�+�����>�g5fS��l��v�s7H�f��
���+��ef�8���������aSWF��gNyJ���gNG��3/�����N����Mw;]�I�`��d�a�Y�3�3eYz������ ;�d��k
`e��w�F\��n/������/��/MB���af����� p���Q���3����eyYm������|x{��(Xq��������V���4�"�J#�GEX.���\�xo��{������b����uf83����Xs�S�.�)q>�*����_8�� �g�(�/bo���]v6p�����&!�9{��+�t�3���:���u�-K��M�l���aR���H6��@���m��;��������;��Y)[#��VD��g��%�,������Z]���:9�RMJ�.
,�Y� Y�6�U-�f� x��8����Uk�%�<�
����~ �[���#�����".���;'S �:m$� �9���x�F
T��z_]Z��9����M1� ]X�|��4#�h��a[2�����p��W�z��Is���
���LZ��x����u�5��n���.y
�Oxtuy~~tx�=`���������pzuxsy%��?;�Y�H�����.���Z���^�j,�h2VS��ON��J>E�wHNiW0�Y�����r�-A��W"e�{����d 'W=���kM�
Y8G���7Em�,K��v��i����UW�V�Jw���T����sn��sn����9�~��{[�Us�-�s��s�"8�1�B8��<�9s�#��'����3,������&��I��K��s���l��pq�v4�0
�q�=��2���[Wb4��>�P��T����Es�KQ����/[�zx�����m�[c�����P;!&~H��S^6��\��\3\�f��8
��)'c"��nF�{H��t�o{�[U�)s��O��;�7W����)+�8�<,����T/`X�2J��3Q{���N/�j���^���5����#����X��W����YH4~"���r��~J�'y�SnC)����x8l���q���� �g{�������,��������*�HJm��-��U�
�E�L��&������2�/��2A�/���_,�<����(a���M)��
6���6�h�d���d��j7Z������=��: 0012-Add-support-of-array-ops-to-btree-kNN-v14.patch.gzapplication/x-gzip; name=0012-Add-support-of-array-ops-to-btree-kNN-v14.patch.gzDownload
���m] 0012-Add-support-of-array-ops-to-btree-kNN-v14.patch �=ks�����WL��n���ao|�q��o;�x���T��`�XHDv|����=���C�In�]����w����0���i[3�6Ts�����O�~�j:���13����)y�dLD�U�a�]U��s �C��7��o���<�$���3o �~]Q|�h�p�Wf�t�<]�h:���P�!��;�pG��j�j}������y�z���w���o����h�XaL�����&�""q@�qH)�8:����v�D��=���;��m�(�������x}$���V�u+�e�z�����Va}O�e��~��C���#��3st�h~R�����_n/��|�����A��I����y��o�!<�G6�z����oG��-};Jq�7s�$����C�:�$���t��O��$k�g�)b��F�vH�c"����:��q� �1���j�b����E��(����?H��TM%3����-��:
1
��~D��
���j*���z�7������f��>scbm���5��V������:]M�t��n�������S���"���f������J���U@$�M�h�N����1|z� 6)�������V _1���H��Z����|J��������j�[d����4�,B
��4�i$�qD �[�u �i�C�)���3[,z�=O�g���z�V���<��
>,����,{k5 J|]m�D����!�� ���z������D�wM�sJ����,�����QL��
��(���`��Y���'�g��7+6�&�[X�"25`^{/���<�pB�/&�'�F'��5���8����u�Y����A;3�N��FQ/8k�NN�O���_���ahh�B������)v�����*~b�E������d
pq��s���l����Ql�6=�^���p8��K���W3������f"��������[�}.��Y�x���l1�S�KX�l��c<,�����Y��2���@���-OvOJu�x�����{���al��)�D�/_����Pqmy6�����lK 2m���w\�� ��63��9�� l�@w[�~�Nysx�l����������oa��� `���0�#O�"�������2}�1�'�x�?���M�� ��W�� �C/C��,/������8�{���������jk��N(#N���. j����l�j�����D��]1i>���L�8~��a��C6���Z��hH?�m<;�6T����X;^$���,���d�I����xN�G���c�����S� [? e��=�� �!���c�W��W�&[|��+�g�O&�7�|�Vo��#���?���/�^T@��=p��di!��������j���qx����'�q��j���8A�D�S��,��� �\��wKP��u����e�
g8���bf�]�z �n������x|��Fj��M�!8>�'���W�F�������H�h�)�G+��(�%Q�yH �~2k�X�k��0 -Y'W��V%_?��T�r�
YK�����&�d��^�?��2���&
����-�>�P�5�Q��ROf���Q|��!���C�L� �a�L�'x������p�3 D�����D�K��54�'��Gt(�mf��TC�P�gK�N
7uT����LQ�~Z��5�&e+O�d�H�36�T��
�Y�n
58�]��4���_��It�����9��������P��'P�#�������l�3��t}7v-/��M����[�"a7�ys����8�����2�>$�JN�W�V������������D�Iet��g��}���<�zW� "��0�������� ����~����\����#:�S�(1�)y�B�V����L���!��h9�M���� ���
Wd]���$b����#��E����_�H�f�G %SL`�i9�<L� k6���� �����]u}�2�%��������p���!�@��il,��"�L�1,��� s>M����"��*1����Fc���d|��r|J���� -��&�l3��� (���Q�] � F{��q�~ A{�g�n��'�e�6md������vvG`8���)����Q���V8uaka����k�������P�pT���A�E|�B��xA���"<35�V,�:�c�,�z��u��0�+B/�q��q��3wC���=]@LO�Lv0c"���[�Q�@@(��YS�:��������D�V����\��b1t�0�$'������#���(��/�M��������0W�R������[��b��Gh_5fS����T�f�I�3��|��FFRE�MESI�Y�[�4�HO6��)cHj���y��9�v�,�����o;QA)�^����U���H�1�����
� (+�t����D]�w����{&�K��O\����a\N���� �����L &r��L]G��,�d�M3�����bYb�?���H�$�Z��+�W�k
|l�gzW���3�tt�N�����|�`��W)Cd�Egi��g��.]��6�����q��
1�I�M�p�f���2�����(H��BzFPB����
��)
��r�!`� ��[��b6�2a�/K�h>�����1�i90�v|������.-�e������W7���z0���� r<I�����s����f�[ Q��U��d��5 �K�H����������/m Z'Ha�A��q�fR��#~��O~T &�c�j�t�)t�G���Gg� ��Q�).�R!���W�0H22ZN��-��� ���Y������?_�n_�� �Sn`����%��Y�~�`N����!���$�}��IPO�3����������z�����C�����j������~���jf5���Y=�>4��h���
�����p�r��������r���L� ����t���f�)2T�������+�"c��V6�V��#(��Navl�����H`����V� *=`�[�JRxM�,J5Y�����(�M�B�� ����8&�`m1Y���j9Ue"'��G� h����tV�|e�S�����2���������7!GAL��m��U��Fb��Q�\��kc@�<��s�b�k =��������� ��u��{AQ��hY_�l`�i�]�q3�����&�X�!�������?��f
3%�,����������,��K
���AD�_p��7W
����9�'�<R'x�N����Nf�,��DG�wK�#�m��{K?��,��)�Z�������q�("���,@��� ����������~��� Q�zf��tMX���,#]'R�8�c�M$�
�w.A�8�<����,x������l
�f
�#�>�����2`N0�\
�:�_��Q�=��������+�;��`�)s��Q���H�%�U^����&�?���Y���E�Y�'��eb�.i��*+�L�|�%$p,|�t���X�����W� ��FKJN��8=<���19�}t����G����������1��6��^��(h�������Pm����p�;����i]��zky%�7����Qt����I��kz3��$q�DV�.>
�#�m+M2�����AGz��k�t��c��w
�5�����%D1:��!R�j���u�g�R#A����w����Q�8fH�N}�}�J�����:D����TMfS��Q���5���9[�2����R9���u*K��:r �RD.X�0�G����L�hj�t
��:�?��xb������G�6u}������tDq��m�,��-X�K�V���������c>H�%C��� b�@��;�����`�r�r�O�UMAM<;s��?��39����.Iv�S���L��D*��� � �TU�v�bI�$��0���`Im�)@�LN�B�a7�rES�����s����H�.��|�q|j��}�<�O�g��RZ�&���u�q����a44�:�����Qg(��H/���C���5*���j��o-J6�%�O��� Y%� R����4lnQY1)��4���I��`�|78�3��&^.���s��'������%:�|^� ��W1C�2���3�k���3#8yC�+H��sVB�S�w"���7X*
q��s��q��\A�r�!S�<�w� t�b^?�N�^���B ��':��0���.�E�����o����=K�I�g.��������}v������R�rK�b9��v���"�+a����z�#3_9�7�M��z��&��o��aa��g8���S��9A��K��)d���|�� |8�|�^KW�-��O� P
��A���9���_�-�o�G�b�
�0%^�m��6�*���LA�
4�[EeBZOHnw\K���B�U]������.0���j�vr==I��!}D�%�l��|��y�}���0�,�m�s�)i�a
W��������(;���~����<������Y�i����?���tPv�WCJ}��cG1
�2�����b�Fl��;Kl�[���j����"����[9����V@��%.�!6��%��w\� ���%�B�J� ��|nr8�������Ef�����ok�$p�z��K�Dk�{�f��3Z��[�������a
5mf��A���J@e�.
a��C�q�o)[�'U%������IlA3>o��(ITb�����L��9�I�����D�4@��wG�'�����)W�vv���1�
�zl�<��n�������y��mL�nW��}>���f=��9r=�){�7yuh0+��w!'�C1����/�=#z��t�= I�>�ICSI\ah���X����B�T�,����z�~��������l��F����d����� �qp<>�������x�rtp����iy�z����+�z�����}t2JF��&>�*���=��D�+��Y�:�_���������qu�����M��������5�F�SPI��C F��G[�?�
b�!E7>=��C�h��-����ZU�� 5Z�q��g���Ru��f���qZd��6�����!��10�S����1^f��&~K��7a�&�lQ&.)>��������]��e?�������H�Y�O�^gg�=#���0R�= �8��,�[9���)����f��[��W����Zek��?�[��c���l�n$g��rk��U�l5+[���^e+~,��+�*[�����0��(�v+[��V��U�l�U����~e����yQ ?j"@`��)�B���
����'��]''����#*���'�
��7��*�|�;�f�,G�8����k_����v0BY.��]�*� ��*��.��,S��5�����.o�(�4U� r�_HX��|�%����w�<C8E
�E���+f������M���'(�Z����+�'��Nl��I�2��
��}~�����b��n�����m�pS����d�N�\|�s��0����\�40�U����#v���C�!�r&h��^�j������n����������x�����/��vX*bzMUJ�m�i�++�JW1>�uRC�+�y���e/� ������BT�J2O�������Q"�W;+��]���}w��X��Y�}�t9����2�Y�X�(q�����th
�\�Mr�{�b���t�T#�Ixr����GF�cptT�$%�F$���Ez
�f�/���]�C��!J��c3�d�y���= )�_V�����3�a�oI@T��������W3T�)�Q����L>��b�o�u�rC�#��� ]K#xMD�7�2��_kTw�Q��Fk��5Jg��5s�_�����~h�g���|���(����������R^���C���^�}s�C�}���C���*��]D���Y�����E?;Wk��x)�u��.��a�[Q�����S�e���A7
)F����u�����4�WHk��W���v�K��S2���aI�F.����:� z|����ew+�Z�)7��k�[*���g6�XF�(iF�j��N�{�L=4}�}�������7#����������m�g��`������,J��\��;��t���o�M���r���"�'�!��N�nyh��S�xR�b�xT�o�v_��=G�t�m�����-�jT\��F�P��%���a|�Y�]��/��'L�n�������yD��z&�����5���Y.�]���V��H��u���
lz?*m�(y���o�Z��-��o!���o�>����o�7�qk&_K���^%�T�m�����r���
������u3�/��u2L�@$�>8���������j��_W��^���J� ;�����������-�} On 2019-Sep-03, Alexander Korotkov wrote:
I think patches 0001-0008 are very clear and extends our index-AM
infrastructure in query straightforward way. I'm going to propose
them for commit after some further polishing.
Hmm. Why is 0001 needed? I see that 0005 introduces a call to that
function, but if attnum == 0 then it doesn't call it. Maybe it was
necessary in an older version of the patch?
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Tue, Sep 3, 2019 at 2:19 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
On 2019-Sep-03, Alexander Korotkov wrote:
I think patches 0001-0008 are very clear and extends our index-AM
infrastructure in query straightforward way. I'm going to propose
them for commit after some further polishing.Hmm. Why is 0001 needed? I see that 0005 introduces a call to that
function, but if attnum == 0 then it doesn't call it. Maybe it was
necessary in an older version of the patch?
Regarding "attno >= 1" check I agree with you. It should be changed
to assert. But "attno <= rd_index->indnkeyatts" check appears to be
needed for current code already. It appears that gistproperty() can
ask get_index_column_opclass() for non-key attribute. Then
get_index_column_opclass() returns garbage past oidvector value.
Typically get_opclass_opfamily_and_input_type() doesn't find this
garbage opclass oid and gistproperty() returns null as expected. But
this is bug and needs to be fixed.
I'm going to push 0001 changing "attno >= 1" to assert.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On Sun, Sep 8, 2019 at 11:35 PM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:
I'm going to push 0001 changing "attno >= 1" to assert.
Pushed. Rebased patchset is attached. I propose to limit
consideration during this commitfest to this set of 7 remaining
patches. The rest of patches could be considered later. I made some
minor editing in preparation to commit. But I decided I've couple
more notes to Nikita.
* 0003 extracts part of fields from BTScanOpaqueData struct into new
BTScanStateData struct. However, there is a big comment regarding
BTScanOpaqueData just before definition of BTScanPosItem. It needs to
be revised.
* 0004 adds "knnState" field to BTScanOpaqueData in addition to
"state" field. Wherein "knnState" might unused during knn scan if it
could be done in one direction. This seems counter-intuitive. Could
we rework this to have "forwardState" and "backwardState" fields
instead of "state" and "knnState"?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0003-Extract-structure-BTScanState-v15.patch.gzapplication/x-gzip; name=0003-Extract-structure-BTScanState-v15.patch.gzDownload
���v] 0003-Extract-structure-BTScanState-v15.patch �}�s�F����_��I i�����������7�J�@�)a\ ��I����nt��!����q�D$���w�{���J�z���]����q�_��r1����qo�?ug��t�K�s�3��#��|O��N���
��^�.������E|-~����
>�����<��2������?���WI�����U�t��;�~8��w,�N���;[M�K����oN>=�I��.^~���4KV�l�H�������2so��j��4�=���K����L�G�4K�T�3����`*����m��J?�]�
P��8Lk��VY���?����]�h�������?��y�,�����<��2�e�K9���ifz�����P,�P�bv�G�r���@Q*�,��t�;���#1���_Z{{�`���y� ��P�n�hW�Y�z��t:�e��vw1���yw+�v:�� v�9� ���������5G��?G~���4;�Y�Z�r����B�3������A� �� 9���#�����+)�X<��O���4�o����������'��4n=�/�aH\O��_���CA�38>�?��2a�����o����z�c���7����-��R���21�Ev�0;-�����T� ���e�@3I�y������'��mz�h��b���N���}#oS������8=M�����Z�VI? w�� �;���t��2#�.�k)��V�����t��~���%�^��� :����?��7�����L�G8�S�^>��P�VfM�}���d2�&� I����VB��O"���>�6w�C�=n>��fep�������2L%����8�5��`{�>��+��m����%�^�OIh�D�����H�b���f���1�k�Z� 1D~F�"��&�L���.G�� ���["������h��� ����F`(V�� -�8��Fc ����?�>!�2���K�r G��>p����������k������~�EY��F��������u;����vqn��D_��"+`�wI&�{L� ��@ �i�]��J��t��=����~"�P�uDB[����e5Q��QC0|�� ��/?s��~��<���y�.�Qdp2����DZ��g�%x6�&