Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 19d8e23

Browse files
committed
Ignore BRIN indexes when checking for HOT updates
When determining whether an index update may be skipped by using HOT, we can ignore attributes indexed by block summarizing indexes without references to individual tuples that need to be cleaned up. A new type TU_UpdateIndexes provides a signal to the executor to determine which indexes to update - no indexes, all indexes, or only the summarizing indexes. This also removes rd_indexattr list, and replaces it with rd_attrsvalid flag. The list was not used anywhere, and a simple flag is sufficient. This was originally committed as 5753d4e, but then got reverted by e3fcca0 because of correctness issues. Original patch by Josef Simanek, various fixes and improvements by Tomas Vondra and me. Authors: Matthias van de Meent, Josef Simanek, Tomas Vondra Reviewed-by: Tomas Vondra, Alvaro Herrera Discussion: https://postgr.es/m/05ebcb44-f383-86e3-4f31-0a97a55634cf@enterprisedb.com Discussion: https://postgr.es/m/CAFp7QwpMRGcDAQumN7onN9HjrJ3u4X3ZRXdGFT0K5G2JWvnbWg%40mail.gmail.com
1 parent e858312 commit 19d8e23

File tree

30 files changed

+448
-76
lines changed

30 files changed

+448
-76
lines changed

doc/src/sgml/indexam.sgml

+13
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ typedef struct IndexAmRoutine
127127
bool amcaninclude;
128128
/* does AM use maintenance_work_mem? */
129129
bool amusemaintenanceworkmem;
130+
/* does AM summarize tuples, with at least all tuples in the block
131+
* summarized in one summary */
132+
bool amsummarizing;
130133
/* OR of parallel vacuum flags */
131134
uint8 amparallelvacuumoptions;
132135
/* type of data stored in index, or InvalidOid if variable */
@@ -247,6 +250,16 @@ typedef struct IndexAmRoutine
247250
null, independently of <structfield>amoptionalkey</structfield>.
248251
</para>
249252

253+
<para>
254+
The <structfield>amsummarizing</structfield> flag indicates whether the
255+
access method summarizes the indexed tuples, with summarizing granularity
256+
of at least per block.
257+
Access methods that do not point to individual tuples, but to block ranges
258+
(like <acronym>BRIN</acronym>), may allow the <acronym>HOT</acronym> optimization
259+
to continue. This does not apply to attributes referenced in index
260+
predicates, an update of such attribute always disables <acronym>HOT</acronym>.
261+
</para>
262+
250263
</sect1>
251264

252265
<sect1 id="index-functions">

src/backend/access/brin/brin.c

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ brinhandler(PG_FUNCTION_ARGS)
109109
amroutine->amcanparallel = false;
110110
amroutine->amcaninclude = false;
111111
amroutine->amusemaintenanceworkmem = false;
112+
amroutine->amsummarizing = true;
112113
amroutine->amparallelvacuumoptions =
113114
VACUUM_OPTION_PARALLEL_CLEANUP;
114115
amroutine->amkeytype = InvalidOid;

src/backend/access/gin/ginutil.c

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ ginhandler(PG_FUNCTION_ARGS)
5656
amroutine->amcanparallel = false;
5757
amroutine->amcaninclude = false;
5858
amroutine->amusemaintenanceworkmem = true;
59+
amroutine->amsummarizing = false;
5960
amroutine->amparallelvacuumoptions =
6061
VACUUM_OPTION_PARALLEL_BULKDEL | VACUUM_OPTION_PARALLEL_CLEANUP;
6162
amroutine->amkeytype = InvalidOid;

src/backend/access/gist/gist.c

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ gisthandler(PG_FUNCTION_ARGS)
7878
amroutine->amcanparallel = false;
7979
amroutine->amcaninclude = true;
8080
amroutine->amusemaintenanceworkmem = false;
81+
amroutine->amsummarizing = false;
8182
amroutine->amparallelvacuumoptions =
8283
VACUUM_OPTION_PARALLEL_BULKDEL | VACUUM_OPTION_PARALLEL_COND_CLEANUP;
8384
amroutine->amkeytype = InvalidOid;

src/backend/access/hash/hash.c

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ hashhandler(PG_FUNCTION_ARGS)
7575
amroutine->amcanparallel = false;
7676
amroutine->amcaninclude = false;
7777
amroutine->amusemaintenanceworkmem = false;
78+
amroutine->amsummarizing = false;
7879
amroutine->amparallelvacuumoptions =
7980
VACUUM_OPTION_PARALLEL_BULKDEL;
8081
amroutine->amkeytype = INT4OID;

src/backend/access/heap/heapam.c

