@@ -96,11 +96,8 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
96
96
Buffer newbuf , HeapTuple oldtup ,
97
97
HeapTuple newtup , HeapTuple old_key_tup ,
98
98
bool all_visible_cleared , bool new_all_visible_cleared );
99
- static void HeapSatisfiesHOTandKeyUpdate (Relation relation ,
100
- Bitmapset * hot_attrs ,
101
- Bitmapset * key_attrs , Bitmapset * id_attrs ,
102
- bool * satisfies_hot , bool * satisfies_key ,
103
- bool * satisfies_id ,
99
+ static Bitmapset * HeapDetermineModifiedColumns (Relation relation ,
100
+ Bitmapset * interesting_cols ,
104
101
HeapTuple oldtup , HeapTuple newtup );
105
102
static bool heap_acquire_tuplock (Relation relation , ItemPointer tid ,
106
103
LockTupleMode mode , LockWaitPolicy wait_policy ,
@@ -3471,6 +3468,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3471
3468
Bitmapset * hot_attrs ;
3472
3469
Bitmapset * key_attrs ;
3473
3470
Bitmapset * id_attrs ;
3471
+ Bitmapset * interesting_attrs ;
3472
+ Bitmapset * modified_attrs ;
3474
3473
ItemId lp ;
3475
3474
HeapTupleData oldtup ;
3476
3475
HeapTuple heaptup ;
@@ -3488,10 +3487,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3488
3487
pagefree ;
3489
3488
bool have_tuple_lock = false;
3490
3489
bool iscombo ;
3491
- bool satisfies_hot ;
3492
- bool satisfies_key ;
3493
- bool satisfies_id ;
3494
3490
bool use_hot_update = false;
3491
+ bool hot_attrs_checked = false;
3495
3492
bool key_intact ;
3496
3493
bool all_visible_cleared = false;
3497
3494
bool all_visible_cleared_new = false;
@@ -3517,26 +3514,50 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3517
3514
errmsg ("cannot update tuples during a parallel operation" )));
3518
3515
3519
3516
/*
3520
- * Fetch the list of attributes to be checked for HOT update. This is
3521
- * wasted effort if we fail to update or have to put the new tuple on a
3522
- * different page. But we must compute the list before obtaining buffer
3523
- * lock --- in the worst case, if we are doing an update on one of the
3524
- * relevant system catalogs, we could deadlock if we try to fetch the list
3525
- * later. In any case, the relcache caches the data so this is usually
3526
- * pretty cheap.
3517
+ * Fetch the list of attributes to be checked for various operations.
3527
3518
*
3528
- * Note that we get a copy here, so we need not worry about relcache flush
3529
- * happening midway through.
3519
+ * For HOT considerations, this is wasted effort if we fail to update or
3520
+ * have to put the new tuple on a different page. But we must compute the
3521
+ * list before obtaining buffer lock --- in the worst case, if we are doing
3522
+ * an update on one of the relevant system catalogs, we could deadlock if
3523
+ * we try to fetch the list later. In any case, the relcache caches the
3524
+ * data so this is usually pretty cheap.
3525
+ *
3526
+ * We also need columns used by the replica identity and columns that are
3527
+ * considered the "key" of rows in the table.
3528
+ *
3529
+ * Note that we get copies of each bitmap, so we need not worry about
3530
+ * relcache flush happening midway through.
3530
3531
*/
3531
3532
hot_attrs = RelationGetIndexAttrBitmap (relation , INDEX_ATTR_BITMAP_ALL );
3532
3533
key_attrs = RelationGetIndexAttrBitmap (relation , INDEX_ATTR_BITMAP_KEY );
3533
3534
id_attrs = RelationGetIndexAttrBitmap (relation ,
3534
3535
INDEX_ATTR_BITMAP_IDENTITY_KEY );
3535
3536
3537
+
3536
3538
block = ItemPointerGetBlockNumber (otid );
3537
3539
buffer = ReadBuffer (relation , block );
3538
3540
page = BufferGetPage (buffer );
3539
3541
3542
+ interesting_attrs = NULL ;
3543
+ /*
3544
+ * If the page is already full, there is hardly any chance of doing a HOT
3545
+ * update on this page. It might be wasteful effort to look for index
3546
+ * column updates only to later reject HOT updates for lack of space in the
3547
+ * same page. So we be conservative and only fetch hot_attrs if the page is
3548
+ * not already full. Since we are already holding a pin on the buffer,
3549
+ * there is no chance that the buffer can get cleaned up concurrently and
3550
+ * even if that was possible, in the worst case we lose a chance to do a
3551
+ * HOT update.
3552
+ */
3553
+ if (!PageIsFull (page ))
3554
+ {
3555
+ interesting_attrs = bms_add_members (interesting_attrs , hot_attrs );
3556
+ hot_attrs_checked = true;
3557
+ }
3558
+ interesting_attrs = bms_add_members (interesting_attrs , key_attrs );
3559
+ interesting_attrs = bms_add_members (interesting_attrs , id_attrs );
3560
+
3540
3561
/*
3541
3562
* Before locking the buffer, pin the visibility map page if it appears to
3542
3563
* be necessary. Since we haven't got the lock yet, someone else might be
@@ -3552,7 +3573,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3552
3573
Assert (ItemIdIsNormal (lp ));
3553
3574
3554
3575
/*
3555
- * Fill in enough data in oldtup for HeapSatisfiesHOTandKeyUpdate to work
3576
+ * Fill in enough data in oldtup for HeapDetermineModifiedColumns to work
3556
3577
* properly.
3557
3578
*/
3558
3579
oldtup .t_tableOid = RelationGetRelid (relation );
@@ -3578,6 +3599,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3578
3599
Assert (!(newtup -> t_data -> t_infomask & HEAP_HASOID ));
3579
3600
}
3580
3601
3602
+ /* Determine columns modified by the update. */
3603
+ modified_attrs = HeapDetermineModifiedColumns (relation , interesting_attrs ,
3604
+ & oldtup , newtup );
3605
+
3581
3606
/*
3582
3607
* If we're not updating any "key" column, we can grab a weaker lock type.
3583
3608
* This allows for more concurrency when we are running simultaneously
@@ -3589,10 +3614,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3589
3614
* is updates that don't manipulate key columns, not those that
3590
3615
* serendipitiously arrive at the same key values.
3591
3616
*/
3592
- HeapSatisfiesHOTandKeyUpdate (relation , hot_attrs , key_attrs , id_attrs ,
3593
- & satisfies_hot , & satisfies_key ,
3594
- & satisfies_id , & oldtup , newtup );
3595
- if (satisfies_key )
3617
+ if (!bms_overlap (modified_attrs , key_attrs ))
3596
3618
{
3597
3619
* lockmode = LockTupleNoKeyExclusive ;
3598
3620
mxact_status = MultiXactStatusNoKeyUpdate ;
@@ -3831,6 +3853,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3831
3853
bms_free (hot_attrs );
3832
3854
bms_free (key_attrs );
3833
3855
bms_free (id_attrs );
3856
+ bms_free (modified_attrs );
3857
+ bms_free (interesting_attrs );
3834
3858
return result ;
3835
3859
}
3836
3860
@@ -4133,9 +4157,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
4133
4157
/*
4134
4158
* Since the new tuple is going into the same page, we might be able
4135
4159
* to do a HOT update. Check if any of the index columns have been
4136
- * changed. If not, then HOT update is possible.
4160
+ * changed. If the page was already full, we may have skipped checking
4161
+ * for index columns. If so, HOT update is possible.
4137
4162
*/
4138
- if (satisfies_hot )
4163
+ if (hot_attrs_checked && ! bms_overlap ( modified_attrs , hot_attrs ) )
4139
4164
use_hot_update = true;
4140
4165
}
4141
4166
else
@@ -4150,7 +4175,9 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
4150
4175
* ExtractReplicaIdentity() will return NULL if nothing needs to be
4151
4176
* logged.
4152
4177
*/
4153
- old_key_tuple = ExtractReplicaIdentity (relation , & oldtup , !satisfies_id , & old_key_copied );
4178
+ old_key_tuple = ExtractReplicaIdentity (relation , & oldtup ,
4179
+ bms_overlap (modified_attrs , id_attrs ),
4180
+ & old_key_copied );
4154
4181
4155
4182
/* NO EREPORT(ERROR) from here till changes are logged */
4156
4183
START_CRIT_SECTION ();
@@ -4298,13 +4325,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
4298
4325
bms_free (hot_attrs );
4299
4326
bms_free (key_attrs );
4300
4327
bms_free (id_attrs );
4328
+ bms_free (modified_attrs );
4329
+ bms_free (interesting_attrs );
4301
4330
4302
4331
return HeapTupleMayBeUpdated ;
4303
4332
}
4304
4333
4305
4334
/*
4306
4335
* Check if the specified attribute's value is same in both given tuples.
4307
- * Subroutine for HeapSatisfiesHOTandKeyUpdate .
4336
+ * Subroutine for HeapDetermineModifiedColumns .
4308
4337
*/
4309
4338
static bool
4310
4339
heap_tuple_attr_equals (TupleDesc tupdesc , int attrnum ,
@@ -4338,7 +4367,7 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
4338
4367
4339
4368
/*
4340
4369
* Extract the corresponding values. XXX this is pretty inefficient if
4341
- * there are many indexed columns. Should HeapSatisfiesHOTandKeyUpdate do
4370
+ * there are many indexed columns. Should HeapDetermineModifiedColumns do
4342
4371
* a single heap_deform_tuple call on each tuple, instead? But that
4343
4372
* doesn't work for system columns ...
4344
4373
*/
@@ -4383,114 +4412,30 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
4383
4412
/*
4384
4413
* Check which columns are being updated.
4385
4414
*
4386
- * This simultaneously checks conditions for HOT updates, for FOR KEY
4387
- * SHARE updates, and REPLICA IDENTITY concerns. Since much of the time they
4388
- * will be checking very similar sets of columns, and doing the same tests on
4389
- * them, it makes sense to optimize and do them together.
4390
- *
4391
- * We receive three bitmapsets comprising the three sets of columns we're
4392
- * interested in. Note these are destructively modified; that is OK since
4393
- * this is invoked at most once in heap_update.
4415
+ * Given an updated tuple, determine (and return into the output bitmapset),
4416
+ * from those listed as interesting, the set of columns that changed.
4394
4417
*
4395
- * hot_result is set to TRUE if it's okay to do a HOT update (i.e. it does not
4396
- * modified indexed columns); key_result is set to TRUE if the update does not
4397
- * modify columns used in the key; id_result is set to TRUE if the update does
4398
- * not modify columns in any index marked as the REPLICA IDENTITY.
4418
+ * The input bitmapset is destructively modified; that is OK since this is
4419
+ * invoked at most once in heap_update.
4399
4420
*/
4400
- static void
4401
- HeapSatisfiesHOTandKeyUpdate (Relation relation , Bitmapset * hot_attrs ,
4402
- Bitmapset * key_attrs , Bitmapset * id_attrs ,
4403
- bool * satisfies_hot , bool * satisfies_key ,
4404
- bool * satisfies_id ,
4421
+ static Bitmapset *
4422
+ HeapDetermineModifiedColumns (Relation relation , Bitmapset * interesting_cols ,
4405
4423
HeapTuple oldtup , HeapTuple newtup )
4406
4424
{
4407
- int next_hot_attnum ;
4408
- int next_key_attnum ;
4409
- int next_id_attnum ;
4410
- bool hot_result = true;
4411
- bool key_result = true;
4412
- bool id_result = true;
4413
-
4414
- /* If REPLICA IDENTITY is set to FULL, id_attrs will be empty. */
4415
- Assert (bms_is_subset (id_attrs , key_attrs ));
4416
- Assert (bms_is_subset (key_attrs , hot_attrs ));
4417
-
4418
- /*
4419
- * If one of these sets contains no remaining bits, bms_first_member will
4420
- * return -1, and after adding FirstLowInvalidHeapAttributeNumber (which
4421
- * is negative!) we'll get an attribute number that can't possibly be
4422
- * real, and thus won't match any actual attribute number.
4423
- */
4424
- next_hot_attnum = bms_first_member (hot_attrs );
4425
- next_hot_attnum += FirstLowInvalidHeapAttributeNumber ;
4426
- next_key_attnum = bms_first_member (key_attrs );
4427
- next_key_attnum += FirstLowInvalidHeapAttributeNumber ;
4428
- next_id_attnum = bms_first_member (id_attrs );
4429
- next_id_attnum += FirstLowInvalidHeapAttributeNumber ;
4425
+ int attnum ;
4426
+ Bitmapset * modified = NULL ;
4430
4427
4431
- for (;; )
4428
+ while (( attnum = bms_first_member ( interesting_cols )) >= 0 )
4432
4429
{
4433
- bool changed ;
4434
- int check_now ;
4430
+ attnum += FirstLowInvalidHeapAttributeNumber ;
4435
4431
4436
- /*
4437
- * Since the HOT attributes are a superset of the key attributes and
4438
- * the key attributes are a superset of the id attributes, this logic
4439
- * is guaranteed to identify the next column that needs to be checked.
4440
- */
4441
- if (hot_result && next_hot_attnum > FirstLowInvalidHeapAttributeNumber )
4442
- check_now = next_hot_attnum ;
4443
- else if (key_result && next_key_attnum > FirstLowInvalidHeapAttributeNumber )
4444
- check_now = next_key_attnum ;
4445
- else if (id_result && next_id_attnum > FirstLowInvalidHeapAttributeNumber )
4446
- check_now = next_id_attnum ;
4447
- else
4448
- break ;
4449
-
4450
- /* See whether it changed. */
4451
- changed = !heap_tuple_attr_equals (RelationGetDescr (relation ),
4452
- check_now , oldtup , newtup );
4453
- if (changed )
4454
- {
4455
- if (check_now == next_hot_attnum )
4456
- hot_result = false;
4457
- if (check_now == next_key_attnum )
4458
- key_result = false;
4459
- if (check_now == next_id_attnum )
4460
- id_result = false;
4461
-
4462
- /* if all are false now, we can stop checking */
4463
- if (!hot_result && !key_result && !id_result )
4464
- break ;
4465
- }
4466
-
4467
- /*
4468
- * Advance the next attribute numbers for the sets that contain the
4469
- * attribute we just checked. As we work our way through the columns,
4470
- * the next_attnum values will rise; but when each set becomes empty,
4471
- * bms_first_member() will return -1 and the attribute number will end
4472
- * up with a value less than FirstLowInvalidHeapAttributeNumber.
4473
- */
4474
- if (hot_result && check_now == next_hot_attnum )
4475
- {
4476
- next_hot_attnum = bms_first_member (hot_attrs );
4477
- next_hot_attnum += FirstLowInvalidHeapAttributeNumber ;
4478
- }
4479
- if (key_result && check_now == next_key_attnum )
4480
- {
4481
- next_key_attnum = bms_first_member (key_attrs );
4482
- next_key_attnum += FirstLowInvalidHeapAttributeNumber ;
4483
- }
4484
- if (id_result && check_now == next_id_attnum )
4485
- {
4486
- next_id_attnum = bms_first_member (id_attrs );
4487
- next_id_attnum += FirstLowInvalidHeapAttributeNumber ;
4488
- }
4432
+ if (!heap_tuple_attr_equals (RelationGetDescr (relation ),
4433
+ attnum , oldtup , newtup ))
4434
+ modified = bms_add_member (modified ,
4435
+ attnum - FirstLowInvalidHeapAttributeNumber );
4489
4436
}
4490
4437
4491
- * satisfies_hot = hot_result ;
4492
- * satisfies_key = key_result ;
4493
- * satisfies_id = id_result ;
4438
+ return modified ;
4494
4439
}
4495
4440
4496
4441
/*
0 commit comments