diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/access/gist/gist.c | 8 | ||||
-rw-r--r-- | src/backend/access/gist/gistget.c | 66 | ||||
-rw-r--r-- | src/backend/access/gist/gistproc.c | 37 | ||||
-rw-r--r-- | src/backend/access/gist/gistscan.c | 18 | ||||
-rw-r--r-- | src/backend/access/gist/gistutil.c | 64 | ||||
-rw-r--r-- | src/backend/access/index/indexam.c | 12 | ||||
-rw-r--r-- | src/backend/access/spgist/spgscan.c | 1 | ||||
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 22 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 3 |
9 files changed, 213 insertions, 18 deletions
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index db2a452a4ab..96b7701633f 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 diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c index 717cb85f773..e4c00c2c9f5 100644 --- a/src/backend/access/gist/gistget.c +++ b/src/backend/access/gist/gistget.c @@ -228,7 +228,9 @@ gistindex_keytest(IndexScanDesc scan, * 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, - * heap tuple TIDs are pushed into individual search queue items. + * heap tuple TIDs are pushed into individual search queue items. In an + * index-only scan, reconstructed index tuples are returned along with the + * TIDs. * * 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 @@ -239,6 +241,8 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, TIDBitmap *tbm, int64 *ntids) { GISTScanOpaque so = (GISTScanOpaque) scan->opaque; + GISTSTATE *giststate = so->giststate; + Relation r = scan->indexRelation; Buffer buffer; Page page; GISTPageOpaque opaque; @@ -288,6 +292,8 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, } so->nPageData = so->curPageData = 0; + if (so->pageDataCxt) + MemoryContextReset(so->pageDataCxt); /* * check all tuples on page @@ -326,10 +332,21 @@ 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[] */ so->pageData[so->nPageData].heapPtr = it->t_tid; so->pageData[so->nPageData].recheck = recheck; + + /* + * In an index-only scan, also fetch the data from the tuple. + */ + if (scan->xs_want_itup) + { + oldcxt = MemoryContextSwitchTo(so->pageDataCxt); + so->pageData[so->nPageData].ftup = + gistFetchTuple(giststate, r, it); + MemoryContextSwitchTo(oldcxt); + } so->nPageData++; } else @@ -352,6 +369,12 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, item->blkno = InvalidBlockNumber; item->data.heap.heapPtr = it->t_tid; item->data.heap.recheck = recheck; + + /* + * In an index-only scan, also fetch the data from the tuple. + */ + if (scan->xs_want_itup) + item->data.heap.ftup = gistFetchTuple(giststate, r, it); } else { @@ -412,6 +435,13 @@ getNextNearest(IndexScanDesc scan) GISTScanOpaque so = (GISTScanOpaque) scan->opaque; bool res = false; + if (scan->xs_itup) + { + /* free previously returned tuple */ + pfree(scan->xs_itup); + scan->xs_itup = NULL; + } + do { GISTSearchItem *item = getNextGISTSearchItem(so); @@ -424,6 +454,10 @@ 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; + + /* in an index-only scan, also return the reconstructed tuple. */ + if (scan->xs_want_itup) + scan->xs_itup = item->data.heap.ftup; res = true; } else @@ -465,6 +499,8 @@ gistgettuple(PG_FUNCTION_ARGS) so->firstCall = false; so->curPageData = so->nPageData = 0; + if (so->pageDataCxt) + MemoryContextReset(so->pageDataCxt); fakeItem.blkno = GIST_ROOT_BLKNO; memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN)); @@ -483,10 +519,17 @@ gistgettuple(PG_FUNCTION_ARGS) { if (so->curPageData < so->nPageData) { + /* 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; + + /* in an index-only scan, also return the reconstructed tuple */ + if (scan->xs_want_itup) + scan->xs_itup = so->pageData[so->curPageData].ftup; + so->curPageData++; + PG_RETURN_BOOL(true); } @@ -533,6 +576,8 @@ gistgetbitmap(PG_FUNCTION_ARGS) /* Begin the scan by processing the root page */ so->curPageData = so->nPageData = 0; + if (so->pageDataCxt) + MemoryContextReset(so->pageDataCxt); fakeItem.blkno = GIST_ROOT_BLKNO; memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN)); @@ -558,3 +603,20 @@ gistgetbitmap(PG_FUNCTION_ARGS) PG_RETURN_INT64(ntids); } + +/* + * Can we do index-only scans on the given index column? + * + * Opclasses that implement a fetch function support index-only scans. + */ +Datum +gistcanreturn(PG_FUNCTION_ARGS) +{ + Relation index = (Relation) PG_GETARG_POINTER(0); + int attno = PG_GETARG_INT32(1); + + if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC))) + PG_RETURN_BOOL(true); + else + PG_RETURN_BOOL(false); +} diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c index 9fab6c87c05..9d21e3fb947 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,33 @@ gist_point_compress(PG_FUNCTION_ARGS) PG_RETURN_POINTER(entry); } +/* + * GiST Fetch method for point + * + * Get point coordinates from its bounding box coordinates and form new + * gistentry. + */ +Datum +gist_point_fetch(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + BOX *in = DatumGetBoxP(entry->key); + Point *r; + GISTENTRY *retval; + + retval = palloc(sizeof(GISTENTRY)); + + 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); + + 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 991858ff43f..3522d75a496 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. + */ + scan->xs_itupdesc = RelationGetDescr(r); + MemoryContextSwitchTo(oldCxt); PG_RETURN_POINTER(scan); @@ -141,6 +148,17 @@ gistrescan(PG_FUNCTION_ARGS) first_time = false; } + /* + * If we're doing an index-only scan, also create a memory context to hold + * the returned tuples. + */ + if (scan->xs_want_itup && so->pageDataCxt == NULL) + so->pageDataCxt = AllocSetContextCreate(so->giststate->scanCxt, + "GiST page data context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + /* create new, empty RBTree for search queue */ oldCxt = MemoryContextSwitchTo(so->queueCxt); so->queue = pairingheap_allocate(pairingheap_GISTSearchItem_cmp, scan); diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 824c40eb203..1680251a18b 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -294,8 +294,9 @@ gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, for (i = 0; i < r->rd_att->natts; i++) { - Datum datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]); + Datum datum; + datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]); gistdentryinit(giststate, i, &attdata[i], datum, r, p, o, FALSE, isnull[i]); @@ -598,6 +599,67 @@ gistFormTuple(GISTSTATE *giststate, Relation r, return res; } +/* + * initialize a GiST entry with fetched value in key field + */ +static Datum +gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r) +{ + GISTENTRY fentry; + GISTENTRY *fep; + + gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false); + + fep = (GISTENTRY *) + DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey], + giststate->supportCollation[nkey], + PointerGetDatum(&fentry))); + + /* fetchFn set 'key', return it to the caller */ + return fep->key; +} + +/* + * Fetch all keys in tuple. + * returns new IndexTuple that contains GISTENTRY with fetched data + */ +IndexTuple +gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple) +{ + MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt); + Datum fetchatt[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + int i; + + for (i = 0; i < r->rd_att->natts; i++) + { + Datum datum; + + datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]); + + if (giststate->fetchFn[i].fn_oid != InvalidOid) + { + if (!isnull[i]) + fetchatt[i] = gistFetchAtt(giststate, i, datum, r); + else + fetchatt[i] = (Datum) 0; + } + else + { + /* + * Index-only scans not supported for this column. Since the + * planner chose an index-only scan anyway, it is not interested + * in this column, and we can replace it with a NULL. + */ + isnull[i] = true; + fetchatt[i] = (Datum) 0; + } + } + 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 00c1d693766..2b27e732f13 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -722,11 +722,14 @@ index_vacuum_cleanup(IndexVacuumInfo *info, } /* ---------------- - * index_can_return - does index support index-only scans? + * index_can_return + * + * Does the index access method support index-only scans for the given + * column? * ---------------- */ bool -index_can_return(Relation indexRelation) +index_can_return(Relation indexRelation, int attno) { FmgrInfo *procedure; @@ -738,8 +741,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/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c index 3c79fb99b88..06c6944fc72 100644 --- a/src/backend/access/spgist/spgscan.c +++ b/src/backend/access/spgist/spgscan.c @@ -658,6 +658,7 @@ Datum spgcanreturn(PG_FUNCTION_ARGS) { Relation index = (Relation) PG_GETARG_POINTER(0); + /* int i = PG_GETARG_INT32(1); */ SpGistCache *cache; /* We can do it if the opclass config function says so */ diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 49ab3666b93..fdd6baba6c6 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -1786,15 +1786,13 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) { bool result; Bitmapset *attrs_used = NULL; - Bitmapset *index_attrs = NULL; + Bitmapset *index_canreturn_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 @@ -1824,7 +1822,10 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) pull_varattnos((Node *) rinfo->clause, rel->relid, &attrs_used); } - /* Construct a bitmapset of columns stored in the index. */ + /* + * Construct a bitmapset of columns that the index can return back in an + * index-only scan. + */ for (i = 0; i < index->ncolumns; i++) { int attno = index->indexkeys[i]; @@ -1836,16 +1837,17 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) if (attno == 0) continue; - index_attrs = - bms_add_member(index_attrs, - attno - FirstLowInvalidHeapAttributeNumber); + if (index->canreturn[i]) + index_canreturn_attrs = + bms_add_member(index_canreturn_attrs, + attno - FirstLowInvalidHeapAttributeNumber); } /* Do we have all the necessary attributes? */ - result = bms_is_subset(attrs_used, index_attrs); + result = bms_is_subset(attrs_used, index_canreturn_attrs); bms_free(attrs_used); - bms_free(index_attrs); + bms_free(index_canreturn_attrs); return result; } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 313a5c1ab2b..8abed2ae0da 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 + 1); } 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; |