+44-4
Original file line numberDiff line numberDiff line change
@@ -2924,11 +2924,13 @@ simple_heap_delete(Relation relation, ItemPointer tid)
29242924
TM_Result
29252925
heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
29262926
CommandId cid, Snapshot crosscheck, bool wait,
2927-
TM_FailureData *tmfd, LockTupleMode *lockmode)
2927+
TM_FailureData *tmfd, LockTupleMode *lockmode,
2928+
TU_UpdateIndexes *update_indexes)
29282929
{
29292930
TM_Result result;
29302931
TransactionId xid = GetCurrentTransactionId();
29312932
Bitmapset *hot_attrs;
2933+
Bitmapset *sum_attrs;
29322934
Bitmapset *key_attrs;
29332935
Bitmapset *id_attrs;
29342936
Bitmapset *interesting_attrs;
@@ -2951,6 +2953,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
29512953
bool have_tuple_lock = false;
29522954
bool iscombo;
29532955
bool use_hot_update = false;
2956+
bool summarized_update = false;
29542957
bool key_intact;
29552958
bool all_visible_cleared = false;
29562959
bool all_visible_cleared_new = false;
@@ -2996,12 +2999,16 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
29962999
* Note that we get copies of each bitmap, so we need not worry about
29973000
* relcache flush happening midway through.
29983001
*/
2999-
hot_attrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_ALL);
3002+
hot_attrs = RelationGetIndexAttrBitmap(relation,
3003+
INDEX_ATTR_BITMAP_HOT_BLOCKING);
3004+
sum_attrs = RelationGetIndexAttrBitmap(relation,
3005+
INDEX_ATTR_BITMAP_SUMMARIZED);
30003006
key_attrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_KEY);
30013007
id_attrs = RelationGetIndexAttrBitmap(relation,
30023008
INDEX_ATTR_BITMAP_IDENTITY_KEY);
30033009
interesting_attrs = NULL;
30043010
interesting_attrs = bms_add_members(interesting_attrs, hot_attrs);
3011+
interesting_attrs = bms_add_members(interesting_attrs, sum_attrs);
30053012
interesting_attrs = bms_add_members(interesting_attrs, key_attrs);
30063013
interesting_attrs = bms_add_members(interesting_attrs, id_attrs);
30073014

@@ -3311,7 +3318,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
33113318
UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
33123319
if (vmbuffer != InvalidBuffer)
33133320
ReleaseBuffer(vmbuffer);
3321+
*update_indexes = TU_None;
3322+
33143323
bms_free(hot_attrs);
3324+
bms_free(sum_attrs);
33153325
bms_free(key_attrs);
33163326
bms_free(id_attrs);
33173327
bms_free(modified_attrs);
@@ -3633,7 +3643,19 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
36333643
* changed.
36343644
*/
36353645
if (!bms_overlap(modified_attrs, hot_attrs))
3646+
{
36363647
use_hot_update = true;
3648+
3649+
/*
3650+
* If none of the columns that are used in hot-blocking indexes
3651+
* were updated, we can apply HOT, but we do still need to check
3652+
* if we need to update the summarizing indexes, and update those
3653+
* indexes if the columns were updated, or we may fail to detect
3654+
* e.g. value bound changes in BRIN minmax indexes.
3655+
*/
3656+
if (bms_overlap(modified_attrs, sum_attrs))
3657+
summarized_update = true;
3658+
}
36373659
}
36383660
else
36393661
{
@@ -3793,10 +3815,27 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
37933815
heap_freetuple(heaptup);
37943816
}
37953817

3818+
/*
3819+
* If it is a HOT update, the update may still need to update summarized
3820+
* indexes, lest we fail to update those summaries and get incorrect
3821+
* results (for example, minmax bounds of the block may change with this
3822+
* update).
3823+
*/
3824+
if (use_hot_update)
3825+
{
3826+
if (summarized_update)
3827+
*update_indexes = TU_Summarizing;
3828+
else
3829+
*update_indexes = TU_None;
3830+
}
3831+
else
3832+
*update_indexes = TU_All;
3833+
37963834
if (old_key_tuple != NULL && old_key_copied)
37973835
heap_freetuple(old_key_tuple);
37983836

