diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index 95ac35e3bb..db633a9475 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -112,8 +112,9 @@ gistkillitems(IndexScanDesc scan) * Similarly, *recheck_distances_p is set to indicate whether the distances * need to be rechecked, and it is also ignored for non-leaf entries. * - * 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). @@ -134,7 +135,8 @@ gistindex_keytest(IndexScanDesc scan, 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; @@ -152,7 +154,10 @@ gistindex_keytest(IndexScanDesc scan, 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; } @@ -235,7 +240,8 @@ gistindex_keytest(IndexScanDesc scan, /* 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) { @@ -249,8 +255,9 @@ gistindex_keytest(IndexScanDesc scan, 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 { @@ -287,11 +294,13 @@ gistindex_keytest(IndexScanDesc scan, ObjectIdGetDatum(key->sk_subtype), PointerGetDatum(&recheck)); *recheck_distances_p |= recheck; - *distance_p = DatumGetFloat8(dist); + *distance_value_p = DatumGetFloat8(dist); + *distance_null_p = false; } key++; - distance_p++; + distance_value_p++; + distance_null_p++; keySize--; } @@ -304,7 +313,8 @@ gistindex_keytest(IndexScanDesc scan, * * 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 * @@ -321,7 +331,8 @@ gistindex_keytest(IndexScanDesc scan, * 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; @@ -359,7 +370,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, GISTSearchItem *item; /* This can't happen when starting at the root */ - Assert(myDistances != NULL); + Assert(myDistanceValues != NULL && myDistanceNulls != NULL); oldcxt = MemoryContextSwitchTo(so->queueCxt); @@ -369,8 +380,10 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, item->data.parentlsn = pageItem->data.parentlsn; /* Insert it into the queue using same distances as for this page */ - memcpy(item->distances, myDistances, - sizeof(double) * scan->numberOfOrderBys); + memcpy(GISTSearchItemDistanceValues(item, scan->numberOfOrderBys), + myDistanceValues, sizeof(double) * scan->numberOfOrderBys); + memcpy(GISTSearchItemDistanceNulls(item, scan->numberOfOrderBys), + myDistanceNulls, sizeof(bool) * scan->numberOfOrderBys); pairingheap_add(so->queue, &item->phNode); @@ -479,6 +492,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, * search. */ GISTSearchItem *item; + int nOrderBys = scan->numberOfOrderBys; oldcxt = MemoryContextSwitchTo(so->queueCxt); @@ -513,8 +527,10 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, } /* Insert it into the queue using new distance data */ - memcpy(item->distances, so->distances, - sizeof(double) * scan->numberOfOrderBys); + memcpy(GISTSearchItemDistanceValues(item, nOrderBys), + so->distanceValues, sizeof(double) * nOrderBys); + memcpy(GISTSearchItemDistanceNulls(item, nOrderBys), + so->distanceNulls, sizeof(bool) * nOrderBys); pairingheap_add(so->queue, &item->phNode); @@ -579,7 +595,8 @@ getNextNearest(IndexScanDesc scan) scan->xs_recheck = item->data.heap.recheck; index_store_float8_orderby_distances(scan, so->orderByTypes, - item->distances, + GISTSearchItemDistanceValues(item, scan->numberOfOrderBys), + GISTSearchItemDistanceNulls(item, scan->numberOfOrderBys), item->data.heap.recheckDistances); /* in an index-only scan, also return the reconstructed tuple. */ @@ -592,7 +609,10 @@ getNextNearest(IndexScanDesc scan) /* visit an index page, extract its items into queue */ CHECK_FOR_INTERRUPTS(); - gistScanPage(scan, item, item->distances, NULL, NULL); + gistScanPage(scan, item, + GISTSearchItemDistanceValues(item, scan->numberOfOrderBys), + GISTSearchItemDistanceNulls(item, scan->numberOfOrderBys), + NULL, NULL); } pfree(item); @@ -630,7 +650,7 @@ gistgettuple(IndexScanDesc scan, ScanDirection dir) 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) @@ -724,7 +744,10 @@ gistgettuple(IndexScanDesc scan, ScanDirection dir) * this page, we fall out of the inner "do" and loop around to * return them. */ - gistScanPage(scan, item, item->distances, NULL, NULL); + gistScanPage(scan, item, + GISTSearchItemDistanceValues(item, scan->numberOfOrderBys), + GISTSearchItemDistanceNulls(item, scan->numberOfOrderBys), + NULL, NULL); pfree(item); } while (so->nPageData == 0); @@ -755,7 +778,7 @@ gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm) 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 @@ -770,7 +793,10 @@ gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm) CHECK_FOR_INTERRUPTS(); - gistScanPage(scan, item, item->distances, tbm, &ntids); + gistScanPage(scan, item, + GISTSearchItemDistanceValues(item, scan->numberOfOrderBys), + GISTSearchItemDistanceNulls(item, scan->numberOfOrderBys), + tbm, &ntids); pfree(item); } diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c index 636aa62b68..e72bf08f95 100644 --- a/src/backend/access/gist/gistscan.c +++ b/src/backend/access/gist/gistscan.c @@ -33,14 +33,30 @@ pairingheap_GISTSearchItem_cmp(const pairingheap_node *a, const pairingheap_node const GISTSearchItem *sb = (const GISTSearchItem *) b; IndexScanDesc scan = (IndexScanDesc) arg; int i; + double *da = GISTSearchItemDistanceValues(sa, scan->numberOfOrderBys), + *db = GISTSearchItemDistanceValues(sb, scan->numberOfOrderBys); + bool *na = GISTSearchItemDistanceNulls(sa, scan->numberOfOrderBys), + *nb = GISTSearchItemDistanceNulls(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; + } } /* Heap items go before inner pages, to ensure a depth-first search */ @@ -84,7 +100,8 @@ gistbeginscan(Relation r, int nkeys, int norderbys) so->queueCxt = giststate->scanCxt; /* see gistrescan */ /* workspaces with size dependent on numberOfOrderBys: */ - so->distances = palloc(sizeof(double) * 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 */ if (scan->numberOfOrderBys > 0) { diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 28edd4aca7..2e8f53a37c 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -847,13 +847,14 @@ index_getprocinfo(Relation irel, */ void index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, - double *distances, bool recheckOrderBy) + double *distanceValues, + bool *distanceNulls, bool recheckOrderBy) { int i; scan->xs_recheckorderby = recheckOrderBy; - if (!distances) + if (!distanceValues) { Assert(!scan->xs_recheckorderby); @@ -868,6 +869,11 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, for (i = 0; i < scan->numberOfOrderBys; i++) { + if (distanceNulls && distanceNulls[i]) + { + scan->xs_orderbyvals[i] = (Datum) 0; + scan->xs_orderbynulls[i] = true; + } if (orderByTypes[i] == FLOAT8OID) { #ifndef USE_FLOAT8_BYVAL @@ -875,7 +881,7 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, if (!scan->xs_orderbynulls[i]) pfree(DatumGetPointer(scan->xs_orderbyvals[i])); #endif - scan->xs_orderbyvals[i] = Float8GetDatum(distances[i]); + scan->xs_orderbyvals[i] = Float8GetDatum(distanceValues[i]); scan->xs_orderbynulls[i] = false; } else if (orderByTypes[i] == FLOAT4OID) @@ -886,7 +892,7 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, if (!scan->xs_orderbynulls[i]) pfree(DatumGetPointer(scan->xs_orderbyvals[i])); #endif - scan->xs_orderbyvals[i] = Float4GetDatum((float4) distances[i]); + scan->xs_orderbyvals[i] = Float4GetDatum((float4) distanceValues[i]); scan->xs_orderbynulls[i] = false; } else diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c index 1cf28ecf2f..2bd4037af9 100644 --- a/src/backend/access/spgist/spgscan.c +++ b/src/backend/access/spgist/spgscan.c @@ -929,6 +929,7 @@ spggettuple(IndexScanDesc scan, ScanDirection dir) if (so->numberOfOrderBys > 0) index_store_float8_orderby_distances(scan, so->orderByTypes, so->distances[so->iPtr], + NULL, so->recheckDistances[so->iPtr]); so->iPtr++; return true; diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 8c053be2ca..6c56717ba7 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -178,7 +178,9 @@ extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum, extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum); extern void index_store_float8_orderby_distances(IndexScanDesc scan, - Oid *orderByTypes, double *distances, + Oid *orderByTypes, + double *distanceValues, + bool *distanceNulls, bool recheckOrderBy); /* diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index fc1a311556..ed5b643885 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -137,13 +137,30 @@ typedef struct GISTSearchItem /* we must store parentlsn to detect whether a split occurred */ GISTSearchHeapItem heap; /* heap info, if heap tuple */ } data; - double distances[FLEXIBLE_ARRAY_MEMBER]; /* 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. + */ } GISTSearchItem; #define GISTSearchItemIsHeap(item) ((item).blkno == InvalidBlockNumber) -#define SizeOfGISTSearchItem(n_distances) (offsetof(GISTSearchItem, distances) + sizeof(double) * (n_distances)) +#define SizeOfGISTSearchItem(n_distances) (DOUBLEALIGN(sizeof(GISTSearchItem)) + \ + (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 GISTSearchItemDistanceValues(item, n_distances) \ + ((double *) ((Pointer) (item) + DOUBLEALIGN(sizeof(GISTSearchItem)))) + +#define GISTSearchItemDistanceNulls(item, n_distances) \ + ((bool *) ((Pointer) (item) + DOUBLEALIGN(sizeof(GISTSearchItem)) + sizeof(double) * (n_distances))) /* * GISTScanOpaqueData: private state for a scan of a GiST index @@ -159,7 +176,8 @@ typedef struct GISTScanOpaqueData bool firstCall; /* true until first gistgettuple call */ /* pre-allocated workspace arrays */ - double *distances; /* output area for gistindex_keytest */ + double *distanceValues; /* output area for gistindex_keytest */ + bool *distanceNulls; /* info about killed items if any (killedItems is NULL if never used) */ OffsetNumber *killedItems; /* offset numbers of killed items */ diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index 117b11a1a6..324db1b6ae 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -531,8 +531,8 @@ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; (-5,-12) (5.1,34.5) (1e+300,Infinity) - (NaN,NaN) + (10 rows) EXPLAIN (COSTS OFF)