Thanks for answer. Now it seems to be applied correctly. 2015-02-12 3:12 GMT+04:00 Thom Brown <t...@linux.com>:
> On 11 February 2015 at 22:50, Anastasia Lubennikova < > lubennikov...@gmail.com> wrote: > >> Finally there is a new version of patch (in attachments). >> It provides multicolumn index-only scan for GiST indexes. >> >> - Memory leak is fixed. >> - little code cleanup >> - example of performance test in attachmens >> - function OIDs have debugging values (1111*) just to avoid merge >> conflicts while testing patch >> >> Wiki page of the project is >> >> https://wiki.postgresql.org/wiki/Support_for_Index-only_scans_for_GIST_GSoC_2014 >> >> Waiting for feedback. >> > > Hi Anastasia. Thanks for the updated patch. I've just tried applying it > to head and it doesn't appear to apply cleanly. > > $ patch -p1 < ~/Downloads/indexonlyscan_gist_2.0.patch > (Stripping trailing CRs from patch; use --binary to disable.) > patching file src/backend/access/gist/gist.c > Hunk #1 succeeded at 1404 (offset 9 lines). > Hunk #2 succeeded at 1434 (offset 9 lines). > (Stripping trailing CRs from patch; use --binary to disable.) > patching file src/backend/access/gist/gistget.c > Hunk #1 succeeded at 227 (offset 1 line). > Hunk #2 succeeded at 243 (offset 1 line). > Hunk #3 succeeded at 293 (offset -4 lines). > Hunk #4 succeeded at 330 (offset -4 lines). > Hunk #5 succeeded at 365 (offset -5 lines). > Hunk #6 succeeded at 444 (offset -27 lines). > Hunk #7 succeeded at 474 (offset -27 lines). > Hunk #8 FAILED at 518. > Hunk #9 succeeded at 507 (offset -28 lines). > Hunk #10 succeeded at 549 with fuzz 1 (offset -28 lines). > Hunk #11 FAILED at 601. > 2 out of 11 hunks FAILED -- saving rejects to file > src/backend/access/gist/gistget.c.rej > ... > > -- > Thom > -- Best regards, Lubennikova Anastasia
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index db2a452..53750da 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -1404,6 +1404,14 @@ initGISTstate(Relation index) else giststate->distanceFn[i].fn_oid = InvalidOid; + /* opclasses are not required to provide a Fetch method */ + if (OidIsValid(index_getprocid(index, i + 1, GIST_FETCH_PROC))) + fmgr_info_copy(&(giststate->fetchFn[i]), + index_getprocinfo(index, i + 1, GIST_FETCH_PROC), + scanCxt); + else + giststate->fetchFn[i].fn_oid = InvalidOid; + /* * If the index column has a specified collation, we should honor that * while doing comparisons. However, we may have a collatable storage @@ -1426,6 +1434,22 @@ initGISTstate(Relation index) return giststate; } +/* + * Gistcanreturn is supposed to be true if ANY FetchFn method is defined. + * If FetchFn exists it would be used in index-only scan + * Thus the responsibility rests with the opclass developer. + */ + +Datum +gistcanreturn(PG_FUNCTION_ARGS) { + Relation index = (Relation) PG_GETARG_POINTER(0); + int i = PG_GETARG_INT32(1); + if (OidIsValid(index_getprocid(index, i+1, GIST_FETCH_PROC))) + PG_RETURN_BOOL(true); + else + PG_RETURN_BOOL(false); +} + void freeGISTstate(GISTSTATE *giststate) { diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index 717cb85..0925e56 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -227,8 +227,10 @@ gistindex_keytest(IndexScanDesc scan, * If tbm/ntids aren't NULL, we are doing an amgetbitmap scan, and heap * tuples should be reported directly into the bitmap. If they are NULL, * we're doing a plain or ordered indexscan. For a plain indexscan, heap - * tuple TIDs are returned into so->pageData[]. For an ordered indexscan, + * tuple TIDs are returned into so->pageData. For an ordered indexscan, * heap tuple TIDs are pushed into individual search queue items. + * If index-only scan is possible, heap tuples themselves are returned + * into so->pageData or into search queue. * * If we detect that the index page has split since we saw its downlink * in the parent, we push its new right sibling onto the queue so the @@ -241,6 +243,10 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, GISTScanOpaque so = (GISTScanOpaque) scan->opaque; Buffer buffer; Page page; + GISTSTATE *giststate = so->giststate; + Relation r = scan->indexRelation; + bool isnull[INDEX_MAX_KEYS]; + GISTSearchHeapItem *tmpListItem; GISTPageOpaque opaque; OffsetNumber maxoff; OffsetNumber i; @@ -287,8 +293,6 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, MemoryContextSwitchTo(oldcxt); } - so->nPageData = so->curPageData = 0; - /* * check all tuples on page */ @@ -326,11 +330,20 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page)) { /* - * Non-ordered scan, so report heap tuples in so->pageData[] + * Non-ordered scan, so report tuples in so->pageData + */ + + /* form tmpListItem and fill it with data to add into so->pageData */ + tmpListItem = palloc(sizeof(GISTSearchHeapItem)); + tmpListItem->heapPtr = it->t_tid; + tmpListItem->recheck = recheck; + /* + * If index-only scan is possible add the data fetched from index field */ - so->pageData[so->nPageData].heapPtr = it->t_tid; - so->pageData[so->nPageData].recheck = recheck; - so->nPageData++; + if (scan->xs_want_itup) + tmpListItem->ftup = gistFetchTuple(giststate, r, it, isnull); + + so->pageData = lappend(so->pageData, tmpListItem); } else { @@ -352,6 +365,13 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, item->blkno = InvalidBlockNumber; item->data.heap.heapPtr = it->t_tid; item->data.heap.recheck = recheck; + + /* + * If index-only scan is possible add the data fetched from index field + */ + if (scan->xs_want_itup) { + item->data.heap.ftup = gistFetchTuple(giststate, r, it, isnull); + } } else { @@ -424,6 +444,11 @@ getNextNearest(IndexScanDesc scan) /* found a heap item at currently minimal distance */ scan->xs_ctup.t_self = item->data.heap.heapPtr; scan->xs_recheck = item->data.heap.recheck; + /* + * If index-only scan is possible fill the slot with data fetched from index field + */ + if(scan->xs_want_itup) + scan->xs_itup = item->data.heap.ftup; res = true; } else @@ -449,6 +474,7 @@ gistgettuple(PG_FUNCTION_ARGS) IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1); GISTScanOpaque so = (GISTScanOpaque) scan->opaque; + bool first_tuple = true; if (dir != ForwardScanDirection) elog(ERROR, "GiST only supports forward scan direction"); @@ -464,7 +490,6 @@ gistgettuple(PG_FUNCTION_ARGS) pgstat_count_index_scan(scan->indexRelation); so->firstCall = false; - so->curPageData = so->nPageData = 0; fakeItem.blkno = GIST_ROOT_BLKNO; memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN)); @@ -481,13 +506,27 @@ gistgettuple(PG_FUNCTION_ARGS) /* Fetch tuples index-page-at-a-time */ for (;;) { - if (so->curPageData < so->nPageData) + if(list_length(so->pageData)>0) { /* continuing to return tuples from a leaf page */ - scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr; - scan->xs_recheck = so->pageData[so->curPageData].recheck; - so->curPageData++; - PG_RETURN_BOOL(true); + GISTSearchHeapItem *tmp = (GISTSearchHeapItem *)lfirst(list_head(so->pageData)); + scan->xs_ctup.t_self = tmp->heapPtr; + scan->xs_recheck = tmp->recheck; + /* If index-only scan is possible, return fetched data*/ + if(scan->xs_want_itup) { + scan->xs_itup = tmp->ftup; + if(!first_tuple) + pfree(tmp->ftup); + first_tuple=false; + } + pfree(tmp); + + /* + * Delete ListCell that we have already read. + * It's always head of so->pageData + */ + so->pageData = list_delete_first(so->pageData); + PG_RETURN_BOOL(TRUE); } /* find and process the next index page */ @@ -509,7 +548,7 @@ gistgettuple(PG_FUNCTION_ARGS) gistScanPage(scan, item, item->distances, NULL, NULL); pfree(item); - } while (so->nPageData == 0); + } while (list_length(so->pageData)==0); } } } @@ -532,7 +571,6 @@ gistgetbitmap(PG_FUNCTION_ARGS) pgstat_count_index_scan(scan->indexRelation); /* Begin the scan by processing the root page */ - so->curPageData = so->nPageData = 0; fakeItem.blkno = GIST_ROOT_BLKNO; memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN)); diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 9fab6c8..e1355dd 100644 --- a/src/backend/access/gist/gistproc.c +++ b/src/backend/access/gist/gistproc.c @@ -152,6 +152,16 @@ gist_box_decompress(PG_FUNCTION_ARGS) } /* + * GiST Fetch method for boxes + * do not do anything --- we just return the stored box as is. + */ +Datum +gist_box_fetch(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(PG_GETARG_POINTER(0)); +} + +/* * The GiST Penalty method for boxes (also used for points) * * As in the R-tree paper, we use change in area as our penalty metric @@ -1186,6 +1196,41 @@ gist_point_compress(PG_FUNCTION_ARGS) PG_RETURN_POINTER(entry); } +/* + * GiST Fetch method for point + * get point coordinates from it's bounding box coordinates + * and form new gistentry + */ +Datum +gist_point_fetch(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *retval; + retval = palloc(sizeof(GISTENTRY)); + if (DatumGetBoxP(entry->key) != NULL) + { + BOX *in = DatumGetBoxP(entry->key); + Point *r; + + r = (Point *) palloc(sizeof(Point)); + r->x = in->high.x; + r->y = in->high.y; + gistentryinit(*retval, PointerGetDatum(r), + entry->rel, entry->page, + entry->offset, FALSE); + + } + else + { + gistentryinit(*retval, (Datum) 0, + entry->rel, entry->page, + entry->offset, FALSE); + } + + PG_RETURN_POINTER(retval); +} + + #define point_point_distance(p1,p2) \ DatumGetFloat8(DirectFunctionCall2(point_distance, \ PointPGetDatum(p1), PointPGetDatum(p2))) diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c index cc8d818..98a1b54 100644 --- a/src/backend/access/gist/gistscan.c +++ b/src/backend/access/gist/gistscan.c @@ -88,6 +88,13 @@ gistbeginscan(PG_FUNCTION_ARGS) scan->opaque = so; + /* All fields required for index-only scans are null until gistrescan. + * However, we set up scan->xs_itupdesc whether we'll need it or not, + * since that's cheap. + */ + so->pageData = NULL; + scan->xs_itupdesc = RelationGetDescr(r); + MemoryContextSwitchTo(oldCxt); PG_RETURN_POINTER(scan); diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 38af0e0..b03966a 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -618,6 +618,65 @@ gistFormTuple(GISTSTATE *giststate, Relation r, return res; } +/* + * initialize a GiST entry with fetched value in key field + */ +void +gistfentryinit(GISTSTATE *giststate, int nkey, + GISTENTRY *e, Datum k, Relation r, + Page pg, OffsetNumber o, bool l, bool isNull) +{ + + if (!isNull) + { + + GISTENTRY *fep; + + gistentryinit(*e, k, r, pg, o, l); + + + fep = (GISTENTRY *) + DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey], + giststate->supportCollation[nkey], + PointerGetDatum(e))); + /* fecthFn returns the given pointer */ + if (fep != e) + gistentryinit(*e, fep->key, fep->rel, fep->page, fep->offset, + fep->leafkey); + } + else + gistentryinit(*e, (Datum) 0, r, pg, o, l); +} + +/* + * Fetch all keys in tuple. + * returns new IndexTuple that contains GISTENTRY with fetched data + */ +IndexTuple +gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple, bool isnull[]) +{ + MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt); + GISTENTRY fentry[INDEX_MAX_KEYS]; + Datum fetchatt[INDEX_MAX_KEYS]; + int i; + IndexTuple res; + + + for (i = 0; i < r->rd_att->natts; i++) + { + Datum datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]); + gistfentryinit(giststate, i, &fentry[i], + datum, r, NULL, (OffsetNumber) 0, + FALSE, FALSE); + + fetchatt[i] = fentry[i].key; + + } + MemoryContextSwitchTo(oldcxt); + + return index_form_tuple(giststate->tupdesc, fetchatt, isnull); +} + float gistpenalty(GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 00c1d69..62b10ce 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -722,11 +722,11 @@ index_vacuum_cleanup(IndexVacuumInfo *info, } /* ---------------- - * index_can_return - does index support index-only scans? + * index_can_return - does index column with number 'attno' supports index-only scans? * ---------------- */ bool -index_can_return(Relation indexRelation) +index_can_return(Relation indexRelation, int attno) { FmgrInfo *procedure; @@ -738,8 +738,9 @@ index_can_return(Relation indexRelation) GET_REL_PROCEDURE(amcanreturn); - return DatumGetBool(FunctionCall1(procedure, - PointerGetDatum(indexRelation))); + return DatumGetBool(FunctionCall2(procedure, + PointerGetDatum(indexRelation), + Int32GetDatum(attno))); } /* ---------------- diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index b86a3cd..8e08f2e 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -1782,14 +1782,13 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) bool result; Bitmapset *attrs_used = NULL; Bitmapset *index_attrs = NULL; + Bitmapset *index_only_attrs = NULL; ListCell *lc; int i; - /* Index-only scans must be enabled, and index must be capable of them */ + /* Index-only scans must be enabled */ if (!enable_indexonlyscan) return false; - if (!index->canreturn) - return false; /* * Check that all needed attributes of the relation are available from the @@ -1834,14 +1833,17 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) index_attrs = bms_add_member(index_attrs, attno - FirstLowInvalidHeapAttributeNumber); + if (index->canreturn[i]) + index_only_attrs = bms_add_member(index_only_attrs, + attno - FirstLowInvalidHeapAttributeNumber); } - /* Do we have all the necessary attributes? */ - result = bms_is_subset(attrs_used, index_attrs); - + /* Do we have all the necessary attributes? And do all of them support index-only scan? */ + result = ((bms_is_subset(attrs_used, index_attrs))&& + (bms_is_subset(attrs_used, index_only_attrs))); bms_free(attrs_used); bms_free(index_attrs); - + bms_free(index_only_attrs); return result; } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index fb7db6d..192f418 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -207,6 +207,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, info->indexcollations = (Oid *) palloc(sizeof(Oid) * ncolumns); info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns); info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns); + info->canreturn = (bool *) palloc(sizeof(bool) * ncolumns); for (i = 0; i < ncolumns; i++) { @@ -214,11 +215,11 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, info->indexcollations[i] = indexRelation->rd_indcollation[i]; info->opfamily[i] = indexRelation->rd_opfamily[i]; info->opcintype[i] = indexRelation->rd_opcintype[i]; + info->canreturn[i] = index_can_return(indexRelation, i); } info->relam = indexRelation->rd_rel->relam; info->amcostestimate = indexRelation->rd_am->amcostestimate; - info->canreturn = index_can_return(indexRelation); info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop; info->amoptionalkey = indexRelation->rd_am->amoptionalkey; info->amsearcharray = indexRelation->rd_am->amsearcharray; diff --git a/src/include/access/genam.h b/src/include/access/genam.h index d1d6247..d86590a 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -156,7 +156,7 @@ extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info, void *callback_state); extern IndexBulkDeleteResult *index_vacuum_cleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); -extern bool index_can_return(Relation indexRelation); +extern bool index_can_return(Relation indexRelation, int attno); extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum); extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum, diff --git a/src/include/access/gist.h b/src/include/access/gist.h index 01f0a70..50261b8 100644 --- a/src/include/access/gist.h +++ b/src/include/access/gist.h @@ -33,7 +33,8 @@ #define GIST_PICKSPLIT_PROC 6 #define GIST_EQUAL_PROC 7 #define GIST_DISTANCE_PROC 8 -#define GISTNProcs 8 +#define GIST_FETCH_PROC 9 +#define GISTNProcs 9 /* * strategy numbers for GiST opclasses that want to implement the old diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 382826e..14f83d4 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -87,6 +87,7 @@ typedef struct GISTSTATE FmgrInfo picksplitFn[INDEX_MAX_KEYS]; FmgrInfo equalFn[INDEX_MAX_KEYS]; FmgrInfo distanceFn[INDEX_MAX_KEYS]; + FmgrInfo fetchFn[INDEX_MAX_KEYS]; /* Collations to pass to the support functions */ Oid supportCollation[INDEX_MAX_KEYS]; @@ -109,7 +110,7 @@ typedef struct GISTSTATE * 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 + * remembered in GISTScanOpaqueData.pageData and returned directly from * there, instead of building a separate GISTSearchItem for each one. */ @@ -118,6 +119,7 @@ typedef struct GISTSearchHeapItem { ItemPointerData heapPtr; bool recheck; /* T if quals must be rechecked */ + IndexTuple ftup; /* Tuple contains datum fetched from key for index-only scans */ } GISTSearchHeapItem; /* Unvisited item, either index page or heap tuple */ @@ -153,9 +155,8 @@ typedef struct GISTScanOpaqueData double *distances; /* output area for gistindex_keytest */ /* 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 */ + List *pageData; + } GISTScanOpaqueData; typedef GISTScanOpaqueData *GISTScanOpaque; @@ -408,6 +409,7 @@ typedef struct GiSTOptions /* gist.c */ extern Datum gistbuildempty(PG_FUNCTION_ARGS); extern Datum gistinsert(PG_FUNCTION_ARGS); +extern Datum gistcanreturn(PG_FUNCTION_ARGS); extern MemoryContext createTempGistContext(void); extern GISTSTATE *initGISTstate(Relation index); extern void freeGISTstate(GISTSTATE *giststate); @@ -508,6 +510,10 @@ extern bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b); extern void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull); +extern void gistfentryinit(GISTSTATE *giststate, int nkey, + GISTENTRY *e, Datum k, Relation r, + Page pg, OffsetNumber o, bool l, bool isNull); +extern IndexTuple gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple, bool *isnull); extern void gistMakeUnionKey(GISTSTATE *giststate, int attno, GISTENTRY *entry1, bool isnull1, GISTENTRY *entry2, bool isnull2, diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h index 0531222..79609f7 100644 --- a/src/include/catalog/pg_am.h +++ b/src/include/catalog/pg_am.h @@ -123,7 +123,7 @@ DESCR("b-tree index access method"); DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup - hashcostestimate hashoptions )); DESCR("hash index access method"); #define HASH_AM_OID 405 -DATA(insert OID = 783 ( gist 0 8 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup - gistcostestimate gistoptions )); +DATA(insert OID = 783 ( gist 0 9 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup gistcanreturn gistcostestimate gistoptions )); DESCR("GiST index access method"); #define GIST_AM_OID 783 DATA(insert OID = 2742 ( gin 0 6 f f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions )); diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 49d3d13..c71cb8f 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -191,6 +191,7 @@ DATA(insert ( 1029 600 600 5 2581 )); DATA(insert ( 1029 600 600 6 2582 )); DATA(insert ( 1029 600 600 7 2584 )); DATA(insert ( 1029 600 600 8 3064 )); +DATA(insert ( 1029 600 600 9 11113 )); DATA(insert ( 2593 603 603 1 2578 )); DATA(insert ( 2593 603 603 2 2583 )); DATA(insert ( 2593 603 603 3 2579 )); @@ -198,6 +199,7 @@ DATA(insert ( 2593 603 603 4 2580 )); DATA(insert ( 2593 603 603 5 2581 )); DATA(insert ( 2593 603 603 6 2582 )); DATA(insert ( 2593 603 603 7 2584 )); +DATA(insert ( 2593 603 603 9 11112 )); DATA(insert ( 2594 604 604 1 2585 )); DATA(insert ( 2594 604 604 2 2583 )); DATA(insert ( 2594 604 604 3 2586 )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 9edfdb8..dc8ddea 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -558,7 +558,7 @@ DATA(insert OID = 332 ( btbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v 4 DESCR("btree(internal)"); DATA(insert OID = 972 ( btvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ btvacuumcleanup _null_ _null_ _null_ )); DESCR("btree(internal)"); -DATA(insert OID = 276 ( btcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281" _null_ _null_ _null_ _null_ btcanreturn _null_ _null_ _null_ )); +DATA(insert OID = 276 ( btcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281 2281" _null_ _null_ _null_ _null_ btcanreturn _null_ _null_ _null_ )); DESCR("btree(internal)"); DATA(insert OID = 1268 ( btcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ btcostestimate _null_ _null_ _null_ )); DESCR("btree(internal)"); @@ -981,6 +981,8 @@ DATA(insert OID = 776 ( gistbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v DESCR("gist(internal)"); DATA(insert OID = 2561 ( gistvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gistvacuumcleanup _null_ _null_ _null_ )); DESCR("gist(internal)"); +DATA(insert OID = 11111 ( gistcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281 2281" _null_ _null_ _null_ _null_ gistcanreturn _null_ _null_ _null_ )); +DESCR("gist(internal)"); DATA(insert OID = 772 ( gistcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gistcostestimate _null_ _null_ _null_ )); DESCR("gist(internal)"); DATA(insert OID = 2787 ( gistoptions PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ gistoptions _null_ _null_ _null_ )); @@ -4062,6 +4064,8 @@ DATA(insert OID = 2579 ( gist_box_compress PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("GiST support"); DATA(insert OID = 2580 ( gist_box_decompress PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_box_decompress _null_ _null_ _null_ )); DESCR("GiST support"); +DATA(insert OID = 11112 ( gist_box_fetch PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_box_fetch _null_ _null_ _null_ )); +DESCR("GiST support"); DATA(insert OID = 2581 ( gist_box_penalty PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ gist_box_penalty _null_ _null_ _null_ )); DESCR("GiST support"); DATA(insert OID = 2582 ( gist_box_picksplit PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gist_box_picksplit _null_ _null_ _null_ )); @@ -4080,6 +4084,8 @@ DATA(insert OID = 2592 ( gist_circle_compress PGNSP PGUID 12 1 0 0 0 f f f f t DESCR("GiST support"); DATA(insert OID = 1030 ( gist_point_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_compress _null_ _null_ _null_ )); DESCR("GiST support"); +DATA(insert OID = 11113 ( gist_point_fetch PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_fetch _null_ _null_ _null_ )); +DESCR("GiST support"); DATA(insert OID = 2179 ( gist_point_consistent PGNSP PGUID 12 1 0 0 0 f 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 0 f f f f t f i 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_ gist_point_distance _null_ _null_ _null_ )); @@ -5012,7 +5018,7 @@ DATA(insert OID = 4011 ( spgbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v DESCR("spgist(internal)"); DATA(insert OID = 4012 ( spgvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ spgvacuumcleanup _null_ _null_ _null_ )); DESCR("spgist(internal)"); -DATA(insert OID = 4032 ( spgcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281" _null_ _null_ _null_ _null_ spgcanreturn _null_ _null_ _null_ )); +DATA(insert OID = 4032 ( spgcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281 2281" _null_ _null_ _null_ _null_ spgcanreturn _null_ _null_ _null_ )); DESCR("spgist(internal)"); DATA(insert OID = 4013 ( spgcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ spgcostestimate _null_ _null_ _null_ )); DESCR("spgist(internal)"); diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 6845a40..e134e38 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -533,7 +533,7 @@ typedef struct IndexOptInfo bool unique; /* true if a unique index */ bool immediate; /* is uniqueness enforced immediately? */ bool hypothetical; /* true if index doesn't really exist */ - bool canreturn; /* can index return IndexTuples? */ + bool *canreturn; /* can index columns return IndexTuples? */ 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? */ diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h index 0b6d3c3..786096e 100644 --- a/src/include/utils/geo_decls.h +++ b/src/include/utils/geo_decls.h @@ -410,6 +410,7 @@ extern Datum gist_box_picksplit(PG_FUNCTION_ARGS); extern Datum gist_box_consistent(PG_FUNCTION_ARGS); extern Datum gist_box_penalty(PG_FUNCTION_ARGS); extern Datum gist_box_same(PG_FUNCTION_ARGS); +extern Datum gist_box_fetch(PG_FUNCTION_ARGS); extern Datum gist_poly_compress(PG_FUNCTION_ARGS); extern Datum gist_poly_consistent(PG_FUNCTION_ARGS); extern Datum gist_circle_compress(PG_FUNCTION_ARGS); @@ -417,6 +418,8 @@ extern Datum gist_circle_consistent(PG_FUNCTION_ARGS); extern Datum gist_point_compress(PG_FUNCTION_ARGS); extern Datum gist_point_consistent(PG_FUNCTION_ARGS); extern Datum gist_point_distance(PG_FUNCTION_ARGS); +extern Datum gist_point_fetch(PG_FUNCTION_ARGS); + /* geo_selfuncs.c */ extern Datum areasel(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index 5603817..abe64e5 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -384,7 +384,7 @@ SELECT * FROM fast_emp4000 ---------------------------------------------------------------- Sort Sort Key: ((home_base[0])[0]) - -> Index Scan using grect2ind on fast_emp4000 + -> Index Only Scan using grect2ind on fast_emp4000 Index Cond: (home_base @ '(2000,1000),(200,200)'::box) (4 rows) @@ -402,7 +402,7 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; QUERY PLAN ------------------------------------------------------------- Aggregate - -> Index Scan using grect2ind on fast_emp4000 + -> Index Only Scan using grect2ind on fast_emp4000 Index Cond: (home_base && '(1000,1000),(0,0)'::box) (3 rows) @@ -414,10 +414,10 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; EXPLAIN (COSTS OFF) SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +------------------------------------------------------- Aggregate - -> Index Scan using grect2ind on fast_emp4000 + -> Index Only Scan using grect2ind on fast_emp4000 Index Cond: (home_base IS NULL) (3 rows) @@ -501,7 +501,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)'; QUERY PLAN ---------------------------------------------------- Aggregate - -> Index Scan using gpointind on point_tbl + -> Index Only Scan using gpointind on point_tbl Index Cond: (f1 <@ '(100,100),(0,0)'::box) (3 rows) @@ -516,8 +516,8 @@ SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1; QUERY PLAN ---------------------------------------------------- Aggregate - -> Index Scan using gpointind on point_tbl - Index Cond: ('(100,100),(0,0)'::box @> f1) + -> Index Only Scan using gpointind on point_tbl + Index Cond: (f1 <@ '(100,100),(0,0)'::box) (3 rows) SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1; @@ -531,7 +531,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50, QUERY PLAN ---------------------------------------------------------------------------------------- Aggregate - -> Index Scan using gpointind on point_tbl + -> Index Only Scan using gpointind on point_tbl Index Cond: (f1 <@ '((0,0),(0,100),(100,100),(50,50),(100,0),(0,0))'::polygon) (3 rows) @@ -546,7 +546,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>'; QUERY PLAN ---------------------------------------------------- Aggregate - -> Index Scan using gpointind on point_tbl + -> Index Only Scan using gpointind on point_tbl Index Cond: (f1 <@ '<(50,50),50>'::circle) (3 rows) @@ -558,10 +558,10 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)'; - QUERY PLAN -------------------------------------------------- + QUERY PLAN +------------------------------------------------------ Aggregate - -> Index Scan using gpointind on point_tbl p + -> Index Only Scan using gpointind on point_tbl p Index Cond: (f1 << '(0,0)'::point) (3 rows) @@ -573,10 +573,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)'; - QUERY PLAN -------------------------------------------------- + QUERY PLAN +------------------------------------------------------ Aggregate - -> Index Scan using gpointind on point_tbl p + -> Index Only Scan using gpointind on point_tbl p Index Cond: (f1 >> '(0,0)'::point) (3 rows) @@ -588,10 +588,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)'; - QUERY PLAN -------------------------------------------------- + QUERY PLAN +------------------------------------------------------ Aggregate - -> Index Scan using gpointind on point_tbl p + -> Index Only Scan using gpointind on point_tbl p Index Cond: (f1 <^ '(0,0)'::point) (3 rows) @@ -603,10 +603,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)'; - QUERY PLAN -------------------------------------------------- + QUERY PLAN +------------------------------------------------------ Aggregate - -> Index Scan using gpointind on point_tbl p + -> Index Only Scan using gpointind on point_tbl p Index Cond: (f1 >^ '(0,0)'::point) (3 rows) @@ -618,10 +618,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)'; EXPLAIN (COSTS OFF) SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; - QUERY PLAN -------------------------------------------------- + QUERY PLAN +------------------------------------------------------ Aggregate - -> Index Scan using gpointind on point_tbl p + -> Index Only Scan using gpointind on point_tbl p Index Cond: (f1 ~= '(-5,-12)'::point) (3 rows) @@ -633,9 +633,9 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)'; EXPLAIN (COSTS OFF) SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; - QUERY PLAN ------------------------------------------ - Index Scan using gpointind on point_tbl + QUERY PLAN +---------------------------------------------- + Index Only Scan using gpointind on point_tbl Order By: (f1 <-> '(0,1)'::point) (2 rows) @@ -653,9 +653,9 @@ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1'; EXPLAIN (COSTS OFF) SELECT * FROM point_tbl WHERE f1 IS NULL; - QUERY PLAN ------------------------------------------ - Index Scan using gpointind on point_tbl + QUERY PLAN +---------------------------------------------- + Index Only Scan using gpointind on point_tbl Index Cond: (f1 IS NULL) (2 rows) @@ -667,9 +667,9 @@ 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'; - QUERY PLAN ------------------------------------------ - Index Scan using gpointind on point_tbl + QUERY PLAN +---------------------------------------------- + Index Only Scan using gpointind on point_tbl Index Cond: (f1 IS NOT NULL) Order By: (f1 <-> '(0,1)'::point) (3 rows) @@ -689,7 +689,7 @@ 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 Only Scan using gpointind on point_tbl Index Cond: (f1 <@ '(10,10),(-10,-10)'::box) Order By: (f1 <-> '(0,1)'::point) (3 rows) diff --git a/src/test/regress/expected/gist_indexonly.out b/src/test/regress/expected/gist_indexonly.out new file mode 100644 index 0000000..0a51cdd --- /dev/null +++ b/src/test/regress/expected/gist_indexonly.out @@ -0,0 +1,50 @@ +-- +-- Test Index-only scan plan on GiST indexes +-- +CREATE TABLE gist_tbl (b box, p point); +insert into gist_tbl select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)), + point(0.05*i, 0.05*i) FROM generate_series(0,10000) as i; +vacuum analyze; +SET enable_seqscan=off; +SET enable_bitmapscan=off; +-- Check singlecolumn index-only scan for point opclass +CREATE INDEX gist_tbl_point_index ON gist_tbl USING gist (p); +EXPLAIN (COSTS OFF) +select p from gist_tbl where p <@ box(point(0,0), point(100,100)) and length(p::text) < 10; + QUERY PLAN +-------------------------------------------------------- + Index Only Scan using gist_tbl_point_index on gist_tbl + Index Cond: (p <@ '(100,100),(0,0)'::box) + Filter: (length((p)::text) < 10) +(3 rows) + +DROP INDEX gist_tbl_point_index; +-- Check singlecolumn index-only scan for box opclass +CREATE INDEX gist_tbl_box_index ON gist_tbl USING gist (b); +vacuum analyze; +EXPLAIN (COSTS OFF) +select b from gist_tbl where b <@ box(point(5,5), point(6,6)); + QUERY PLAN +------------------------------------------------------ + Index Only Scan using gist_tbl_box_index on gist_tbl + Index Cond: (b <@ '(6,6),(5,5)'::box) +(2 rows) + +DROP INDEX gist_tbl_box_index; +-- Check multicolumn indexonlyscan for gist +CREATE INDEX gist_tbl_multi_index ON gist_tbl USING gist (b, p); +vacuum analyze; +EXPLAIN (COSTS OFF) +select b, p from gist_tbl where ( (b <@ box(point(5,5), point(6,6))) and (p <@ box(point(5,5), point(5.5,5.5)))); + QUERY PLAN +----------------------------------------------------------------------------- + Index Only Scan using gist_tbl_multi_index on gist_tbl + Index Cond: ((b <@ '(6,6),(5,5)'::box) AND (p <@ '(5.5,5.5),(5,5)'::box)) +(2 rows) + +DROP INDEX gist_tbl_multi_index; +RESET enable_seqscan; +RESET enable_bitmapscan; +RESET enable_indexscan; +RESET enable_indexonlyscan; +DROP TABLE gist_tbl; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index e0ae2f2..758c326 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -96,7 +96,7 @@ test: rules # ---------- # Another group of parallel tests # ---------- -test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast equivclass +test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast equivclass gist_indexonly # ---------- # Another group of parallel tests # NB: temp.sql does a reconnect which transiently uses 2 connections, diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 7f762bd..47803a3 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -91,6 +91,7 @@ test: portals test: arrays test: btree_index test: hash_index +test: gist_indexonly test: update test: delete test: namespace diff --git a/src/test/regress/sql/gist_indexonly.sql b/src/test/regress/sql/gist_indexonly.sql new file mode 100644 index 0000000..49da865 --- /dev/null +++ b/src/test/regress/sql/gist_indexonly.sql @@ -0,0 +1,43 @@ +-- +-- Test Index-only scan plan on GiST indexes +-- + +CREATE TABLE gist_tbl (b box, p point); + +insert into gist_tbl select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)), + point(0.05*i, 0.05*i) FROM generate_series(0,10000) as i; + +vacuum analyze; + +SET enable_seqscan=off; +SET enable_bitmapscan=off; + +-- Check singlecolumn index-only scan for point opclass + +CREATE INDEX gist_tbl_point_index ON gist_tbl USING gist (p); +EXPLAIN (COSTS OFF) +select p from gist_tbl where p <@ box(point(0,0), point(100,100)) and length(p::text) < 10; +DROP INDEX gist_tbl_point_index; + +-- Check singlecolumn index-only scan for box opclass + +CREATE INDEX gist_tbl_box_index ON gist_tbl USING gist (b); +vacuum analyze; +EXPLAIN (COSTS OFF) +select b from gist_tbl where b <@ box(point(5,5), point(6,6)); +DROP INDEX gist_tbl_box_index; + +-- Check multicolumn indexonlyscan for gist + +CREATE INDEX gist_tbl_multi_index ON gist_tbl USING gist (b, p); +vacuum analyze; +EXPLAIN (COSTS OFF) +select b, p from gist_tbl where ( (b <@ box(point(5,5), point(6,6))) and (p <@ box(point(5,5), point(5.5,5.5)))); +DROP INDEX gist_tbl_multi_index; + +RESET enable_seqscan; +RESET enable_bitmapscan; +RESET enable_indexscan; +RESET enable_indexonlyscan; + +DROP TABLE gist_tbl;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers