Skip to content

Commit 86d911e

Browse files
committed
Allow index AMs to cache data across aminsert calls within a SQL command.
It's always been possible for index AMs to cache data across successive amgettuple calls within a single SQL command: the IndexScanDesc.opaque field is meant for precisely that. However, no comparable facility exists for amortizing setup work across successive aminsert calls. This patch adds such a feature and teaches GIN, GIST, and BRIN to use it to amortize catalog lookups they'd previously been doing on every call. (The other standard index AMs keep everything they need in the relcache, so there's little to improve there.) For GIN, the overall improvement in a statement that inserts many rows can be as much as 10%, though it seems a bit less for the other two. In addition, this makes a really significant difference in runtime for CLOBBER_CACHE_ALWAYS tests, since in those builds the repeated catalog lookups are vastly more expensive. The reason this has been hard up to now is that the aminsert function is not passed any useful place to cache per-statement data. What I chose to do is to add suitable fields to struct IndexInfo and pass that to aminsert. That's not widening the index AM API very much because IndexInfo is already within the ken of ambuild; in fact, by passing the same info to aminsert as to ambuild, this is really removing an inconsistency in the AM API. Discussion: https://postgr.es/m/27568.1486508680@sss.pgh.pa.us
1 parent 7c5d8c1 commit 86d911e

File tree

26 files changed

+117
-52
lines changed

26 files changed

+117
-52
lines changed

contrib/bloom/blinsert.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ blbuildempty(Relation index)
190190
*/
191191
bool
192192
blinsert(Relation index, Datum *values, bool *isnull,
193-
ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
193+
ItemPointer ht_ctid, Relation heapRel,
194+
IndexUniqueCheck checkUnique,
195+
IndexInfo *indexInfo)
194196
{
195197
BloomState blstate;
196198
BloomTuple *itup;

contrib/bloom/bloom.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ extern bool blvalidate(Oid opclassoid);
189189
/* index access method interface functions */
190190
extern bool blinsert(Relation index, Datum *values, bool *isnull,
191191
ItemPointer ht_ctid, Relation heapRel,
192-
IndexUniqueCheck checkUnique);
192+
IndexUniqueCheck checkUnique,
193+
struct IndexInfo *indexInfo);
193194
extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
194195
extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
195196
extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,

doc/src/sgml/indexam.sgml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ aminsert (Relation indexRelation,
259259
bool *isnull,
260260
ItemPointer heap_tid,
261261
Relation heapRelation,
262-
IndexUniqueCheck checkUnique);
262+
IndexUniqueCheck checkUnique,
263+
IndexInfo *indexInfo);
263264
</programlisting>
264265
Insert a new tuple into an existing index. The <literal>values</> and
265266
<literal>isnull</> arrays give the key values to be indexed, and
@@ -287,6 +288,14 @@ aminsert (Relation indexRelation,
287288
indexed, <function>aminsert</> should just return without doing anything.
288289
</para>
289290

291+
<para>
292+
If the index AM wishes to cache data across successive index insertions
293+
within a SQL statement, it can allocate space
294+
in <literal>indexInfo-&gt;ii_Context</literal> and store a pointer to the
295+
data in <literal>indexInfo-&gt;ii_AmCache</literal> (which will be NULL
296+
initially).
297+
</para>
298+
290299
<para>
291300
<programlisting>
292301
IndexBulkDeleteResult *

src/backend/access/brin/brin.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,15 @@ brinhandler(PG_FUNCTION_ARGS)
131131
bool
132132
brininsert(Relation idxRel, Datum *values, bool *nulls,
133133
ItemPointer heaptid, Relation heapRel,
134-
IndexUniqueCheck checkUnique)
134+
IndexUniqueCheck checkUnique,
135+
IndexInfo *indexInfo)
135136
{
136137
BlockNumber pagesPerRange;
137-
BrinDesc *bdesc = NULL;
138+
BrinDesc *bdesc = (BrinDesc *) indexInfo->ii_AmCache;
138139
BrinRevmap *revmap;
139140
Buffer buf = InvalidBuffer;
140141
MemoryContext tupcxt = NULL;
141-
MemoryContext oldcxt = NULL;
142+
MemoryContext oldcxt = CurrentMemoryContext;
142143

143144
revmap = brinRevmapInitialize(idxRel, &pagesPerRange, NULL);
144145

@@ -163,14 +164,21 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
163164
if (!brtup)
164165
break;
165166

166-
/* First time through? */
167+
/* First time through in this statement? */
167168
if (bdesc == NULL)
168169
{
170+
MemoryContextSwitchTo(indexInfo->ii_Context);
169171
bdesc = brin_build_desc(idxRel);
172+
indexInfo->ii_AmCache = (void *) bdesc;
173+
MemoryContextSwitchTo(oldcxt);
174+
}
175+
/* First time through in this brininsert call? */
176+
if (tupcxt == NULL)
177+
{
170178
tupcxt = AllocSetContextCreate(CurrentMemoryContext,
171179
"brininsert cxt",
172180
ALLOCSET_DEFAULT_SIZES);
173-
oldcxt = MemoryContextSwitchTo(tupcxt);
181+
MemoryContextSwitchTo(tupcxt);
174182
}
175183

176184
dtup = brin_deform_tuple(bdesc, brtup);
@@ -261,12 +269,9 @@ brininsert(Relation idxRel, Datum *values, bool *nulls,
261269
brinRevmapTerminate(revmap);
262270
if (BufferIsValid(buf))
263271
ReleaseBuffer(buf);
264-
if (bdesc != NULL)
265-
{
266-
brin_free_desc(bdesc);
267-
MemoryContextSwitchTo(oldcxt);
272+
MemoryContextSwitchTo(oldcxt);
273+
if (tupcxt != NULL)
268274
MemoryContextDelete(tupcxt);
269-
}
270275

271276
return false;
272277
}

src/backend/access/gin/gininsert.c

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -482,39 +482,48 @@ ginHeapTupleInsert(GinState *ginstate, OffsetNumber attnum,
482482
bool
483483
gininsert(Relation index, Datum *values, bool *isnull,
484484
ItemPointer ht_ctid, Relation heapRel,
485-
IndexUniqueCheck checkUnique)
485+
IndexUniqueCheck checkUnique,
486+
IndexInfo *indexInfo)
486487
{
487-
GinState ginstate;
488+
GinState *ginstate = (GinState *) indexInfo->ii_AmCache;
488489
MemoryContext oldCtx;
489490
MemoryContext insertCtx;
490491
int i;
491492

493+
/* Initialize GinState cache if first call in this statement */
494+
if (ginstate == NULL)
495+
{
496+
oldCtx = MemoryContextSwitchTo(indexInfo->ii_Context);
497+
ginstate = (GinState *) palloc(sizeof(GinState));
498+
initGinState(ginstate, index);
499+
indexInfo->ii_AmCache = (void *) ginstate;
500+
MemoryContextSwitchTo(oldCtx);
501+
}
502+
492503
insertCtx = AllocSetContextCreate(CurrentMemoryContext,
493504
"Gin insert temporary context",
494505
ALLOCSET_DEFAULT_SIZES);
495506

496507
oldCtx = MemoryContextSwitchTo(insertCtx);
497508

498-
initGinState(&ginstate, index);
499-
500509
if (GinGetUseFastUpdate(index))
501510
{
502511
GinTupleCollector collector;
503512

504513
memset(&collector, 0, sizeof(GinTupleCollector));
505514

506-
for (i = 0; i < ginstate.origTupdesc->natts; i++)
507-
ginHeapTupleFastCollect(&ginstate, &collector,
515+
for (i = 0; i < ginstate->origTupdesc->natts; i++)
516+
ginHeapTupleFastCollect(ginstate, &collector,
508517
(OffsetNumber) (i + 1),
509518
values[i], isnull[i],
510519
ht_ctid);
511520

512-
ginHeapTupleFastInsert(&ginstate, &collector);
521+
ginHeapTupleFastInsert(ginstate, &collector);
513522
}
514523
else
515524
{
516-
for (i = 0; i < ginstate.origTupdesc->natts; i++)
517-
ginHeapTupleInsert(&ginstate, (OffsetNumber) (i + 1),
525+
for (i = 0; i < ginstate->origTupdesc->natts; i++)
526+
ginHeapTupleInsert(ginstate, (OffsetNumber) (i + 1),
518527
values[i], isnull[i],
519528
ht_ctid);
520529
}

src/backend/access/gist/gist.c

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "access/gistscan.h"
1919
#include "catalog/pg_collation.h"
2020
#include "miscadmin.h"
21+
#include "nodes/execnodes.h"
2122
#include "utils/builtins.h"
2223
#include "utils/index_selfuncs.h"
2324
#include "utils/memutils.h"
@@ -144,21 +145,23 @@ gistbuildempty(Relation index)
144145
bool
145146
gistinsert(Relation r, Datum *values, bool *isnull,
146147
ItemPointer ht_ctid, Relation heapRel,
147-
IndexUniqueCheck checkUnique)
148+
IndexUniqueCheck checkUnique,
149+
IndexInfo *indexInfo)
148150
{
151+
GISTSTATE *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
149152
IndexTuple itup;
150-
GISTSTATE *giststate;
151153
MemoryContext oldCxt;
152154

153-
giststate = initGISTstate(r);
155+
/* Initialize GISTSTATE cache if first call in this statement */
156+
if (giststate == NULL)
157+
{
158+
oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
159+
giststate = initGISTstate(r);
160+
giststate->tempCxt = createTempGistContext();
161+
indexInfo->ii_AmCache = (void *) giststate;
162+
MemoryContextSwitchTo(oldCxt);
163+
}
154164

155-
/*
156-
* We use the giststate's scan context as temp context too. This means
157-
* that any memory leaked by the support functions is not reclaimed until
158-
* end of insert. In most cases, we aren't going to call the support
159-
* functions very many times before finishing the insert, so this seems
160-
* cheaper than resetting a temp context for each function call.
161-
*/
162165
oldCxt = MemoryContextSwitchTo(giststate->tempCxt);
163166

164167
itup = gistFormTuple(giststate, r,
@@ -169,7 +172,7 @@ gistinsert(Relation r, Datum *values, bool *isnull,
169172

170173
/* cleanup */
171174
MemoryContextSwitchTo(oldCxt);
172-
freeGISTstate(giststate);
175+
MemoryContextReset(giststate->tempCxt);
173176

174177
return false;
175178
}

src/backend/access/hash/hash.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ hashbuildCallback(Relation index,
232232
bool
233233
hashinsert(Relation rel, Datum *values, bool *isnull,
234234
ItemPointer ht_ctid, Relation heapRel,
235-
IndexUniqueCheck checkUnique)
235+
IndexUniqueCheck checkUnique,
236+
IndexInfo *indexInfo)
236237
{
237238
Datum index_values[1];
238239
bool index_isnull[1];

src/backend/access/heap/tuptoaster.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,7 +1604,9 @@ toast_save_datum(Relation rel, Datum value,
16041604
* Create the index entry. We cheat a little here by not using
16051605
* FormIndexDatum: this relies on the knowledge that the index columns
16061606
* are the same as the initial columns of the table for all the
1607-
* indexes.
1607+
* indexes. We also cheat by not providing an IndexInfo: this is okay
1608+
* for now because btree doesn't need one, but we might have to be
1609+
* more honest someday.
16081610
*
16091611
* Note also that there had better not be any user-created index on
16101612
* the TOAST table, since we don't bother to update anything else.
@@ -1617,7 +1619,8 @@ toast_save_datum(Relation rel, Datum value,
16171619
&(toasttup->t_self),
16181620
toastrel,
16191621
toastidxs[i]->rd_index->indisunique ?
1620-
UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
1622+
UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
1623+
NULL);
16211624
}
16221625

16231626
/*

src/backend/access/index/indexam.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ index_insert(Relation indexRelation,
196196
bool *isnull,
197197
ItemPointer heap_t_ctid,
198198
Relation heapRelation,
199-
IndexUniqueCheck checkUnique)
199+
IndexUniqueCheck checkUnique,
200+
IndexInfo *indexInfo)
200201
{
201202
RELATION_CHECKS;
202203
CHECK_REL_PROCEDURE(aminsert);
@@ -208,7 +209,7 @@ index_insert(Relation indexRelation,
208209

209210
return indexRelation->rd_amroutine->aminsert(indexRelation, values, isnull,
210211
heap_t_ctid, heapRelation,
211-
checkUnique);
212+
checkUnique, indexInfo);
212213
}
213214

214215
/*

src/backend/access/nbtree/nbtree.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ btbuildempty(Relation index)
276276
bool
277277
btinsert(Relation rel, Datum *values, bool *isnull,
278278
ItemPointer ht_ctid, Relation heapRel,
279-
IndexUniqueCheck checkUnique)
279+
IndexUniqueCheck checkUnique,
280+
IndexInfo *indexInfo)
280281
{
281282
bool result;
282283
IndexTuple itup;

src/backend/access/spgist/spginsert.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ spgbuildempty(Relation index)
206206
bool
207207
spginsert(Relation index, Datum *values, bool *isnull,
208208
ItemPointer ht_ctid, Relation heapRel,
209-
IndexUniqueCheck checkUnique)
209+
IndexUniqueCheck checkUnique,
210+
IndexInfo *indexInfo)
210211
{
211212
SpGistState spgstate;
212213
MemoryContext oldCtx;

src/backend/catalog/index.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,10 @@ BuildIndexInfo(Relation index)
16871687
ii->ii_Concurrent = false;
16881688
ii->ii_BrokenHotChain = false;
16891689

1690+
/* set up for possible use by index AM */
1691+
ii->ii_AmCache = NULL;
1692+
ii->ii_Context = CurrentMemoryContext;
1693+
16901694
return ii;
16911695
}
16921696

@@ -3158,7 +3162,8 @@ validate_index_heapscan(Relation heapRelation,
31583162
&rootTuple,
31593163
heapRelation,
31603164
indexInfo->ii_Unique ?
3161-
UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
3165+
UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
3166+
indexInfo);
31623167

31633168
state->tups_inserted += 1;
31643169
}

src/backend/catalog/indexing.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
139139
&(heapTuple->t_self), /* tid of heap tuple */
140140
heapRelation,
141141
relationDescs[i]->rd_index->indisunique ?
142-
UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
142+
UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
143+
indexInfo);
143144
}
144145

145146
ExecDropSingleTupleTableSlot(slot);

src/backend/catalog/toasting.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
315315
indexInfo->ii_ReadyForInserts = true;
316316
indexInfo->ii_Concurrent = false;
317317
indexInfo->ii_BrokenHotChain = false;
318+
indexInfo->ii_AmCache = NULL;
319+
indexInfo->ii_Context = CurrentMemoryContext;
318320

319321
collationObjectId[0] = InvalidOid;
320322
collationObjectId[1] = InvalidOid;

src/backend/commands/constraint.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ unique_key_recheck(PG_FUNCTION_ARGS)
165165
* index will know about.
166166
*/
167167
index_insert(indexRel, values, isnull, &(new_row->t_self),
168-
trigdata->tg_relation, UNIQUE_CHECK_EXISTING);
168+
trigdata->tg_relation, UNIQUE_CHECK_EXISTING,
169+
indexInfo);
169170
}
170171
else
171172
{

src/backend/commands/indexcmds.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ CheckIndexCompatible(Oid oldId,
183183
indexInfo->ii_ExclusionOps = NULL;
184184
indexInfo->ii_ExclusionProcs = NULL;
185185
indexInfo->ii_ExclusionStrats = NULL;
186+
indexInfo->ii_AmCache = NULL;
187+
indexInfo->ii_Context = CurrentMemoryContext;
186188
typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
187189
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
188190
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
@@ -562,6 +564,8 @@ DefineIndex(Oid relationId,
562564
indexInfo->ii_ReadyForInserts = !stmt->concurrent;
563565
indexInfo->ii_Concurrent = stmt->concurrent;
564566
indexInfo->ii_BrokenHotChain = false;
567+
indexInfo->ii_AmCache = NULL;
568+
indexInfo->ii_Context = CurrentMemoryContext;
565569

566570
typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
567571
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));

src/backend/executor/execIndexing.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,8 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
391391
isnull, /* null flags */
392392
tupleid, /* tid of heap tuple */
393393
heapRelation, /* heap relation */
394-
checkUnique); /* type of uniqueness check to do */
394+
checkUnique, /* type of uniqueness check to do */
395+
indexInfo); /* index AM may need this */
395396

396397
/*
397398
* If the index has an associated exclusion constraint, check that.

src/include/access/amapi.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ typedef bool (*aminsert_function) (Relation indexRelation,
7272
bool *isnull,
7373
ItemPointer heap_tid,
7474
Relation heapRelation,
75-
IndexUniqueCheck checkUnique);
75+
IndexUniqueCheck checkUnique,
76+
struct IndexInfo *indexInfo);
7677

7778
/* bulk delete */
7879
typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info,

src/include/access/brin_internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ extern IndexBuildResult *brinbuild(Relation heap, Relation index,
8989
extern void brinbuildempty(Relation index);
9090
extern bool brininsert(Relation idxRel, Datum *values, bool *nulls,
9191
ItemPointer heaptid, Relation heapRel,
92-
IndexUniqueCheck checkUnique);
92+
IndexUniqueCheck checkUnique,
93+
struct IndexInfo *indexInfo);
9394
extern IndexScanDesc brinbeginscan(Relation r, int nkeys, int norderbys);
9495
extern int64 bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
9596
extern void brinrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,

0 commit comments

Comments
 (0)