@@ -96,11 +96,8 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
9696 Buffer newbuf , HeapTuple oldtup ,
9797 HeapTuple newtup , HeapTuple old_key_tup ,
9898 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 ,
104101 HeapTuple oldtup , HeapTuple newtup );
105102static bool heap_acquire_tuplock (Relation relation , ItemPointer tid ,
106103 LockTupleMode mode , LockWaitPolicy wait_policy ,
@@ -3471,6 +3468,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
34713468 Bitmapset * hot_attrs ;
34723469 Bitmapset * key_attrs ;
34733470 Bitmapset * id_attrs ;
3471+ Bitmapset * interesting_attrs ;
3472+ Bitmapset * modified_attrs ;
34743473 ItemId lp ;
34753474 HeapTupleData oldtup ;
34763475 HeapTuple heaptup ;
@@ -3488,10 +3487,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
34883487 pagefree ;
34893488 bool have_tuple_lock = false;
34903489 bool iscombo ;
3491- bool satisfies_hot ;
3492- bool satisfies_key ;
3493- bool satisfies_id ;
34943490 bool use_hot_update = false;
3491+ bool hot_attrs_checked = false;
34953492 bool key_intact ;
34963493 bool all_visible_cleared = false;
34973494 bool all_visible_cleared_new = false;
@@ -3517,26 +3514,50 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
35173514 errmsg ("cannot update tuples during a parallel operation" )));
35183515
35193516 /*
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.
35273518 *
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.
35303531 */
35313532 hot_attrs = RelationGetIndexAttrBitmap (relation , INDEX_ATTR_BITMAP_ALL );
35323533 key_attrs = RelationGetIndexAttrBitmap (relation , INDEX_ATTR_BITMAP_KEY );
35333534 id_attrs = RelationGetIndexAttrBitmap (relation ,
35343535 INDEX_ATTR_BITMAP_IDENTITY_KEY );
35353536
3537+
35363538 block = ItemPointerGetBlockNumber (otid );
35373539 buffer = ReadBuffer (relation , block );
35383540 page = BufferGetPage (buffer );
35393541
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+
35403561 /*
35413562 * Before locking the buffer, pin the visibility map page if it appears to
35423563 * 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,
35523573 Assert (ItemIdIsNormal (lp ));
35533574
35543575 /*
3555- * Fill in enough data in oldtup for HeapSatisfiesHOTandKeyUpdate to work
3576+ * Fill in enough data in oldtup for HeapDetermineModifiedColumns to work
35563577 * properly.
35573578 */
35583579 oldtup .t_tableOid = RelationGetRelid (relation );
@@ -3578,6 +3599,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
35783599 Assert (!(newtup -> t_data -> t_infomask & HEAP_HASOID ));
35793600 }
35803601
3602+ /* Determine columns modified by the update. */
3603+ modified_attrs = HeapDetermineModifiedColumns (relation , interesting_attrs ,
3604+ & oldtup , newtup );
3605+
35813606 /*
35823607 * If we're not updating any "key" column, we can grab a weaker lock type.
35833608 * This allows for more concurrency when we are running simultaneously
@@ -3589,10 +3614,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
35893614 * is updates that don't manipulate key columns, not those that
35903615 * serendipitiously arrive at the same key values.
35913616 */
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 ))
35963618 {
35973619 * lockmode = LockTupleNoKeyExclusive ;
35983620 mxact_status = MultiXactStatusNoKeyUpdate ;
@@ -3831,6 +3853,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
38313853 bms_free (hot_attrs );
38323854 bms_free (key_attrs );
38333855 bms_free (id_attrs );
3856+ bms_free (modified_attrs );
3857+ bms_free (interesting_attrs );
38343858 return result ;
38353859 }
38363860
@@ -4133,9 +4157,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
41334157 /*
41344158 * Since the new tuple is going into the same page, we might be able
41354159 * 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.
41374162 */
4138- if (satisfies_hot )
4163+ if (hot_attrs_checked && ! bms_overlap ( modified_attrs , hot_attrs ) )
41394164 use_hot_update = true;
41404165 }
41414166 else
@@ -4150,7 +4175,9 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
41504175 * ExtractReplicaIdentity() will return NULL if nothing needs to be
41514176 * logged.
41524177 */
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 );
41544181
41554182 /* NO EREPORT(ERROR) from here till changes are logged */
41564183 START_CRIT_SECTION ();
@@ -4298,13 +4325,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
42984325 bms_free (hot_attrs );
42994326 bms_free (key_attrs );
43004327 bms_free (id_attrs );
4328+ bms_free (modified_attrs );
4329+ bms_free (interesting_attrs );
43014330
43024331 return HeapTupleMayBeUpdated ;
43034332}
43044333
43054334/*
43064335 * Check if the specified attribute's value is same in both given tuples.
4307- * Subroutine for HeapSatisfiesHOTandKeyUpdate .
4336+ * Subroutine for HeapDetermineModifiedColumns .
43084337 */
43094338static bool
43104339heap_tuple_attr_equals (TupleDesc tupdesc , int attrnum ,
@@ -4338,7 +4367,7 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
43384367
43394368 /*
43404369 * 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
43424371 * a single heap_deform_tuple call on each tuple, instead? But that
43434372 * doesn't work for system columns ...
43444373 */
@@ -4383,114 +4412,30 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
43834412/*
43844413 * Check which columns are being updated.
43854414 *
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.
43944417 *
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.
43994420 */
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 ,
44054423 HeapTuple oldtup , HeapTuple newtup )
44064424{
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 ;
44304427
4431- for (;; )
4428+ while (( attnum = bms_first_member ( interesting_cols )) >= 0 )
44324429 {
4433- bool changed ;
4434- int check_now ;
4430+ attnum += FirstLowInvalidHeapAttributeNumber ;
44354431
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 );
44894436 }
44904437
4491- * satisfies_hot = hot_result ;
4492- * satisfies_key = key_result ;
4493- * satisfies_id = id_result ;
4438+ return modified ;
44944439}
44954440
44964441/*
0 commit comments