* request it. recheck is not interesting when examining a non-leaf entry,
* since we must visit the lower index page if there's any doubt.
*
- * If we are doing an ordered scan, so->distances[] is filled with distance
- * data from the distance() functions before returning success.
+ * If we are doing an ordered scan, so->distancesValues[] and
+ * so->distancesNulls[] is filled with distance data from the distance()
+ * functions before returning success.
*
* We must decompress the key in the IndexTuple before passing it to the
* sk_funcs (which actually are the opclass Consistent or Distance methods).
GISTSTATE *giststate = so->giststate;
ScanKey key = scan->keyData;
int keySize = scan->numberOfKeys;
- double *distance_p;
+ double *distance_value_p;
+ bool *distance_null_p;
Relation r = scan->indexRelation;
*recheck_p = false;
if (GistPageIsLeaf(page)) /* shouldn't happen */
elog(ERROR, "invalid GiST tuple found on leaf page");
for (i = 0; i < scan->numberOfOrderBys; i++)
- so->distances[i] = -get_float8_infinity();
+ {
+ so->distanceValues[i] = -get_float8_infinity();
+ so->distanceNulls[i] = false;
+ }
return true;
}
/* OK, it passes --- now let's compute the distances */
key = scan->orderByData;
- distance_p = so->distances;
+ distance_value_p = so->distanceValues;
+ distance_null_p = so->distanceNulls;
keySize = scan->numberOfOrderBys;
while (keySize > 0)
{
if ((key->sk_flags & SK_ISNULL) || isNull)
{
- /* Assume distance computes as null and sorts to the end */
- *distance_p = get_float8_infinity();
+ /* Assume distance computes as null */
+ *distance_value_p = 0.0;
+ *distance_null_p = true;
}
else
{
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
- Int32GetDatum(key->sk_strategy),
+ Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype));
-
- *distance_p = DatumGetFloat8(dist);
+ *distance_value_p = DatumGetFloat8(dist);
+ *distance_null_p = false;
}
key++;
- distance_p++;
+ distance_value_p++;
+ distance_null_p++;
keySize--;
}
*
* scan: index scan we are executing
* pageItem: search queue item identifying an index page to scan
- * myDistances: distances array associated with pageItem, or NULL at the root
+ * myDistanceValues: distances array associated with pageItem, or NULL at the root
+ * myDistanceNulls: null flags for myDistanceValues array, or NULL at the root
* tbm: if not NULL, gistgetbitmap's output bitmap
* ntids: if not NULL, gistgetbitmap's output tuple counter
*
* sibling will be processed next.
*/
static void
-gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
+gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem,
+ double *myDistanceValues, bool *myDistanceNulls,
TIDBitmap *tbm, int64 *ntids)
{
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
GISTSearchItem *item;
/* This can't happen when starting at the root */
- Assert(myDistances != NULL);
+ Assert(myDistanceValues != NULL && myDistanceNulls != NULL);
oldcxt = MemoryContextSwitchTo(so->queueCxt);
/* Insert it into the queue using same distances as for this page */
tmpItem->head = item;
tmpItem->lastHeap = NULL;
- memcpy(tmpItem->distances, myDistances,
- sizeof(double) * scan->numberOfOrderBys);
+ memcpy(GISTSearchTreeItemDistanceValues(tmpItem, scan->numberOfOrderBys),
+ myDistanceValues, sizeof(double) * scan->numberOfOrderBys);
+ memcpy(GISTSearchTreeItemDistanceNulls(tmpItem, scan->numberOfOrderBys),
+ myDistanceNulls, sizeof(bool) * scan->numberOfOrderBys);
(void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew);
* search.
*/
GISTSearchItem *item;
+ int nOrderBys = scan->numberOfOrderBys;
oldcxt = MemoryContextSwitchTo(so->queueCxt);
/* Insert it into the queue using new distance data */
tmpItem->head = item;
tmpItem->lastHeap = GISTSearchItemIsHeap(*item) ? item : NULL;
- memcpy(tmpItem->distances, so->distances,
- sizeof(double) * scan->numberOfOrderBys);
+ memcpy(GISTSearchTreeItemDistanceValues(tmpItem, nOrderBys),
+ so->distanceValues, sizeof(double) * nOrderBys);
+ memcpy(GISTSearchTreeItemDistanceNulls(tmpItem, nOrderBys),
+ so->distanceNulls, sizeof(bool) * nOrderBys);
(void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew);
/* visit an index page, extract its items into queue */
CHECK_FOR_INTERRUPTS();
- gistScanPage(scan, item, so->curTreeItem->distances, NULL, NULL);
+ gistScanPage(scan, item,
+ GISTSearchTreeItemDistanceValues(so->curTreeItem, scan->numberOfOrderBys),
+ GISTSearchTreeItemDistanceNulls(so->curTreeItem, scan->numberOfOrderBys),
+ NULL, NULL);
}
pfree(item);
fakeItem.blkno = GIST_ROOT_BLKNO;
memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
- gistScanPage(scan, &fakeItem, NULL, NULL, NULL);
+ gistScanPage(scan, &fakeItem, NULL, NULL, NULL, NULL);
}
if (scan->numberOfOrderBys > 0)
* this page, we fall out of the inner "do" and loop around to
* return them.
*/
- gistScanPage(scan, item, so->curTreeItem->distances, NULL, NULL);
+ gistScanPage(scan, item,
+ GISTSearchTreeItemDistanceValues(so->curTreeItem, scan->numberOfOrderBys),
+ GISTSearchTreeItemDistanceNulls(so->curTreeItem, scan->numberOfOrderBys),
+ NULL, NULL);
pfree(item);
} while (so->nPageData == 0);
fakeItem.blkno = GIST_ROOT_BLKNO;
memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
- gistScanPage(scan, &fakeItem, NULL, tbm, &ntids);
+ gistScanPage(scan, &fakeItem, NULL, NULL, tbm, &ntids);
/*
* While scanning a leaf page, ItemPointers of matching heap tuples will
CHECK_FOR_INTERRUPTS();
- gistScanPage(scan, item, so->curTreeItem->distances, tbm, &ntids);
+ gistScanPage(scan, item,
+ GISTSearchTreeItemDistanceValues(so->curTreeItem, scan->numberOfOrderBys),
+ GISTSearchTreeItemDistanceNulls(so->curTreeItem, scan->numberOfOrderBys),
+ tbm, &ntids);
pfree(item);
}
const GISTSearchTreeItem *sb = (const GISTSearchTreeItem *) b;
IndexScanDesc scan = (IndexScanDesc) arg;
int i;
+ double *da = GISTSearchTreeItemDistanceValues(sa, scan->numberOfOrderBys),
+ *db = GISTSearchTreeItemDistanceValues(sb, scan->numberOfOrderBys);
+ bool *na = GISTSearchTreeItemDistanceNulls(sa, scan->numberOfOrderBys),
+ *nb = GISTSearchTreeItemDistanceNulls(sb, scan->numberOfOrderBys);
/* Order according to distance comparison */
for (i = 0; i < scan->numberOfOrderBys; i++)
{
- int cmp = float8_cmp_internal(sa->distances[i], sb->distances[i]);
+ if (na[i])
+ {
+ if (!nb[i])
+ return 1;
+ }
+ else if (nb[i])
+ {
+ return -1;
+ }
+ else
+ {
+ int cmp = float8_cmp_internal(da[i], db[i]);
- if (cmp != 0)
- return cmp;
+ if (cmp != 0)
+ return cmp;
+ }
}
return 0;
{
IndexScanDesc scan = (IndexScanDesc) arg;
- return palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys);
+ return palloc(SizeOfGISTSearchTreeItem(scan->numberOfOrderBys));
}
static void
so->queueCxt = giststate->scanCxt; /* see gistrescan */
/* workspaces with size dependent on numberOfOrderBys: */
- so->tmpTreeItem = palloc(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys);
- so->distances = palloc(sizeof(double) * scan->numberOfOrderBys);
+ so->tmpTreeItem = palloc(SizeOfGISTSearchTreeItem(scan->numberOfOrderBys));
+ so->distanceValues = palloc(sizeof(double) * scan->numberOfOrderBys);
+ so->distanceNulls = palloc(sizeof(bool) * scan->numberOfOrderBys);
so->qual_ok = true; /* in case there are zero keys */
scan->opaque = so;
/* create new, empty RBTree for search queue */
oldCxt = MemoryContextSwitchTo(so->queueCxt);
- so->queue = rb_create(GSTIHDRSZ + sizeof(double) * scan->numberOfOrderBys,
+ so->queue = rb_create(SizeOfGISTSearchTreeItem(scan->numberOfOrderBys),
GISTSearchTreeItemComparator,
GISTSearchTreeItemCombiner,
GISTSearchTreeItemAllocator,
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 */
+
+ /*
+ * This data structure is followed by arrays of distance values and
+ * distance null flags. Size of both arrays is
+ * IndexScanDesc->numberOfOrderBys. See macros below for accessing those
+ * arrays.
+ */
} GISTSearchTreeItem;
-#define GSTIHDRSZ offsetof(GISTSearchTreeItem, distances)
+#define SizeOfGISTSearchTreeItem(n_distances) (DOUBLEALIGN(sizeof(GISTSearchTreeItem)) + \
+ (sizeof(double) + sizeof(bool)) * (n_distances))
+
+/*
+ * We actually don't need n_distances compute pointer to distance values.
+ * Nevertheless take n_distances as argument to have same arguments list for
+ * GISTSearchItemDistanceValues() and GISTSearchItemDistanceNulls().
+ */
+#define GISTSearchTreeItemDistanceValues(item, n_distances) \
+ ((double *) ((Pointer) (item) + DOUBLEALIGN(sizeof(GISTSearchTreeItem))))
+
+#define GISTSearchTreeItemDistanceNulls(item, n_distances) \
+ ((bool *) ((Pointer) (item) + DOUBLEALIGN(sizeof(GISTSearchTreeItem)) + sizeof(double) * (n_distances)))
/*
* GISTScanOpaqueData: private state for a scan of a GiST index
/* pre-allocated workspace arrays */
GISTSearchTreeItem *tmpTreeItem; /* workspace to pass to rb_insert */
- double *distances; /* output area for gistindex_keytest */
+ double *distanceValues; /* output area for gistindex_keytest */
+ bool *distanceNulls;
/* In a non-ordered search, returnable heap items are stored here: */
GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];