diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index ea579a0..1dbefda 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -95,11 +95,8 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf, Buffer newbuf, HeapTuple oldtup, HeapTuple newtup, HeapTuple old_key_tup, bool all_visible_cleared, bool new_all_visible_cleared); -static void HeapSatisfiesHOTandKeyUpdate(Relation relation, - Bitmapset *hot_attrs, - Bitmapset *key_attrs, Bitmapset *id_attrs, - bool *satisfies_hot, bool *satisfies_key, - bool *satisfies_id, +static Bitmapset *HeapDetermineModifiedColumns(Relation relation, + Bitmapset *interesting_cols, HeapTuple oldtup, HeapTuple newtup); static bool heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode, LockWaitPolicy wait_policy, @@ -3443,6 +3440,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, Bitmapset *hot_attrs; Bitmapset *key_attrs; Bitmapset *id_attrs; + Bitmapset *interesting_attrs; + Bitmapset *modified_attrs; ItemId lp; HeapTupleData oldtup; HeapTuple heaptup; @@ -3460,9 +3459,6 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, pagefree; bool have_tuple_lock = false; bool iscombo; - bool satisfies_hot; - bool satisfies_key; - bool satisfies_id; bool use_hot_update = false; bool key_intact; bool all_visible_cleared = false; @@ -3504,6 +3500,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, key_attrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_KEY); id_attrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_IDENTITY_KEY); + interesting_attrs = bms_add_members(NULL, hot_attrs); + interesting_attrs = bms_add_members(interesting_attrs, key_attrs); + interesting_attrs = bms_add_members(interesting_attrs, id_attrs); + block = ItemPointerGetBlockNumber(otid); buffer = ReadBuffer(relation, block); @@ -3524,7 +3524,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, Assert(ItemIdIsNormal(lp)); /* - * Fill in enough data in oldtup for HeapSatisfiesHOTandKeyUpdate to work + * Fill in enough data in oldtup for HeapDetermineModifiedColumns to work * properly. */ oldtup.t_tableOid = RelationGetRelid(relation); @@ -3551,6 +3551,13 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, } /* + * Determine which columns are being modified by the update. + */ + + modified_attrs = HeapDetermineModifiedColumns(relation, interesting_attrs, + &oldtup, newtup); + + /* * If we're not updating any "key" column, we can grab a weaker lock type. * This allows for more concurrency when we are running simultaneously * with foreign key checks. @@ -3561,10 +3568,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, * is updates that don't manipulate key columns, not those that * serendipitiously arrive at the same key values. */ - HeapSatisfiesHOTandKeyUpdate(relation, hot_attrs, key_attrs, id_attrs, - &satisfies_hot, &satisfies_key, - &satisfies_id, &oldtup, newtup); - if (satisfies_key) + if (!bms_overlap(modified_attrs, key_attrs)) { *lockmode = LockTupleNoKeyExclusive; mxact_status = MultiXactStatusNoKeyUpdate; @@ -3803,6 +3807,8 @@ l2: bms_free(hot_attrs); bms_free(key_attrs); bms_free(id_attrs); + bms_free(modified_attrs); + bms_free(interesting_attrs); return result; } @@ -4107,7 +4113,7 @@ l2: * to do a HOT update. Check if any of the index columns have been * changed. If not, then HOT update is possible. */ - if (satisfies_hot) + if (!bms_overlap(modified_attrs, hot_attrs)) use_hot_update = true; } else @@ -4122,7 +4128,9 @@ l2: * ExtractReplicaIdentity() will return NULL if nothing needs to be * logged. */ - old_key_tuple = ExtractReplicaIdentity(relation, &oldtup, !satisfies_id, &old_key_copied); + old_key_tuple = ExtractReplicaIdentity(relation, &oldtup, + bms_overlap(modified_attrs, id_attrs), + &old_key_copied); /* NO EREPORT(ERROR) from here till changes are logged */ START_CRIT_SECTION(); @@ -4270,6 +4278,8 @@ l2: bms_free(hot_attrs); bms_free(key_attrs); bms_free(id_attrs); + bms_free(modified_attrs); + bms_free(interesting_attrs); return HeapTupleMayBeUpdated; } @@ -4369,100 +4379,24 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum, * modify columns used in the key; id_result is set to TRUE if the update does * not modify columns in any index marked as the REPLICA IDENTITY. */ -static void -HeapSatisfiesHOTandKeyUpdate(Relation relation, Bitmapset *hot_attrs, - Bitmapset *key_attrs, Bitmapset *id_attrs, - bool *satisfies_hot, bool *satisfies_key, - bool *satisfies_id, +static Bitmapset * +HeapDetermineModifiedColumns(Relation relation, Bitmapset *interesting_cols, HeapTuple oldtup, HeapTuple newtup) { - int next_hot_attnum; - int next_key_attnum; - int next_id_attnum; - bool hot_result = true; - bool key_result = true; - bool id_result = true; + int attnum; + Bitmapset *modified = NULL; - /* If REPLICA IDENTITY is set to FULL, id_attrs will be empty. */ - Assert(bms_is_subset(id_attrs, key_attrs)); - Assert(bms_is_subset(key_attrs, hot_attrs)); - - /* - * If one of these sets contains no remaining bits, bms_first_member will - * return -1, and after adding FirstLowInvalidHeapAttributeNumber (which - * is negative!) we'll get an attribute number that can't possibly be - * real, and thus won't match any actual attribute number. - */ - next_hot_attnum = bms_first_member(hot_attrs); - next_hot_attnum += FirstLowInvalidHeapAttributeNumber; - next_key_attnum = bms_first_member(key_attrs); - next_key_attnum += FirstLowInvalidHeapAttributeNumber; - next_id_attnum = bms_first_member(id_attrs); - next_id_attnum += FirstLowInvalidHeapAttributeNumber; - - for (;;) + while ((attnum = bms_first_member(interesting_cols)) >= 0) { - bool changed; - int check_now; + attnum += FirstLowInvalidHeapAttributeNumber; - /* - * Since the HOT attributes are a superset of the key attributes and - * the key attributes are a superset of the id attributes, this logic - * is guaranteed to identify the next column that needs to be checked. - */ - if (hot_result && next_hot_attnum > FirstLowInvalidHeapAttributeNumber) - check_now = next_hot_attnum; - else if (key_result && next_key_attnum > FirstLowInvalidHeapAttributeNumber) - check_now = next_key_attnum; - else if (id_result && next_id_attnum > FirstLowInvalidHeapAttributeNumber) - check_now = next_id_attnum; - else - break; - - /* See whether it changed. */ - changed = !heap_tuple_attr_equals(RelationGetDescr(relation), - check_now, oldtup, newtup); - if (changed) - { - if (check_now == next_hot_attnum) - hot_result = false; - if (check_now == next_key_attnum) - key_result = false; - if (check_now == next_id_attnum) - id_result = false; - - /* if all are false now, we can stop checking */ - if (!hot_result && !key_result && !id_result) - break; - } - - /* - * Advance the next attribute numbers for the sets that contain the - * attribute we just checked. As we work our way through the columns, - * the next_attnum values will rise; but when each set becomes empty, - * bms_first_member() will return -1 and the attribute number will end - * up with a value less than FirstLowInvalidHeapAttributeNumber. - */ - if (hot_result && check_now == next_hot_attnum) - { - next_hot_attnum = bms_first_member(hot_attrs); - next_hot_attnum += FirstLowInvalidHeapAttributeNumber; - } - if (key_result && check_now == next_key_attnum) - { - next_key_attnum = bms_first_member(key_attrs); - next_key_attnum += FirstLowInvalidHeapAttributeNumber; - } - if (id_result && check_now == next_id_attnum) - { - next_id_attnum = bms_first_member(id_attrs); - next_id_attnum += FirstLowInvalidHeapAttributeNumber; - } + if (!heap_tuple_attr_equals(RelationGetDescr(relation), + attnum, oldtup, newtup)) + modified = bms_add_member(modified, + attnum - FirstLowInvalidHeapAttributeNumber); } - *satisfies_hot = hot_result; - *satisfies_key = key_result; - *satisfies_id = id_result; + return modified; } /*