37993837
bms_free(hot_attrs);
3838+
bms_free(sum_attrs);
38003839
bms_free(key_attrs);
38013840
bms_free(id_attrs);
38023841
bms_free(modified_attrs);
@@ -3951,7 +3990,8 @@ HeapDetermineColumnsInfo(Relation relation,
39513990
* via ereport().
39523991
*/
39533992
void
3954-
simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
3993+
simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup,
3994+
TU_UpdateIndexes *update_indexes)
39553995
{
39563996
TM_Result result;
39573997
TM_FailureData tmfd;
@@ -3960,7 +4000,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
39604000
result = heap_update(relation, otid, tup,
39614001
GetCurrentCommandId(true), InvalidSnapshot,
39624002
true /* wait for commit */ ,
3963-
&tmfd, &lockmode);
4003+
&tmfd, &lockmode, update_indexes);
39644004
switch (result)
39654005
{
39664006
case TM_SelfModified:

src/backend/access/heap/heapam_handler.c

+15-4
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ static TM_Result
314314
heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
315315
CommandId cid, Snapshot snapshot, Snapshot crosscheck,
316316
bool wait, TM_FailureData *tmfd,
317-
LockTupleMode *lockmode, bool *update_indexes)
317+
LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
318318
{
319319
bool shouldFree = true;
320320
HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
@@ -325,7 +325,7 @@ heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
325325
tuple->t_tableOid = slot->tts_tableOid;
326326

327327
result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
328-
tmfd, lockmode);
328+
tmfd, lockmode, update_indexes);
329329
ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
330330

331331
/*
@@ -334,9 +334,20 @@ heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
334334
* Note: heap_update returns the tid (location) of the new tuple in the
335335
* t_self field.
336336
*
337-
* If it's a HOT update, we mustn't insert new index entries.
337+
* If the update is not HOT, we must update all indexes. If the update
338+
* is HOT, it could be that we updated summarized columns, so we either
339+
* update only summarized indexes, or none at all.
338340
*/
339-
*update_indexes = result == TM_Ok && !HeapTupleIsHeapOnly(tuple);
341+
if (result != TM_Ok)
342+
{
343+
Assert(*update_indexes == TU_None);
344+
*update_indexes = TU_None;
345+
}
346+
else if (!HeapTupleIsHeapOnly(tuple))
347+
Assert(*update_indexes == TU_All);
348+
else
349+
Assert((*update_indexes == TU_Summarizing) ||
350+
(*update_indexes == TU_None));
340351

341352
if (shouldFree)
342353
pfree(tuple);

src/backend/access/nbtree/nbtree.c

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ bthandler(PG_FUNCTION_ARGS)
114114
amroutine->amcanparallel = true;
115115
amroutine->amcaninclude = true;
116116
amroutine->amusemaintenanceworkmem = false;
117+
amroutine->amsummarizing = false;
117118
amroutine->amparallelvacuumoptions =
118119
VACUUM_OPTION_PARALLEL_BULKDEL | VACUUM_OPTION_PARALLEL_COND_CLEANUP;
119120
amroutine->amkeytype = InvalidOid;

src/backend/access/spgist/spgutils.c

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ spghandler(PG_FUNCTION_ARGS)
6262
amroutine->amcanparallel = false;
6363
amroutine->amcaninclude = true;
6464
amroutine->amusemaintenanceworkmem = false;
65+
amroutine->amsummarizing = false;
6566
amroutine->amparallelvacuumoptions =
6667
VACUUM_OPTION_PARALLEL_BULKDEL | VACUUM_OPTION_PARALLEL_COND_CLEANUP;
6768
amroutine->amkeytype = InvalidOid;

src/backend/access/table/tableam.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ void
345345
simple_table_tuple_update(Relation rel, ItemPointer otid,
346346
TupleTableSlot *slot,
347347
Snapshot snapshot,
348-
bool *update_indexes)
348+
TU_UpdateIndexes *update_indexes)
349349
{
350350
TM_Result result;
351351
TM_FailureData tmfd;

src/backend/catalog/index.c

+6-3
Original file line numberDiff line numberDiff line change
@@ -1383,7 +1383,8 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId,
13831383
oldInfo->ii_Unique,
13841384
oldInfo->ii_NullsNotDistinct,
13851385
false, /* not ready for inserts */
1386-
true);
1386+
true,
1387+
indexRelation->rd_indam->amsummarizing);
13871388

13881389
/*
13891390
* Extract the list of column names and the column numbers for the new
@@ -2455,7 +2456,8 @@ BuildIndexInfo(Relation index)
24552456
indexStruct->indisunique,
24562457
indexStruct->indnullsnotdistinct,
24572458
indexStruct->indisready,
2458-
false);
2459+
false,
2460+
index->rd_indam->amsummarizing);
24592461

24602462
/* fill in attribute numbers */
24612463
for (i = 0; i < numAtts; i++)
@@ -2515,7 +2517,8 @@ BuildDummyIndexInfo(Relation index)
25152517
indexStruct->indisunique,
25162518
indexStruct->indnullsnotdistinct,
25172519
indexStruct->indisready,
2518-
false);
2520+
false,
2521+
index->rd_indam->amsummarizing);
25192522

25202523
/* fill in attribute numbers */
25212524
for (i = 0; i < numAtts; i++)

