On 02/27/2015 04:19 PM, Anastasia Lubennikova wrote:
I add MemoryContext listCxt to avoid memory leak. listCxt is created once
in gistrescan (only for index-only scan plan ) and reseted when scan of the
leaf page is finished.
I do not sure if the problem was completely solved, so I wait for feedback.
Yeah, I think that solves it.
* What's the reason for turning GISTScanOpaqueData.pageData from an array
to a List?
This array is field of structure GISTScanOpaqueData. Memory for that
structure allocated in function gistbeginscan(). The array is static so
it's declared only one time in structure:
GISTSearchHeapItem pageData [BLCKSZ/sizeof(IndexTupleData)]
But how could we know size of array if we don't know what data would be
returned? I mean type and amount.
You're only adding a pointer to the IndexTuple to GISTSearchHeapItem.
The GISTSearchHeapItem struct itself is still of constant size.
I spent a little time cleaning this up. I reverted that pageData change
so that it's an array again, put back the gist_indexonly.sql and
expected output files that were missing from your latest version,
removed a couple of unused local variables that gcc complained about. I
refactored gistFetchTuple a bit, because it was doing IMHO quite bogus
things with NULLs. It was passing NULLs to the opclass' fetch function,
but it didn't pass the isNull flag correctly. I changed it so that the
fetch function is not called at all for NULLs.
I think this is pretty close to being committable. I'll make a round of
editorializing over the docs, and the code comments as well.
The opr_sanity regression test is failing, there's apparently something
wrong with the pg_proc entries of the *canreturn functions. I haven't
looked into that yet; could you fix that?
- Heikki
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..80532c8 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -229,6 +229,8 @@ gistindex_keytest(IndexScanDesc scan,
* 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.
+ * 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,8 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
Buffer buffer;
Page page;
+ GISTSTATE *giststate = so->giststate;
+ Relation r = scan->indexRelation;
GISTPageOpaque opaque;
OffsetNumber maxoff;
OffsetNumber i;
@@ -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,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[]
*/
so->pageData[so->nPageData].heapPtr = it->t_tid;
so->pageData[so->nPageData].recheck = recheck;
+
+ /*
+ * If index-only scan is possible add the data fetched from index.
+ */
+ 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 +368,12 @@ 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);
}
else
{
@@ -424,6 +446,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
@@ -465,6 +492,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 +512,16 @@ 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;
+ /* If index-only scan is possible, return fetched data */
+ if (scan->xs_want_itup)
+ scan->xs_itup = so->pageData[so->curPageData].ftup;
+
so->curPageData++;
+
PG_RETURN_BOOL(true);
}
@@ -533,6 +568,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));
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 991858f..cb461fd 100644
--- a/src/backend/access/gist/gistscan.c
+++ b/src/backend/access/gist/gistscan.c
@@ -88,6 +88,12 @@ 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);
@@ -106,6 +112,12 @@ gistrescan(PG_FUNCTION_ARGS)
int i;
MemoryContext oldCxt;
+ 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);
/* rescan an existing indexscan --- reset state */
/*
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 38af0e0..1e755ed 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -618,6 +618,50 @@ 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 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 = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
+ if (!isnull[i])
+ fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
+ else
+ 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 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 313a5c1..a4d380e 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 ce83042..b8e0ba7 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];
@@ -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. Used for index-only scans */
} GISTSearchHeapItem;
/* Unvisited item, either index page or heap tuple */
@@ -157,6 +159,8 @@ typedef struct GISTScanOpaqueData
GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
OffsetNumber nPageData; /* number of valid items in array */
OffsetNumber curPageData; /* next item to return */
+ MemoryContext pageDataCxt; /* context holding the fetched tuples, for
+ index-only scans */
} GISTScanOpaqueData;
typedef GISTScanOpaqueData *GISTScanOpaque;
@@ -409,6 +413,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,7 +513,7 @@ extern void gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
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 IndexTuple gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple);
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 4268b99..6d6c8f4 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_ ));
@@ -4064,6 +4066,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_ ));
@@ -4082,6 +4086,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_ ));
@@ -5014,7 +5020,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 8da6c6c..2a91620 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