From 55b59eda13a742f8af913734e22ecc8a21754414 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas <heikki.linnakangas@iki.fi> Date: Thu, 26 Mar 2015 23:07:52 +0200 Subject: [PATCH] Fix GiST index-only scans for opclasses with different storage type. We cannot use the index's tuple descriptor directly to describe the index tuples returned in an index-only scan. That's because the index might use a different datatype for the values stored on disk than the type originally indexed. As long as they were both pass-by-ref, it worked, but will not work for pass-by-value types of different sizes. I noticed this as a crash when I started hacking a patch to add fetch methods to btree_gist. --- src/backend/access/gist/gistscan.c | 34 ++++++++++++++++++++++++------ src/backend/access/gist/gistutil.c | 2 +- src/include/access/gist_private.h | 2 ++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c index 3522d75a496..6f653982302 100644 --- a/src/backend/access/gist/gistscan.c +++ b/src/backend/access/gist/gistscan.c @@ -89,11 +89,9 @@ 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. + * All fields required for index-only scans are initialized in gistrescan, + * as we don't know yet if we're doing an index-only scan or not. */ - scan->xs_itupdesc = RelationGetDescr(r); MemoryContextSwitchTo(oldCxt); @@ -149,15 +147,37 @@ gistrescan(PG_FUNCTION_ARGS) } /* - * If we're doing an index-only scan, also create a memory context to hold - * the returned tuples. + * If we're doing an index-only scan, on the first call, also initialize + * a tuple descriptor to represent the returned index tuples and create a + * memory context to hold them during the scan. */ - if (scan->xs_want_itup && so->pageDataCxt == NULL) + if (scan->xs_want_itup && !scan->xs_itupdesc) + { + int natts; + int attno; + + /* + * The storage type of the index can be different from the original + * datatype being indexed, so we cannot just grab the index's tuple + * descriptor. Instead, construct a descriptor with the original data + * types. + */ + natts = RelationGetNumberOfAttributes(scan->indexRelation); + so->giststate->fetchTupdesc = CreateTemplateTupleDesc(natts, false); + for (attno = 1; attno <= natts; attno++) + { + TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL, + scan->indexRelation->rd_opcintype[attno - 1], + -1, 0); + } + scan->xs_itupdesc = so->giststate->fetchTupdesc; + 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); diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 1680251a18b..bf9fbf30a8b 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -657,7 +657,7 @@ gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple) } MemoryContextSwitchTo(oldcxt); - return index_form_tuple(giststate->tupdesc, fetchatt, isnull); + return index_form_tuple(giststate->fetchTupdesc, fetchatt, isnull); } float diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h index 3693893e261..9d3714d27d2 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -78,6 +78,8 @@ typedef struct GISTSTATE MemoryContext tempCxt; /* short-term context for calling functions */ TupleDesc tupdesc; /* index's tuple descriptor */ + TupleDesc fetchTupdesc; /* tuple descriptor for tuples returned in an + * index-only scan */ FmgrInfo consistentFn[INDEX_MAX_KEYS]; FmgrInfo unionFn[INDEX_MAX_KEYS]; -- GitLab