src/backend/catalog/indexing.c

+25-10
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ CatalogCloseIndexes(CatalogIndexState indstate)
7272
* This is effectively a cut-down version of ExecInsertIndexTuples.
7373
*/
7474
static void
75-
CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
75+
CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple,
76+
TU_UpdateIndexes updateIndexes)
7677
{
7778
int i;
7879
int numIndexes;
@@ -82,17 +83,21 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
8283
IndexInfo **indexInfoArray;
8384
Datum values[INDEX_MAX_KEYS];
8485
bool isnull[INDEX_MAX_KEYS];
86+
bool onlySummarized = (updateIndexes == TU_Summarizing);
8587

8688
/*
8789
* HOT update does not require index inserts. But with asserts enabled we
8890
* want to check that it'd be legal to currently insert into the
8991
* table/index.
9092
*/
9193
#ifndef USE_ASSERT_CHECKING
92-
if (HeapTupleIsHeapOnly(heapTuple))
94+
if (HeapTupleIsHeapOnly(heapTuple) && !onlySummarized)
9395
return;
9496
#endif
9597

98+
/* When only updating summarized indexes, the tuple has to be HOT. */
99+
Assert((!onlySummarized) || HeapTupleIsHeapOnly(heapTuple));
100+
96101
/*
97102
* Get information from the state structure. Fall out if nothing to do.
98103
*/
@@ -135,13 +140,20 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
135140

136141
/* see earlier check above */
137142
#ifdef USE_ASSERT_CHECKING
138-
if (HeapTupleIsHeapOnly(heapTuple))
143+
if (HeapTupleIsHeapOnly(heapTuple) && !onlySummarized)
139144
{
140145
Assert(!ReindexIsProcessingIndex(RelationGetRelid(index)));
141146
continue;
142147
}
143148
#endif /* USE_ASSERT_CHECKING */
144149

150+
/*
151+
* Skip insertions into non-summarizing indexes if we only need
152+
* to update summarizing indexes.
153+
*/
154+
if (onlySummarized && !indexInfo->ii_Summarizing)
155+
continue;
156+
145157
/*
146158
* FormIndexDatum fills in its values and isnull parameters with the
147159
* appropriate values for the column(s) of the index.
@@ -228,7 +240,7 @@ CatalogTupleInsert(Relation heapRel, HeapTuple tup)
228240

229241
simple_heap_insert(heapRel, tup);
230242

231-
CatalogIndexInsert(indstate, tup);
243+
CatalogIndexInsert(indstate, tup, TU_All);
232244
CatalogCloseIndexes(indstate);
233245
}
234246

@@ -248,7 +260,7 @@ CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup,
248260

249261
simple_heap_insert(heapRel, tup);
250262

251-
CatalogIndexInsert(indstate, tup);
263+
CatalogIndexInsert(indstate, tup, TU_All);
252264
}
253265

254266
/*
@@ -279,7 +291,7 @@ CatalogTuplesMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot,
279291

280292
tuple = ExecFetchSlotHeapTuple(slot[i], true, &should_free);
281293
tuple->t_tableOid = slot[i]->tts_tableOid;
282-
CatalogIndexInsert(indstate, tuple);
294+
CatalogIndexInsert(indstate, tuple, TU_All);
283295

284296
if (should_free)
285297
heap_freetuple(tuple);
@@ -301,14 +313,15 @@ void
301313
CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
302314
{
303315
CatalogIndexState indstate;
316+
TU_UpdateIndexes updateIndexes = TU_All;
304317

305318
CatalogTupleCheckConstraints(heapRel, tup);
306319

307320
indstate = CatalogOpenIndexes(heapRel);
308321

309-
simple_heap_update(heapRel, otid, tup);
322+
simple_heap_update(heapRel, otid, tup, &updateIndexes);
310323

311-
CatalogIndexInsert(indstate, tup);
324+
CatalogIndexInsert(indstate, tup, updateIndexes);
312325
CatalogCloseIndexes(indstate);
313326
}
314327

@@ -324,11 +337,13 @@ void
324337
CatalogTupleUpdateWithInfo(Relation heapRel, ItemPointer otid, HeapTuple tup,
325338
CatalogIndexState indstate)
326339
{
340+
TU_UpdateIndexes updateIndexes = TU_All;
341+
327342
CatalogTupleCheckConstraints(heapRel, tup);
328343

329-
simple_heap_update(heapRel, otid, tup);
344+
simple_heap_update(heapRel, otid, tup, &updateIndexes);
330345

331-
CatalogIndexInsert(indstate, tup);
346+
CatalogIndexInsert(indstate, tup, updateIndexes);
332347
}
333348

334349
/*

0 commit comments

Comments
 (0)