Commit 55450687 authored by Tom Lane's avatar Tom Lane

KNNGIST, otherwise known as order-by-operator support for GIST.

This commit represents a rather heavily editorialized version of
Teodor's builtin_knngist_itself-0.8.2 and builtin_knngist_proc-0.8.1
patches.  I redid the opclass API to add a separate Distance method
instead of turning the Consistent method into an illogical mess,
fixed some bit-rot in the rbtree interfaces, and generally worked over
the code style and comments.

There's still no non-code documentation to speak of, but I'll work on
that separately.  Some contrib-module changes are also yet to come
(right now, point <-> point is the only KNN-ified operator).

Teodor Sigaev and Tom Lane
parent c0a4d3e0
...@@ -1030,6 +1030,9 @@ gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer ke ...@@ -1030,6 +1030,9 @@ gistnewroot(Relation r, Buffer buffer, IndexTuple *itup, int len, ItemPointer ke
END_CRIT_SECTION(); END_CRIT_SECTION();
} }
/*
* Fill a GISTSTATE with information about the index
*/
void void
initGISTstate(GISTSTATE *giststate, Relation index) initGISTstate(GISTSTATE *giststate, Relation index)
{ {
...@@ -1064,6 +1067,13 @@ initGISTstate(GISTSTATE *giststate, Relation index) ...@@ -1064,6 +1067,13 @@ initGISTstate(GISTSTATE *giststate, Relation index)
fmgr_info_copy(&(giststate->equalFn[i]), fmgr_info_copy(&(giststate->equalFn[i]),
index_getprocinfo(index, i + 1, GIST_EQUAL_PROC), index_getprocinfo(index, i + 1, GIST_EQUAL_PROC),
CurrentMemoryContext); CurrentMemoryContext);
/* opclasses are not required to provide a Distance method */
if (OidIsValid(index_getprocid(index, i + 1, GIST_DISTANCE_PROC)))
fmgr_info_copy(&(giststate->distanceFn[i]),
index_getprocinfo(index, i + 1, GIST_DISTANCE_PROC),
CurrentMemoryContext);
else
giststate->distanceFn[i].fn_oid = InvalidOid;
} }
} }
......
This diff is collapsed.
...@@ -904,6 +904,76 @@ gist_point_compress(PG_FUNCTION_ARGS) ...@@ -904,6 +904,76 @@ gist_point_compress(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(entry); PG_RETURN_POINTER(entry);
} }
#define point_point_distance(p1,p2) \
DatumGetFloat8(DirectFunctionCall2(point_distance, \
PointPGetDatum(p1), PointPGetDatum(p2)))
static double
computeDistance(bool isLeaf, BOX *box, Point *point)
{
double result = 0.0;
if (isLeaf)
{
/* simple point to point distance */
result = point_point_distance(point, &box->low);
}
else if (point->x <= box->high.x && point->x >= box->low.x &&
point->y <= box->high.y && point->y >= box->low.y)
{
/* point inside the box */
result = 0.0;
}
else if (point->x <= box->high.x && point->x >= box->low.x)
{
/* point is over or below box */
Assert(box->low.y <= box->high.y);
if (point->y > box->high.y)
result = point->y - box->high.y;
else if (point->y < box->low.y)
result = box->low.y - point->y;
else
elog(ERROR, "inconsistent point values");
}
else if (point->y <= box->high.y && point->y >= box->low.y)
{
/* point is to left or right of box */
Assert(box->low.x <= box->high.x);
if (point->x > box->high.x)
result = point->x - box->high.x;
else if (point->x < box->low.x)
result = box->low.x - point->x;
else
elog(ERROR, "inconsistent point values");
}
else
{
/* closest point will be a vertex */
Point p;
double subresult;
result = point_point_distance(point, &box->low);
subresult = point_point_distance(point, &box->high);
if (result > subresult)
result = subresult;
p.x = box->low.x;
p.y = box->high.y;
subresult = point_point_distance(point, &p);
if (result > subresult)
result = subresult;
p.x = box->high.x;
p.y = box->low.y;
subresult = point_point_distance(point, &p);
if (result > subresult)
result = subresult;
}
return result;
}
static bool static bool
gist_point_consistent_internal(StrategyNumber strategy, gist_point_consistent_internal(StrategyNumber strategy,
bool isLeaf, BOX *key, Point *query) bool isLeaf, BOX *key, Point *query)
...@@ -954,8 +1024,8 @@ gist_point_consistent(PG_FUNCTION_ARGS) ...@@ -954,8 +1024,8 @@ gist_point_consistent(PG_FUNCTION_ARGS)
{ {
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
bool result;
bool *recheck = (bool *) PG_GETARG_POINTER(4); bool *recheck = (bool *) PG_GETARG_POINTER(4);
bool result;
StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset; StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
switch (strategyGroup) switch (strategyGroup)
...@@ -1034,9 +1104,32 @@ gist_point_consistent(PG_FUNCTION_ARGS) ...@@ -1034,9 +1104,32 @@ gist_point_consistent(PG_FUNCTION_ARGS)
} }
break; break;
default: default:
result = false; /* silence compiler warning */
elog(ERROR, "unknown strategy number: %d", strategy); elog(ERROR, "unknown strategy number: %d", strategy);
result = false; /* keep compiler quiet */
} }
PG_RETURN_BOOL(result); PG_RETURN_BOOL(result);
} }
Datum
gist_point_distance(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
double distance;
StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
switch (strategyGroup)
{
case PointStrategyNumberGroup:
distance = computeDistance(GIST_LEAF(entry),
DatumGetBoxP(entry->key),
PG_GETARG_POINT_P(1));
break;
default:
elog(ERROR, "unknown strategy number: %d", strategy);
distance = 0.0; /* keep compiler quiet */
}
PG_RETURN_FLOAT8(distance);
}
...@@ -20,8 +20,84 @@ ...@@ -20,8 +20,84 @@
#include "access/relscan.h" #include "access/relscan.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h"
static void gistfreestack(GISTSearchStack *s);
/*
* RBTree support functions for the GISTSearchTreeItem queue
*/
static int
GISTSearchTreeItemComparator(const RBNode *a, const RBNode *b, void *arg)
{
const GISTSearchTreeItem *sa = (const GISTSearchTreeItem *) a;
const GISTSearchTreeItem *sb = (const GISTSearchTreeItem *) b;
IndexScanDesc scan = (IndexScanDesc) arg;
int i;
/* Order according to distance comparison */
for (i = 0; i < scan->numberOfOrderBys; i++)
{
if (sa->distances[i] != sb->distances[i])
return (sa->distances[i] > sb->distances[i]) ? 1 : -1;
}
return 0;
}
static void
GISTSearchTreeItemCombiner(RBNode *existing, const RBNode *newrb, void *arg)
{
GISTSearchTreeItem *scurrent = (GISTSearchTreeItem *) existing;
const GISTSearchTreeItem *snew = (const GISTSearchTreeItem *) newrb;
GISTSearchItem *newitem = snew->head;
/* snew should have just one item in its chain */
Assert(newitem && newitem->next == NULL);
/*
* If new item is heap tuple, it goes to front of chain; otherwise insert
* it before the first index-page item, so that index pages are visited
* in LIFO order, ensuring depth-first search of index pages. See
* comments in gist_private.h.
*/
if (GISTSearchItemIsHeap(*newitem))
{
newitem->next = scurrent->head;
scurrent->head = newitem;
if (scurrent->lastHeap == NULL)
scurrent->lastHeap = newitem;
}
else if (scurrent->lastHeap == NULL)
{
newitem->next = scurrent->head;
scurrent->head = newitem;
}
else
{
newitem->next = scurrent->lastHeap->next;
scurrent->lastHeap->next = newitem;
}
}
static RBNode *
GISTSearchTreeItemAllocator(void *arg)
{
IndexScanDesc scan = (IndexScanDesc) arg;
return palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys);
}
static void
GISTSearchTreeItemDeleter(RBNode *rb, void *arg)
{
pfree(rb);
}
/*
* Index AM API functions for scanning GiST indexes
*/
Datum Datum
gistbeginscan(PG_FUNCTION_ARGS) gistbeginscan(PG_FUNCTION_ARGS)
...@@ -32,18 +108,22 @@ gistbeginscan(PG_FUNCTION_ARGS) ...@@ -32,18 +108,22 @@ gistbeginscan(PG_FUNCTION_ARGS)
IndexScanDesc scan; IndexScanDesc scan;
GISTScanOpaque so; GISTScanOpaque so;
/* no order by operators allowed */
Assert(norderbys == 0);
scan = RelationGetIndexScan(r, nkeys, norderbys); scan = RelationGetIndexScan(r, nkeys, norderbys);
/* initialize opaque data */ /* initialize opaque data */
so = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData)); so = (GISTScanOpaque) palloc0(sizeof(GISTScanOpaqueData));
so->stack = NULL; so->queueCxt = AllocSetContextCreate(CurrentMemoryContext,
"GiST queue context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
so->tempCxt = createTempGistContext(); so->tempCxt = createTempGistContext();
so->curbuf = InvalidBuffer;
so->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE)); so->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
initGISTstate(so->giststate, scan->indexRelation); initGISTstate(so->giststate, scan->indexRelation);
/* workspaces with size dependent on numberOfOrderBys: */
so->tmpTreeItem = palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys);
so->distances = palloc(sizeof(double) * scan->numberOfOrderBys);
so->qual_ok = true; /* in case there are zero keys */
scan->opaque = so; scan->opaque = so;
...@@ -55,27 +135,27 @@ gistrescan(PG_FUNCTION_ARGS) ...@@ -55,27 +135,27 @@ gistrescan(PG_FUNCTION_ARGS)
{ {
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
ScanKey key = (ScanKey) PG_GETARG_POINTER(1); ScanKey key = (ScanKey) PG_GETARG_POINTER(1);
/* remaining arguments are ignored */ ScanKey orderbys = (ScanKey) PG_GETARG_POINTER(3);
/* nkeys and norderbys arguments are ignored */
GISTScanOpaque so = (GISTScanOpaque) scan->opaque; GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
int i; int i;
MemoryContext oldCxt;
/* rescan an existing indexscan --- reset state */ /* rescan an existing indexscan --- reset state */
gistfreestack(so->stack); MemoryContextReset(so->queueCxt);
so->stack = NULL; so->curTreeItem = NULL;
/* drop pins on buffers -- no locks held */
if (BufferIsValid(so->curbuf))
{
ReleaseBuffer(so->curbuf);
so->curbuf = InvalidBuffer;
}
/* /* create new, empty RBTree for search queue */
* Clear all the pointers. oldCxt = MemoryContextSwitchTo(so->queueCxt);
*/ so->queue = rb_create(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys,
ItemPointerSetInvalid(&so->curpos); GISTSearchTreeItemComparator,
so->nPageData = so->curPageData = 0; GISTSearchTreeItemCombiner,
GISTSearchTreeItemAllocator,
GISTSearchTreeItemDeleter,
scan);
MemoryContextSwitchTo(oldCxt);
so->qual_ok = true; so->firstCall = true;
/* Update scan key, if a new one is given */ /* Update scan key, if a new one is given */
if (key && scan->numberOfKeys > 0) if (key && scan->numberOfKeys > 0)
...@@ -84,7 +164,7 @@ gistrescan(PG_FUNCTION_ARGS) ...@@ -84,7 +164,7 @@ gistrescan(PG_FUNCTION_ARGS)
scan->numberOfKeys * sizeof(ScanKeyData)); scan->numberOfKeys * sizeof(ScanKeyData));
/* /*
* Modify the scan key so that all the Consistent method is called for * Modify the scan key so that the Consistent method is called for
* all comparisons. The original operator is passed to the Consistent * all comparisons. The original operator is passed to the Consistent
* function in the form of its strategy number, which is available * function in the form of its strategy number, which is available
* from the sk_strategy field, and its subtype from the sk_subtype * from the sk_strategy field, and its subtype from the sk_subtype
...@@ -94,9 +174,11 @@ gistrescan(PG_FUNCTION_ARGS) ...@@ -94,9 +174,11 @@ gistrescan(PG_FUNCTION_ARGS)
* SK_SEARCHNULL/SK_SEARCHNOTNULL then nothing can be found (ie, we * SK_SEARCHNULL/SK_SEARCHNOTNULL then nothing can be found (ie, we
* assume all indexable operators are strict). * assume all indexable operators are strict).
*/ */
so->qual_ok = true;
for (i = 0; i < scan->numberOfKeys; i++) for (i = 0; i < scan->numberOfKeys; i++)
{ {
ScanKey skey = &(scan->keyData[i]); ScanKey skey = scan->keyData + i;
skey->sk_func = so->giststate->consistentFn[skey->sk_attno - 1]; skey->sk_func = so->giststate->consistentFn[skey->sk_attno - 1];
...@@ -108,6 +190,33 @@ gistrescan(PG_FUNCTION_ARGS) ...@@ -108,6 +190,33 @@ gistrescan(PG_FUNCTION_ARGS)
} }
} }
/* Update order-by key, if a new one is given */
if (orderbys && scan->numberOfOrderBys > 0)
{
memmove(scan->orderByData, orderbys,
scan->numberOfOrderBys * sizeof(ScanKeyData));
/*
* Modify the order-by key so that the Distance method is called for
* all comparisons. The original operator is passed to the Distance
* function in the form of its strategy number, which is available
* from the sk_strategy field, and its subtype from the sk_subtype
* field.
*/
for (i = 0; i < scan->numberOfOrderBys; i++)
{
ScanKey skey = scan->orderByData + i;
skey->sk_func = so->giststate->distanceFn[skey->sk_attno - 1];
/* Check we actually have a distance function ... */
if (!OidIsValid(skey->sk_func.fn_oid))
elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
GIST_DISTANCE_PROC, skey->sk_attno,
RelationGetRelationName(scan->indexRelation));
}
}
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
...@@ -131,26 +240,12 @@ gistendscan(PG_FUNCTION_ARGS) ...@@ -131,26 +240,12 @@ gistendscan(PG_FUNCTION_ARGS)
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
GISTScanOpaque so = (GISTScanOpaque) scan->opaque; GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
gistfreestack(so->stack); freeGISTstate(so->giststate);
if (so->giststate != NULL) MemoryContextDelete(so->queueCxt);
freeGISTstate(so->giststate);
/* drop pins on buffers -- we aren't holding any locks */
if (BufferIsValid(so->curbuf))
ReleaseBuffer(so->curbuf);
MemoryContextDelete(so->tempCxt); MemoryContextDelete(so->tempCxt);
pfree(so->tmpTreeItem);
pfree(so->distances);
pfree(so); pfree(so);
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
static void
gistfreestack(GISTSearchStack *s)
{
while (s != NULL)
{
GISTSearchStack *p = s->next;
pfree(s);
s = p;
}
}
...@@ -32,7 +32,8 @@ ...@@ -32,7 +32,8 @@
#define GIST_PENALTY_PROC 5 #define GIST_PENALTY_PROC 5
#define GIST_PICKSPLIT_PROC 6 #define GIST_PICKSPLIT_PROC 6
#define GIST_EQUAL_PROC 7 #define GIST_EQUAL_PROC 7
#define GISTNProcs 7 #define GIST_DISTANCE_PROC 8
#define GISTNProcs 8
/* /*
* strategy numbers for GiST opclasses that want to implement the old * strategy numbers for GiST opclasses that want to implement the old
...@@ -52,6 +53,7 @@ ...@@ -52,6 +53,7 @@
#define RTOverAboveStrategyNumber 12 #define RTOverAboveStrategyNumber 12
#define RTOldContainsStrategyNumber 13 /* for old spelling of @> */ #define RTOldContainsStrategyNumber 13 /* for old spelling of @> */
#define RTOldContainedByStrategyNumber 14 /* for old spelling of <@ */ #define RTOldContainedByStrategyNumber 14 /* for old spelling of <@ */
#define RTKNNSearchStrategyNumber 15
/* /*
* Page opaque data in a GiST index page. * Page opaque data in a GiST index page.
...@@ -131,13 +133,13 @@ typedef struct GISTENTRY ...@@ -131,13 +133,13 @@ typedef struct GISTENTRY
#define GistClearTuplesDeleted(page) ( GistPageGetOpaque(page)->flags &= ~F_TUPLES_DELETED) #define GistClearTuplesDeleted(page) ( GistPageGetOpaque(page)->flags &= ~F_TUPLES_DELETED)
/* /*
* Vector of GISTENTRY structs; user-defined methods union and pick * Vector of GISTENTRY structs; user-defined methods union and picksplit
* split takes it as one of their arguments * take it as one of their arguments
*/ */
typedef struct typedef struct
{ {
int32 n; /* number of elements */ int32 n; /* number of elements */
GISTENTRY vector[1]; GISTENTRY vector[1]; /* variable-length array */
} GistEntryVector; } GistEntryVector;
#define GEVHDRSZ (offsetof(GistEntryVector, vector)) #define GEVHDRSZ (offsetof(GistEntryVector, vector))
......
...@@ -17,34 +17,19 @@ ...@@ -17,34 +17,19 @@
#include "access/gist.h" #include "access/gist.h"
#include "access/itup.h" #include "access/itup.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/rbtree.h"
#define GIST_UNLOCK BUFFER_LOCK_UNLOCK /* Buffer lock modes */
#define GIST_SHARE BUFFER_LOCK_SHARE #define GIST_SHARE BUFFER_LOCK_SHARE
#define GIST_EXCLUSIVE BUFFER_LOCK_EXCLUSIVE #define GIST_EXCLUSIVE BUFFER_LOCK_EXCLUSIVE
#define GIST_UNLOCK BUFFER_LOCK_UNLOCK
/* /*
* XXX old comment!!! * GISTSTATE: information needed for any GiST index operation
* When we descend a tree, we keep a stack of parent pointers. This
* allows us to follow a chain of internal node points until we reach
* a leaf node, and then back up the stack to re-examine the internal
* nodes.
* *
* 'parent' is the previous stack entry -- i.e. the node we arrived * This struct retains call info for the index's opclass-specific support
* from. 'block' is the node's block number. 'offset' is the offset in * functions (per index column), plus the index's tuple descriptor.
* the node's page that we stopped at (i.e. we followed the child
* pointer located at the specified offset).
*/ */
typedef struct GISTSearchStack
{
struct GISTSearchStack *next;
BlockNumber block;
/* to identify page changed */
GistNSN lsn;
/* to recognize split occured */
GistNSN parentlsn;
} GISTSearchStack;
typedef struct GISTSTATE typedef struct GISTSTATE
{ {
FmgrInfo consistentFn[INDEX_MAX_KEYS]; FmgrInfo consistentFn[INDEX_MAX_KEYS];
...@@ -54,38 +39,96 @@ typedef struct GISTSTATE ...@@ -54,38 +39,96 @@ typedef struct GISTSTATE
FmgrInfo penaltyFn[INDEX_MAX_KEYS]; FmgrInfo penaltyFn[INDEX_MAX_KEYS];
FmgrInfo picksplitFn[INDEX_MAX_KEYS]; FmgrInfo picksplitFn[INDEX_MAX_KEYS];
FmgrInfo equalFn[INDEX_MAX_KEYS]; FmgrInfo equalFn[INDEX_MAX_KEYS];
FmgrInfo distanceFn[INDEX_MAX_KEYS];
TupleDesc tupdesc; TupleDesc tupdesc;
} GISTSTATE; } GISTSTATE;
typedef struct ItemResult
/*
* During a GiST index search, we must maintain a queue of unvisited items,
* which can be either individual heap tuples or whole index pages. If it
* is an ordered search, the unvisited items should be visited in distance
* order. Unvisited items at the same distance should be visited in
* depth-first order, that is heap items first, then lower index pages, then
* upper index pages; this rule avoids doing extra work during a search that
* ends early due to LIMIT.
*
* To perform an ordered search, we use an RBTree to manage the distance-order
* queue. Each GISTSearchTreeItem stores all unvisited items of the same
* distance; they are GISTSearchItems chained together via their next fields.
*
* In a non-ordered search (no order-by operators), the RBTree degenerates
* to a single item, which we use as a queue of unvisited index pages only.
* In this case matched heap items from the current index leaf page are
* remembered in GISTScanOpaqueData.pageData[] and returned directly from
* there, instead of building a separate GISTSearchItem for each one.
*/
/* Individual heap tuple to be visited */
typedef struct GISTSearchHeapItem
{ {
ItemPointerData heapPtr; ItemPointerData heapPtr;
OffsetNumber pageOffset; /* offset in index page */ bool recheck; /* T if quals must be rechecked */
bool recheck; } GISTSearchHeapItem;
} ItemResult;
/* Unvisited item, either index page or heap tuple */
typedef struct GISTSearchItem
{
struct GISTSearchItem *next; /* list link */
BlockNumber blkno; /* index page number, or InvalidBlockNumber */
union
{
GistNSN parentlsn; /* parent page's LSN, if index page */
/* we must store parentlsn to detect whether a split occurred */
GISTSearchHeapItem heap; /* heap info, if heap tuple */
} data;
} GISTSearchItem;
#define GISTSearchItemIsHeap(item) ((item).blkno == InvalidBlockNumber)
/* /*
* When we're doing a scan, we need to keep track of the parent stack * Within a GISTSearchTreeItem's chain, heap items always appear before
* for the marked and current items. * index-page items, since we want to visit heap items first. lastHeap points
* to the last heap item in the chain, or is NULL if there are none.
*/
typedef struct GISTSearchTreeItem
{
RBNode rbnode; /* this is an RBTree item */
GISTSearchItem *head; /* first chain member */
GISTSearchItem *lastHeap; /* last heap-tuple member, if any */
double distances[1]; /* array with numberOfOrderBys entries */
} GISTSearchTreeItem;
#define GSTIHDRSZ offsetof(GISTSearchTreeItem, distances)
/*
* GISTScanOpaqueData: private state for a scan of a GiST index
*/ */
typedef struct GISTScanOpaqueData typedef struct GISTScanOpaqueData
{ {
GISTSearchStack *stack; GISTSTATE *giststate; /* index information, see above */
GISTSearchStack *markstk; RBTree *queue; /* queue of unvisited items */
MemoryContext queueCxt; /* context holding the queue */
MemoryContext tempCxt; /* workspace context for calling functions */
bool qual_ok; /* false if qual can never be satisfied */ bool qual_ok; /* false if qual can never be satisfied */
GISTSTATE *giststate; bool firstCall; /* true until first gistgettuple call */
MemoryContext tempCxt;
Buffer curbuf; GISTSearchTreeItem *curTreeItem; /* current queue item, if any */
ItemPointerData curpos;
/* pre-allocated workspace arrays */
ItemResult pageData[BLCKSZ / sizeof(IndexTupleData)]; GISTSearchTreeItem *tmpTreeItem; /* workspace to pass to rb_insert */
OffsetNumber nPageData; double *distances; /* workspace for computeKeyTupleDistance */
OffsetNumber curPageData;
/* In a non-ordered search, returnable heap items are stored here: */
GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
OffsetNumber nPageData; /* number of valid items in array */
OffsetNumber curPageData; /* next item to return */
} GISTScanOpaqueData; } GISTScanOpaqueData;
typedef GISTScanOpaqueData *GISTScanOpaque; typedef GISTScanOpaqueData *GISTScanOpaque;
/* XLog stuff */ /* XLog stuff */
#define XLOG_GIST_PAGE_UPDATE 0x00 #define XLOG_GIST_PAGE_UPDATE 0x00
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201012021 #define CATALOG_VERSION_NO 201012031
#endif #endif
...@@ -117,7 +117,7 @@ DESCR("b-tree index access method"); ...@@ -117,7 +117,7 @@ DESCR("b-tree index access method");
DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions )); DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
DESCR("hash index access method"); DESCR("hash index access method");
#define HASH_AM_OID 405 #define HASH_AM_OID 405
DATA(insert OID = 783 ( gist 0 7 f f f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions )); DATA(insert OID = 783 ( gist 0 8 f t f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
DESCR("GiST index access method"); DESCR("GiST index access method");
#define GIST_AM_OID 783 #define GIST_AM_OID 783
DATA(insert OID = 2742 ( gin 0 5 f f f f t t f f t f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions )); DATA(insert OID = 2742 ( gin 0 5 f f f f t t f f t f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
......
...@@ -604,6 +604,7 @@ DATA(insert ( 1029 600 600 1 s 507 783 0 )); ...@@ -604,6 +604,7 @@ DATA(insert ( 1029 600 600 1 s 507 783 0 ));
DATA(insert ( 1029 600 600 5 s 508 783 0 )); DATA(insert ( 1029 600 600 5 s 508 783 0 ));
DATA(insert ( 1029 600 600 10 s 509 783 0 )); DATA(insert ( 1029 600 600 10 s 509 783 0 ));
DATA(insert ( 1029 600 600 6 s 510 783 0 )); DATA(insert ( 1029 600 600 6 s 510 783 0 ));
DATA(insert ( 1029 600 600 15 o 517 783 1970 ));
DATA(insert ( 1029 603 600 27 s 433 783 0 )); DATA(insert ( 1029 603 600 27 s 433 783 0 ));
DATA(insert ( 1029 600 603 28 s 511 783 0 )); DATA(insert ( 1029 600 603 28 s 511 783 0 ));
DATA(insert ( 1029 604 600 47 s 757 783 0 )); DATA(insert ( 1029 604 600 47 s 757 783 0 ));
......
...@@ -205,6 +205,7 @@ DATA(insert ( 1029 600 600 4 2580 )); ...@@ -205,6 +205,7 @@ DATA(insert ( 1029 600 600 4 2580 ));
DATA(insert ( 1029 600 600 5 2581 )); DATA(insert ( 1029 600 600 5 2581 ));
DATA(insert ( 1029 600 600 6 2582 )); DATA(insert ( 1029 600 600 6 2582 ));
DATA(insert ( 1029 600 600 7 2584 )); DATA(insert ( 1029 600 600 7 2584 ));
DATA(insert ( 1029 600 600 8 3064 ));
/* gin */ /* gin */
......
...@@ -4325,7 +4325,9 @@ DATA(insert OID = 2592 ( gist_circle_compress PGNSP PGUID 12 1 0 0 f f f t f i ...@@ -4325,7 +4325,9 @@ DATA(insert OID = 2592 ( gist_circle_compress PGNSP PGUID 12 1 0 0 f f f t f i
DESCR("GiST support"); DESCR("GiST support");
DATA(insert OID = 1030 ( gist_point_compress PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_compress _null_ _null_ _null_ )); DATA(insert OID = 1030 ( gist_point_compress PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_compress _null_ _null_ _null_ ));
DESCR("GiST support"); DESCR("GiST support");
DATA(insert OID = 2179 ( gist_point_consistent PGNSP PGUID 12 1 0 0 f f f t f i 5 0 16 "2281 603 23 26 2281" _null_ _null_ _null_ _null_ gist_point_consistent _null_ _null_ _null_ )); DATA(insert OID = 2179 ( gist_point_consistent PGNSP PGUID 12 1 0 0 f f f t f i 5 0 16 "2281 600 23 26 2281" _null_ _null_ _null_ _null_ gist_point_consistent _null_ _null_ _null_ ));
DESCR("GiST support");
DATA(insert OID = 3064 ( gist_point_distance PGNSP PGUID 12 1 0 0 f f f t f i 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_ gist_point_distance _null_ _null_ _null_ ));
DESCR("GiST support"); DESCR("GiST support");
/* GIN */ /* GIN */
......
...@@ -424,6 +424,7 @@ extern Datum gist_circle_compress(PG_FUNCTION_ARGS); ...@@ -424,6 +424,7 @@ extern Datum gist_circle_compress(PG_FUNCTION_ARGS);
extern Datum gist_circle_consistent(PG_FUNCTION_ARGS); extern Datum gist_circle_consistent(PG_FUNCTION_ARGS);
extern Datum gist_point_compress(PG_FUNCTION_ARGS); extern Datum gist_point_compress(PG_FUNCTION_ARGS);
extern Datum gist_point_consistent(PG_FUNCTION_ARGS); extern Datum gist_point_consistent(PG_FUNCTION_ARGS);
extern Datum gist_point_distance(PG_FUNCTION_ARGS);
/* geo_selfuncs.c */ /* geo_selfuncs.c */
extern Datum areasel(PG_FUNCTION_ARGS); extern Datum areasel(PG_FUNCTION_ARGS);
......
...@@ -51,6 +51,7 @@ CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops) ...@@ -51,6 +51,7 @@ CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops)
CREATE INDEX grect2ind ON fast_emp4000 USING gist (home_base); CREATE INDEX grect2ind ON fast_emp4000 USING gist (home_base);
CREATE INDEX gpolygonind ON polygon_tbl USING gist (f1); CREATE INDEX gpolygonind ON polygon_tbl USING gist (f1);
CREATE INDEX gcircleind ON circle_tbl USING gist (f1); CREATE INDEX gcircleind ON circle_tbl USING gist (f1);
INSERT INTO POINT_TBL(f1) VALUES (NULL);
CREATE INDEX gpointind ON point_tbl USING gist (f1); CREATE INDEX gpointind ON point_tbl USING gist (f1);
CREATE TEMP TABLE gpolygon_tbl AS CREATE TEMP TABLE gpolygon_tbl AS
SELECT polygon(home_base) AS f1 FROM slow_emp4000; SELECT polygon(home_base) AS f1 FROM slow_emp4000;
...@@ -60,6 +61,7 @@ CREATE TEMP TABLE gcircle_tbl AS ...@@ -60,6 +61,7 @@ CREATE TEMP TABLE gcircle_tbl AS
SELECT circle(home_base) AS f1 FROM slow_emp4000; SELECT circle(home_base) AS f1 FROM slow_emp4000;
CREATE INDEX ggpolygonind ON gpolygon_tbl USING gist (f1); CREATE INDEX ggpolygonind ON gpolygon_tbl USING gist (f1);
CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1); CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1);
-- get non-indexed results for comparison purposes
SET enable_seqscan = ON; SET enable_seqscan = ON;
SET enable_indexscan = OFF; SET enable_indexscan = OFF;
SET enable_bitmapscan = OFF; SET enable_bitmapscan = OFF;
...@@ -167,6 +169,44 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; ...@@ -167,6 +169,44 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
1 1
(1 row) (1 row)
SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
f1
------------
(0,0)
(-3,4)
(-10,0)
(10,10)
(-5,-12)
(5.1,34.5)
(7 rows)
SELECT * FROM point_tbl WHERE f1 IS NULL;
f1
----
(1 row)
SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
f1
------------
(0,0)
(-3,4)
(-10,0)
(10,10)
(-5,-12)
(5.1,34.5)
(6 rows)
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
f1
---------
(0,0)
(-3,4)
(-10,0)
(10,10)
(4 rows)
SET enable_seqscan = OFF; SET enable_seqscan = OFF;
SET enable_indexscan = ON; SET enable_indexscan = ON;
SET enable_bitmapscan = ON; SET enable_bitmapscan = ON;
...@@ -435,6 +475,102 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; ...@@ -435,6 +475,102 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
1 1
(1 row) (1 row)
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
QUERY PLAN
-----------------------------------------
Index Scan using gpointind on point_tbl
Order By: (f1 <-> '(0,1)'::point)
(2 rows)
SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
f1
------------
(0,0)
(-3,4)
(-10,0)
(10,10)
(-5,-12)
(5.1,34.5)
(7 rows)
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl WHERE f1 IS NULL;
QUERY PLAN
-----------------------------------------
Index Scan using gpointind on point_tbl
Index Cond: (f1 IS NULL)
(2 rows)
SELECT * FROM point_tbl WHERE f1 IS NULL;
f1
----
(1 row)
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
QUERY PLAN
-----------------------------------------
Index Scan using gpointind on point_tbl
Index Cond: (f1 IS NOT NULL)
Order By: (f1 <-> '(0,1)'::point)
(3 rows)
SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
f1
------------
(0,0)
(-3,4)
(-10,0)
(10,10)
(-5,-12)
(5.1,34.5)
(6 rows)
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
QUERY PLAN
------------------------------------------------
Index Scan using gpointind on point_tbl
Index Cond: (f1 <@ '(10,10),(-10,-10)'::box)
Order By: (f1 <-> '(0,1)'::point)
(3 rows)
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
f1
---------
(0,0)
(-3,4)
(-10,0)
(10,10)
(4 rows)
SET enable_seqscan = OFF;
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
QUERY PLAN
------------------------------------------------------------
Sort
Sort Key: ((f1 <-> '(0,1)'::point))
-> Bitmap Heap Scan on point_tbl
Recheck Cond: (f1 <@ '(10,10),(-10,-10)'::box)
-> Bitmap Index Scan on gpointind
Index Cond: (f1 <@ '(10,10),(-10,-10)'::box)
(6 rows)
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
f1
---------
(0,0)
(-3,4)
(-10,0)
(10,10)
(4 rows)
RESET enable_seqscan; RESET enable_seqscan;
RESET enable_indexscan; RESET enable_indexscan;
RESET enable_bitmapscan; RESET enable_bitmapscan;
......
...@@ -991,6 +991,7 @@ ORDER BY 1, 2, 3; ...@@ -991,6 +991,7 @@ ORDER BY 1, 2, 3;
783 | 12 | |&> 783 | 12 | |&>
783 | 13 | ~ 783 | 13 | ~
783 | 14 | @ 783 | 14 | @
783 | 15 | <->
783 | 27 | @> 783 | 27 | @>
783 | 28 | <@ 783 | 28 | <@
783 | 47 | @> 783 | 47 | @>
...@@ -1003,7 +1004,7 @@ ORDER BY 1, 2, 3; ...@@ -1003,7 +1004,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@ 2742 | 2 | @@@
2742 | 3 | <@ 2742 | 3 | <@
2742 | 4 | = 2742 | 4 | =
(39 rows) (40 rows)
-- Check that all opclass search operators have selectivity estimators. -- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing -- This is not absolutely required, but it seems a reasonable thing
...@@ -1136,11 +1137,11 @@ WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND ...@@ -1136,11 +1137,11 @@ WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
-- Detect missing pg_amproc entries: should have as many support functions -- Detect missing pg_amproc entries: should have as many support functions
-- as AM expects for each datatype combination supported by the opfamily. -- as AM expects for each datatype combination supported by the opfamily.
-- GIN is a special case because it has an optional support function. -- GIST/GIN are special cases because each has an optional support function.
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3 FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
p1.amname <> 'gin' AND p1.amname <> 'gist' AND p1.amname <> 'gin' AND
p1.amsupport != (SELECT count(*) FROM pg_amproc AS p4 p1.amsupport != (SELECT count(*) FROM pg_amproc AS p4
WHERE p4.amprocfamily = p2.oid AND WHERE p4.amprocfamily = p2.oid AND
p4.amproclefttype = p3.amproclefttype AND p4.amproclefttype = p3.amproclefttype AND
...@@ -1149,26 +1150,27 @@ WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND ...@@ -1149,26 +1150,27 @@ WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
--------+---------+----------------+----------------- --------+---------+----------------+-----------------
(0 rows) (0 rows)
-- Similar check for GIN, allowing one optional proc -- Similar check for GIST/GIN, allowing one optional proc
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3 FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
p1.amname = 'gin' AND (p1.amname = 'gist' OR p1.amname = 'gin') AND
p1.amsupport - 1 > (SELECT count(*) FROM pg_amproc AS p4 (SELECT count(*) FROM pg_amproc AS p4
WHERE p4.amprocfamily = p2.oid AND WHERE p4.amprocfamily = p2.oid AND
p4.amproclefttype = p3.amproclefttype AND p4.amproclefttype = p3.amproclefttype AND
p4.amprocrighttype = p3.amprocrighttype); p4.amprocrighttype = p3.amprocrighttype)
NOT IN (p1.amsupport, p1.amsupport - 1);
amname | opfname | amproclefttype | amprocrighttype amname | opfname | amproclefttype | amprocrighttype
--------+---------+----------------+----------------- --------+---------+----------------+-----------------
(0 rows) (0 rows)
-- Also, check if there are any pg_opclass entries that don't seem to have -- Also, check if there are any pg_opclass entries that don't seem to have
-- pg_amproc support. Again, GIN has to be checked separately. -- pg_amproc support. Again, GIST/GIN have to be checked specially.
SELECT amname, opcname, count(*) SELECT amname, opcname, count(*)
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
amproclefttype = amprocrighttype AND amproclefttype = opcintype amproclefttype = amprocrighttype AND amproclefttype = opcintype
WHERE am.amname <> 'gin' WHERE am.amname <> 'gist' AND am.amname <> 'gin'
GROUP BY amname, amsupport, opcname, amprocfamily GROUP BY amname, amsupport, opcname, amprocfamily
HAVING count(*) != amsupport OR amprocfamily IS NULL; HAVING count(*) != amsupport OR amprocfamily IS NULL;
amname | opcname | count amname | opcname | count
...@@ -1179,9 +1181,10 @@ SELECT amname, opcname, count(*) ...@@ -1179,9 +1181,10 @@ SELECT amname, opcname, count(*)
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
amproclefttype = amprocrighttype AND amproclefttype = opcintype amproclefttype = amprocrighttype AND amproclefttype = opcintype
WHERE am.amname = 'gin' WHERE am.amname = 'gist' OR am.amname = 'gin'
GROUP BY amname, amsupport, opcname, amprocfamily GROUP BY amname, amsupport, opcname, amprocfamily
HAVING count(*) < amsupport - 1 OR amprocfamily IS NULL; HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
OR amprocfamily IS NULL;
amname | opcname | count amname | opcname | count
--------+---------+------- --------+---------+-------
(0 rows) (0 rows)
......
...@@ -76,6 +76,8 @@ CREATE INDEX gpolygonind ON polygon_tbl USING gist (f1); ...@@ -76,6 +76,8 @@ CREATE INDEX gpolygonind ON polygon_tbl USING gist (f1);
CREATE INDEX gcircleind ON circle_tbl USING gist (f1); CREATE INDEX gcircleind ON circle_tbl USING gist (f1);
INSERT INTO POINT_TBL(f1) VALUES (NULL);
CREATE INDEX gpointind ON point_tbl USING gist (f1); CREATE INDEX gpointind ON point_tbl USING gist (f1);
CREATE TEMP TABLE gpolygon_tbl AS CREATE TEMP TABLE gpolygon_tbl AS
...@@ -90,6 +92,8 @@ CREATE INDEX ggpolygonind ON gpolygon_tbl USING gist (f1); ...@@ -90,6 +92,8 @@ CREATE INDEX ggpolygonind ON gpolygon_tbl USING gist (f1);
CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1); CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1);
-- get non-indexed results for comparison purposes
SET enable_seqscan = ON; SET enable_seqscan = ON;
SET enable_indexscan = OFF; SET enable_indexscan = OFF;
SET enable_bitmapscan = OFF; SET enable_bitmapscan = OFF;
...@@ -130,6 +134,14 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)'; ...@@ -130,6 +134,14 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
SELECT * FROM point_tbl WHERE f1 IS NULL;
SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
SET enable_seqscan = OFF; SET enable_seqscan = OFF;
SET enable_indexscan = ON; SET enable_indexscan = ON;
SET enable_bitmapscan = ON; SET enable_bitmapscan = ON;
...@@ -206,6 +218,30 @@ EXPLAIN (COSTS OFF) ...@@ -206,6 +218,30 @@ EXPLAIN (COSTS OFF)
SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl WHERE f1 IS NULL;
SELECT * FROM point_tbl WHERE f1 IS NULL;
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
SET enable_seqscan = OFF;
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
EXPLAIN (COSTS OFF)
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
RESET enable_seqscan; RESET enable_seqscan;
RESET enable_indexscan; RESET enable_indexscan;
RESET enable_bitmapscan; RESET enable_bitmapscan;
......
...@@ -888,36 +888,37 @@ WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND ...@@ -888,36 +888,37 @@ WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
-- Detect missing pg_amproc entries: should have as many support functions -- Detect missing pg_amproc entries: should have as many support functions
-- as AM expects for each datatype combination supported by the opfamily. -- as AM expects for each datatype combination supported by the opfamily.
-- GIN is a special case because it has an optional support function. -- GIST/GIN are special cases because each has an optional support function.
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3 FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
p1.amname <> 'gin' AND p1.amname <> 'gist' AND p1.amname <> 'gin' AND
p1.amsupport != (SELECT count(*) FROM pg_amproc AS p4 p1.amsupport != (SELECT count(*) FROM pg_amproc AS p4
WHERE p4.amprocfamily = p2.oid AND WHERE p4.amprocfamily = p2.oid AND
p4.amproclefttype = p3.amproclefttype AND p4.amproclefttype = p3.amproclefttype AND
p4.amprocrighttype = p3.amprocrighttype); p4.amprocrighttype = p3.amprocrighttype);
-- Similar check for GIN, allowing one optional proc -- Similar check for GIST/GIN, allowing one optional proc
SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3 FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
p1.amname = 'gin' AND (p1.amname = 'gist' OR p1.amname = 'gin') AND
p1.amsupport - 1 > (SELECT count(*) FROM pg_amproc AS p4 (SELECT count(*) FROM pg_amproc AS p4
WHERE p4.amprocfamily = p2.oid AND WHERE p4.amprocfamily = p2.oid AND
p4.amproclefttype = p3.amproclefttype AND p4.amproclefttype = p3.amproclefttype AND
p4.amprocrighttype = p3.amprocrighttype); p4.amprocrighttype = p3.amprocrighttype)
NOT IN (p1.amsupport, p1.amsupport - 1);
-- Also, check if there are any pg_opclass entries that don't seem to have -- Also, check if there are any pg_opclass entries that don't seem to have
-- pg_amproc support. Again, GIN has to be checked separately. -- pg_amproc support. Again, GIST/GIN have to be checked specially.
SELECT amname, opcname, count(*) SELECT amname, opcname, count(*)
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
amproclefttype = amprocrighttype AND amproclefttype = opcintype amproclefttype = amprocrighttype AND amproclefttype = opcintype
WHERE am.amname <> 'gin' WHERE am.amname <> 'gist' AND am.amname <> 'gin'
GROUP BY amname, amsupport, opcname, amprocfamily GROUP BY amname, amsupport, opcname, amprocfamily
HAVING count(*) != amsupport OR amprocfamily IS NULL; HAVING count(*) != amsupport OR amprocfamily IS NULL;
...@@ -925,9 +926,10 @@ SELECT amname, opcname, count(*) ...@@ -925,9 +926,10 @@ SELECT amname, opcname, count(*)
FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
amproclefttype = amprocrighttype AND amproclefttype = opcintype amproclefttype = amprocrighttype AND amproclefttype = opcintype
WHERE am.amname = 'gin' WHERE am.amname = 'gist' OR am.amname = 'gin'
GROUP BY amname, amsupport, opcname, amprocfamily GROUP BY amname, amsupport, opcname, amprocfamily
HAVING count(*) < amsupport - 1 OR amprocfamily IS NULL; HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
OR amprocfamily IS NULL;
-- Unfortunately, we can't check the amproc link very well because the -- Unfortunately, we can't check the amproc link very well because the
-- signature of the function may be different for different support routines -- signature of the function may be different for different support routines
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment