diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c index 06077af..4ab30d6 100644 --- a/contrib/bloom/blutils.c +++ b/contrib/bloom/blutils.c @@ -138,6 +138,7 @@ blhandler(PG_FUNCTION_ARGS) amroutine->amendscan = blendscan; amroutine->ammarkpos = NULL; amroutine->amrestrpos = NULL; + amroutine->amrecheck = NULL; PG_RETURN_POINTER(amroutine); } diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index d60ddd2..3785045 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -112,6 +112,7 @@ brinhandler(PG_FUNCTION_ARGS) amroutine->amendscan = brinendscan; amroutine->ammarkpos = NULL; amroutine->amrestrpos = NULL; + amroutine->amrecheck = NULL; PG_RETURN_POINTER(amroutine); } diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 597056a..d4634af 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -89,6 +89,7 @@ gisthandler(PG_FUNCTION_ARGS) amroutine->amendscan = gistendscan; amroutine->ammarkpos = NULL; amroutine->amrestrpos = NULL; + amroutine->amrecheck = NULL; PG_RETURN_POINTER(amroutine); } diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index a64a9b9..40fede5 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -86,6 +86,7 @@ hashhandler(PG_FUNCTION_ARGS) amroutine->amendscan = hashendscan; amroutine->ammarkpos = NULL; amroutine->amrestrpos = NULL; + amroutine->amrecheck = hashrecheck; PG_RETURN_POINTER(amroutine); } @@ -266,6 +267,8 @@ hashgettuple(IndexScanDesc scan, ScanDirection dir) OffsetNumber offnum; ItemPointer current; bool res; + IndexTuple itup; + /* Hash indexes are always lossy since we store only the hash code */ scan->xs_recheck = true; @@ -303,8 +306,6 @@ hashgettuple(IndexScanDesc scan, ScanDirection dir) offnum <= maxoffnum; offnum = OffsetNumberNext(offnum)) { - IndexTuple itup; - itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); if (ItemPointerEquals(&(so->hashso_heappos), &(itup->t_tid))) break; diff --git a/src/backend/access/hash/hashsearch.c b/src/backend/access/hash/hashsearch.c index a59ad6f..46a334c 100644 --- a/src/backend/access/hash/hashsearch.c +++ b/src/backend/access/hash/hashsearch.c @@ -59,6 +59,8 @@ _hash_next(IndexScanDesc scan, ScanDirection dir) itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); so->hashso_heappos = itup->t_tid; + if (scan->xs_want_itup) + scan->xs_itup = itup; return true; } @@ -408,6 +410,9 @@ _hash_first(IndexScanDesc scan, ScanDirection dir) itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); so->hashso_heappos = itup->t_tid; + if (scan->xs_want_itup) + scan->xs_itup = itup; + return true; } diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c index c705531..dcba734 100644 --- a/src/backend/access/hash/hashutil.c +++ b/src/backend/access/hash/hashutil.c @@ -17,8 +17,12 @@ #include "access/hash.h" #include "access/reloptions.h" #include "access/relscan.h" +#include "catalog/index.h" +#include "executor/executor.h" +#include "nodes/execnodes.h" #include "utils/lsyscache.h" #include "utils/rel.h" +#include "utils/datum.h" #define CALC_NEW_BUCKET(old_bucket, lowmask) \ old_bucket | (lowmask + 1) @@ -446,3 +450,109 @@ _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket, return new_bucket; } + +/* + * Recheck if the heap tuple satisfies the key stored in the index tuple + */ +bool +hashrecheck(Relation indexRel, IndexTuple indexTuple, + Relation heapRel, HeapTuple heapTuple) +{ + IndexInfo *indexInfo; + EState *estate; + ExprContext *econtext; + TupleTableSlot *slot; + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + Datum values2[INDEX_MAX_KEYS]; + bool isnull2[INDEX_MAX_KEYS]; + int i; + bool equal; + int natts = indexRel->rd_rel->relnatts; + Form_pg_attribute att; + + indexInfo = BuildIndexInfo(indexRel); + + /* + * The heap tuple must be put into a slot for FormIndexDatum. + */ + slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRel)); + + ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); + + /* + * Typically the index won't have expressions, but if it does we need an + * EState to evaluate them. We need it for exclusion constraints too, + * even if they are just on simple columns. + */ + if (indexInfo->ii_Expressions != NIL || + indexInfo->ii_ExclusionOps != NULL) + { + estate = CreateExecutorState(); + econtext = GetPerTupleExprContext(estate); + econtext->ecxt_scantuple = slot; + } + else + estate = NULL; + + /* + * Form the index values and isnull flags for the index entry that we need + * to check. + * + * Note: if the index uses functions that are not as immutable as they are + * supposed to be, this could produce an index tuple different from the + * original. The index AM can catch such errors by verifying that it + * finds a matching index entry with the tuple's TID. For exclusion + * constraints we check this in check_exclusion_constraint(). + */ + FormIndexDatum(indexInfo, slot, estate, values, isnull); + + /* + * HASH indexes compute a hash value of the key and store that in the + * index. So we must first obtain the hash of the value obtained from the + * heap and then do a comparison + */ + _hash_convert_tuple(indexRel, values, isnull, values2, isnull2); + + equal = true; + for (i = 1; i <= natts; i++) + { + Datum indxvalue; + bool indxisnull; + + indxvalue = index_getattr(indexTuple, i, indexRel->rd_att, &indxisnull); + + /* + * If both are NULL then they are equal + */ + if (isnull2[i - 1] && indxisnull) + continue; + + /* + * If either is NULL then they are not equal + */ + if (isnull2[i - 1] || indxisnull) + { + equal = false; + break; + } + + /* + * Now do a raw memory comparison + */ + att = indexRel->rd_att->attrs[i - 1]; + if (!datumIsEqual(values2[i - 1], indxvalue, att->attbyval, + att->attlen)) + { + equal = false; + break; + } + } + + if (estate != NULL) + FreeExecutorState(estate); + + ExecDropSingleTupleTableSlot(slot); + + return equal; +} diff --git a/src/backend/access/heap/README.WARM b/src/backend/access/heap/README.WARM new file mode 100644 index 0000000..f793570 --- /dev/null +++ b/src/backend/access/heap/README.WARM @@ -0,0 +1,271 @@ +src/backend/access/heap/README.WARM + +Write Amplification Reduction Method (WARM) +=========================================== + +The Heap Only Tuple (HOT) feature greatly eliminated redudant index +entries and allowed re-use of the dead space occupied by previously +updated or deleted tuples (see src/backend/access/heap/README.HOT) + +One of the necessary conditions for satisfying HOT update is that the +update must not change a column used in any of the indexes on the table. +The condition is sometimes hard to meet, especially for complex +workloads with several indexes on large yet frequently updated tables. +Worse, sometimes only one or two index columns may be updated, but the +regular non-HOT update will still insert a new index entry in every +index on the table, irrespective of whether the key pertaining to the +index changed or not. + +WARM is a technique devised to address these problems. + + +Update Chains With Multiple Index Entries Pointing to the Root +-------------------------------------------------------------- + +When a non-HOT update is caused by an index key change, a new index +entry must be inserted for the changed index. But if the index key +hasn't changed for other indexes, we don't really need to insert a new +entry. Even though the existing index entry is pointing to the old +tuple, the new tuple is reachable via the t_ctid chain. To keep things +simple, a WARM update requires that the heap block must have enough +space to store the new version of the tuple. This is same as HOT +updates. + +In WARM, we ensure that every index entry always points to the root of +the WARM chain. In fact, a WARM chain looks exactly like a HOT chain +except for the fact that there could be multiple index entries pointing +to the root of the chain. So when new entry is inserted in an index for +updated tuple, and if we are doing a WARM update, the new entry is made +point to the root of the WARM chain. + +For example, if we have a table with two columns and two indexes on each +of the column. When a tuple is first inserted the table, we have exactly +one index entry pointing to the tuple from both indexes. + + lp [1] + [1111, aaaa] + + Index1's entry (1111) points to 1 + Index2's entry (aaaa) also points to 1 + +Now if the tuple's second column is updated and if there is room on the +page, we perform a WARM update. To do so, Index1 does not get any new +entry and Index2's new entry will still point to the root tuple of the +chain. + + lp [1] [2] + [1111, aaaa]->[111, bbbb] + + Index1's entry (1111) points to 1 + Index2's old entry (aaaa) points to 1 + Index2's new entry (bbbb) also points to 1 + +"A update chain which has more than one index entries pointing to its +root line pointer is called WARM chain and the action that creates a +WARM chain is called WARM update." + +Since all indexes always point to the root of the WARM chain, even when +there are more than one index entries, WARM chains can be pruned and +dead tuples can be removed without a need to do corresponding index +cleanup. + +While this solves the problem of pruning dead tuples from a HOT/WARM +chain, it also opens up a new technical challenge because now we have a +situation where a heap tuple is reachable from multiple index entries, +each having a different index key. While MVCC still ensures that only +valid tuples are returned, a tuple with a wrong index key may be +returned because of wrong index entries. In the above example, tuple +[1111, bbbb] is reachable from both keys (aaaa) as well as (bbbb). For +this reason, tuples returned from a WARM chain must always be rechecked +for index key-match. + +Recheck Index Key Againt Heap Tuple +----------------------------------- + +Since every Index AM has it's own notion of index tuples, each Index AM +must implement its own method to recheck heap tuples. For example, a +hash index stores the hash value of the column and hence recheck routine +for hash AM must first compute the hash value of the heap attribute and +then compare it against the value stored in the index tuple. + +The patch currently implement recheck routines for hash and btree +indexes. If the table has an index which doesn't support recheck +routine, WARM updates are disabled on such tables. + +Problem With Duplicate (key, ctid) Index Entries +------------------------------------------------ + +The index-key recheck logic works as long as there are no duplicate +index keys, both pointing to the same WARM chain. In that case, the same +valid tuple will be reachable via multiple index keys, yet satisfying +the index key checks. In the above example, if the tuple [1111, bbbb] is +again updated to [1111, aaaa] and if we insert a new index entry (aaaa) +pointing to the root line pointer, we will end up with the following +structure: + + lp [1] [2] [3] + [1111, aaaa]->[1111, bbbb]->[1111, aaaa] + + Index1's entry (1111) points to 1 + Index2's oldest entry (aaaa) points to 1 + Index2's old entry (bbbb) also points to 1 + Index2's new entry (aaaa) also points to 1 + +We must solve this problem to ensure that the same tuple is not +reachable via multiple index pointers. There are couple of ways to +address this issue: + +1. Do not allow WARM update to a tuple from a WARM chain. This +guarantees that there can never be duplicate index entries to the same +root line pointer because we must have checked for old and new index +keys while doing the first WARM update. + +2. Do not allow duplicate (key, ctid) index pointers. In the above +example, since (aaaa, 1) already exists in the index, we must not insert +a duplicate index entry. + +The patch currently implements 1 i.e. do not do WARM updates to a tuple +from a WARM chain. HOT updates are fine because they do not add a new +index entry. + +Even with the restriction, this is a significant improvement because the +number of regular UPDATEs are curtailed down to half. + +Expression and Partial Indexes +------------------------------ + +Expressions may evaluate to the same value even if the underlying column +values have changed. A simple example is an index on "lower(col)" which +will return the same value if the new heap value only differs in the +case sensitivity. So we can not solely rely on the heap column check to +decide whether or not to insert a new index entry for expression +indexes. Similarly, for partial indexes, the predicate expression must +be evaluated to decide whether or not to cause a new index entry when +columns referred in the predicate expressions change. + +(None of these things are currently implemented and we squarely disallow +WARM update if a column from expression indexes or predicate has +changed). + + +Efficiently Finding the Root Line Pointer +----------------------------------------- + +During WARM update, we must be able to find the root line pointer of the +tuple being updated. It must be noted that the t_ctid field in the heap +tuple header is usually used to find the next tuple in the update chain. +But the tuple that we are updating, must be the last tuple in the update +chain. In such cases, the c_tid field usually points the tuple itself. +So in theory, we could use the t_ctid to store additional information in +the last tuple of the update chain, if the information about the tuple +being the last tuple is stored elsewhere. + +We now utilize another bit from t_infomask2 to explicitly identify that +this is the last tuple in the update chain. + +HEAP_LATEST_TUPLE - When this bit is set, the tuple is the last tuple in +the update chain. The OffsetNumber part of t_ctid points to the root +line pointer of the chain when HEAP_LATEST_TUPLE flag is set. + +If UPDATE operation is aborted, the last tuple in the update chain +becomes dead. The root line pointer information stored in the tuple +which remains the last valid tuple in the chain is also lost. In such +rare cases, the root line pointer must be found in a hard way by +scanning the entire heap page. + +Tracking WARM Chains +-------------------- + +The old and every subsequent tuple in the chain is marked with a special +HEAP_WARM_TUPLE flag. We use the last remaining bit in t_infomask2 to +store this information. + +When a tuple is returned from a WARM chain, the caller must do +additional checks to ensure that the tuple matches the index key. Even +if the tuple comes precedes the WARM update in the chain, it must still +be rechecked for the index key match (case when old tuple is returned by +the new index key). So we must follow the update chain everytime to the +end to see check if this is a WARM chain. + +When the old updated tuple is retired and the root line pointer is +converted into a redirected line pointer, we can copy the information +about WARM chain to the redirected line pointer by storing a special +value in the lp_len field of the line pointer. This will handle the most +common case where a WARM chain is replaced by a redirect line pointer +and a single tuple in the chain. + +Converting WARM chains back to HOT chains (VACUUM ?) +---------------------------------------------------- + +The current implementation of WARM allows only one WARM update per +chain. This simplifies the design and addresses certain issues around +duplicate scans. But this also implies that the benefit of WARM will be +no more than 50%, which is still significant, but if we could return +WARM chains back to normal status, we could do far more WARM updates. + +A distinct property of a WARM chain is that at least one index has more +than one live index entries pointing to the root of the chain. In other +words, if we can remove duplicate entry from every index or conclusively +prove that there are no duplicate index entries for the root line +pointer, the chain can again be marked as HOT. + +Here is one idea: + +A WARM chain has two parts, separated by the tuple that caused WARM +update. All tuples in each part has matching index keys, but certain +index keys may not match between these two parts. Lets say we mark heap +tuples in each part with a special Red-Blue flag. The same flag is +replicated in the index tuples. For example, when new rows are inserted +in a table, they are marked with Blue flag and the index entries +associated with those rows are also marked with Blue flag. When a row is +WARM updated, the new version is marked with Red flag and the new index +entry created by the update is also marked with Red flag. + + +Heap chain: [1] [2] [3] [4] + [aaaa, 1111]B -> [aaaa, 1111]B -> [bbbb, 1111]R -> [bbbb, 1111]R + +Index1: (aaaa)B points to 1 (satisfies only tuples marked with B) + (bbbb)R points to 1 (satisfies only tuples marked with R) + +Index2: (1111)B points to 1 (satisfied bith B and R tuples) + + +It's clear that for indexes with Red and Blue pointers, a heap tuple +with Blue flag will be reachable from Blue pointer and that with Red +flag will be reachable from Red pointer. But for indexes which did not +create a new entry, both Blue and Red tuples will be reachable from Blue +pointer (there is no Red pointer in such indexes). So, as a side note, +matching Red and Blue flags is not enough from index scan perspective. + +During first heap scan of VACUUM, we look for tuples with +HEAP_WARM_TUPLE set. If all live tuples in the chain are either marked +with Blue flag or Red flag (but no mix of Red and Blue), then the chain +is a candidate for HOT conversion. We remember the root line pointer +and Red-Blue flag of the WARM chain in a separate array. + +If we have a Red WARM chain, then our goal is to remove Blue pointers +and vice versa. But there is a catch. For Index2 above, there is only +Blue pointer and that must not be removed. IOW we should remove Blue +pointer iff a Red pointer exists. Since index vacuum may visit Red and +Blue pointers in any order, I think we will need another index pass to +remove dead index pointers. So in the first index pass we check which +WARM candidates have 2 index pointers. In the second pass, we remove the +dead pointer and reset Red flag is the surviving index pointer is Red. + +During the second heap scan, we fix WARM chain by clearing +HEAP_WARM_TUPLE flag and also reset Red flag to Blue. + +There are some more problems around aborted vacuums. For example, if +vacuum aborts after changing Red index flag to Blue but before removing +the other Blue pointer, we will end up with two Blue pointers to a Red +WARM chain. But since the HEAP_WARM_TUPLE flag on the heap tuple is +still set, further WARM updates to the chain will be blocked. I guess we +will need some special handling for case with multiple Blue pointers. We +can either leave these WARM chains alone and let them die with a +subsequent non-WARM update or must apply heap-recheck logic during index +vacuum to find the dead pointer. Given that vacuum-aborts are not +common, I am inclined to leave this case unhandled. We must still check +for presence of multiple Blue pointers and ensure that we don't +accidently remove either of the Blue pointers and not clear WARM chains +either. diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 8e57bae..015aef1 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -1957,6 +1957,78 @@ heap_fetch(Relation relation, } /* + * Check if the HOT chain containing this tid is actually a WARM chain. + * Note that even if the WARM update ultimately aborted, we still must do a + * recheck because the failing UPDATE when have inserted created index entries + * which are now stale, but still referencing this chain. + */ +static bool +hot_check_warm_chain(Page dp, ItemPointer tid) +{ + TransactionId prev_xmax = InvalidTransactionId; + OffsetNumber offnum; + HeapTupleData heapTuple; + + offnum = ItemPointerGetOffsetNumber(tid); + heapTuple.t_self = *tid; + /* Scan through possible multiple members of HOT-chain */ + for (;;) + { + ItemId lp; + + /* check for bogus TID */ + if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp)) + break; + + lp = PageGetItemId(dp, offnum); + + /* check for unused, dead, or redirected items */ + if (!ItemIdIsNormal(lp)) + break; + + heapTuple.t_data = (HeapTupleHeader) PageGetItem(dp, lp); + ItemPointerSetOffsetNumber(&heapTuple.t_self, offnum); + + /* + * The xmin should match the previous xmax value, else chain is + * broken. + */ + if (TransactionIdIsValid(prev_xmax) && + !TransactionIdEquals(prev_xmax, + HeapTupleHeaderGetXmin(heapTuple.t_data))) + break; + + + /* + * Presence of either WARM or WARM updated tuple signals possible + * breakage and the caller must recheck tuple returned from this chain + * for index satisfaction + */ + if (HeapTupleHeaderIsHeapWarmTuple(heapTuple.t_data)) + return true; + + /* + * Check to see if HOT chain continues past this tuple; if so fetch + * the next offnum and loop around. + */ + if (!HeapTupleIsHotUpdated(&heapTuple)) + break; + + /* + * It can't be a HOT chain if the tuple contains root line pointer + */ + if (HeapTupleHeaderHasRootOffset(heapTuple.t_data)) + break; + + offnum = ItemPointerGetOffsetNumber(&heapTuple.t_data->t_ctid); + prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple.t_data); + } + + /* All OK. No need to recheck */ + return false; +} + +/* * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot * * On entry, *tid is the TID of a tuple (either a simple tuple, or the root @@ -1976,11 +2048,14 @@ heap_fetch(Relation relation, * Unlike heap_fetch, the caller must already have pin and (at least) share * lock on the buffer; it is still pinned/locked at exit. Also unlike * heap_fetch, we do not report any pgstats count; caller may do so if wanted. + * + * recheck should be set false on entry by caller, will be set true on exit + * if a WARM tuple is encountered. */ bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, - bool *all_dead, bool first_call) + bool *all_dead, bool first_call, bool *recheck) { Page dp = (Page) BufferGetPage(buffer); TransactionId prev_xmax = InvalidTransactionId; @@ -2034,9 +2109,12 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, ItemPointerSetOffsetNumber(&heapTuple->t_self, offnum); /* - * Shouldn't see a HEAP_ONLY tuple at chain start. + * Shouldn't see a HEAP_ONLY tuple at chain start, unless we are + * dealing with a WARM updated tuple in which case deferred triggers + * may request to fetch a WARM tuple from middle of a chain. */ - if (at_chain_start && HeapTupleIsHeapOnly(heapTuple)) + if (at_chain_start && HeapTupleIsHeapOnly(heapTuple) && + !HeapTupleIsHeapWarmTuple(heapTuple)) break; /* @@ -2049,6 +2127,16 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, break; /* + * Check if there exists a WARM tuple somewhere down the chain and set + * recheck to TRUE. + * + * XXX This is not very efficient right now, and we should look for + * possible improvements here + */ + if (recheck && *recheck == false) + *recheck = hot_check_warm_chain(dp, &heapTuple->t_self); + + /* * When first_call is true (and thus, skip is initially false) we'll * return the first tuple we find. But on later passes, heapTuple * will initially be pointing to the tuple we returned last time. @@ -2097,7 +2185,8 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, * Check to see if HOT chain continues past this tuple; if so fetch * the next offnum and loop around. */ - if (HeapTupleIsHotUpdated(heapTuple)) + if (HeapTupleIsHotUpdated(heapTuple) && + !HeapTupleHeaderHasRootOffset(heapTuple->t_data)) { Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) == ItemPointerGetBlockNumber(tid)); @@ -2121,18 +2210,41 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, */ bool heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot, - bool *all_dead) + bool *all_dead, bool *recheck, Buffer *cbuffer, + HeapTuple heapTuple) { bool result; Buffer buffer; - HeapTupleData heapTuple; + ItemPointerData ret_tid = *tid; buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); LockBuffer(buffer, BUFFER_LOCK_SHARE); - result = heap_hot_search_buffer(tid, relation, buffer, snapshot, - &heapTuple, all_dead, true); - LockBuffer(buffer, BUFFER_LOCK_UNLOCK); - ReleaseBuffer(buffer); + result = heap_hot_search_buffer(&ret_tid, relation, buffer, snapshot, + heapTuple, all_dead, true, recheck); + + /* + * If we are returning a potential candidate tuple from this chain and the + * caller has requested for "recheck" hint, keep the buffer locked and + * pinned. The caller must release the lock and pin on the buffer in all + * such cases + */ + if (!result || !recheck || !(*recheck)) + { + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffer); + } + + /* + * Set the caller supplied tid with the actual location of the tuple being + * returned + */ + if (result) + { + *tid = ret_tid; + if (cbuffer) + *cbuffer = buffer; + } + return result; } @@ -3484,13 +3596,15 @@ simple_heap_delete(Relation relation, ItemPointer tid) HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, - HeapUpdateFailureData *hufd, LockTupleMode *lockmode) + HeapUpdateFailureData *hufd, LockTupleMode *lockmode, + Bitmapset **modified_attrsp, bool *warm_update) { HTSU_Result result; TransactionId xid = GetCurrentTransactionId(); Bitmapset *hot_attrs; Bitmapset *key_attrs; Bitmapset *id_attrs; + Bitmapset *exprindx_attrs; Bitmapset *interesting_attrs; Bitmapset *modified_attrs; ItemId lp; @@ -3513,6 +3627,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, bool have_tuple_lock = false; bool iscombo; bool use_hot_update = false; + bool use_warm_update = false; bool key_intact; bool all_visible_cleared = false; bool all_visible_cleared_new = false; @@ -3537,6 +3652,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, (errcode(ERRCODE_INVALID_TRANSACTION_STATE), errmsg("cannot update tuples during a parallel operation"))); + /* Assume no-warm update */ + if (warm_update) + *warm_update = false; + /* * Fetch the list of attributes to be checked for various operations. * @@ -3558,10 +3677,13 @@ 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); + exprindx_attrs = RelationGetIndexAttrBitmap(relation, + INDEX_ATTR_BITMAP_EXPR_PREDICATE); + 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); - + interesting_attrs = bms_add_members(interesting_attrs, exprindx_attrs); block = ItemPointerGetBlockNumber(otid); offnum = ItemPointerGetOffsetNumber(otid); @@ -3613,6 +3735,9 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, modified_attrs = HeapDetermineModifiedColumns(relation, interesting_attrs, &oldtup, newtup); + if (modified_attrsp) + *modified_attrsp = bms_copy(modified_attrs); + /* * 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 @@ -3868,6 +3993,7 @@ l2: bms_free(hot_attrs); bms_free(key_attrs); bms_free(id_attrs); + bms_free(exprindx_attrs); bms_free(modified_attrs); bms_free(interesting_attrs); return result; @@ -4187,6 +4313,35 @@ l2: */ if (!bms_overlap(modified_attrs, hot_attrs)) use_hot_update = true; + else + { + /* + * If no WARM updates yet on this chain, let this update be a WARM + * update. + * + * We check for both warm and warm updated tuples since if the + * previous WARM update aborted, we may still have added + * another index entry for this HOT chain. In such situations, we + * must not attempt a WARM update until duplicate (key, CTID) index + * entry issue is sorted out + * + * XXX Later we'll add more checks to ensure WARM chains can + * further be WARM updated. This is probably good to do first rounf + * of tests of remaining functionality + * + * XXX Disable WARM updates on system tables. There is nothing in + * principle that stops us from supporting this. But it would + * require API change to propogate the changed columns back to the + * caller so that CatalogUpdateIndexes() can avoid adding new + * entries to indexes that are not changed by update. This will be + * fixed once basic patch is tested. !!FIXME + */ + if (relation->rd_supportswarm && + !bms_overlap(modified_attrs, exprindx_attrs) && + !bms_is_subset(hot_attrs, modified_attrs) && + !HeapTupleIsHeapWarmTuple(&oldtup)) + use_warm_update = true; + } } else { @@ -4234,6 +4389,22 @@ l2: HeapTupleSetHeapOnly(heaptup); /* Mark the caller's copy too, in case different from heaptup */ HeapTupleSetHeapOnly(newtup); + + /* + * Even if we are doing a HOT update, we must carry forward the WARM + * flag because we may have already inserted another index entry + * pointing to our root and a third entry may create duplicates + * + * Note: If we ever have a mechanism to avoid duplicate in + * indexes, we could look at relaxing this restriction and allow even + * more WARM udpates + */ + if (HeapTupleIsHeapWarmTuple(&oldtup)) + { + HeapTupleSetHeapWarmTuple(heaptup); + HeapTupleSetHeapWarmTuple(newtup); + } + /* * For HOT (or WARM) updated tuples, we store the offset of the root * line pointer of this chain in the ip_posid field of the new tuple. @@ -4246,12 +4417,36 @@ l2: if (HeapTupleHeaderHasRootOffset(oldtup.t_data)) root_offnum = HeapTupleHeaderGetRootOffset(oldtup.t_data); } + else if (use_warm_update) + { + /* Mark the old tuple as HOT-updated */ + HeapTupleSetHotUpdated(&oldtup); + HeapTupleSetHeapWarmTuple(&oldtup); + /* And mark the new tuple as heap-only */ + HeapTupleSetHeapOnly(heaptup); + HeapTupleSetHeapWarmTuple(heaptup); + /* Mark the caller's copy too, in case different from heaptup */ + HeapTupleSetHeapOnly(newtup); + HeapTupleSetHeapWarmTuple(newtup); + if (HeapTupleHeaderHasRootOffset(oldtup.t_data)) + root_offnum = HeapTupleHeaderGetRootOffset(oldtup.t_data); + else + heap_get_root_tuple_one(page, + ItemPointerGetOffsetNumber(&(oldtup.t_self)), + &root_offnum); + + /* Let the caller know we did a WARM update */ + if (warm_update) + *warm_update = true; + } else { /* Make sure tuples are correctly marked as not-HOT */ HeapTupleClearHotUpdated(&oldtup); HeapTupleClearHeapOnly(heaptup); HeapTupleClearHeapOnly(newtup); + HeapTupleClearHeapWarmTuple(heaptup); + HeapTupleClearHeapWarmTuple(newtup); root_offnum = InvalidOffsetNumber; } @@ -4360,7 +4555,10 @@ l2: if (have_tuple_lock) UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode); - pgstat_count_heap_update(relation, use_hot_update); + /* + * Count HOT and WARM updates separately + */ + pgstat_count_heap_update(relation, use_hot_update, use_warm_update); /* * If heaptup is a private copy, release it. Don't forget to copy t_self @@ -4500,7 +4698,8 @@ HeapDetermineModifiedColumns(Relation relation, Bitmapset *interesting_cols, * via ereport(). */ void -simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup) +simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup, + bool *warm_update, Bitmapset **modified_attrs) { HTSU_Result result; HeapUpdateFailureData hufd; @@ -4509,7 +4708,8 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup) result = heap_update(relation, otid, tup, GetCurrentCommandId(true), InvalidSnapshot, true /* wait for commit */ , - &hufd, &lockmode); + &hufd, &lockmode, modified_attrs, + warm_update); switch (result) { case HeapTupleSelfUpdated: @@ -7562,6 +7762,7 @@ log_heap_update(Relation reln, Buffer oldbuf, bool need_tuple_data = RelationIsLogicallyLogged(reln); bool init; int bufflags; + bool warm_update = false; /* Caller should not call me on a non-WAL-logged relation */ Assert(RelationNeedsWAL(reln)); @@ -7573,6 +7774,9 @@ log_heap_update(Relation reln, Buffer oldbuf, else info = XLOG_HEAP_UPDATE; + if (HeapTupleIsHeapWarmTuple(newtup)) + warm_update = true; + /* * If the old and new tuple are on the same page, we only need to log the * parts of the new tuple that were changed. That saves on the amount of @@ -7646,6 +7850,8 @@ log_heap_update(Relation reln, Buffer oldbuf, xlrec.flags |= XLH_UPDATE_CONTAINS_OLD_KEY; } } + if (warm_update) + xlrec.flags |= XLH_UPDATE_WARM_UPDATE; /* If new tuple is the single and first tuple on page... */ if (ItemPointerGetOffsetNumber(&(newtup->t_self)) == FirstOffsetNumber && @@ -8623,16 +8829,22 @@ heap_xlog_update(XLogReaderState *record, bool hot_update) Size freespace = 0; XLogRedoAction oldaction; XLogRedoAction newaction; + bool warm_update = false; /* initialize to keep the compiler quiet */ oldtup.t_data = NULL; oldtup.t_len = 0; + if (xlrec->flags & XLH_UPDATE_WARM_UPDATE) + warm_update = true; + XLogRecGetBlockTag(record, 0, &rnode, NULL, &newblk); if (XLogRecGetBlockTag(record, 1, NULL, NULL, &oldblk)) { /* HOT updates are never done across pages */ Assert(!hot_update); + /* WARM updates are never done across pages */ + Assert(!warm_update); } else oldblk = newblk; @@ -8692,6 +8904,11 @@ heap_xlog_update(XLogReaderState *record, bool hot_update) &htup->t_infomask2); HeapTupleHeaderSetXmax(htup, xlrec->old_xmax); HeapTupleHeaderSetCmax(htup, FirstCommandId, false); + + /* Mark the old tuple has a WARM tuple */ + if (warm_update) + HeapTupleHeaderSetHeapWarmTuple(htup); + /* Set forward chain link in t_ctid */ HeapTupleHeaderSetNextTid(htup, &newtid); @@ -8827,6 +9044,10 @@ heap_xlog_update(XLogReaderState *record, bool hot_update) HeapTupleHeaderSetCmin(htup, FirstCommandId); HeapTupleHeaderSetXmax(htup, xlrec->new_xmax); + /* Mark the new tuple has a WARM tuple */ + if (warm_update) + HeapTupleHeaderSetHeapWarmTuple(htup); + offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true); if (offnum == InvalidOffsetNumber) elog(PANIC, "failed to add tuple"); diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 4822af9..de559fd 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -71,10 +71,12 @@ #include "access/xlog.h" #include "catalog/catalog.h" #include "catalog/index.h" +#include "executor/executor.h" #include "pgstat.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/predicate.h" +#include "utils/datum.h" #include "utils/snapmgr.h" #include "utils/tqual.h" @@ -228,6 +230,21 @@ index_beginscan(Relation heapRelation, scan->heapRelation = heapRelation; scan->xs_snapshot = snapshot; + /* + * If the index supports recheck, make sure that index tuple is saved + * during index scans. + * + * XXX Ideally, we should look at all indexes on the table and check if + * WARM is at all supported on the base table. If WARM is not supported + * then we don't need to do any recheck. RelationGetIndexAttrBitmap() does + * do that and sets rd_supportswarm after looking at all indexes. But we + * don't know if the function was called earlier in the session when we're + * here. We can't call it now because there exists a risk of causing + * deadlock. + */ + if (indexRelation->rd_amroutine->amrecheck) + scan->xs_want_itup = true; + return scan; } @@ -409,7 +426,7 @@ index_getnext_tid(IndexScanDesc scan, ScanDirection direction) /* * The AM's amgettuple proc finds the next index entry matching the scan * keys, and puts the TID into scan->xs_ctup.t_self. It should also set - * scan->xs_recheck and possibly scan->xs_itup, though we pay no attention + * scan->xs_tuple_recheck and possibly scan->xs_itup, though we pay no attention * to those fields here. */ found = scan->indexRelation->rd_amroutine->amgettuple(scan, direction); @@ -448,7 +465,7 @@ index_getnext_tid(IndexScanDesc scan, ScanDirection direction) * dropped in a future index_getnext_tid, index_fetch_heap or index_endscan * call). * - * Note: caller must check scan->xs_recheck, and perform rechecking of the + * Note: caller must check scan->xs_tuple_recheck, and perform rechecking of the * scan keys if required. We do not do that here because we don't have * enough information to do it efficiently in the general case. * ---------------- @@ -475,6 +492,15 @@ index_fetch_heap(IndexScanDesc scan) */ if (prev_buf != scan->xs_cbuf) heap_page_prune_opt(scan->heapRelation, scan->xs_cbuf); + + /* + * If we're not always re-checking, reset recheck for this tuple + */ + if (!scan->xs_recheck) + scan->xs_tuple_recheck = false; + else + scan->xs_tuple_recheck = true; + } /* Obtain share-lock on the buffer so we can examine visibility */ @@ -484,32 +510,64 @@ index_fetch_heap(IndexScanDesc scan) scan->xs_snapshot, &scan->xs_ctup, &all_dead, - !scan->xs_continue_hot); + !scan->xs_continue_hot, + &scan->xs_tuple_recheck); LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK); if (got_heap_tuple) { + bool res = true; + + /* + * Ok we got a tuple which satisfies the snapshot, but if its part of a + * WARM chain, we must do additional checks to ensure that we are + * indeed returning a correct tuple. Note that if the index AM does not + * implement amrecheck method, then we don't any additional checks + * since WARM must have been disabled on such tables + * + * XXX What happens when a new index which does not support amcheck is + * added to the table? Do we need to handle this case or is CREATE + * INDEX and CREATE INDEX CONCURRENTLY smart enough to handle this + * issue? + */ + if (scan->xs_tuple_recheck && + scan->xs_itup && + scan->indexRelation->rd_amroutine->amrecheck) + { + LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE); + res = scan->indexRelation->rd_amroutine->amrecheck( + scan->indexRelation, + scan->xs_itup, + scan->heapRelation, + &scan->xs_ctup); + LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK); + } + /* * Only in a non-MVCC snapshot can more than one member of the HOT * chain be visible. */ scan->xs_continue_hot = !IsMVCCSnapshot(scan->xs_snapshot); pgstat_count_heap_fetch(scan->indexRelation); - return &scan->xs_ctup; - } - /* We've reached the end of the HOT chain. */ - scan->xs_continue_hot = false; + if (res) + return &scan->xs_ctup; + } + else + { + /* We've reached the end of the HOT chain. */ + scan->xs_continue_hot = false; - /* - * If we scanned a whole HOT chain and found only dead tuples, tell index - * AM to kill its entry for that TID (this will take effect in the next - * amgettuple call, in index_getnext_tid). We do not do this when in - * recovery because it may violate MVCC to do so. See comments in - * RelationGetIndexScan(). - */ - if (!scan->xactStartedInRecovery) - scan->kill_prior_tuple = all_dead; + /* + * If we scanned a whole HOT chain and found only dead tuples, tell index + * AM to kill its entry for that TID (this will take effect in the next + * amgettuple call, in index_getnext_tid). We do not do this when in + * recovery because it may violate MVCC to do so. See comments in + * RelationGetIndexScan(). + */ + if (!scan->xactStartedInRecovery) + scan->kill_prior_tuple = all_dead; + } return NULL; } diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c index 883d70d..6efccf7 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -19,11 +19,14 @@ #include "access/nbtree.h" #include "access/transam.h" #include "access/xloginsert.h" +#include "catalog/index.h" +#include "executor/executor.h" #include "miscadmin.h" +#include "nodes/execnodes.h" #include "storage/lmgr.h" #include "storage/predicate.h" #include "utils/tqual.h" - +#include "utils/datum.h" typedef struct { @@ -249,6 +252,9 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, BTPageOpaque opaque; Buffer nbuf = InvalidBuffer; bool found = false; + Buffer buffer; + HeapTupleData heapTuple; + bool recheck = false; /* Assume unique until we find a duplicate */ *is_unique = true; @@ -308,6 +314,8 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, curitup = (IndexTuple) PageGetItem(page, curitemid); htid = curitup->t_tid; + recheck = false; + /* * If we are doing a recheck, we expect to find the tuple we * are rechecking. It's not a duplicate, but we have to keep @@ -325,112 +333,153 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, * have just a single index entry for the entire chain. */ else if (heap_hot_search(&htid, heapRel, &SnapshotDirty, - &all_dead)) + &all_dead, &recheck, &buffer, + &heapTuple)) { TransactionId xwait; + bool result = true; /* - * It is a duplicate. If we are only doing a partial - * check, then don't bother checking if the tuple is being - * updated in another transaction. Just return the fact - * that it is a potential conflict and leave the full - * check till later. + * If the tuple was WARM update, we may again see our own + * tuple. Since WARM updates don't create new index + * entries, our own tuple is only reachable via the old + * index pointer */ - if (checkUnique == UNIQUE_CHECK_PARTIAL) + if (checkUnique == UNIQUE_CHECK_EXISTING && + ItemPointerCompare(&htid, &itup->t_tid) == 0) { - if (nbuf != InvalidBuffer) - _bt_relbuf(rel, nbuf); - *is_unique = false; - return InvalidTransactionId; + found = true; + result = false; + if (recheck) + UnlockReleaseBuffer(buffer); } - - /* - * If this tuple is being updated by other transaction - * then we have to wait for its commit/abort. - */ - xwait = (TransactionIdIsValid(SnapshotDirty.xmin)) ? - SnapshotDirty.xmin : SnapshotDirty.xmax; - - if (TransactionIdIsValid(xwait)) + else if (recheck) { - if (nbuf != InvalidBuffer) - _bt_relbuf(rel, nbuf); - /* Tell _bt_doinsert to wait... */ - *speculativeToken = SnapshotDirty.speculativeToken; - return xwait; + result = btrecheck(rel, curitup, heapRel, &heapTuple); + UnlockReleaseBuffer(buffer); } - /* - * Otherwise we have a definite conflict. But before - * complaining, look to see if the tuple we want to insert - * is itself now committed dead --- if so, don't complain. - * This is a waste of time in normal scenarios but we must - * do it to support CREATE INDEX CONCURRENTLY. - * - * We must follow HOT-chains here because during - * concurrent index build, we insert the root TID though - * the actual tuple may be somewhere in the HOT-chain. - * While following the chain we might not stop at the - * exact tuple which triggered the insert, but that's OK - * because if we find a live tuple anywhere in this chain, - * we have a unique key conflict. The other live tuple is - * not part of this chain because it had a different index - * entry. - */ - htid = itup->t_tid; - if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL)) - { - /* Normal case --- it's still live */ - } - else + if (result) { /* - * It's been deleted, so no error, and no need to - * continue searching + * It is a duplicate. If we are only doing a partial + * check, then don't bother checking if the tuple is being + * updated in another transaction. Just return the fact + * that it is a potential conflict and leave the full + * check till later. */ - break; - } + if (checkUnique == UNIQUE_CHECK_PARTIAL) + { + if (nbuf != InvalidBuffer) + _bt_relbuf(rel, nbuf); + *is_unique = false; + return InvalidTransactionId; + } - /* - * Check for a conflict-in as we would if we were going to - * write to this page. We aren't actually going to write, - * but we want a chance to report SSI conflicts that would - * otherwise be masked by this unique constraint - * violation. - */ - CheckForSerializableConflictIn(rel, NULL, buf); + /* + * If this tuple is being updated by other transaction + * then we have to wait for its commit/abort. + */ + xwait = (TransactionIdIsValid(SnapshotDirty.xmin)) ? + SnapshotDirty.xmin : SnapshotDirty.xmax; + + if (TransactionIdIsValid(xwait)) + { + if (nbuf != InvalidBuffer) + _bt_relbuf(rel, nbuf); + /* Tell _bt_doinsert to wait... */ + *speculativeToken = SnapshotDirty.speculativeToken; + return xwait; + } - /* - * This is a definite conflict. Break the tuple down into - * datums and report the error. But first, make sure we - * release the buffer locks we're holding --- - * BuildIndexValueDescription could make catalog accesses, - * which in the worst case might touch this same index and - * cause deadlocks. - */ - if (nbuf != InvalidBuffer) - _bt_relbuf(rel, nbuf); - _bt_relbuf(rel, buf); + /* + * Otherwise we have a definite conflict. But before + * complaining, look to see if the tuple we want to insert + * is itself now committed dead --- if so, don't complain. + * This is a waste of time in normal scenarios but we must + * do it to support CREATE INDEX CONCURRENTLY. + * + * We must follow HOT-chains here because during + * concurrent index build, we insert the root TID though + * the actual tuple may be somewhere in the HOT-chain. + * While following the chain we might not stop at the + * exact tuple which triggered the insert, but that's OK + * because if we find a live tuple anywhere in this chain, + * we have a unique key conflict. The other live tuple is + * not part of this chain because it had a different index + * entry. + */ + recheck = false; + ItemPointerCopy(&itup->t_tid, &htid); + if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL, + &recheck, &buffer, &heapTuple)) + { + bool result = true; + if (recheck) + { + /* + * Recheck if the tuple actually satisfies the + * index key. Otherwise, we might be following + * a wrong index pointer and mustn't entertain + * this tuple + */ + result = btrecheck(rel, itup, heapRel, &heapTuple); + UnlockReleaseBuffer(buffer); + } + if (!result) + break; + /* Normal case --- it's still live */ + } + else + { + /* + * It's been deleted, so no error, and no need to + * continue searching + */ + break; + } - { - Datum values[INDEX_MAX_KEYS]; - bool isnull[INDEX_MAX_KEYS]; - char *key_desc; - - index_deform_tuple(itup, RelationGetDescr(rel), - values, isnull); - - key_desc = BuildIndexValueDescription(rel, values, - isnull); - - ereport(ERROR, - (errcode(ERRCODE_UNIQUE_VIOLATION), - errmsg("duplicate key value violates unique constraint \"%s\"", - RelationGetRelationName(rel)), - key_desc ? errdetail("Key %s already exists.", - key_desc) : 0, - errtableconstraint(heapRel, - RelationGetRelationName(rel)))); + /* + * Check for a conflict-in as we would if we were going to + * write to this page. We aren't actually going to write, + * but we want a chance to report SSI conflicts that would + * otherwise be masked by this unique constraint + * violation. + */ + CheckForSerializableConflictIn(rel, NULL, buf); + + /* + * This is a definite conflict. Break the tuple down into + * datums and report the error. But first, make sure we + * release the buffer locks we're holding --- + * BuildIndexValueDescription could make catalog accesses, + * which in the worst case might touch this same index and + * cause deadlocks. + */ + if (nbuf != InvalidBuffer) + _bt_relbuf(rel, nbuf); + _bt_relbuf(rel, buf); + + { + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + char *key_desc; + + index_deform_tuple(itup, RelationGetDescr(rel), + values, isnull); + + key_desc = BuildIndexValueDescription(rel, values, + isnull); + + ereport(ERROR, + (errcode(ERRCODE_UNIQUE_VIOLATION), + errmsg("duplicate key value violates unique constraint \"%s\"", + RelationGetRelationName(rel)), + key_desc ? errdetail("Key %s already exists.", + key_desc) : 0, + errtableconstraint(heapRel, + RelationGetRelationName(rel)))); + } } } else if (all_dead) diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index 1bb1acf..cb5a796 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -23,6 +23,7 @@ #include "access/xlog.h" #include "catalog/index.h" #include "commands/vacuum.h" +#include "executor/nodeIndexscan.h" #include "storage/indexfsm.h" #include "storage/ipc.h" #include "storage/lmgr.h" @@ -118,6 +119,7 @@ bthandler(PG_FUNCTION_ARGS) amroutine->amendscan = btendscan; amroutine->ammarkpos = btmarkpos; amroutine->amrestrpos = btrestrpos; + amroutine->amrecheck = btrecheck; PG_RETURN_POINTER(amroutine); } @@ -298,8 +300,9 @@ btgettuple(IndexScanDesc scan, ScanDirection dir) BTScanOpaque so = (BTScanOpaque) scan->opaque; bool res; - /* btree indexes are never lossy */ + /* btree indexes are never lossy, except for WARM tuples */ scan->xs_recheck = false; + scan->xs_tuple_recheck = false; /* * If we have any array keys, initialize them during first call for a diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index da0f330..9becaeb 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -20,11 +20,15 @@ #include "access/nbtree.h" #include "access/reloptions.h" #include "access/relscan.h" +#include "catalog/index.h" +#include "executor/executor.h" #include "miscadmin.h" +#include "nodes/execnodes.h" #include "utils/array.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/datum.h" typedef struct BTSortArrayContext @@ -2065,3 +2069,103 @@ btproperty(Oid index_oid, int attno, return false; /* punt to generic code */ } } + +/* + * Check if the index tuple's key matches the one computed from the given heap + * tuple's attribute + */ +bool +btrecheck(Relation indexRel, IndexTuple indexTuple, + Relation heapRel, HeapTuple heapTuple) +{ + IndexInfo *indexInfo; + EState *estate; + ExprContext *econtext; + TupleTableSlot *slot; + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + int i; + bool equal; + int natts = indexRel->rd_rel->relnatts; + Form_pg_attribute att; + + /* Get IndexInfo for this index */ + indexInfo = BuildIndexInfo(indexRel); + + /* + * The heap tuple must be put into a slot for FormIndexDatum. + */ + slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRel)); + + ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); + + /* + * Typically the index won't have expressions, but if it does we need an + * EState to evaluate them. We need it for exclusion constraints too, + * even if they are just on simple columns. + */ + if (indexInfo->ii_Expressions != NIL || + indexInfo->ii_ExclusionOps != NULL) + { + estate = CreateExecutorState(); + econtext = GetPerTupleExprContext(estate); + econtext->ecxt_scantuple = slot; + } + else + estate = NULL; + + /* + * Form the index values and isnull flags for the index entry that we need + * to check. + * + * Note: if the index uses functions that are not as immutable as they are + * supposed to be, this could produce an index tuple different from the + * original. The index AM can catch such errors by verifying that it + * finds a matching index entry with the tuple's TID. For exclusion + * constraints we check this in check_exclusion_constraint(). + */ + FormIndexDatum(indexInfo, slot, estate, values, isnull); + + equal = true; + for (i = 1; i <= natts; i++) + { + Datum indxvalue; + bool indxisnull; + + indxvalue = index_getattr(indexTuple, i, indexRel->rd_att, &indxisnull); + + /* + * If both are NULL, then they are equal + */ + if (isnull[i - 1] && indxisnull) + continue; + + /* + * If just one is NULL, then they are not equal + */ + if (isnull[i - 1] || indxisnull) + { + equal = false; + break; + } + + /* + * Now just do a raw memory comparison. If the index tuple was formed + * using this heap tuple, the computed index values must match + */ + att = indexRel->rd_att->attrs[i - 1]; + if (!datumIsEqual(values[i - 1], indxvalue, att->attbyval, + att->attlen)) + { + equal = false; + break; + } + } + + if (estate != NULL) + FreeExecutorState(estate); + + ExecDropSingleTupleTableSlot(slot); + + return equal; +} diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c index ca4b0bd..b0d2952 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -68,6 +68,7 @@ spghandler(PG_FUNCTION_ARGS) amroutine->amendscan = spgendscan; amroutine->ammarkpos = NULL; amroutine->amrestrpos = NULL; + amroutine->amrecheck = NULL; PG_RETURN_POINTER(amroutine); } diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index a96bf69..eed5b0b 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -1230,6 +1230,9 @@ SetDefaultACL(InternalDefaultACL *iacls) } else { + bool warm_update; + Bitmapset *modified_attrs; + /* Prepare to insert or update pg_default_acl entry */ MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); @@ -1245,6 +1248,8 @@ SetDefaultACL(InternalDefaultACL *iacls) newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); simple_heap_insert(rel, newtuple); + warm_update = false; + modified_attrs = NULL; } else { @@ -1254,11 +1259,12 @@ SetDefaultACL(InternalDefaultACL *iacls) newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), values, nulls, replaces); - simple_heap_update(rel, &newtuple->t_self, newtuple); + simple_heap_update(rel, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); } /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(rel, newtuple); + CatalogUpdateIndexes(rel, newtuple, warm_update, modified_attrs); /* these dependencies don't change in an update */ if (isNew) @@ -1686,13 +1692,17 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, if (need_update) { + bool warm_update; + Bitmapset *modified_attrs; + newtuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attRelation), values, nulls, replaces); - simple_heap_update(attRelation, &newtuple->t_self, newtuple); + simple_heap_update(attRelation, &newtuple->t_self, newtuple, + &warm_update, &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(attRelation, newtuple); + CatalogUpdateIndexes(attRelation, newtuple, warm_update, modified_attrs); /* Update initial privileges for extensions */ recordExtensionInitPriv(relOid, RelationRelationId, attnum, @@ -1899,6 +1909,8 @@ ExecGrant_Relation(InternalGrant *istmt) int nnewmembers; Oid *newmembers; AclObjectKind aclkind; + bool warm_update; + Bitmapset *modified_attrs; /* Determine ID to do the grant as, and available grant options */ select_best_grantor(GetUserId(), this_privileges, @@ -1955,10 +1967,12 @@ ExecGrant_Relation(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, + &warm_update, &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, + modified_attrs); /* Update initial privileges for extensions */ recordExtensionInitPriv(relOid, RelationRelationId, 0, new_acl); @@ -2079,6 +2093,8 @@ ExecGrant_Database(InternalGrant *istmt) Oid *oldmembers; Oid *newmembers; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(datId)); if (!HeapTupleIsValid(tuple)) @@ -2148,10 +2164,11 @@ ExecGrant_Database(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); /* Update the shared dependency ACL info */ updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), 0, @@ -2202,6 +2219,8 @@ ExecGrant_Fdw(InternalGrant *istmt) int nnewmembers; Oid *oldmembers; Oid *newmembers; + bool warm_update; + Bitmapset *modified_attrs; tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)); @@ -2273,10 +2292,11 @@ ExecGrant_Fdw(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); /* Update initial privileges for extensions */ recordExtensionInitPriv(fdwid, ForeignDataWrapperRelationId, 0, @@ -2332,6 +2352,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) int nnewmembers; Oid *oldmembers; Oid *newmembers; + bool warm_update; + Bitmapset *modified_attrs; tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvid)); if (!HeapTupleIsValid(tuple)) @@ -2402,10 +2424,11 @@ ExecGrant_ForeignServer(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); /* Update initial privileges for extensions */ recordExtensionInitPriv(srvid, ForeignServerRelationId, 0, new_acl); @@ -2460,6 +2483,8 @@ ExecGrant_Function(InternalGrant *istmt) int nnewmembers; Oid *oldmembers; Oid *newmembers; + bool warm_update; + Bitmapset *modified_attrs; tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcId)); if (!HeapTupleIsValid(tuple)) @@ -2529,10 +2554,11 @@ ExecGrant_Function(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); /* Update initial privileges for extensions */ recordExtensionInitPriv(funcId, ProcedureRelationId, 0, new_acl); @@ -2586,6 +2612,8 @@ ExecGrant_Language(InternalGrant *istmt) int nnewmembers; Oid *oldmembers; Oid *newmembers; + bool warm_update; + Bitmapset *modified_attrs; tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(langId)); if (!HeapTupleIsValid(tuple)) @@ -2663,10 +2691,11 @@ ExecGrant_Language(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); /* Update initial privileges for extensions */ recordExtensionInitPriv(langId, LanguageRelationId, 0, new_acl); @@ -2724,6 +2753,8 @@ ExecGrant_Largeobject(InternalGrant *istmt) ScanKeyData entry[1]; SysScanDesc scan; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; /* There's no syscache for pg_largeobject_metadata */ ScanKeyInit(&entry[0], @@ -2805,10 +2836,11 @@ ExecGrant_Largeobject(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); /* Update initial privileges for extensions */ recordExtensionInitPriv(loid, LargeObjectRelationId, 0, new_acl); @@ -2863,6 +2895,8 @@ ExecGrant_Namespace(InternalGrant *istmt) int nnewmembers; Oid *oldmembers; Oid *newmembers; + bool warm_update; + Bitmapset *modified_attrs; tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nspid)); if (!HeapTupleIsValid(tuple)) @@ -2933,10 +2967,11 @@ ExecGrant_Namespace(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); /* Update initial privileges for extensions */ recordExtensionInitPriv(nspid, NamespaceRelationId, 0, new_acl); @@ -2990,6 +3025,9 @@ ExecGrant_Tablespace(InternalGrant *istmt) Oid *oldmembers; Oid *newmembers; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; + /* Search syscache for pg_tablespace */ tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tblId)); @@ -3060,10 +3098,11 @@ ExecGrant_Tablespace(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); /* Update the shared dependency ACL info */ updateAclDependencies(TableSpaceRelationId, tblId, 0, @@ -3113,6 +3152,8 @@ ExecGrant_Type(InternalGrant *istmt) Oid *oldmembers; Oid *newmembers; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; /* Search syscache for pg_type */ tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typId)); @@ -3197,10 +3238,11 @@ ExecGrant_Type(InternalGrant *istmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); /* Update initial privileges for extensions */ recordExtensionInitPriv(typId, TypeRelationId, 0, new_acl); @@ -5354,6 +5396,9 @@ recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) /* If we have a new ACL to set, then update the row with it. */ if (new_acl) { + bool warm_update; + Bitmapset *modified_attrs; + MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); MemSet(replace, false, sizeof(replace)); @@ -5364,10 +5409,12 @@ recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) oldtuple = heap_modify_tuple(oldtuple, RelationGetDescr(relation), values, nulls, replace); - simple_heap_update(relation, &oldtuple->t_self, oldtuple); + simple_heap_update(relation, &oldtuple->t_self, oldtuple, + &warm_update, &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, oldtuple); + CatalogUpdateIndexes(relation, oldtuple, warm_update, + modified_attrs); } else /* new_acl is NULL, so delete the entry we found. */ @@ -5396,7 +5443,7 @@ recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl) simple_heap_insert(relation, tuple); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relation, tuple); + CatalogUpdateIndexes(relation, tuple, false, NULL); } systable_endscan(scan); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index bfc54a8..8a8cdee 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -644,9 +644,9 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, simple_heap_insert(pg_attribute_rel, tup); if (indstate != NULL) - CatalogIndexInsert(indstate, tup); + CatalogIndexInsert(indstate, tup, false, NULL); else - CatalogUpdateIndexes(pg_attribute_rel, tup); + CatalogUpdateIndexes(pg_attribute_rel, tup, false, NULL); heap_freetuple(tup); } @@ -837,7 +837,7 @@ InsertPgClassTuple(Relation pg_class_desc, /* finally insert the new tuple, update the indexes, and clean up */ simple_heap_insert(pg_class_desc, tup); - CatalogUpdateIndexes(pg_class_desc, tup); + CatalogUpdateIndexes(pg_class_desc, tup, false, NULL); heap_freetuple(tup); } @@ -1581,6 +1581,9 @@ RemoveAttributeById(Oid relid, AttrNumber attnum) } else { + bool warm_update; + Bitmapset *modified_attrs; + /* Dropping user attributes is lots harder */ /* Mark the attribute as dropped */ @@ -1610,10 +1613,11 @@ RemoveAttributeById(Oid relid, AttrNumber attnum) "........pg.dropped.%d........", attnum); namestrcpy(&(attStruct->attname), newattname); - simple_heap_update(attr_rel, &tuple->t_self, tuple); + simple_heap_update(attr_rel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep the system catalog indexes current */ - CatalogUpdateIndexes(attr_rel, tuple); + CatalogUpdateIndexes(attr_rel, tuple, warm_update, modified_attrs); } /* @@ -1701,6 +1705,8 @@ RemoveAttrDefaultById(Oid attrdefId) HeapTuple tuple; Oid myrelid; AttrNumber myattnum; + bool warm_update; + Bitmapset *modified_attrs; /* Grab an appropriate lock on the pg_attrdef relation */ attrdef_rel = heap_open(AttrDefaultRelationId, RowExclusiveLock); @@ -1742,10 +1748,11 @@ RemoveAttrDefaultById(Oid attrdefId) ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false; - simple_heap_update(attr_rel, &tuple->t_self, tuple); + simple_heap_update(attr_rel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep the system catalog indexes current */ - CatalogUpdateIndexes(attr_rel, tuple); + CatalogUpdateIndexes(attr_rel, tuple, warm_update, modified_attrs); /* * Our update of the pg_attribute row will force a relcache rebuild, so @@ -1945,7 +1952,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, tuple = heap_form_tuple(adrel->rd_att, values, nulls); attrdefOid = simple_heap_insert(adrel, tuple); - CatalogUpdateIndexes(adrel, tuple); + CatalogUpdateIndexes(adrel, tuple, false, NULL); defobject.classId = AttrDefaultRelationId; defobject.objectId = attrdefOid; @@ -1974,10 +1981,14 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, attStruct = (Form_pg_attribute) GETSTRUCT(atttup); if (!attStruct->atthasdef) { + bool warm_update; + Bitmapset *modified_attrs; + attStruct->atthasdef = true; - simple_heap_update(attrrel, &atttup->t_self, atttup); + simple_heap_update(attrrel, &atttup->t_self, atttup, &warm_update, + &modified_attrs); /* keep catalog indexes current */ - CatalogUpdateIndexes(attrrel, atttup); + CatalogUpdateIndexes(attrrel, atttup, warm_update, modified_attrs); } heap_close(attrrel, RowExclusiveLock); heap_freetuple(atttup); @@ -2479,6 +2490,9 @@ MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr, if (con->conrelid == RelationGetRelid(rel)) { + bool warm_update; + Bitmapset *modified_attrs; + /* Found it. Conflicts if not identical check constraint */ if (con->contype == CONSTRAINT_CHECK) { @@ -2572,8 +2586,9 @@ MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr, Assert(is_local); con->connoinherit = true; } - simple_heap_update(conDesc, &tup->t_self, tup); - CatalogUpdateIndexes(conDesc, tup); + simple_heap_update(conDesc, &tup->t_self, tup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(conDesc, tup, warm_update, modified_attrs); break; } } @@ -2611,12 +2626,16 @@ SetRelationNumChecks(Relation rel, int numchecks) if (relStruct->relchecks != numchecks) { + bool warm_update; + Bitmapset *modified_attrs; + relStruct->relchecks = numchecks; - simple_heap_update(relrel, &reltup->t_self, reltup); + simple_heap_update(relrel, &reltup->t_self, reltup, &warm_update, + &modified_attrs); /* keep catalog indexes current */ - CatalogUpdateIndexes(relrel, reltup); + CatalogUpdateIndexes(relrel, reltup, warm_update, modified_attrs); } else { @@ -3159,7 +3178,7 @@ StorePartitionKey(Relation rel, simple_heap_insert(pg_partitioned_table, tuple); /* Update the indexes on pg_partitioned_table */ - CatalogUpdateIndexes(pg_partitioned_table, tuple); + CatalogUpdateIndexes(pg_partitioned_table, tuple, false, NULL); heap_close(pg_partitioned_table, RowExclusiveLock); /* Mark this relation as dependent on a few things as follows */ @@ -3243,6 +3262,8 @@ StorePartitionBound(Relation rel, Relation parent, Node *bound) Datum new_val[Natts_pg_class]; bool new_null[Natts_pg_class], new_repl[Natts_pg_class]; + bool warm_update; + Bitmapset *modified_attrs; /* Update pg_class tuple */ classRel = heap_open(RelationRelationId, RowExclusiveLock); @@ -3276,8 +3297,9 @@ StorePartitionBound(Relation rel, Relation parent, Node *bound) new_val, new_null, new_repl); /* Also set the flag */ ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = true; - simple_heap_update(classRel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(classRel, newtuple); + simple_heap_update(classRel, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(classRel, newtuple, warm_update, modified_attrs); heap_freetuple(newtuple); heap_close(classRel, RowExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 26cbc0e..04ea34f 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -54,6 +54,7 @@ #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" +#include "optimizer/var.h" #include "parser/parser.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" @@ -652,7 +653,7 @@ UpdateIndexRelation(Oid indexoid, simple_heap_insert(pg_index, tuple); /* update the indexes on pg_index */ - CatalogUpdateIndexes(pg_index, tuple); + CatalogUpdateIndexes(pg_index, tuple, false, NULL); /* * close the relation and free the tuple @@ -1324,8 +1325,13 @@ index_constraint_create(Relation heapRelation, if (dirty) { - simple_heap_update(pg_index, &indexTuple->t_self, indexTuple); - CatalogUpdateIndexes(pg_index, indexTuple); + bool warm_update; + Bitmapset *modified_attrs; + + simple_heap_update(pg_index, &indexTuple->t_self, indexTuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_index, indexTuple, warm_update, + modified_attrs); InvokeObjectPostAlterHookArg(IndexRelationId, indexRelationId, 0, InvalidOid, is_internal); @@ -1691,6 +1697,20 @@ BuildIndexInfo(Relation index) ii->ii_Concurrent = false; ii->ii_BrokenHotChain = false; + /* build a bitmap of all table attributes referred by this index */ + for (i = 0; i < ii->ii_NumIndexAttrs; i++) + { + AttrNumber attr = ii->ii_KeyAttrNumbers[i]; + ii->ii_indxattrs = bms_add_member(ii->ii_indxattrs, attr - + FirstLowInvalidHeapAttributeNumber); + } + + /* Collect all attributes used in expressions, too */ + pull_varattnos((Node *) ii->ii_Expressions, 1, &ii->ii_indxattrs); + + /* Collect all attributes in the index predicate, too */ + pull_varattnos((Node *) ii->ii_Predicate, 1, &ii->ii_indxattrs); + return ii; } @@ -2090,6 +2110,8 @@ index_build(Relation heapRelation, Relation pg_index; HeapTuple indexTuple; Form_pg_index indexForm; + bool warm_update; + Bitmapset *modified_attrs; pg_index = heap_open(IndexRelationId, RowExclusiveLock); @@ -2103,8 +2125,9 @@ index_build(Relation heapRelation, Assert(!indexForm->indcheckxmin); indexForm->indcheckxmin = true; - simple_heap_update(pg_index, &indexTuple->t_self, indexTuple); - CatalogUpdateIndexes(pg_index, indexTuple); + simple_heap_update(pg_index, &indexTuple->t_self, indexTuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_index, indexTuple, warm_update, modified_attrs); heap_freetuple(indexTuple); heap_close(pg_index, RowExclusiveLock); @@ -3441,6 +3464,9 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, (indexForm->indcheckxmin && !indexInfo->ii_BrokenHotChain) || early_pruning_enabled) { + bool warm_update; + Bitmapset *modified_attrs; + if (!indexInfo->ii_BrokenHotChain && !early_pruning_enabled) indexForm->indcheckxmin = false; else if (index_bad || early_pruning_enabled) @@ -3448,8 +3474,10 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, indexForm->indisvalid = true; indexForm->indisready = true; indexForm->indislive = true; - simple_heap_update(pg_index, &indexTuple->t_self, indexTuple); - CatalogUpdateIndexes(pg_index, indexTuple); + simple_heap_update(pg_index, &indexTuple->t_self, indexTuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_index, indexTuple, warm_update, + modified_attrs); /* * Invalidate the relcache for the table, so that after we commit diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index 1915ca3..5046fd1 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -67,9 +67,13 @@ CatalogCloseIndexes(CatalogIndexState indstate) * This should be called for each inserted or updated catalog tuple. * * This is effectively a cut-down version of ExecInsertIndexTuples. + * + * See comments at CatalogUpdateIndexes for details about warm_update and + * modified_attrs */ void -CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) +CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple, + bool warm_update, Bitmapset *modified_attrs) { int i; int numIndexes; @@ -79,12 +83,27 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) IndexInfo **indexInfoArray; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; + ItemPointerData root_tid; - /* HOT update does not require index inserts */ - if (HeapTupleIsHeapOnly(heapTuple)) + /* + * HOT update does not require index inserts, but WARM may need for some + * indexes + */ + if (HeapTupleIsHeapOnly(heapTuple) && !warm_update) return; /* + * If we've done a WARM update, then we must index the TID of the root line + * pointer and not the actual TID of the new tuple. + */ + if (warm_update) + ItemPointerSet(&root_tid, + ItemPointerGetBlockNumber(&(heapTuple->t_self)), + HeapTupleHeaderGetRootOffset(heapTuple->t_data)); + else + ItemPointerCopy(&heapTuple->t_self, &root_tid); + + /* * Get information from the state structure. Fall out if nothing to do. */ numIndexes = indstate->ri_NumIndices; @@ -112,6 +131,17 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) continue; /* + * If we've done WARM update, then we must not insert a new index tuple + * if none of the index keys have changed. This is not just an + * optimization, but a requirement for WARM to work correctly. + */ + if (warm_update) + { + if (!bms_overlap(modified_attrs, indexInfo->ii_indxattrs)) + continue; + } + + /* * Expressional and partial indexes on system catalogs are not * supported, nor exclusion constraints, nor deferred uniqueness */ @@ -136,7 +166,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) index_insert(relationDescs[i], /* index relation */ values, /* array of index Datums */ isnull, /* is-null flags */ - &(heapTuple->t_self), /* tid of heap tuple */ + &root_tid, heapRelation, relationDescs[i]->rd_index->indisunique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO); @@ -152,13 +182,21 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) * to insert or update a single tuple in a system catalog. Avoid using it for * multiple tuples, since opening the indexes and building the index info * structures is moderately expensive. + * + * warm_update is passed as true if the indexes are being updated as a result + * of an update operation on the underlying system table and that update was a + * WARM update. + * + * modified_attrs contains a bitmap of attributes modified by the update + * operation. */ void -CatalogUpdateIndexes(Relation heapRel, HeapTuple heapTuple) +CatalogUpdateIndexes(Relation heapRel, HeapTuple heapTuple, + bool warm_update, Bitmapset *modified_attrs) { CatalogIndexState indstate; indstate = CatalogOpenIndexes(heapRel); - CatalogIndexInsert(indstate, heapTuple); + CatalogIndexInsert(indstate, heapTuple, warm_update, modified_attrs); CatalogCloseIndexes(indstate); } diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 3a4e22f..4642614 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -676,7 +676,7 @@ AggregateCreate(const char *aggName, tup = heap_form_tuple(tupDesc, values, nulls); simple_heap_insert(aggdesc, tup); - CatalogUpdateIndexes(aggdesc, tup); + CatalogUpdateIndexes(aggdesc, tup, false, NULL); heap_close(aggdesc, RowExclusiveLock); diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c index 694c0f6..d143a4a 100644 --- a/src/backend/catalog/pg_collation.c +++ b/src/backend/catalog/pg_collation.c @@ -138,7 +138,7 @@ CollationCreate(const char *collname, Oid collnamespace, Assert(OidIsValid(oid)); /* update the index if any */ - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); /* set up dependencies for the new collation */ myself.classId = CollationRelationId; diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index b5a0ce9..6757d9c 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -229,7 +229,7 @@ CreateConstraintEntry(const char *constraintName, conOid = simple_heap_insert(conDesc, tup); /* update catalog indexes */ - CatalogUpdateIndexes(conDesc, tup); + CatalogUpdateIndexes(conDesc, tup, false, NULL); conobject.classId = ConstraintRelationId; conobject.objectId = conOid; @@ -570,6 +570,8 @@ RemoveConstraintById(Oid conId) Relation pgrel; HeapTuple relTup; Form_pg_class classForm; + bool warm_update; + Bitmapset *modified_attrs; pgrel = heap_open(RelationRelationId, RowExclusiveLock); relTup = SearchSysCacheCopy1(RELOID, @@ -584,9 +586,10 @@ RemoveConstraintById(Oid conId) RelationGetRelationName(rel)); classForm->relchecks--; - simple_heap_update(pgrel, &relTup->t_self, relTup); + simple_heap_update(pgrel, &relTup->t_self, relTup, &warm_update, + &modified_attrs); - CatalogUpdateIndexes(pgrel, relTup); + CatalogUpdateIndexes(pgrel, relTup, warm_update, modified_attrs); heap_freetuple(relTup); @@ -632,6 +635,8 @@ RenameConstraintById(Oid conId, const char *newname) Relation conDesc; HeapTuple tuple; Form_pg_constraint con; + bool warm_update; + Bitmapset *modified_attrs; conDesc = heap_open(ConstraintRelationId, RowExclusiveLock); @@ -666,10 +671,11 @@ RenameConstraintById(Oid conId, const char *newname) /* OK, do the rename --- tuple is a copy, so OK to scribble on it */ namestrcpy(&(con->conname), newname); - simple_heap_update(conDesc, &tuple->t_self, tuple); + simple_heap_update(conDesc, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* update the system catalog indexes */ - CatalogUpdateIndexes(conDesc, tuple); + CatalogUpdateIndexes(conDesc, tuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0); @@ -731,13 +737,17 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, /* Don't update if the object is already part of the namespace */ if (conform->connamespace == oldNspId && oldNspId != newNspId) { + bool warm_update; + Bitmapset *modified_attrs; + tup = heap_copytuple(tup); conform = (Form_pg_constraint) GETSTRUCT(tup); conform->connamespace = newNspId; - simple_heap_update(conRel, &tup->t_self, tup); - CatalogUpdateIndexes(conRel, tup); + simple_heap_update(conRel, &tup->t_self, tup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(conRel, tup, warm_update, modified_attrs); /* * Note: currently, the constraint will not have its own diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c index adaf7b8..99da1f3 100644 --- a/src/backend/catalog/pg_conversion.c +++ b/src/backend/catalog/pg_conversion.c @@ -108,7 +108,7 @@ ConversionCreate(const char *conname, Oid connamespace, simple_heap_insert(rel, tup); /* update the index if any */ - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); myself.classId = ConversionRelationId; myself.objectId = HeapTupleGetOid(tup); diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c index 117cc8d..f592419 100644 --- a/src/backend/catalog/pg_db_role_setting.c +++ b/src/backend/catalog/pg_db_role_setting.c @@ -78,6 +78,8 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) bool repl_null[Natts_pg_db_role_setting]; bool repl_repl[Natts_pg_db_role_setting]; HeapTuple newtuple; + bool warm_update; + Bitmapset *modified_attrs; memset(repl_repl, false, sizeof(repl_repl)); @@ -88,10 +90,11 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tuple->t_self, newtuple); + simple_heap_update(rel, &tuple->t_self, newtuple, &warm_update, + &modified_attrs); /* Update indexes */ - CatalogUpdateIndexes(rel, newtuple); + CatalogUpdateIndexes(rel, newtuple, warm_update, modified_attrs); } else simple_heap_delete(rel, &tuple->t_self); @@ -106,6 +109,8 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) Datum datum; bool isnull; ArrayType *a; + bool warm_update; + Bitmapset *modified_attrs; memset(repl_repl, false, sizeof(repl_repl)); repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true; @@ -129,10 +134,11 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tuple->t_self, newtuple); + simple_heap_update(rel, &tuple->t_self, newtuple, &warm_update, + &modified_attrs); /* Update indexes */ - CatalogUpdateIndexes(rel, newtuple); + CatalogUpdateIndexes(rel, newtuple, warm_update, modified_attrs); } else simple_heap_delete(rel, &tuple->t_self); @@ -158,7 +164,7 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) simple_heap_insert(rel, newtuple); /* Update indexes */ - CatalogUpdateIndexes(rel, newtuple); + CatalogUpdateIndexes(rel, newtuple, false, NULL); } InvokeObjectPostAlterHookArg(DbRoleSettingRelationId, diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index b71fa1b..cae00ad 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -113,7 +113,7 @@ recordMultipleDependencies(const ObjectAddress *depender, if (indstate == NULL) indstate = CatalogOpenIndexes(dependDesc); - CatalogIndexInsert(indstate, tup); + CatalogIndexInsert(indstate, tup, false, NULL); heap_freetuple(tup); } @@ -356,14 +356,18 @@ changeDependencyFor(Oid classId, Oid objectId, simple_heap_delete(depRel, &tup->t_self); else { + bool warm_update; + Bitmapset *modified_attrs; + /* make a modifiable copy */ tup = heap_copytuple(tup); depform = (Form_pg_depend) GETSTRUCT(tup); depform->refobjid = newRefObjectId; - simple_heap_update(depRel, &tup->t_self, tup); - CatalogUpdateIndexes(depRel, tup); + simple_heap_update(depRel, &tup->t_self, tup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(depRel, tup, warm_update, modified_attrs); heap_freetuple(tup); } diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c index 089a9a0..4bf90a4 100644 --- a/src/backend/catalog/pg_enum.c +++ b/src/backend/catalog/pg_enum.c @@ -126,7 +126,7 @@ EnumValuesCreate(Oid enumTypeOid, List *vals) HeapTupleSetOid(tup, oids[elemno]); simple_heap_insert(pg_enum, tup); - CatalogUpdateIndexes(pg_enum, tup); + CatalogUpdateIndexes(pg_enum, tup, false, NULL); heap_freetuple(tup); elemno++; @@ -459,7 +459,7 @@ restart: enum_tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls); HeapTupleSetOid(enum_tup, newOid); simple_heap_insert(pg_enum, enum_tup); - CatalogUpdateIndexes(pg_enum, enum_tup); + CatalogUpdateIndexes(pg_enum, enum_tup, false, NULL); heap_freetuple(enum_tup); heap_close(pg_enum, RowExclusiveLock); @@ -483,6 +483,8 @@ RenameEnumLabel(Oid enumTypeOid, HeapTuple old_tup; bool found_new; int i; + bool warm_update; + Bitmapset *modified_attrs; /* check length of new label is ok */ if (strlen(newVal) > (NAMEDATALEN - 1)) @@ -543,8 +545,9 @@ RenameEnumLabel(Oid enumTypeOid, /* Update the pg_enum entry */ namestrcpy(&en->enumlabel, newVal); - simple_heap_update(pg_enum, &enum_tup->t_self, enum_tup); - CatalogUpdateIndexes(pg_enum, enum_tup); + simple_heap_update(pg_enum, &enum_tup->t_self, enum_tup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(pg_enum, enum_tup, warm_update, modified_attrs); heap_freetuple(enum_tup); heap_close(pg_enum, RowExclusiveLock); @@ -588,6 +591,8 @@ RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems) HeapTuple newtup; Form_pg_enum en; float4 newsortorder; + bool warm_update; + Bitmapset *modified_attrs; newtup = heap_copytuple(existing[i]); en = (Form_pg_enum) GETSTRUCT(newtup); @@ -597,9 +602,10 @@ RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems) { en->enumsortorder = newsortorder; - simple_heap_update(pg_enum, &newtup->t_self, newtup); + simple_heap_update(pg_enum, &newtup->t_self, newtup, &warm_update, + &modified_attrs); - CatalogUpdateIndexes(pg_enum, newtup); + CatalogUpdateIndexes(pg_enum, newtup, warm_update, modified_attrs); } heap_freetuple(newtup); diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c index 24edf6a..7ece246 100644 --- a/src/backend/catalog/pg_largeobject.c +++ b/src/backend/catalog/pg_largeobject.c @@ -66,7 +66,7 @@ LargeObjectCreate(Oid loid) loid_new = simple_heap_insert(pg_lo_meta, ntup); Assert(!OidIsValid(loid) || loid == loid_new); - CatalogUpdateIndexes(pg_lo_meta, ntup); + CatalogUpdateIndexes(pg_lo_meta, ntup, false, NULL); heap_freetuple(ntup); diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c index f048ad4..6b31e7e 100644 --- a/src/backend/catalog/pg_namespace.c +++ b/src/backend/catalog/pg_namespace.c @@ -79,7 +79,7 @@ NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp) nspoid = simple_heap_insert(nspdesc, tup); Assert(OidIsValid(nspoid)); - CatalogUpdateIndexes(nspdesc, tup); + CatalogUpdateIndexes(nspdesc, tup, false, NULL); heap_close(nspdesc, RowExclusiveLock); diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 556f9fe..77bbd97 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -264,7 +264,7 @@ OperatorShellMake(const char *operatorName, */ operatorObjectId = simple_heap_insert(pg_operator_desc, tup); - CatalogUpdateIndexes(pg_operator_desc, tup); + CatalogUpdateIndexes(pg_operator_desc, tup, false, NULL); /* Add dependencies for the entry */ makeOperatorDependencies(tup, false); @@ -350,6 +350,8 @@ OperatorCreate(const char *operatorName, NameData oname; int i; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* * Sanity checks @@ -526,7 +528,8 @@ OperatorCreate(const char *operatorName, nulls, replaces); - simple_heap_update(pg_operator_desc, &tup->t_self, tup); + simple_heap_update(pg_operator_desc, &tup->t_self, tup, &warm_update, + &modified_attrs); } else { @@ -536,10 +539,12 @@ OperatorCreate(const char *operatorName, values, nulls); operatorObjectId = simple_heap_insert(pg_operator_desc, tup); + warm_update = false; + modified_attrs = NULL; } /* Must update the indexes in either case */ - CatalogUpdateIndexes(pg_operator_desc, tup); + CatalogUpdateIndexes(pg_operator_desc, tup, warm_update, modified_attrs); /* Add dependencies for the entry */ address = makeOperatorDependencies(tup, isUpdate); @@ -695,8 +700,12 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete) /* If any columns were found to need modification, update tuple. */ if (update_commutator) { - simple_heap_update(pg_operator_desc, &tup->t_self, tup); - CatalogUpdateIndexes(pg_operator_desc, tup); + bool warm_update; + Bitmapset *modified_attrs; + simple_heap_update(pg_operator_desc, &tup->t_self, tup, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_operator_desc, tup, warm_update, + modified_attrs); /* * Do CCI to make the updated tuple visible. We must do this in @@ -741,8 +750,13 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete) /* If any columns were found to need modification, update tuple. */ if (update_negator) { - simple_heap_update(pg_operator_desc, &tup->t_self, tup); - CatalogUpdateIndexes(pg_operator_desc, tup); + bool warm_update; + Bitmapset *modified_attrs; + + simple_heap_update(pg_operator_desc, &tup->t_self, tup, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_operator_desc, tup, warm_update, + modified_attrs); /* * In the deletion case, do CCI to make the updated tuple visible. diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 7ae192a..0f7027a 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -118,6 +118,8 @@ ProcedureCreate(const char *procedureName, referenced; int i; Oid trfid; + bool warm_update; + Bitmapset *modified_attrs; /* * sanity checks @@ -573,7 +575,8 @@ ProcedureCreate(const char *procedureName, /* Okay, do it... */ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); - simple_heap_update(rel, &tup->t_self, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, + &modified_attrs); ReleaseSysCache(oldtup); is_update = true; @@ -593,10 +596,12 @@ ProcedureCreate(const char *procedureName, tup = heap_form_tuple(tupDesc, values, nulls); simple_heap_insert(rel, tup); is_update = false; + warm_update = false; + modified_attrs = NULL; } /* Need to update indexes for either the insert or update case */ - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); retval = HeapTupleGetOid(tup); diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c index 576b7fa..b93f7c3 100644 --- a/src/backend/catalog/pg_publication.c +++ b/src/backend/catalog/pg_publication.c @@ -149,7 +149,7 @@ publication_add_relation(Oid pubid, Relation targetrel, /* Insert tuple into catalog. */ prrelid = simple_heap_insert(rel, tup); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); heap_freetuple(tup); ObjectAddressSet(myself, PublicationRelRelationId, prrelid); diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c index d3a4c26..7d4cc5d 100644 --- a/src/backend/catalog/pg_range.c +++ b/src/backend/catalog/pg_range.c @@ -59,7 +59,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation, tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls); simple_heap_insert(pg_range, tup); - CatalogUpdateIndexes(pg_range, tup); + CatalogUpdateIndexes(pg_range, tup, false, NULL); heap_freetuple(tup); /* record type's dependencies on range-related items */ diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 60ed957..3019b4e 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -253,6 +253,8 @@ shdepChangeDep(Relation sdepRel, } else if (oldtup) { + bool warm_update; + Bitmapset *modified_attrs; /* Need to update existing entry */ Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup); @@ -260,10 +262,11 @@ shdepChangeDep(Relation sdepRel, shForm->refclassid = refclassid; shForm->refobjid = refobjid; - simple_heap_update(sdepRel, &oldtup->t_self, oldtup); + simple_heap_update(sdepRel, &oldtup->t_self, oldtup, &warm_update, + &modified_attrs); /* keep indexes current */ - CatalogUpdateIndexes(sdepRel, oldtup); + CatalogUpdateIndexes(sdepRel, oldtup, warm_update, modified_attrs); } else { @@ -290,7 +293,7 @@ shdepChangeDep(Relation sdepRel, simple_heap_insert(sdepRel, oldtup); /* keep indexes current */ - CatalogUpdateIndexes(sdepRel, oldtup); + CatalogUpdateIndexes(sdepRel, oldtup, false, NULL); } if (oldtup) @@ -762,7 +765,7 @@ copyTemplateDependencies(Oid templateDbId, Oid newDbId) simple_heap_insert(sdepRel, newtup); /* Keep indexes current */ - CatalogIndexInsert(indstate, newtup); + CatalogIndexInsert(indstate, newtup, false, NULL); heap_freetuple(newtup); } @@ -885,7 +888,7 @@ shdepAddDependency(Relation sdepRel, simple_heap_insert(sdepRel, tup); /* keep indexes current */ - CatalogUpdateIndexes(sdepRel, tup); + CatalogUpdateIndexes(sdepRel, tup, false, NULL); /* clean up */ heap_freetuple(tup); diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 6d9a324..5857045 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -144,7 +144,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) */ typoid = simple_heap_insert(pg_type_desc, tup); - CatalogUpdateIndexes(pg_type_desc, tup); + CatalogUpdateIndexes(pg_type_desc, tup, false, NULL); /* * Create dependencies. We can/must skip this in bootstrap mode. @@ -237,6 +237,8 @@ TypeCreate(Oid newTypeOid, int i; Acl *typacl = NULL; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* * We assume that the caller validated the arguments individually, but did @@ -430,7 +432,8 @@ TypeCreate(Oid newTypeOid, nulls, replaces); - simple_heap_update(pg_type_desc, &tup->t_self, tup); + simple_heap_update(pg_type_desc, &tup->t_self, tup, &warm_update, + &modified_attrs); typeObjectId = HeapTupleGetOid(tup); @@ -459,10 +462,12 @@ TypeCreate(Oid newTypeOid, /* else allow system to assign oid */ typeObjectId = simple_heap_insert(pg_type_desc, tup); + warm_update = false; + modified_attrs = NULL; } /* Update indexes */ - CatalogUpdateIndexes(pg_type_desc, tup); + CatalogUpdateIndexes(pg_type_desc, tup, warm_update, modified_attrs); /* * Create dependencies. We can/must skip this in bootstrap mode. @@ -700,6 +705,8 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace) HeapTuple tuple; Form_pg_type typ; Oid arrayOid; + bool warm_update; + Bitmapset *modified_attrs; pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock); @@ -724,10 +731,11 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace) /* OK, do the rename --- tuple is a copy, so OK to scribble on it */ namestrcpy(&(typ->typname), newTypeName); - simple_heap_update(pg_type_desc, &tuple->t_self, tuple); + simple_heap_update(pg_type_desc, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* update the system catalog indexes */ - CatalogUpdateIndexes(pg_type_desc, tuple); + CatalogUpdateIndexes(pg_type_desc, tuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 4dfedf8..27bc137 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -487,6 +487,7 @@ CREATE VIEW pg_stat_all_tables AS pg_stat_get_tuples_updated(C.oid) AS n_tup_upd, pg_stat_get_tuples_deleted(C.oid) AS n_tup_del, pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd, + pg_stat_get_tuples_warm_updated(C.oid) AS n_tup_warm_upd, pg_stat_get_live_tuples(C.oid) AS n_live_tup, pg_stat_get_dead_tuples(C.oid) AS n_dead_tup, pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze, @@ -517,7 +518,8 @@ CREATE VIEW pg_stat_xact_all_tables AS pg_stat_get_xact_tuples_inserted(C.oid) AS n_tup_ins, pg_stat_get_xact_tuples_updated(C.oid) AS n_tup_upd, pg_stat_get_xact_tuples_deleted(C.oid) AS n_tup_del, - pg_stat_get_xact_tuples_hot_updated(C.oid) AS n_tup_hot_upd + pg_stat_get_xact_tuples_hot_updated(C.oid) AS n_tup_hot_upd, + pg_stat_get_xact_tuples_warm_updated(C.oid) AS n_tup_warm_upd FROM pg_class C LEFT JOIN pg_index I ON C.oid = I.indrelid LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index ee4a182..b48f785 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -349,11 +349,15 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, if (!IsBootstrapProcessingMode()) { + bool warm_update; + Bitmapset *modified_attrs; + /* normal case, use a transactional update */ - simple_heap_update(class_rel, &reltup->t_self, reltup); + simple_heap_update(class_rel, &reltup->t_self, reltup, &warm_update, + &modified_attrs); /* Keep catalog indexes current */ - CatalogUpdateIndexes(class_rel, reltup); + CatalogUpdateIndexes(class_rel, reltup, warm_update, modified_attrs); } else { diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 768fcc8..5c03207 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -172,6 +172,8 @@ AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name) bool *nulls; bool *replaces; NameData nameattrdata; + bool warm_update; + Bitmapset *modified_attrs; oldtup = SearchSysCache1(oidCacheId, ObjectIdGetDatum(objectId)); if (!HeapTupleIsValid(oldtup)) @@ -284,8 +286,9 @@ AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name) values, nulls, replaces); /* Perform actual update */ - simple_heap_update(rel, &oldtup->t_self, newtup); - CatalogUpdateIndexes(rel, newtup); + simple_heap_update(rel, &oldtup->t_self, newtup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(rel, newtup, warm_update, modified_attrs); InvokeObjectPostAlterHook(classId, objectId, 0); @@ -617,6 +620,8 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid) Datum *values; bool *nulls; bool *replaces; + bool warm_update; + Bitmapset *modified_attrs; tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid)); if (!HeapTupleIsValid(tup)) /* should not happen */ @@ -722,8 +727,8 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid) values, nulls, replaces); /* Perform actual update */ - simple_heap_update(rel, &tup->t_self, newtup); - CatalogUpdateIndexes(rel, newtup); + simple_heap_update(rel, &tup->t_self, newtup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, newtup, warm_update, modified_attrs); /* Release memory */ pfree(values); @@ -880,6 +885,8 @@ AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId) Datum *values; bool *nulls; bool *replaces; + bool warm_update; + Bitmapset *modified_attrs; /* Superusers can bypass permission checks */ if (!superuser()) @@ -954,8 +961,9 @@ AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId) values, nulls, replaces); /* Perform actual update */ - simple_heap_update(rel, &newtup->t_self, newtup); - CatalogUpdateIndexes(rel, newtup); + simple_heap_update(rel, &newtup->t_self, newtup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(rel, newtup, warm_update, modified_attrs); /* Update owner dependency reference */ if (classId == LargeObjectMetadataRelationId) diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c index 29061b8..2f33b2c 100644 --- a/src/backend/commands/amcmds.c +++ b/src/backend/commands/amcmds.c @@ -88,7 +88,7 @@ CreateAccessMethod(CreateAmStmt *stmt) tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); amoid = simple_heap_insert(rel, tup); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); heap_freetuple(tup); myself.classId = AccessMethodRelationId; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index e3e1a53..b9a9ede 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -1498,6 +1498,8 @@ update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats) Datum values[Natts_pg_statistic]; bool nulls[Natts_pg_statistic]; bool replaces[Natts_pg_statistic]; + bool warm_update; + Bitmapset *modified_attrs; /* Ignore attr if we weren't able to collect stats */ if (!stats->stats_valid) @@ -1589,17 +1591,20 @@ update_attstats(Oid relid, bool inh, int natts, VacAttrStats **vacattrstats) nulls, replaces); ReleaseSysCache(oldtup); - simple_heap_update(sd, &stup->t_self, stup); + simple_heap_update(sd, &stup->t_self, stup, &warm_update, + &modified_attrs); } else { /* No, insert new tuple */ stup = heap_form_tuple(RelationGetDescr(sd), values, nulls); simple_heap_insert(sd, stup); + warm_update = false; + modified_attrs = NULL; } /* update indexes too */ - CatalogUpdateIndexes(sd, stup); + CatalogUpdateIndexes(sd, stup, warm_update, modified_attrs); heap_freetuple(stup); } diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index f9309fc..03ed871 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -522,18 +522,28 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal) */ if (indexForm->indisclustered) { + bool warm_update; + Bitmapset *modified_attrs; + indexForm->indisclustered = false; - simple_heap_update(pg_index, &indexTuple->t_self, indexTuple); - CatalogUpdateIndexes(pg_index, indexTuple); + simple_heap_update(pg_index, &indexTuple->t_self, indexTuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_index, indexTuple, warm_update, + modified_attrs); } else if (thisIndexOid == indexOid) { + bool warm_update; + Bitmapset *modified_attrs; + /* this was checked earlier, but let's be real sure */ if (!IndexIsValid(indexForm)) elog(ERROR, "cannot cluster on invalid index %u", indexOid); indexForm->indisclustered = true; - simple_heap_update(pg_index, &indexTuple->t_self, indexTuple); - CatalogUpdateIndexes(pg_index, indexTuple); + simple_heap_update(pg_index, &indexTuple->t_self, indexTuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_index, indexTuple, warm_update, + modified_attrs); } InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0, @@ -1287,13 +1297,18 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, */ if (!target_is_pg_class) { - simple_heap_update(relRelation, &reltup1->t_self, reltup1); - simple_heap_update(relRelation, &reltup2->t_self, reltup2); + bool warm_update1 = false, warm_update2 = false; + Bitmapset *modified_attrs1, *modified_attrs2; + + simple_heap_update(relRelation, &reltup1->t_self, reltup1, + &warm_update1, &modified_attrs1); + simple_heap_update(relRelation, &reltup2->t_self, reltup2, + &warm_update2, &modified_attrs2); /* Keep system catalogs current */ indstate = CatalogOpenIndexes(relRelation); - CatalogIndexInsert(indstate, reltup1); - CatalogIndexInsert(indstate, reltup2); + CatalogIndexInsert(indstate, reltup1, warm_update1, modified_attrs1); + CatalogIndexInsert(indstate, reltup2, warm_update2, modified_attrs2); CatalogCloseIndexes(indstate); } else @@ -1547,6 +1562,8 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, Relation relRelation; HeapTuple reltup; Form_pg_class relform; + bool warm_update; + Bitmapset *modified_attrs; relRelation = heap_open(RelationRelationId, RowExclusiveLock); @@ -1558,8 +1575,9 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, relform->relfrozenxid = frozenXid; relform->relminmxid = cutoffMulti; - simple_heap_update(relRelation, &reltup->t_self, reltup); - CatalogUpdateIndexes(relRelation, reltup); + simple_heap_update(relRelation, &reltup->t_self, reltup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(relRelation, reltup, warm_update, modified_attrs); heap_close(relRelation, RowExclusiveLock); } diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index ada0b03..60b3631 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -150,6 +150,8 @@ CreateComments(Oid oid, Oid classoid, int32 subid, char *comment) bool nulls[Natts_pg_description]; bool replaces[Natts_pg_description]; int i; + bool warm_update; + Bitmapset *modified_attrs; /* Reduce empty-string to NULL case */ if (comment != NULL && strlen(comment) == 0) @@ -199,7 +201,8 @@ CreateComments(Oid oid, Oid classoid, int32 subid, char *comment) { newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(description), values, nulls, replaces); - simple_heap_update(description, &oldtuple->t_self, newtuple); + simple_heap_update(description, &oldtuple->t_self, newtuple, + &warm_update, &modified_attrs); } break; /* Assume there can be only one match */ @@ -214,12 +217,13 @@ CreateComments(Oid oid, Oid classoid, int32 subid, char *comment) newtuple = heap_form_tuple(RelationGetDescr(description), values, nulls); simple_heap_insert(description, newtuple); + warm_update = false; } /* Update indexes, if necessary */ if (newtuple != NULL) { - CatalogUpdateIndexes(description, newtuple); + CatalogUpdateIndexes(description, newtuple, warm_update, modified_attrs); heap_freetuple(newtuple); } @@ -249,6 +253,8 @@ CreateSharedComments(Oid oid, Oid classoid, char *comment) bool nulls[Natts_pg_shdescription]; bool replaces[Natts_pg_shdescription]; int i; + bool warm_update; + Bitmapset *modified_attrs; /* Reduce empty-string to NULL case */ if (comment != NULL && strlen(comment) == 0) @@ -293,7 +299,8 @@ CreateSharedComments(Oid oid, Oid classoid, char *comment) { newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(shdescription), values, nulls, replaces); - simple_heap_update(shdescription, &oldtuple->t_self, newtuple); + simple_heap_update(shdescription, &oldtuple->t_self, newtuple, + &warm_update, &modified_attrs); } break; /* Assume there can be only one match */ @@ -308,12 +315,14 @@ CreateSharedComments(Oid oid, Oid classoid, char *comment) newtuple = heap_form_tuple(RelationGetDescr(shdescription), values, nulls); simple_heap_insert(shdescription, newtuple); + warm_update = false; } /* Update indexes, if necessary */ if (newtuple != NULL) { - CatalogUpdateIndexes(shdescription, newtuple); + CatalogUpdateIndexes(shdescription, newtuple, warm_update, + modified_attrs); heap_freetuple(newtuple); } diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c index 77cf8ce..faef5b4 100644 --- a/src/backend/commands/constraint.c +++ b/src/backend/commands/constraint.c @@ -40,6 +40,7 @@ unique_key_recheck(PG_FUNCTION_ARGS) TriggerData *trigdata = (TriggerData *) fcinfo->context; const char *funcname = "unique_key_recheck"; HeapTuple new_row; + HeapTupleData heapTuple; ItemPointerData tmptid; Relation indexRel; IndexInfo *indexInfo; @@ -102,7 +103,8 @@ unique_key_recheck(PG_FUNCTION_ARGS) * removed. */ tmptid = new_row->t_self; - if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL)) + if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL, + NULL, NULL, &heapTuple)) { /* * All rows in the HOT chain are dead, so skip the check. diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index c05e14e..55b955a 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2669,6 +2669,8 @@ CopyFrom(CopyState cstate) if (resultRelInfo->ri_NumIndices > 0) recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), + &(tuple->t_self), + NULL, estate, false, NULL, @@ -2823,6 +2825,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid, ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false); recheckIndexes = ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self), + &(bufferedTuples[i]->t_self), NULL, estate, false, NULL, NIL); ExecARInsertTriggers(estate, resultRelInfo, bufferedTuples[i], diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 6ad8fd7..db9f9fc 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -549,7 +549,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) simple_heap_insert(pg_database_rel, tuple); /* Update indexes */ - CatalogUpdateIndexes(pg_database_rel, tuple); + CatalogUpdateIndexes(pg_database_rel, tuple, false, NULL); /* * Now generate additional catalog entries associated with the new DB @@ -978,6 +978,8 @@ RenameDatabase(const char *oldname, const char *newname) int notherbackends; int npreparedxacts; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* * Look up the target database's OID, and get exclusive lock on it. We @@ -1040,8 +1042,9 @@ RenameDatabase(const char *oldname, const char *newname) if (!HeapTupleIsValid(newtup)) elog(ERROR, "cache lookup failed for database %u", db_id); namestrcpy(&(((Form_pg_database) GETSTRUCT(newtup))->datname), newname); - simple_heap_update(rel, &newtup->t_self, newtup); - CatalogUpdateIndexes(rel, newtup); + simple_heap_update(rel, &newtup->t_self, newtup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(rel, newtup, warm_update, modified_attrs); InvokeObjectPostAlterHook(DatabaseRelationId, db_id, 0); @@ -1081,6 +1084,8 @@ movedb(const char *dbname, const char *tblspcname) DIR *dstdir; struct dirent *xlde; movedb_failure_params fparms; + bool warm_update; + Bitmapset *modified_attrs; /* * Look up the target database's OID, and get exclusive lock on it. We @@ -1296,10 +1301,11 @@ movedb(const char *dbname, const char *tblspcname) newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(pgdbrel), new_record, new_record_nulls, new_record_repl); - simple_heap_update(pgdbrel, &oldtuple->t_self, newtuple); + simple_heap_update(pgdbrel, &oldtuple->t_self, newtuple, &warm_update, + &modified_attrs); /* Update indexes */ - CatalogUpdateIndexes(pgdbrel, newtuple); + CatalogUpdateIndexes(pgdbrel, newtuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(DatabaseRelationId, HeapTupleGetOid(newtuple), 0); @@ -1413,6 +1419,8 @@ AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel) Datum new_record[Natts_pg_database]; bool new_record_nulls[Natts_pg_database]; bool new_record_repl[Natts_pg_database]; + bool warm_update; + Bitmapset *modified_attrs; /* Extract options from the statement node tree */ foreach(option, stmt->options) @@ -1554,10 +1562,11 @@ AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel) newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), new_record, new_record_nulls, new_record_repl); - simple_heap_update(rel, &tuple->t_self, newtuple); + simple_heap_update(rel, &tuple->t_self, newtuple, &warm_update, + &modified_attrs); /* Update indexes */ - CatalogUpdateIndexes(rel, newtuple); + CatalogUpdateIndexes(rel, newtuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(DatabaseRelationId, HeapTupleGetOid(newtuple), 0); @@ -1610,6 +1619,8 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) SysScanDesc scan; Form_pg_database datForm; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* * Get the old tuple. We don't need a lock on the database per se, @@ -1692,8 +1703,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) } newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(rel, newtuple); + simple_heap_update(rel, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(rel, newtuple, warm_update, modified_attrs); heap_freetuple(newtuple); diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 8125537..63cdff8 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -406,7 +406,7 @@ insert_event_trigger_tuple(char *trigname, char *eventname, Oid evtOwner, /* Insert heap tuple. */ tuple = heap_form_tuple(tgrel->rd_att, values, nulls); trigoid = simple_heap_insert(tgrel, tuple); - CatalogUpdateIndexes(tgrel, tuple); + CatalogUpdateIndexes(tgrel, tuple, false, NULL); heap_freetuple(tuple); /* Depend on owner. */ @@ -503,6 +503,8 @@ AlterEventTrigger(AlterEventTrigStmt *stmt) Oid trigoid; Form_pg_event_trigger evtForm; char tgenabled = stmt->tgenabled; + bool warm_update; + Bitmapset *modified_attrs; tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock); @@ -524,8 +526,9 @@ AlterEventTrigger(AlterEventTrigStmt *stmt) evtForm = (Form_pg_event_trigger) GETSTRUCT(tup); evtForm->evtenabled = tgenabled; - simple_heap_update(tgrel, &tup->t_self, tup); - CatalogUpdateIndexes(tgrel, tup); + simple_heap_update(tgrel, &tup->t_self, tup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(tgrel, tup, warm_update, modified_attrs); InvokeObjectPostAlterHook(EventTriggerRelationId, trigoid, 0); @@ -602,6 +605,8 @@ static void AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) { Form_pg_event_trigger form; + bool warm_update; + Bitmapset *modified_attrs; form = (Form_pg_event_trigger) GETSTRUCT(tup); @@ -621,8 +626,8 @@ AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) errhint("The owner of an event trigger must be a superuser."))); form->evtowner = newOwnerId; - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); /* Update owner dependency reference */ changeDependencyOnOwner(EventTriggerRelationId, diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 554fdc4..01e6a54 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -1773,7 +1773,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner, tuple = heap_form_tuple(rel->rd_att, values, nulls); extensionOid = simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); + CatalogUpdateIndexes(rel, tuple, false, NULL); heap_freetuple(tuple); heap_close(rel, RowExclusiveLock); @@ -2332,6 +2332,8 @@ pg_extension_config_dump(PG_FUNCTION_ARGS) bool repl_null[Natts_pg_extension]; bool repl_repl[Natts_pg_extension]; ArrayType *a; + bool warm_update; + Bitmapset *modified_attrs; /* * We only allow this to be called from an extension's SQL script. We @@ -2484,8 +2486,9 @@ pg_extension_config_dump(PG_FUNCTION_ARGS) extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel), repl_val, repl_null, repl_repl); - simple_heap_update(extRel, &extTup->t_self, extTup); - CatalogUpdateIndexes(extRel, extTup); + simple_heap_update(extRel, &extTup->t_self, extTup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(extRel, extTup, warm_update, modified_attrs); systable_endscan(extScan); @@ -2516,6 +2519,8 @@ extension_config_remove(Oid extensionoid, Oid tableoid) bool repl_null[Natts_pg_extension]; bool repl_repl[Natts_pg_extension]; ArrayType *a; + bool warm_update; + Bitmapset *modified_attrs; /* Find the pg_extension tuple */ extRel = heap_open(ExtensionRelationId, RowExclusiveLock); @@ -2662,8 +2667,9 @@ extension_config_remove(Oid extensionoid, Oid tableoid) extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel), repl_val, repl_null, repl_repl); - simple_heap_update(extRel, &extTup->t_self, extTup); - CatalogUpdateIndexes(extRel, extTup); + simple_heap_update(extRel, &extTup->t_self, extTup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(extRel, extTup, warm_update, modified_attrs); systable_endscan(extScan); @@ -2691,6 +2697,8 @@ AlterExtensionNamespace(List *names, const char *newschema, Oid *oldschema) HeapTuple depTup; ObjectAddresses *objsMoved; ObjectAddress extAddr; + bool warm_update; + Bitmapset *modified_attrs; if (list_length(names) != 1) ereport(ERROR, @@ -2843,8 +2851,9 @@ AlterExtensionNamespace(List *names, const char *newschema, Oid *oldschema) /* Now adjust pg_extension.extnamespace */ extForm->extnamespace = nspOid; - simple_heap_update(extRel, &extTup->t_self, extTup); - CatalogUpdateIndexes(extRel, extTup); + simple_heap_update(extRel, &extTup->t_self, extTup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(extRel, extTup, warm_update, modified_attrs); heap_close(extRel, RowExclusiveLock); @@ -3042,6 +3051,8 @@ ApplyExtensionUpdates(Oid extensionOid, bool repl[Natts_pg_extension]; ObjectAddress myself; ListCell *lc; + bool warm_update; + Bitmapset *modified_attrs; /* * Fetch parameters for specific version (pcontrol is not changed) @@ -3090,8 +3101,9 @@ ApplyExtensionUpdates(Oid extensionOid, extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel), values, nulls, repl); - simple_heap_update(extRel, &extTup->t_self, extTup); - CatalogUpdateIndexes(extRel, extTup); + simple_heap_update(extRel, &extTup->t_self, extTup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(extRel, extTup, warm_update, modified_attrs); systable_endscan(extScan); diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index 476a023..d76ccda 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -234,6 +234,9 @@ AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI if (form->fdwowner != newOwnerId) { + bool warm_update; + Bitmapset *modified_attrs; + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); @@ -256,8 +259,9 @@ AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerI tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); /* Update owner dependency reference */ changeDependencyOnOwner(ForeignDataWrapperRelationId, @@ -349,6 +353,9 @@ AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) if (form->srvowner != newOwnerId) { + bool warm_update; + Bitmapset *modified_attrs; + /* Superusers can always do it */ if (!superuser()) { @@ -397,8 +404,9 @@ AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); /* Update owner dependency reference */ changeDependencyOnOwner(ForeignServerRelationId, HeapTupleGetOid(tup), @@ -630,7 +638,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) tuple = heap_form_tuple(rel->rd_att, values, nulls); fdwId = simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); + CatalogUpdateIndexes(rel, tuple, false, NULL); heap_freetuple(tuple); @@ -689,6 +697,8 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt) Oid fdwhandler; Oid fdwvalidator; ObjectAddress myself; + bool warm_update; + Bitmapset *modified_attrs; rel = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock); @@ -786,8 +796,8 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt) tp = heap_modify_tuple(tp, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tp->t_self, tp); - CatalogUpdateIndexes(rel, tp); + simple_heap_update(rel, &tp->t_self, tp, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tp, warm_update, modified_attrs); heap_freetuple(tp); @@ -943,7 +953,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) srvId = simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); + CatalogUpdateIndexes(rel, tuple, false, NULL); heap_freetuple(tuple); @@ -985,6 +995,8 @@ AlterForeignServer(AlterForeignServerStmt *stmt) Oid srvId; Form_pg_foreign_server srvForm; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; rel = heap_open(ForeignServerRelationId, RowExclusiveLock); @@ -1056,8 +1068,8 @@ AlterForeignServer(AlterForeignServerStmt *stmt) tp = heap_modify_tuple(tp, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tp->t_self, tp); - CatalogUpdateIndexes(rel, tp); + simple_heap_update(rel, &tp->t_self, tp, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tp, warm_update, modified_attrs); InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0); @@ -1192,7 +1204,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt) umId = simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); + CatalogUpdateIndexes(rel, tuple, false, NULL); heap_freetuple(tuple); @@ -1240,6 +1252,8 @@ AlterUserMapping(AlterUserMappingStmt *stmt) ForeignServer *srv; ObjectAddress address; RoleSpec *role = (RoleSpec *) stmt->user; + bool warm_update; + Bitmapset *modified_attrs; rel = heap_open(UserMappingRelationId, RowExclusiveLock); @@ -1307,8 +1321,8 @@ AlterUserMapping(AlterUserMappingStmt *stmt) tp = heap_modify_tuple(tp, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tp->t_self, tp); - CatalogUpdateIndexes(rel, tp); + simple_heap_update(rel, &tp->t_self, tp, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tp, warm_update, modified_attrs); ObjectAddressSet(address, UserMappingRelationId, umId); @@ -1485,7 +1499,7 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid) tuple = heap_form_tuple(ftrel->rd_att, values, nulls); simple_heap_insert(ftrel, tuple); - CatalogUpdateIndexes(ftrel, tuple); + CatalogUpdateIndexes(ftrel, tuple, false, NULL); heap_freetuple(tuple); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 22aecb2..8a48bdc 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -1181,6 +1181,8 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt) DefElem *rows_item = NULL; DefElem *parallel_item = NULL; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; rel = heap_open(ProcedureRelationId, RowExclusiveLock); @@ -1295,8 +1297,8 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt) procForm->proparallel = interpret_func_parallel(parallel_item); /* Do the update */ - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); InvokeObjectPostAlterHook(ProcedureRelationId, funcOid, 0); @@ -1321,6 +1323,8 @@ SetFunctionReturnType(Oid funcOid, Oid newRetType) Relation pg_proc_rel; HeapTuple tup; Form_pg_proc procForm; + bool warm_update; + Bitmapset *modified_attrs; pg_proc_rel = heap_open(ProcedureRelationId, RowExclusiveLock); @@ -1336,9 +1340,10 @@ SetFunctionReturnType(Oid funcOid, Oid newRetType) procForm->prorettype = newRetType; /* update the catalog and its indexes */ - simple_heap_update(pg_proc_rel, &tup->t_self, tup); + simple_heap_update(pg_proc_rel, &tup->t_self, tup, &warm_update, + &modified_attrs); - CatalogUpdateIndexes(pg_proc_rel, tup); + CatalogUpdateIndexes(pg_proc_rel, tup, warm_update, modified_attrs); heap_close(pg_proc_rel, RowExclusiveLock); } @@ -1355,6 +1360,8 @@ SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType) Relation pg_proc_rel; HeapTuple tup; Form_pg_proc procForm; + bool warm_update; + Bitmapset *modified_attrs; pg_proc_rel = heap_open(ProcedureRelationId, RowExclusiveLock); @@ -1371,9 +1378,10 @@ SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType) procForm->proargtypes.values[argIndex] = newArgType; /* update the catalog and its indexes */ - simple_heap_update(pg_proc_rel, &tup->t_self, tup); + simple_heap_update(pg_proc_rel, &tup->t_self, tup, &warm_update, + &modified_attrs); - CatalogUpdateIndexes(pg_proc_rel, tup); + CatalogUpdateIndexes(pg_proc_rel, tup, warm_update, modified_attrs); heap_close(pg_proc_rel, RowExclusiveLock); } @@ -1661,7 +1669,7 @@ CreateCast(CreateCastStmt *stmt) castid = simple_heap_insert(relation, tuple); - CatalogUpdateIndexes(relation, tuple); + CatalogUpdateIndexes(relation, tuple, false, NULL); /* make dependency entries */ myself.classId = CastRelationId; @@ -1806,6 +1814,8 @@ CreateTransform(CreateTransformStmt *stmt) ObjectAddress myself, referenced; bool is_replace; + bool warm_update; + Bitmapset *modified_attrs; /* * Get the type @@ -1924,7 +1934,8 @@ CreateTransform(CreateTransformStmt *stmt) replaces[Anum_pg_transform_trftosql - 1] = true; newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces); - simple_heap_update(relation, &newtuple->t_self, newtuple); + simple_heap_update(relation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); transformid = HeapTupleGetOid(tuple); ReleaseSysCache(tuple); @@ -1935,9 +1946,11 @@ CreateTransform(CreateTransformStmt *stmt) newtuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); transformid = simple_heap_insert(relation, newtuple); is_replace = false; + warm_update = false; + modified_attrs = NULL; } - CatalogUpdateIndexes(relation, newtuple); + CatalogUpdateIndexes(relation, newtuple, warm_update, modified_attrs); if (is_replace) deleteDependencyRecordsFor(TransformRelationId, transformid, true); diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index 6b5a9b6..c22173d 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -83,6 +83,8 @@ SetMatViewPopulatedState(Relation relation, bool newstate) { Relation pgrel; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; Assert(relation->rd_rel->relkind == RELKIND_MATVIEW); @@ -100,9 +102,10 @@ SetMatViewPopulatedState(Relation relation, bool newstate) ((Form_pg_class) GETSTRUCT(tuple))->relispopulated = newstate; - simple_heap_update(pgrel, &tuple->t_self, tuple); + simple_heap_update(pgrel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); - CatalogUpdateIndexes(pgrel, tuple); + CatalogUpdateIndexes(pgrel, tuple, warm_update, modified_attrs); heap_freetuple(tuple); heap_close(pgrel, RowExclusiveLock); diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index 7cfcc6d..609d92b 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -280,7 +280,7 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) opfamilyoid = simple_heap_insert(rel, tup); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); heap_freetuple(tup); @@ -657,7 +657,7 @@ DefineOpClass(CreateOpClassStmt *stmt) opclassoid = simple_heap_insert(rel, tup); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); heap_freetuple(tup); @@ -1332,7 +1332,7 @@ storeOperators(List *opfamilyname, Oid amoid, entryoid = simple_heap_insert(rel, tup); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); heap_freetuple(tup); @@ -1443,7 +1443,7 @@ storeProcedures(List *opfamilyname, Oid amoid, entryoid = simple_heap_insert(rel, tup); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); heap_freetuple(tup); diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index a273376..e93a71a 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -400,6 +400,8 @@ AlterOperator(AlterOperatorStmt *stmt) List *joinName = NIL; /* optional join sel. procedure */ bool updateJoin = false; Oid joinOid; + bool warm_update; + Bitmapset *modified_attrs; /* Look up the operator */ oprId = LookupOperNameTypeNames(NULL, stmt->opername, @@ -518,8 +520,9 @@ AlterOperator(AlterOperatorStmt *stmt) tup = heap_modify_tuple(tup, RelationGetDescr(catalog), values, nulls, replaces); - simple_heap_update(catalog, &tup->t_self, tup); - CatalogUpdateIndexes(catalog, tup); + simple_heap_update(catalog, &tup->t_self, tup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(catalog, tup, warm_update, modified_attrs); address = makeOperatorDependencies(tup, true); diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 5d9d3a6..a080fc0 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -536,6 +536,8 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id) HeapTuple new_tuple; ObjectAddress target; ObjectAddress myself; + bool warm_update; + Bitmapset *modified_attrs; /* zero-clear */ memset(values, 0, sizeof(values)); @@ -614,10 +616,12 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id) new_tuple = heap_modify_tuple(tuple, RelationGetDescr(pg_policy_rel), values, isnull, replaces); - simple_heap_update(pg_policy_rel, &new_tuple->t_self, new_tuple); + simple_heap_update(pg_policy_rel, &new_tuple->t_self, new_tuple, + &warm_update, &modified_attrs); /* Update Catalog Indexes */ - CatalogUpdateIndexes(pg_policy_rel, new_tuple); + CatalogUpdateIndexes(pg_policy_rel, new_tuple, warm_update, + modified_attrs); /* Remove all old dependencies. */ deleteDependencyRecordsFor(PolicyRelationId, policy_id, false); @@ -826,7 +830,7 @@ CreatePolicy(CreatePolicyStmt *stmt) policy_id = simple_heap_insert(pg_policy_rel, policy_tuple); /* Update Indexes */ - CatalogUpdateIndexes(pg_policy_rel, policy_tuple); + CatalogUpdateIndexes(pg_policy_rel, policy_tuple, false, NULL); /* Record Dependencies */ target.classId = RelationRelationId; @@ -906,6 +910,8 @@ AlterPolicy(AlterPolicyStmt *stmt) char polcmd; bool polcmd_isnull; int i; + bool warm_update; + Bitmapset *modified_attrs; /* Parse role_ids */ if (stmt->roles != NULL) @@ -1150,10 +1156,12 @@ AlterPolicy(AlterPolicyStmt *stmt) new_tuple = heap_modify_tuple(policy_tuple, RelationGetDescr(pg_policy_rel), values, isnull, replaces); - simple_heap_update(pg_policy_rel, &new_tuple->t_self, new_tuple); + simple_heap_update(pg_policy_rel, &new_tuple->t_self, new_tuple, + &warm_update, &modified_attrs); /* Update Catalog Indexes */ - CatalogUpdateIndexes(pg_policy_rel, new_tuple); + CatalogUpdateIndexes(pg_policy_rel, new_tuple, warm_update, + modified_attrs); /* Update Dependencies. */ deleteDependencyRecordsFor(PolicyRelationId, policy_id, false); @@ -1217,6 +1225,8 @@ rename_policy(RenameStmt *stmt) SysScanDesc sscan; HeapTuple policy_tuple; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* Get id of table. Also handles permissions checks. */ table_id = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, @@ -1287,10 +1297,12 @@ rename_policy(RenameStmt *stmt) namestrcpy(&((Form_pg_policy) GETSTRUCT(policy_tuple))->polname, stmt->newname); - simple_heap_update(pg_policy_rel, &policy_tuple->t_self, policy_tuple); + simple_heap_update(pg_policy_rel, &policy_tuple->t_self, policy_tuple, + &warm_update, &modified_attrs); /* keep system catalog indexes current */ - CatalogUpdateIndexes(pg_policy_rel, policy_tuple); + CatalogUpdateIndexes(pg_policy_rel, policy_tuple, warm_update, + modified_attrs); InvokeObjectPostAlterHook(PolicyRelationId, HeapTupleGetOid(policy_tuple), 0); diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index b684f41..aae5ef6 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -336,6 +336,8 @@ create_proc_lang(const char *languageName, bool replace, bool is_update; ObjectAddress myself, referenced; + bool warm_update; + Bitmapset *modified_attrs; rel = heap_open(LanguageRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); @@ -378,7 +380,8 @@ create_proc_lang(const char *languageName, bool replace, /* Okay, do it... */ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); - simple_heap_update(rel, &tup->t_self, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, + &modified_attrs); ReleaseSysCache(oldtup); is_update = true; @@ -389,10 +392,12 @@ create_proc_lang(const char *languageName, bool replace, tup = heap_form_tuple(tupDesc, values, nulls); simple_heap_insert(rel, tup); is_update = false; + warm_update = false; + modified_attrs = NULL; } /* Need to update indexes for either the insert or update case */ - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); /* * Create dependencies for the new language. If we are updating an diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 63dcc10..4980b36 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -215,7 +215,7 @@ CreatePublication(CreatePublicationStmt *stmt) /* Insert tuple into catalog. */ puboid = simple_heap_insert(rel, tup); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); heap_freetuple(tup); recordDependencyOnOwner(PublicationRelationId, puboid, GetUserId()); @@ -260,6 +260,8 @@ AlterPublicationOptions(AlterPublicationStmt *stmt, Relation rel, bool publish_update; bool publish_delete; ObjectAddress obj; + bool warm_update; + Bitmapset *modified_attrs; parse_publication_options(stmt->options, &publish_insert_given, &publish_insert, @@ -294,8 +296,8 @@ AlterPublicationOptions(AlterPublicationStmt *stmt, Relation rel, replaces); /* Update the catalog. */ - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); CommandCounterIncrement(); @@ -666,6 +668,8 @@ PublicationDropTables(Oid pubid, List *rels, bool missing_ok) AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) { Form_pg_publication form; + bool warm_update; + Bitmapset *modified_attrs; form = (Form_pg_publication) GETSTRUCT(tup); @@ -685,8 +689,8 @@ AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) errhint("The owner of a publication must be a superuser."))); form->pubowner = newOwnerId; - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); /* Update owner dependency reference */ changeDependencyOnOwner(PublicationRelationId, diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index c3b37b2..d89e093 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -245,6 +245,8 @@ RenameSchema(const char *oldname, const char *newname) Relation rel; AclResult aclresult; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; rel = heap_open(NamespaceRelationId, RowExclusiveLock); @@ -281,8 +283,8 @@ RenameSchema(const char *oldname, const char *newname) /* rename */ namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname); - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); InvokeObjectPostAlterHook(NamespaceRelationId, HeapTupleGetOid(tup), 0); @@ -370,6 +372,8 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) bool isNull; HeapTuple newtuple; AclResult aclresult; + bool warm_update; + Bitmapset *modified_attrs; /* Otherwise, must be owner of the existing object */ if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId())) @@ -417,8 +421,9 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(rel, newtuple); + simple_heap_update(rel, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); CatalogUpdateIndexes(rel, newtuple, + warm_update, modified_attrs); heap_freetuple(newtuple); diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index 324f2e7..30d7af8 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -260,6 +260,8 @@ SetSharedSecurityLabel(const ObjectAddress *object, Datum values[Natts_pg_shseclabel]; bool nulls[Natts_pg_shseclabel]; bool replaces[Natts_pg_shseclabel]; + bool warm_update; + Bitmapset *modified_attrs; /* Prepare to form or update a tuple, if necessary. */ memset(nulls, false, sizeof(nulls)); @@ -299,7 +301,8 @@ SetSharedSecurityLabel(const ObjectAddress *object, replaces[Anum_pg_shseclabel_label - 1] = true; newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_shseclabel), values, nulls, replaces); - simple_heap_update(pg_shseclabel, &oldtup->t_self, newtup); + simple_heap_update(pg_shseclabel, &oldtup->t_self, newtup, + &warm_update, &modified_attrs); } } systable_endscan(scan); @@ -310,12 +313,13 @@ SetSharedSecurityLabel(const ObjectAddress *object, newtup = heap_form_tuple(RelationGetDescr(pg_shseclabel), values, nulls); simple_heap_insert(pg_shseclabel, newtup); + warm_update = false; } /* Update indexes, if necessary */ if (newtup != NULL) { - CatalogUpdateIndexes(pg_shseclabel, newtup); + CatalogUpdateIndexes(pg_shseclabel, newtup, warm_update, modified_attrs); heap_freetuple(newtup); } @@ -339,6 +343,8 @@ SetSecurityLabel(const ObjectAddress *object, Datum values[Natts_pg_seclabel]; bool nulls[Natts_pg_seclabel]; bool replaces[Natts_pg_seclabel]; + bool warm_update; + Bitmapset *modified_attrs; /* Shared objects have their own security label catalog. */ if (IsSharedRelation(object->classId)) @@ -390,7 +396,8 @@ SetSecurityLabel(const ObjectAddress *object, replaces[Anum_pg_seclabel_label - 1] = true; newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel), values, nulls, replaces); - simple_heap_update(pg_seclabel, &oldtup->t_self, newtup); + simple_heap_update(pg_seclabel, &oldtup->t_self, newtup, + &warm_update, &modified_attrs); } } systable_endscan(scan); @@ -401,12 +408,13 @@ SetSecurityLabel(const ObjectAddress *object, newtup = heap_form_tuple(RelationGetDescr(pg_seclabel), values, nulls); simple_heap_insert(pg_seclabel, newtup); + warm_update = false; } /* Update indexes, if necessary */ if (newtup != NULL) { - CatalogUpdateIndexes(pg_seclabel, newtup); + CatalogUpdateIndexes(pg_seclabel, newtup, warm_update, modified_attrs); heap_freetuple(newtup); } diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5..8d4d9a4 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -237,7 +237,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); + CatalogUpdateIndexes(rel, tuple, false, NULL); heap_freetuple(tuple); heap_close(rel, RowExclusiveLock); @@ -419,6 +419,8 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt) ObjectAddress address; Relation rel; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; /* Open and lock sequence. */ relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok); @@ -504,8 +506,9 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt) relation_close(seqrel, NoLock); - simple_heap_update(rel, &tuple->t_self, tuple); - CatalogUpdateIndexes(rel, tuple); + simple_heap_update(rel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(rel, tuple, warm_update, modified_attrs); heap_close(rel, RowExclusiveLock); return address; diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 2b6d322..ccabde1 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -277,7 +277,7 @@ CreateSubscription(CreateSubscriptionStmt *stmt) /* Insert tuple into catalog. */ subid = simple_heap_insert(rel, tup); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, false, NULL); heap_freetuple(tup); recordDependencyOnOwner(SubscriptionRelationId, subid, owner); @@ -339,6 +339,8 @@ AlterSubscription(AlterSubscriptionStmt *stmt) char *conninfo; char *slot_name; List *publications; + bool warm_update; + Bitmapset *modified_attrs; rel = heap_open(SubscriptionRelationId, RowExclusiveLock); @@ -397,8 +399,8 @@ AlterSubscription(AlterSubscriptionStmt *stmt) replaces); /* Update the catalog. */ - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); ObjectAddressSet(myself, SubscriptionRelationId, subid); @@ -558,6 +560,8 @@ static void AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) { Form_pg_subscription form; + bool warm_update; + Bitmapset *modified_attrs; form = (Form_pg_subscription) GETSTRUCT(tup); @@ -577,8 +581,8 @@ AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) errhint("The owner of an subscription must be a superuser."))); form->subowner = newOwnerId; - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); /* Update owner dependency reference */ changeDependencyOnOwner(SubscriptionRelationId, diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index c4b0011..2d8d419 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -2310,7 +2310,7 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid, simple_heap_insert(inhRelation, tuple); - CatalogUpdateIndexes(inhRelation, tuple); + CatalogUpdateIndexes(inhRelation, tuple, false, NULL); heap_freetuple(tuple); @@ -2397,11 +2397,16 @@ SetRelationHasSubclass(Oid relationId, bool relhassubclass) if (classtuple->relhassubclass != relhassubclass) { + bool warm_update; + Bitmapset *modified_attrs; + classtuple->relhassubclass = relhassubclass; - simple_heap_update(relationRelation, &tuple->t_self, tuple); + simple_heap_update(relationRelation, &tuple->t_self, tuple, + &warm_update, &modified_attrs); /* keep the catalog indexes up to date */ - CatalogUpdateIndexes(relationRelation, tuple); + CatalogUpdateIndexes(relationRelation, tuple, warm_update, + modified_attrs); } else { @@ -2477,6 +2482,8 @@ renameatt_internal(Oid myrelid, HeapTuple atttup; Form_pg_attribute attform; AttrNumber attnum; + bool warm_update; + Bitmapset *modified_attrs; /* * Grab an exclusive lock on the target table, which we will NOT release @@ -2592,10 +2599,11 @@ renameatt_internal(Oid myrelid, /* apply the update */ namestrcpy(&(attform->attname), newattname); - simple_heap_update(attrelation, &atttup->t_self, atttup); + simple_heap_update(attrelation, &atttup->t_self, atttup, &warm_update, + &modified_attrs); /* keep system catalog indexes current */ - CatalogUpdateIndexes(attrelation, atttup); + CatalogUpdateIndexes(attrelation, atttup, warm_update, modified_attrs); InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum); @@ -2871,6 +2879,8 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal) HeapTuple reltup; Form_pg_class relform; Oid namespaceId; + bool warm_update; + Bitmapset *modified_attrs; /* * Grab an exclusive lock on the target table, index, sequence, view, @@ -2902,10 +2912,11 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal) */ namestrcpy(&(relform->relname), newrelname); - simple_heap_update(relrelation, &reltup->t_self, reltup); + simple_heap_update(relrelation, &reltup->t_self, reltup, &warm_update, + &modified_attrs); /* keep the system catalog indexes current */ - CatalogUpdateIndexes(relrelation, reltup); + CatalogUpdateIndexes(relrelation, reltup, warm_update, modified_attrs); InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0, InvalidOid, is_internal); @@ -5039,6 +5050,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, ListCell *child; AclResult aclresult; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) @@ -5069,6 +5082,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid ctypeId; int32 ctypmod; Oid ccollid; + bool warm_update; + Bitmapset *modified_attrs; /* Child column must match on type, typmod, and collation */ typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod); @@ -5097,8 +5112,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Bump the existing child att's inhcount */ childatt->attinhcount++; - simple_heap_update(attrdesc, &tuple->t_self, tuple); - CatalogUpdateIndexes(attrdesc, tuple); + simple_heap_update(attrdesc, &tuple->t_self, tuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(attrdesc, tuple, warm_update, modified_attrs); heap_freetuple(tuple); @@ -5191,10 +5207,11 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, else ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum; - simple_heap_update(pgclass, &reltup->t_self, reltup); + simple_heap_update(pgclass, &reltup->t_self, reltup, &warm_update, + &modified_attrs); /* keep catalog indexes current */ - CatalogUpdateIndexes(pgclass, reltup); + CatalogUpdateIndexes(pgclass, reltup, warm_update, modified_attrs); heap_freetuple(reltup); @@ -5628,12 +5645,16 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode) */ if (((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull) { + bool warm_update; + Bitmapset *modified_attrs; + ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = FALSE; - simple_heap_update(attr_rel, &tuple->t_self, tuple); + simple_heap_update(attr_rel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep the system catalog indexes current */ - CatalogUpdateIndexes(attr_rel, tuple); + CatalogUpdateIndexes(attr_rel, tuple, warm_update, modified_attrs); ObjectAddressSubSet(address, RelationRelationId, RelationGetRelid(rel), attnum); @@ -5706,12 +5727,16 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, */ if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull) { + bool warm_update; + Bitmapset *modified_attrs; + ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE; - simple_heap_update(attr_rel, &tuple->t_self, tuple); + simple_heap_update(attr_rel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep the system catalog indexes current */ - CatalogUpdateIndexes(attr_rel, tuple); + CatalogUpdateIndexes(attr_rel, tuple, warm_update, modified_attrs); /* Tell Phase 3 it needs to test the constraint */ tab->new_notnull = true; @@ -5833,6 +5858,8 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE Form_pg_attribute attrtuple; AttrNumber attnum; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; Assert(IsA(newValue, Integer)); newtarget = intVal(newValue); @@ -5876,10 +5903,11 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE attrtuple->attstattarget = newtarget; - simple_heap_update(attrelation, &tuple->t_self, tuple); + simple_heap_update(attrelation, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep system catalog indexes current */ - CatalogUpdateIndexes(attrelation, tuple); + CatalogUpdateIndexes(attrelation, tuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), @@ -5912,6 +5940,8 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, Datum repl_val[Natts_pg_attribute]; bool repl_null[Natts_pg_attribute]; bool repl_repl[Natts_pg_attribute]; + bool warm_update; + Bitmapset *modified_attrs; attrelation = heap_open(AttributeRelationId, RowExclusiveLock); @@ -5953,8 +5983,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, repl_val, repl_null, repl_repl); /* Update system catalog. */ - simple_heap_update(attrelation, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(attrelation, newtuple); + simple_heap_update(attrelation, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(attrelation, newtuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), @@ -5986,6 +6017,8 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc Form_pg_attribute attrtuple; AttrNumber attnum; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; Assert(IsA(newValue, String)); storagemode = strVal(newValue); @@ -6037,10 +6070,11 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc errmsg("column data type %s can only have storage PLAIN", format_type_be(attrtuple->atttypid)))); - simple_heap_update(attrelation, &tuple->t_self, tuple); + simple_heap_update(attrelation, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep system catalog indexes current */ - CatalogUpdateIndexes(attrelation, tuple); + CatalogUpdateIndexes(attrelation, tuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), @@ -6275,13 +6309,18 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, } else { + bool warm_update; + Bitmapset *modified_attrs; + /* Child column must survive my deletion */ childatt->attinhcount--; - simple_heap_update(attr_rel, &tuple->t_self, tuple); + simple_heap_update(attr_rel, &tuple->t_self, tuple, + &warm_update, &modified_attrs); /* keep the system catalog indexes current */ - CatalogUpdateIndexes(attr_rel, tuple); + CatalogUpdateIndexes(attr_rel, tuple, warm_update, + modified_attrs); /* Make update visible */ CommandCounterIncrement(); @@ -6289,6 +6328,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, } else { + bool warm_update; + Bitmapset *modified_attrs; + /* * If we were told to drop ONLY in this table (no recursion), * we need to mark the inheritors' attributes as locally @@ -6297,10 +6339,12 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, childatt->attinhcount--; childatt->attislocal = true; - simple_heap_update(attr_rel, &tuple->t_self, tuple); + simple_heap_update(attr_rel, &tuple->t_self, tuple, + &warm_update, &modified_attrs); /* keep the system catalog indexes current */ - CatalogUpdateIndexes(attr_rel, tuple); + CatalogUpdateIndexes(attr_rel, tuple, warm_update, + modified_attrs); /* Make update visible */ CommandCounterIncrement(); @@ -6333,6 +6377,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, Relation class_rel; Form_pg_class tuple_class; AlteredTableInfo *tab; + bool warm_update; + Bitmapset *modified_attrs; class_rel = heap_open(RelationRelationId, RowExclusiveLock); @@ -6344,10 +6390,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, tuple_class = (Form_pg_class) GETSTRUCT(tuple); tuple_class->relhasoids = false; - simple_heap_update(class_rel, &tuple->t_self, tuple); + simple_heap_update(class_rel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* Keep the catalog indexes up to date */ - CatalogUpdateIndexes(class_rel, tuple); + CatalogUpdateIndexes(class_rel, tuple, warm_update, modified_attrs); heap_close(class_rel, RowExclusiveLock); @@ -7189,6 +7236,8 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, SysScanDesc tgscan; Relation tgrel; ListCell *lc; + bool warm_update; + Bitmapset *modified_attrs; /* * Now update the catalog, while we have the door open. @@ -7197,8 +7246,9 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple); copy_con->condeferrable = cmdcon->deferrable; copy_con->condeferred = cmdcon->initdeferred; - simple_heap_update(conrel, ©Tuple->t_self, copyTuple); - CatalogUpdateIndexes(conrel, copyTuple); + simple_heap_update(conrel, ©Tuple->t_self, copyTuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(conrel, copyTuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(ConstraintRelationId, HeapTupleGetOid(contuple), 0); @@ -7223,6 +7273,8 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, { Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple); Form_pg_trigger copy_tg; + bool warm_update; + Bitmapset *modified_attrs; /* * Remember OIDs of other relation(s) involved in FK constraint. @@ -7251,8 +7303,9 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, copy_tg->tgdeferrable = cmdcon->deferrable; copy_tg->tginitdeferred = cmdcon->initdeferred; - simple_heap_update(tgrel, ©Tuple->t_self, copyTuple); - CatalogUpdateIndexes(tgrel, copyTuple); + simple_heap_update(tgrel, ©Tuple->t_self, copyTuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(tgrel, copyTuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(TriggerRelationId, HeapTupleGetOid(tgtuple), 0); @@ -7351,6 +7404,8 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse, { HeapTuple copyTuple; Form_pg_constraint copy_con; + bool warm_update; + Bitmapset *modified_attrs; if (con->contype == CONSTRAINT_FOREIGN) { @@ -7438,8 +7493,9 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse, copyTuple = heap_copytuple(tuple); copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple); copy_con->convalidated = true; - simple_heap_update(conrel, ©Tuple->t_self, copyTuple); - CatalogUpdateIndexes(conrel, copyTuple); + simple_heap_update(conrel, ©Tuple->t_self, copyTuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(conrel, copyTuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(ConstraintRelationId, HeapTupleGetOid(tuple), 0); @@ -8339,10 +8395,14 @@ ATExecDropConstraint(Relation rel, const char *constrName, } else { + bool warm_update; + Bitmapset *modified_attrs; + /* Child constraint must survive my deletion */ con->coninhcount--; - simple_heap_update(conrel, ©_tuple->t_self, copy_tuple); - CatalogUpdateIndexes(conrel, copy_tuple); + simple_heap_update(conrel, ©_tuple->t_self, copy_tuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(conrel, copy_tuple, warm_update, modified_attrs); /* Make update visible */ CommandCounterIncrement(); @@ -8350,6 +8410,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, } else { + bool warm_update; + Bitmapset *modified_attrs; + /* * If we were told to drop ONLY in this table (no recursion), we * need to mark the inheritors' constraints as locally defined @@ -8358,8 +8421,10 @@ ATExecDropConstraint(Relation rel, const char *constrName, con->coninhcount--; con->conislocal = true; - simple_heap_update(conrel, ©_tuple->t_self, copy_tuple); - CatalogUpdateIndexes(conrel, copy_tuple); + simple_heap_update(conrel, ©_tuple->t_self, copy_tuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(conrel, copy_tuple, warm_update, + modified_attrs); /* Make update visible */ CommandCounterIncrement(); @@ -8675,6 +8740,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, SysScanDesc scan; HeapTuple depTup; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; attrelation = heap_open(AttributeRelationId, RowExclusiveLock); @@ -9005,10 +9072,11 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, ReleaseSysCache(typeTuple); - simple_heap_update(attrelation, &heapTup->t_self, heapTup); + simple_heap_update(attrelation, &heapTup->t_self, heapTup, &warm_update, + &modified_attrs); /* keep system catalog indexes current */ - CatalogUpdateIndexes(attrelation, heapTup); + CatalogUpdateIndexes(attrelation, heapTup, warm_update, modified_attrs); heap_close(attrelation, RowExclusiveLock); @@ -9079,6 +9147,8 @@ ATExecAlterColumnGenericOptions(Relation rel, Form_pg_attribute atttableform; AttrNumber attnum; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; if (options == NIL) return InvalidObjectAddress; @@ -9146,8 +9216,9 @@ ATExecAlterColumnGenericOptions(Relation rel, newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel), repl_val, repl_null, repl_repl); - simple_heap_update(attrel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(attrel, newtuple); + simple_heap_update(attrel, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(attrel, newtuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), @@ -9617,6 +9688,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock Datum aclDatum; bool isNull; HeapTuple newtuple; + bool warm_update; + Bitmapset *modified_attrs; /* skip permission checks when recursing to index or toast table */ if (!recursing) @@ -9667,8 +9740,9 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl); - simple_heap_update(class_rel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(class_rel, newtuple); + simple_heap_update(class_rel, &newtuple->t_self, newtuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(class_rel, newtuple, warm_update, modified_attrs); heap_freetuple(newtuple); @@ -9770,6 +9844,8 @@ change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId) Datum aclDatum; bool isNull; HeapTuple newtuple; + bool warm_update; + Bitmapset *modified_attrs; /* Ignore dropped columns */ if (att->attisdropped) @@ -9795,8 +9871,10 @@ change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId) RelationGetDescr(attRelation), repl_val, repl_null, repl_repl); - simple_heap_update(attRelation, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(attRelation, newtuple); + simple_heap_update(attRelation, &newtuple->t_self, newtuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(attRelation, newtuple, warm_update, + modified_attrs); heap_freetuple(newtuple); } @@ -9966,6 +10044,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, bool repl_null[Natts_pg_class]; bool repl_repl[Natts_pg_class]; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + bool warm_update; + Bitmapset *modified_attrs; if (defList == NIL && operation != AT_ReplaceRelOptions) return; /* nothing to do */ @@ -10073,9 +10153,10 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass), repl_val, repl_null, repl_repl); - simple_heap_update(pgclass, &newtuple->t_self, newtuple); + simple_heap_update(pgclass, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); - CatalogUpdateIndexes(pgclass, newtuple); + CatalogUpdateIndexes(pgclass, newtuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0); @@ -10088,6 +10169,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, { Relation toastrel; Oid toastid = rel->rd_rel->reltoastrelid; + bool warm_update; + Bitmapset *modified_attrs; toastrel = heap_open(toastid, lockmode); @@ -10132,9 +10215,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass), repl_val, repl_null, repl_repl); - simple_heap_update(pgclass, &newtuple->t_self, newtuple); + simple_heap_update(pgclass, &newtuple->t_self, newtuple, &warm_update, &modified_attrs); - CatalogUpdateIndexes(pgclass, newtuple); + CatalogUpdateIndexes(pgclass, newtuple, warm_update, modified_attrs); InvokeObjectPostAlterHookArg(RelationRelationId, RelationGetRelid(toastrel), 0, @@ -10169,6 +10252,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) ForkNumber forkNum; List *reltoastidxids = NIL; ListCell *lc; + bool warm_update; + Bitmapset *modified_attrs; /* * Need lock here in case we are recursing to toast table or index @@ -10295,8 +10380,9 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) /* update the pg_class row */ rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace; rd_rel->relfilenode = newrelfilenode; - simple_heap_update(pg_class, &tuple->t_self, tuple); - CatalogUpdateIndexes(pg_class, tuple); + simple_heap_update(pg_class, &tuple->t_self, tuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(pg_class, tuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0); @@ -10901,6 +10987,9 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel) attributeName); if (HeapTupleIsValid(tuple)) { + bool warm_update; + Bitmapset *modified_attrs; + /* Check they are same type, typmod, and collation */ Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple); @@ -10946,8 +11035,9 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel) childatt->attislocal = false; } - simple_heap_update(attrrel, &tuple->t_self, tuple); - CatalogUpdateIndexes(attrrel, tuple); + simple_heap_update(attrrel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(attrrel, tuple, warm_update, modified_attrs); heap_freetuple(tuple); } else @@ -10976,6 +11066,8 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel) if (HeapTupleIsValid(tuple)) { Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple); + bool warm_update; + Bitmapset *modified_attrs; /* See comments above; these changes should be the same */ childatt->attinhcount++; @@ -10986,8 +11078,9 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel) childatt->attislocal = false; } - simple_heap_update(attrrel, &tuple->t_self, tuple); - CatalogUpdateIndexes(attrrel, tuple); + simple_heap_update(attrrel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(attrrel, tuple, warm_update, modified_attrs); heap_freetuple(tuple); } else @@ -11071,6 +11164,8 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel) { Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple); HeapTuple child_copy; + bool warm_update; + Bitmapset *modified_attrs; if (child_con->contype != CONSTRAINT_CHECK) continue; @@ -11124,8 +11219,10 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel) child_con->conislocal = false; } - simple_heap_update(catalog_relation, &child_copy->t_self, child_copy); - CatalogUpdateIndexes(catalog_relation, child_copy); + simple_heap_update(catalog_relation, &child_copy->t_self, + child_copy, &warm_update, &modified_attrs); + CatalogUpdateIndexes(catalog_relation, child_copy, warm_update, + modified_attrs); heap_freetuple(child_copy); found = true; @@ -11290,13 +11387,17 @@ RemoveInheritance(Relation child_rel, Relation parent_rel) /* Decrement inhcount and possibly set islocal to true */ HeapTuple copyTuple = heap_copytuple(attributeTuple); Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple); + bool warm_update; + Bitmapset *modified_attrs; copy_att->attinhcount--; if (copy_att->attinhcount == 0) copy_att->attislocal = true; - simple_heap_update(catalogRelation, ©Tuple->t_self, copyTuple); - CatalogUpdateIndexes(catalogRelation, copyTuple); + simple_heap_update(catalogRelation, ©Tuple->t_self, copyTuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(catalogRelation, copyTuple, warm_update, + modified_attrs); heap_freetuple(copyTuple); } } @@ -11361,6 +11462,8 @@ RemoveInheritance(Relation child_rel, Relation parent_rel) /* Decrement inhcount and possibly set islocal to true */ HeapTuple copyTuple = heap_copytuple(constraintTuple); Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple); + bool warm_update; + Bitmapset *modified_attrs; if (copy_con->coninhcount <= 0) /* shouldn't happen */ elog(ERROR, "relation %u has non-inherited constraint \"%s\"", @@ -11370,8 +11473,10 @@ RemoveInheritance(Relation child_rel, Relation parent_rel) if (copy_con->coninhcount == 0) copy_con->conislocal = true; - simple_heap_update(catalogRelation, ©Tuple->t_self, copyTuple); - CatalogUpdateIndexes(catalogRelation, copyTuple); + simple_heap_update(catalogRelation, ©Tuple->t_self, copyTuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(catalogRelation, copyTuple, warm_update, + modified_attrs); heap_freetuple(copyTuple); } } @@ -11468,6 +11573,8 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode) ObjectAddress tableobj, typeobj; HeapTuple classtuple; + bool warm_update; + Bitmapset *modified_attrs; /* Validate the type. */ typetuple = typenameType(NULL, ofTypename, NULL); @@ -11571,8 +11678,10 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode) if (!HeapTupleIsValid(classtuple)) elog(ERROR, "cache lookup failed for relation %u", relid); ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid; - simple_heap_update(relationRelation, &classtuple->t_self, classtuple); - CatalogUpdateIndexes(relationRelation, classtuple); + simple_heap_update(relationRelation, &classtuple->t_self, classtuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(relationRelation, classtuple, warm_update, + modified_attrs); InvokeObjectPostAlterHook(RelationRelationId, relid, 0); @@ -11596,6 +11705,8 @@ ATExecDropOf(Relation rel, LOCKMODE lockmode) Oid relid = RelationGetRelid(rel); Relation relationRelation; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; if (!OidIsValid(rel->rd_rel->reloftype)) ereport(ERROR, @@ -11616,8 +11727,9 @@ ATExecDropOf(Relation rel, LOCKMODE lockmode) if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relid); ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid; - simple_heap_update(relationRelation, &tuple->t_self, tuple); - CatalogUpdateIndexes(relationRelation, tuple); + simple_heap_update(relationRelation, &tuple->t_self, tuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(relationRelation, tuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(RelationRelationId, relid, 0); @@ -11656,9 +11768,14 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple); if (pg_class_form->relreplident != ri_type) { + bool warm_update; + Bitmapset *modified_attrs; + pg_class_form->relreplident = ri_type; - simple_heap_update(pg_class, &pg_class_tuple->t_self, pg_class_tuple); - CatalogUpdateIndexes(pg_class, pg_class_tuple); + simple_heap_update(pg_class, &pg_class_tuple->t_self, pg_class_tuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_class, pg_class_tuple, warm_update, + modified_attrs); } heap_close(pg_class, RowExclusiveLock); heap_freetuple(pg_class_tuple); @@ -11717,8 +11834,13 @@ relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, if (dirty) { - simple_heap_update(pg_index, &pg_index_tuple->t_self, pg_index_tuple); - CatalogUpdateIndexes(pg_index, pg_index_tuple); + bool warm_update; + Bitmapset *modified_attrs; + + simple_heap_update(pg_index, &pg_index_tuple->t_self, + pg_index_tuple, &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_index, pg_index_tuple, warm_update, + modified_attrs); InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0, InvalidOid, is_internal); } @@ -11856,6 +11978,8 @@ ATExecEnableRowSecurity(Relation rel) Relation pg_class; Oid relid; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; relid = RelationGetRelid(rel); @@ -11867,10 +11991,11 @@ ATExecEnableRowSecurity(Relation rel) elog(ERROR, "cache lookup failed for relation %u", relid); ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = true; - simple_heap_update(pg_class, &tuple->t_self, tuple); + simple_heap_update(pg_class, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep catalog indexes current */ - CatalogUpdateIndexes(pg_class, tuple); + CatalogUpdateIndexes(pg_class, tuple, warm_update, modified_attrs); heap_close(pg_class, RowExclusiveLock); heap_freetuple(tuple); @@ -11882,6 +12007,8 @@ ATExecDisableRowSecurity(Relation rel) Relation pg_class; Oid relid; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; relid = RelationGetRelid(rel); @@ -11894,10 +12021,11 @@ ATExecDisableRowSecurity(Relation rel) elog(ERROR, "cache lookup failed for relation %u", relid); ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = false; - simple_heap_update(pg_class, &tuple->t_self, tuple); + simple_heap_update(pg_class, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep catalog indexes current */ - CatalogUpdateIndexes(pg_class, tuple); + CatalogUpdateIndexes(pg_class, tuple, warm_update, modified_attrs); heap_close(pg_class, RowExclusiveLock); heap_freetuple(tuple); @@ -11912,6 +12040,8 @@ ATExecForceNoForceRowSecurity(Relation rel, bool force_rls) Relation pg_class; Oid relid; HeapTuple tuple; + bool warm_update; + Bitmapset *modified_attrs; relid = RelationGetRelid(rel); @@ -11923,10 +12053,11 @@ ATExecForceNoForceRowSecurity(Relation rel, bool force_rls) elog(ERROR, "cache lookup failed for relation %u", relid); ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls; - simple_heap_update(pg_class, &tuple->t_self, tuple); + simple_heap_update(pg_class, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep catalog indexes current */ - CatalogUpdateIndexes(pg_class, tuple); + CatalogUpdateIndexes(pg_class, tuple, warm_update, modified_attrs); heap_close(pg_class, RowExclusiveLock); heap_freetuple(tuple); @@ -11948,6 +12079,8 @@ ATExecGenericOptions(Relation rel, List *options) bool repl_repl[Natts_pg_foreign_table]; Datum datum; Form_pg_foreign_table tableform; + bool warm_update; + Bitmapset *modified_attrs; if (options == NIL) return; @@ -11994,8 +12127,8 @@ ATExecGenericOptions(Relation rel, List *options) tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel), repl_val, repl_null, repl_repl); - simple_heap_update(ftrel, &tuple->t_self, tuple); - CatalogUpdateIndexes(ftrel, tuple); + simple_heap_update(ftrel, &tuple->t_self, tuple, &warm_update, &modified_attrs); + CatalogUpdateIndexes(ftrel, tuple, warm_update, modified_attrs); /* * Invalidate relcache so that all sessions will refresh any cached plans @@ -12278,6 +12411,9 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid, already_done = object_address_present(&thisobj, objsMoved); if (!already_done && oldNspOid != newNspOid) { + bool warm_update; + Bitmapset *modified_attrs; + /* check for duplicate name (more friendly than unique-index failure) */ if (get_relname_relid(NameStr(classForm->relname), newNspOid) != InvalidOid) @@ -12290,8 +12426,9 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid, /* classTup is a copy, so OK to scribble on */ classForm->relnamespace = newNspOid; - simple_heap_update(classRel, &classTup->t_self, classTup); - CatalogUpdateIndexes(classRel, classTup); + simple_heap_update(classRel, &classTup->t_self, classTup, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(classRel, classTup, warm_update, modified_attrs); /* Update dependency on schema if caller said so */ if (hasDependEntry && @@ -13499,6 +13636,8 @@ ATExecDetachPartition(Relation rel, RangeVar *name) new_null[Natts_pg_class], new_repl[Natts_pg_class]; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; partRel = heap_openrv(name, AccessShareLock); @@ -13526,8 +13665,9 @@ ATExecDetachPartition(Relation rel, RangeVar *name) new_val, new_null, new_repl); ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false; - simple_heap_update(classRel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(classRel, newtuple); + simple_heap_update(classRel, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(classRel, newtuple, warm_update, modified_attrs); heap_freetuple(newtuple); heap_close(classRel, RowExclusiveLock); diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index 651e1b3..bc18cfb 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -346,7 +346,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) tablespaceoid = simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); + CatalogUpdateIndexes(rel, tuple, false, NULL); heap_freetuple(tuple); @@ -920,6 +920,8 @@ RenameTableSpace(const char *oldname, const char *newname) HeapTuple newtuple; Form_pg_tablespace newform; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* Search pg_tablespace */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); @@ -971,8 +973,9 @@ RenameTableSpace(const char *oldname, const char *newname) /* OK, update the entry */ namestrcpy(&(newform->spcname), newname); - simple_heap_update(rel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(rel, newtuple); + simple_heap_update(rel, &newtuple->t_self, newtuple, &warm_update, + &modified_attrs); + CatalogUpdateIndexes(rel, newtuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(TableSpaceRelationId, tspId, 0); @@ -1001,6 +1004,8 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) bool repl_null[Natts_pg_tablespace]; bool repl_repl[Natts_pg_tablespace]; HeapTuple newtuple; + bool warm_update; + Bitmapset *modified_attrs; /* Search pg_tablespace */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); @@ -1044,8 +1049,8 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) repl_null, repl_repl); /* Update system catalog. */ - simple_heap_update(rel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(rel, newtuple); + simple_heap_update(rel, &newtuple->t_self, newtuple, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, newtuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(TableSpaceRelationId, HeapTupleGetOid(tup), 0); diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 3fc3a21..1ed9e4b 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -166,6 +166,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, referenced; char *oldtablename = NULL; char *newtablename = NULL; + bool warm_update; + Bitmapset *modified_attrs; if (OidIsValid(relOid)) rel = heap_open(relOid, ShareRowExclusiveLock); @@ -777,7 +779,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, */ simple_heap_insert(tgrel, tuple); - CatalogUpdateIndexes(tgrel, tuple); + CatalogUpdateIndexes(tgrel, tuple, false, NULL); heap_freetuple(tuple); heap_close(tgrel, RowExclusiveLock); @@ -804,9 +806,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true; - simple_heap_update(pgrel, &tuple->t_self, tuple); + simple_heap_update(pgrel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); - CatalogUpdateIndexes(pgrel, tuple); + CatalogUpdateIndexes(pgrel, tuple, warm_update, modified_attrs); heap_freetuple(tuple); heap_close(pgrel, RowExclusiveLock); @@ -1436,6 +1439,9 @@ renametrig(RenameStmt *stmt) NULL, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { + bool warm_update; + Bitmapset *modified_attrs; + tgoid = HeapTupleGetOid(tuple); /* @@ -1446,10 +1452,11 @@ renametrig(RenameStmt *stmt) namestrcpy(&((Form_pg_trigger) GETSTRUCT(tuple))->tgname, stmt->newname); - simple_heap_update(tgrel, &tuple->t_self, tuple); + simple_heap_update(tgrel, &tuple->t_self, tuple, &warm_update, + &modified_attrs); /* keep system catalog indexes current */ - CatalogUpdateIndexes(tgrel, tuple); + CatalogUpdateIndexes(tgrel, tuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(TriggerRelationId, HeapTupleGetOid(tuple), 0); @@ -1559,13 +1566,16 @@ EnableDisableTrigger(Relation rel, const char *tgname, /* need to change this one ... make a copy to scribble on */ HeapTuple newtup = heap_copytuple(tuple); Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup); + bool warm_update; + Bitmapset *modified_attrs; newtrig->tgenabled = fires_when; - simple_heap_update(tgrel, &newtup->t_self, newtup); + simple_heap_update(tgrel, &newtup->t_self, newtup, &warm_update, + &modified_attrs); /* Keep catalog indexes current */ - CatalogUpdateIndexes(tgrel, newtup); + CatalogUpdateIndexes(tgrel, newtup, warm_update, modified_attrs); heap_freetuple(newtup); diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index 479a160..d8355f6 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -273,7 +273,7 @@ DefineTSParser(List *names, List *parameters) prsOid = simple_heap_insert(prsRel, tup); - CatalogUpdateIndexes(prsRel, tup); + CatalogUpdateIndexes(prsRel, tup, false, NULL); address = makeParserDependencies(tup); @@ -484,7 +484,7 @@ DefineTSDictionary(List *names, List *parameters) dictOid = simple_heap_insert(dictRel, tup); - CatalogUpdateIndexes(dictRel, tup); + CatalogUpdateIndexes(dictRel, tup, false, NULL); address = makeDictionaryDependencies(tup); @@ -540,6 +540,8 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) bool repl_null[Natts_pg_ts_dict]; bool repl_repl[Natts_pg_ts_dict]; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; dictId = get_ts_dict_oid(stmt->dictname, false); @@ -620,9 +622,10 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) newtup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &newtup->t_self, newtup); + simple_heap_update(rel, &newtup->t_self, newtup, &warm_update, + &modified_attrs); - CatalogUpdateIndexes(rel, newtup); + CatalogUpdateIndexes(rel, newtup, warm_update, modified_attrs); InvokeObjectPostAlterHook(TSDictionaryRelationId, dictId, 0); @@ -808,7 +811,7 @@ DefineTSTemplate(List *names, List *parameters) tmplOid = simple_heap_insert(tmplRel, tup); - CatalogUpdateIndexes(tmplRel, tup); + CatalogUpdateIndexes(tmplRel, tup, false, NULL); address = makeTSTemplateDependencies(tup); @@ -1068,7 +1071,7 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied) cfgOid = simple_heap_insert(cfgRel, tup); - CatalogUpdateIndexes(cfgRel, tup); + CatalogUpdateIndexes(cfgRel, tup, false, NULL); if (OidIsValid(sourceOid)) { @@ -1108,7 +1111,7 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied) simple_heap_insert(mapRel, newmaptup); - CatalogUpdateIndexes(mapRel, newmaptup); + CatalogUpdateIndexes(mapRel, newmaptup, false, NULL); heap_freetuple(newmaptup); } @@ -1398,6 +1401,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, bool repl_null[Natts_pg_ts_config_map]; bool repl_repl[Natts_pg_ts_config_map]; HeapTuple newtup; + bool warm_update; + Bitmapset *modified_attrs; memset(repl_val, 0, sizeof(repl_val)); memset(repl_null, false, sizeof(repl_null)); @@ -1409,9 +1414,10 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, newtup = heap_modify_tuple(maptup, RelationGetDescr(relMap), repl_val, repl_null, repl_repl); - simple_heap_update(relMap, &newtup->t_self, newtup); + simple_heap_update(relMap, &newtup->t_self, newtup, + &warm_update, &modified_attrs); - CatalogUpdateIndexes(relMap, newtup); + CatalogUpdateIndexes(relMap, newtup, warm_update, modified_attrs); } } @@ -1437,7 +1443,7 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, tup = heap_form_tuple(relMap->rd_att, values, nulls); simple_heap_insert(relMap, tup); - CatalogUpdateIndexes(relMap, tup); + CatalogUpdateIndexes(relMap, tup, false, NULL); heap_freetuple(tup); } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 4c33d55..ccbe96f 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -2138,6 +2138,8 @@ AlterDomainDefault(List *names, Node *defaultRaw) HeapTuple newtuple; Form_pg_type typTup; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeTypeNameFromNameList(names); @@ -2221,9 +2223,9 @@ AlterDomainDefault(List *names, Node *defaultRaw) new_record, new_record_nulls, new_record_repl); - simple_heap_update(rel, &tup->t_self, newtuple); + simple_heap_update(rel, &tup->t_self, newtuple, &warm_update, &modified_attrs); - CatalogUpdateIndexes(rel, newtuple); + CatalogUpdateIndexes(rel, newtuple, warm_update, modified_attrs); /* Rebuild dependencies */ GenerateTypeDependencies(typTup->typnamespace, @@ -2272,6 +2274,8 @@ AlterDomainNotNull(List *names, bool notNull) HeapTuple tup; Form_pg_type typTup; ObjectAddress address = InvalidObjectAddress; + bool warm_update; + Bitmapset *modified_attrs; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeTypeNameFromNameList(names); @@ -2360,9 +2364,9 @@ AlterDomainNotNull(List *names, bool notNull) */ typTup->typnotnull = notNull; - simple_heap_update(typrel, &tup->t_self, tup); + simple_heap_update(typrel, &tup->t_self, tup, &warm_update, &modified_attrs); - CatalogUpdateIndexes(typrel, tup); + CatalogUpdateIndexes(typrel, tup, warm_update, modified_attrs); InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0); @@ -2598,6 +2602,8 @@ AlterDomainValidateConstraint(List *names, char *constrName) HeapTuple copyTuple; ScanKeyData key; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeTypeNameFromNameList(names); @@ -2662,8 +2668,8 @@ AlterDomainValidateConstraint(List *names, char *constrName) copyTuple = heap_copytuple(tuple); copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple); copy_con->convalidated = true; - simple_heap_update(conrel, ©Tuple->t_self, copyTuple); - CatalogUpdateIndexes(conrel, copyTuple); + simple_heap_update(conrel, ©Tuple->t_self, copyTuple, &warm_update, &modified_attrs); + CatalogUpdateIndexes(conrel, copyTuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(ConstraintRelationId, HeapTupleGetOid(copyTuple), 0); @@ -3374,6 +3380,8 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId) Acl *newAcl; Datum aclDatum; bool isNull; + bool warm_update; + Bitmapset *modified_attrs; rel = heap_open(TypeRelationId, RowExclusiveLock); @@ -3404,9 +3412,9 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId) tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); - simple_heap_update(rel, &tup->t_self, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, &modified_attrs); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); /* If it has an array type, update that too */ if (OidIsValid(typTup->typarray)) @@ -3561,13 +3569,16 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, if (oldNspOid != nspOid) { + bool warm_update; + Bitmapset *modified_attrs; + /* OK, modify the pg_type row */ /* tup is a copy, so we can scribble directly on it */ typform->typnamespace = nspOid; - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + simple_heap_update(rel, &tup->t_self, tup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(rel, tup, warm_update, modified_attrs); } /* diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index e6fdac3..5d67041 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -434,7 +434,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) * Insert new record in the pg_authid table */ roleid = simple_heap_insert(pg_authid_rel, tuple); - CatalogUpdateIndexes(pg_authid_rel, tuple); + CatalogUpdateIndexes(pg_authid_rel, tuple, false, NULL); /* * Advance command counter so we can see new record; else tests in @@ -531,6 +531,8 @@ AlterRole(AlterRoleStmt *stmt) DefElem *dvalidUntil = NULL; DefElem *dbypassRLS = NULL; Oid roleid; + bool warm_update; + Bitmapset *modified_attrs; check_rolespec_name(stmt->role, "Cannot alter reserved roles."); @@ -838,10 +840,11 @@ AlterRole(AlterRoleStmt *stmt) new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record, new_record_nulls, new_record_repl); - simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple); + simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple, &warm_update, + &modified_attrs); /* Update indexes */ - CatalogUpdateIndexes(pg_authid_rel, new_tuple); + CatalogUpdateIndexes(pg_authid_rel, new_tuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0); @@ -1149,6 +1152,8 @@ RenameRole(const char *oldname, const char *newname) Oid roleid; ObjectAddress address; Form_pg_authid authform; + bool warm_update; + Bitmapset *modified_attrs; rel = heap_open(AuthIdRelationId, RowExclusiveLock); dsc = RelationGetDescr(rel); @@ -1243,9 +1248,9 @@ RenameRole(const char *oldname, const char *newname) } newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl); - simple_heap_update(rel, &oldtuple->t_self, newtuple); + simple_heap_update(rel, &oldtuple->t_self, newtuple, &warm_update, &modified_attrs); - CatalogUpdateIndexes(rel, newtuple); + CatalogUpdateIndexes(rel, newtuple, warm_update, modified_attrs); InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0); @@ -1527,13 +1532,16 @@ AddRoleMems(const char *rolename, Oid roleid, if (HeapTupleIsValid(authmem_tuple)) { + bool warm_update; + Bitmapset *modified_attrs; + new_record_repl[Anum_pg_auth_members_grantor - 1] = true; new_record_repl[Anum_pg_auth_members_admin_option - 1] = true; tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc, new_record, new_record_nulls, new_record_repl); - simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple); - CatalogUpdateIndexes(pg_authmem_rel, tuple); + simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple, &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_authmem_rel, tuple, warm_update, modified_attrs); ReleaseSysCache(authmem_tuple); } else @@ -1541,7 +1549,7 @@ AddRoleMems(const char *rolename, Oid roleid, tuple = heap_form_tuple(pg_authmem_dsc, new_record, new_record_nulls); simple_heap_insert(pg_authmem_rel, tuple); - CatalogUpdateIndexes(pg_authmem_rel, tuple); + CatalogUpdateIndexes(pg_authmem_rel, tuple, false, NULL); } /* CCI after each change, in case there are duplicates in list */ @@ -1637,6 +1645,8 @@ DelRoleMems(const char *rolename, Oid roleid, Datum new_record[Natts_pg_auth_members]; bool new_record_nulls[Natts_pg_auth_members]; bool new_record_repl[Natts_pg_auth_members]; + bool warm_update; + Bitmapset *modified_attrs; /* Build a tuple to update with */ MemSet(new_record, 0, sizeof(new_record)); @@ -1649,8 +1659,10 @@ DelRoleMems(const char *rolename, Oid roleid, tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc, new_record, new_record_nulls, new_record_repl); - simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple); - CatalogUpdateIndexes(pg_authmem_rel, tuple); + simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple, + &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_authmem_rel, tuple, warm_update, + modified_attrs); } ReleaseSysCache(authmem_tuple); diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 005440e..9e3d0ee 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -2158,6 +2158,22 @@ heap_page_is_all_visible(Relation rel, Buffer buf, break; } + /* + * If this tuple was ever WARM updated or is a WARM tuple, + * there could be multiple index entries pointing to the + * root of this chain. We can't do index-only scans for + * such tuples without verifying index key check. So mark + * the page as !all_visible + * + * XXX Should we look at the root line pointer and check if + * WARM flag is set there or checking for tuples in the + * chain is good enough? + */ + if (HeapTupleHeaderIsHeapWarmTuple(tuple.t_data)) + { + all_visible = false; + } + /* Track newest xmin on page. */ if (TransactionIdFollows(xmin, *visibility_cutoff_xid)) *visibility_cutoff_xid = xmin; diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 9920f48..94cf92f 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -270,6 +270,8 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo) List * ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, + ItemPointer root_tid, + Bitmapset *modified_attrs, EState *estate, bool noDupErr, bool *specConflict, @@ -324,6 +326,17 @@ ExecInsertIndexTuples(TupleTableSlot *slot, if (!indexInfo->ii_ReadyForInserts) continue; + /* + * If modified_attrs is set, we only insert index entries for those + * indexes whose column has changed. All other indexes can use their + * existing index pointers to look up the new tuple + */ + if (modified_attrs) + { + if (!bms_overlap(modified_attrs, indexInfo->ii_indxattrs)) + continue; + } + /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { @@ -389,7 +402,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot, index_insert(indexRelation, /* index relation */ values, /* array of index Datums */ isnull, /* null flags */ - tupleid, /* tid of heap tuple */ + root_tid, /* tid of heap or root tuple */ heapRelation, /* heap relation */ checkUnique); /* type of uniqueness check to do */ @@ -790,6 +803,9 @@ retry: { if (!HeapTupleHeaderIsHeapLatest(tup->t_data, &tup->t_self)) HeapTupleHeaderGetNextTid(tup->t_data, &ctid_wait); + else + ItemPointerCopy(&tup->t_self, &ctid_wait); + reason_wait = indexInfo->ii_ExclusionOps ? XLTW_RecheckExclusionConstr : XLTW_InsertIndex; index_endscan(index_scan); diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index a18ae51..fb81633 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -399,6 +399,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot) if (resultRelInfo->ri_NumIndices > 0) recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), + &(tuple->t_self), NULL, estate, false, NULL, NIL); @@ -445,6 +446,8 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, if (!skip_tuple) { List *recheckIndexes = NIL; + bool warm_update; + Bitmapset *modified_attrs; /* Check the constraints of the tuple */ if (rel->rd_att->constr) @@ -455,13 +458,31 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, /* OK, update the tuple and index entries for it */ simple_heap_update(rel, &searchslot->tts_tuple->t_self, - slot->tts_tuple); + slot->tts_tuple, + &warm_update, + &modified_attrs); if (resultRelInfo->ri_NumIndices > 0 && - !HeapTupleIsHeapOnly(slot->tts_tuple)) + (!HeapTupleIsHeapOnly(slot->tts_tuple) || warm_update)) + { + ItemPointerData root_tid; + + if (warm_update) + ItemPointerSet(&root_tid, + ItemPointerGetBlockNumber(&(tuple->t_self)), + HeapTupleHeaderGetRootOffset(tuple->t_data)); + else + { + ItemPointerCopy(&tuple->t_self, &root_tid); + bms_free(modified_attrs); + modified_attrs = NULL; + } + recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), + &root_tid, modified_attrs, estate, false, NULL, NIL); + } /* AFTER ROW UPDATE Triggers */ ExecARUpdateTriggers(estate, resultRelInfo, diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index f18827d..f81d290 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -37,6 +37,7 @@ #include "access/relscan.h" #include "access/transam.h" +#include "access/valid.h" #include "executor/execdebug.h" #include "executor/nodeBitmapHeapscan.h" #include "pgstat.h" @@ -362,11 +363,27 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres) OffsetNumber offnum = tbmres->offsets[curslot]; ItemPointerData tid; HeapTupleData heapTuple; + bool recheck = false; ItemPointerSet(&tid, page, offnum); if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot, - &heapTuple, NULL, true)) - scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid); + &heapTuple, NULL, true, &recheck)) + { + bool valid = true; + + if (scan->rs_key) + HeapKeyTest(&heapTuple, RelationGetDescr(scan->rs_rd), + scan->rs_nkeys, scan->rs_key, valid); + if (valid) + scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid); + + /* + * If the heap tuple needs a recheck because of a WARM update, + * it's a lossy case + */ + if (recheck) + tbmres->recheck = true; + } } } else diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 5734550..c7be366 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -115,10 +115,10 @@ IndexNext(IndexScanState *node) false); /* don't pfree */ /* - * If the index was lossy, we have to recheck the index quals using - * the fetched tuple. + * If the index was lossy or the tuple was WARM, we have to recheck + * the index quals using the fetched tuple. */ - if (scandesc->xs_recheck) + if (scandesc->xs_recheck || scandesc->xs_tuple_recheck) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 2ac7407..142eb57 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -512,6 +512,7 @@ ExecInsert(ModifyTableState *mtstate, /* insert index entries for tuple */ recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), + &(tuple->t_self), NULL, estate, true, &specConflict, arbiterIndexes); @@ -558,6 +559,7 @@ ExecInsert(ModifyTableState *mtstate, /* insert index entries for tuple */ if (resultRelInfo->ri_NumIndices > 0) recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), + &(tuple->t_self), NULL, estate, false, NULL, arbiterIndexes); } @@ -891,6 +893,9 @@ ExecUpdate(ItemPointer tupleid, HTSU_Result result; HeapUpdateFailureData hufd; List *recheckIndexes = NIL; + Bitmapset *modified_attrs = NULL; + ItemPointerData root_tid; + bool warm_update; /* * abort the operation if not running transactions @@ -1007,7 +1012,7 @@ lreplace:; estate->es_output_cid, estate->es_crosscheck_snapshot, true /* wait for commit */ , - &hufd, &lockmode); + &hufd, &lockmode, &modified_attrs, &warm_update); switch (result) { case HeapTupleSelfUpdated: @@ -1094,10 +1099,28 @@ lreplace:; * the t_self field. * * If it's a HOT update, we mustn't insert new index entries. + * + * If it's a WARM update, then we must insert new entries with TID + * pointing to the root of the WARM chain. */ - if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple)) + if (resultRelInfo->ri_NumIndices > 0 && + (!HeapTupleIsHeapOnly(tuple) || warm_update)) + { + if (warm_update) + ItemPointerSet(&root_tid, + ItemPointerGetBlockNumber(&(tuple->t_self)), + HeapTupleHeaderGetRootOffset(tuple->t_data)); + else + { + ItemPointerCopy(&tuple->t_self, &root_tid); + bms_free(modified_attrs); + modified_attrs = NULL; + } recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), + &root_tid, + modified_attrs, estate, false, NULL, NIL); + } } if (canSetTag) diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 7176cf1..432dd4b 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -1823,7 +1823,7 @@ pgstat_count_heap_insert(Relation rel, int n) * pgstat_count_heap_update - count a tuple update */ void -pgstat_count_heap_update(Relation rel, bool hot) +pgstat_count_heap_update(Relation rel, bool hot, bool warm) { PgStat_TableStatus *pgstat_info = rel->pgstat_info; @@ -1841,6 +1841,8 @@ pgstat_count_heap_update(Relation rel, bool hot) /* t_tuples_hot_updated is nontransactional, so just advance it */ if (hot) pgstat_info->t_counts.t_tuples_hot_updated++; + else if (warm) + pgstat_info->t_counts.t_tuples_warm_updated++; } } @@ -4085,6 +4087,7 @@ pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry, Oid tableoid, bool create) result->tuples_updated = 0; result->tuples_deleted = 0; result->tuples_hot_updated = 0; + result->tuples_warm_updated = 0; result->n_live_tuples = 0; result->n_dead_tuples = 0; result->changes_since_analyze = 0; @@ -5194,6 +5197,7 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len) tabentry->tuples_updated = tabmsg->t_counts.t_tuples_updated; tabentry->tuples_deleted = tabmsg->t_counts.t_tuples_deleted; tabentry->tuples_hot_updated = tabmsg->t_counts.t_tuples_hot_updated; + tabentry->tuples_warm_updated = tabmsg->t_counts.t_tuples_warm_updated; tabentry->n_live_tuples = tabmsg->t_counts.t_delta_live_tuples; tabentry->n_dead_tuples = tabmsg->t_counts.t_delta_dead_tuples; tabentry->changes_since_analyze = tabmsg->t_counts.t_changed_tuples; @@ -5221,6 +5225,7 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len) tabentry->tuples_updated += tabmsg->t_counts.t_tuples_updated; tabentry->tuples_deleted += tabmsg->t_counts.t_tuples_deleted; tabentry->tuples_hot_updated += tabmsg->t_counts.t_tuples_hot_updated; + tabentry->tuples_warm_updated += tabmsg->t_counts.t_tuples_warm_updated; /* If table was truncated, first reset the live/dead counters */ if (tabmsg->t_counts.t_truncated) { diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c index d7dda6a..adafe23 100644 --- a/src/backend/replication/logical/origin.c +++ b/src/backend/replication/logical/origin.c @@ -300,7 +300,7 @@ replorigin_create(char *roname) tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); + CatalogUpdateIndexes(rel, tuple, false, NULL); CommandCounterIncrement(); break; } diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 864d45f..59f163a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -77,6 +77,8 @@ InsertRule(char *rulname, ObjectAddress myself, referenced; bool is_update = false; + bool warm_update; + Bitmapset *modified_attrs; /* * Set up *nulls and *values arrays @@ -124,7 +126,7 @@ InsertRule(char *rulname, tup = heap_modify_tuple(oldtup, RelationGetDescr(pg_rewrite_desc), values, nulls, replaces); - simple_heap_update(pg_rewrite_desc, &tup->t_self, tup); + simple_heap_update(pg_rewrite_desc, &tup->t_self, tup, &warm_update, &modified_attrs); ReleaseSysCache(oldtup); @@ -136,10 +138,12 @@ InsertRule(char *rulname, tup = heap_form_tuple(pg_rewrite_desc->rd_att, values, nulls); rewriteObjectId = simple_heap_insert(pg_rewrite_desc, tup); + warm_update = false; + modified_attrs = NULL; } /* Need to update indexes in either case */ - CatalogUpdateIndexes(pg_rewrite_desc, tup); + CatalogUpdateIndexes(pg_rewrite_desc, tup, warm_update, modified_attrs); heap_freetuple(tup); @@ -549,6 +553,8 @@ DefineQueryRewrite(char *rulename, Oid toastrelid; HeapTuple classTup; Form_pg_class classForm; + bool warm_update; + Bitmapset *modified_attrs; relationRelation = heap_open(RelationRelationId, RowExclusiveLock); toastrelid = event_relation->rd_rel->reltoastrelid; @@ -613,8 +619,8 @@ DefineQueryRewrite(char *rulename, classForm->relminmxid = InvalidMultiXactId; classForm->relreplident = REPLICA_IDENTITY_NOTHING; - simple_heap_update(relationRelation, &classTup->t_self, classTup); - CatalogUpdateIndexes(relationRelation, classTup); + simple_heap_update(relationRelation, &classTup->t_self, classTup, &warm_update, &modified_attrs); + CatalogUpdateIndexes(relationRelation, classTup, warm_update, modified_attrs); heap_freetuple(classTup); heap_close(relationRelation, RowExclusiveLock); @@ -864,12 +870,15 @@ EnableDisableRule(Relation rel, const char *rulename, if (DatumGetChar(((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_enabled) != fires_when) { + bool warm_update; + Bitmapset *modified_attrs; + ((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_enabled = CharGetDatum(fires_when); - simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup); + simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup, &warm_update, &modified_attrs); /* keep system catalog indexes current */ - CatalogUpdateIndexes(pg_rewrite_desc, ruletup); + CatalogUpdateIndexes(pg_rewrite_desc, ruletup, warm_update, modified_attrs); changed = true; } @@ -938,6 +947,8 @@ RenameRewriteRule(RangeVar *relation, const char *oldName, Form_pg_rewrite ruleform; Oid ruleOid; ObjectAddress address; + bool warm_update; + Bitmapset *modified_attrs; /* * Look up name, check permissions, and acquire lock (which we will NOT @@ -985,10 +996,10 @@ RenameRewriteRule(RangeVar *relation, const char *oldName, /* OK, do the update */ namestrcpy(&(ruleform->rulename), newName); - simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup); + simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup, &warm_update, &modified_attrs); /* keep system catalog indexes current */ - CatalogUpdateIndexes(pg_rewrite_desc, ruletup); + CatalogUpdateIndexes(pg_rewrite_desc, ruletup, warm_update, modified_attrs); heap_freetuple(ruletup); heap_close(pg_rewrite_desc, RowExclusiveLock); diff --git a/src/backend/rewrite/rewriteSupport.c b/src/backend/rewrite/rewriteSupport.c index 0154072..848ee7a 100644 --- a/src/backend/rewrite/rewriteSupport.c +++ b/src/backend/rewrite/rewriteSupport.c @@ -69,13 +69,16 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules) if (classForm->relhasrules != relHasRules) { + bool warm_update; + Bitmapset *modified_attrs; + /* Do the update */ classForm->relhasrules = relHasRules; - simple_heap_update(relationRelation, &tuple->t_self, tuple); + simple_heap_update(relationRelation, &tuple->t_self, tuple, &warm_update, &modified_attrs); /* Keep the catalog indexes up to date */ - CatalogUpdateIndexes(relationRelation, tuple); + CatalogUpdateIndexes(relationRelation, tuple, warm_update, modified_attrs); } else { diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c index 262b0b2..7a643bf 100644 --- a/src/backend/storage/large_object/inv_api.c +++ b/src/backend/storage/large_object/inv_api.c @@ -638,6 +638,9 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) */ if (olddata != NULL && olddata->pageno == pageno) { + bool warm_update; + Bitmapset *modified_attrs; + /* * Update an existing page with fresh data. * @@ -678,8 +681,9 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) replace[Anum_pg_largeobject_data - 1] = true; newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r), values, nulls, replace); - simple_heap_update(lo_heap_r, &newtup->t_self, newtup); - CatalogIndexInsert(indstate, newtup); + simple_heap_update(lo_heap_r, &newtup->t_self, newtup, + &warm_update, &modified_attrs); + CatalogIndexInsert(indstate, newtup, warm_update, modified_attrs); heap_freetuple(newtup); /* @@ -722,7 +726,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls); simple_heap_insert(lo_heap_r, newtup); - CatalogIndexInsert(indstate, newtup); + CatalogIndexInsert(indstate, newtup, false, NULL); heap_freetuple(newtup); } pageno++; @@ -824,6 +828,8 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len) bytea *datafield; int pagelen; bool pfreeit; + bool warm_update; + Bitmapset *modified_attrs; getdatafield(olddata, &datafield, &pagelen, &pfreeit); memcpy(workb, VARDATA(datafield), pagelen); @@ -850,8 +856,9 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len) replace[Anum_pg_largeobject_data - 1] = true; newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r), values, nulls, replace); - simple_heap_update(lo_heap_r, &newtup->t_self, newtup); - CatalogIndexInsert(indstate, newtup); + simple_heap_update(lo_heap_r, &newtup->t_self, newtup, &warm_update, + &modified_attrs); + CatalogIndexInsert(indstate, newtup, warm_update, modified_attrs); heap_freetuple(newtup); } else @@ -889,7 +896,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len) values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls); simple_heap_insert(lo_heap_r, newtup); - CatalogIndexInsert(indstate, newtup); + CatalogIndexInsert(indstate, newtup, false, NULL); heap_freetuple(newtup); } diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index a987d0d..b8677f3 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -145,6 +145,22 @@ pg_stat_get_tuples_hot_updated(PG_FUNCTION_ARGS) Datum +pg_stat_get_tuples_warm_updated(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_StatTabEntry *tabentry; + + if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) + result = 0; + else + result = (int64) (tabentry->tuples_warm_updated); + + PG_RETURN_INT64(result); +} + + +Datum pg_stat_get_live_tuples(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); @@ -1644,6 +1660,21 @@ pg_stat_get_xact_tuples_hot_updated(PG_FUNCTION_ARGS) } Datum +pg_stat_get_xact_tuples_warm_updated(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int64 result; + PgStat_TableStatus *tabentry; + + if ((tabentry = find_tabstat_entry(relid)) == NULL) + result = 0; + else + result = (int64) (tabentry->t_counts.t_tuples_warm_updated); + + PG_RETURN_INT64(result); +} + +Datum pg_stat_get_xact_blocks_fetched(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 26ff7e1..1976753 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -2338,6 +2338,7 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc) list_free_deep(relation->rd_fkeylist); list_free(relation->rd_indexlist); bms_free(relation->rd_indexattr); + bms_free(relation->rd_exprindexattr); bms_free(relation->rd_keyattr); bms_free(relation->rd_pkattr); bms_free(relation->rd_idattr); @@ -3419,6 +3420,8 @@ RelationSetNewRelfilenode(Relation relation, char persistence, Relation pg_class; HeapTuple tuple; Form_pg_class classform; + bool warm_update; + Bitmapset *modified_attrs; /* Indexes, sequences must have Invalid frozenxid; other rels must not */ Assert((relation->rd_rel->relkind == RELKIND_INDEX || @@ -3484,8 +3487,8 @@ RelationSetNewRelfilenode(Relation relation, char persistence, classform->relminmxid = minmulti; classform->relpersistence = persistence; - simple_heap_update(pg_class, &tuple->t_self, tuple); - CatalogUpdateIndexes(pg_class, tuple); + simple_heap_update(pg_class, &tuple->t_self, tuple, &warm_update, &modified_attrs); + CatalogUpdateIndexes(pg_class, tuple, warm_update, modified_attrs); heap_freetuple(tuple); @@ -4757,6 +4760,8 @@ Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) { Bitmapset *indexattrs; /* indexed columns */ + Bitmapset *exprindexattrs; /* indexed columns in expression/prediacate + indexes */ Bitmapset *uindexattrs; /* columns in unique indexes */ Bitmapset *pkindexattrs; /* columns in the primary index */ Bitmapset *idindexattrs; /* columns in the replica identity */ @@ -4765,6 +4770,7 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) Oid relreplindex; ListCell *l; MemoryContext oldcxt; + bool supportswarm = true;/* True if the table can be WARM updated */ /* Quick exit if we already computed the result. */ if (relation->rd_indexattr != NULL) @@ -4779,6 +4785,8 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) return bms_copy(relation->rd_pkattr); case INDEX_ATTR_BITMAP_IDENTITY_KEY: return bms_copy(relation->rd_idattr); + case INDEX_ATTR_BITMAP_EXPR_PREDICATE: + return bms_copy(relation->rd_exprindexattr); default: elog(ERROR, "unknown attrKind %u", attrKind); } @@ -4819,6 +4827,7 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) * won't be returned at all by RelationGetIndexList. */ indexattrs = NULL; + exprindexattrs = NULL; uindexattrs = NULL; pkindexattrs = NULL; idindexattrs = NULL; @@ -4873,19 +4882,38 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) } /* Collect all attributes used in expressions, too */ - pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs); + pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &exprindexattrs); /* Collect all attributes in the index predicate, too */ - pull_varattnos((Node *) indexInfo->ii_Predicate, 1, &indexattrs); + pull_varattnos((Node *) indexInfo->ii_Predicate, 1, &exprindexattrs); + + /* + * indexattrs should include attributes referenced in index expressions + * and predicates too + */ + indexattrs = bms_add_members(indexattrs, exprindexattrs); + + /* + * Check if the index has amrecheck method defined. If the method is + * not defined, the index does not support WARM update. Completely + * disable WARM updates on such tables + */ + if (!indexDesc->rd_amroutine->amrecheck) + supportswarm = false; index_close(indexDesc, AccessShareLock); } list_free(indexoidlist); + /* Remember if the table can do WARM updates */ + relation->rd_supportswarm = supportswarm; + /* Don't leak the old values of these bitmaps, if any */ bms_free(relation->rd_indexattr); relation->rd_indexattr = NULL; + bms_free(relation->rd_exprindexattr); + relation->rd_exprindexattr = NULL; bms_free(relation->rd_keyattr); relation->rd_keyattr = NULL; bms_free(relation->rd_pkattr); @@ -4904,7 +4932,8 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) relation->rd_keyattr = bms_copy(uindexattrs); relation->rd_pkattr = bms_copy(pkindexattrs); relation->rd_idattr = bms_copy(idindexattrs); - relation->rd_indexattr = bms_copy(indexattrs); + relation->rd_exprindexattr = bms_copy(exprindexattrs); + relation->rd_indexattr = bms_copy(bms_union(indexattrs, exprindexattrs)); MemoryContextSwitchTo(oldcxt); /* We return our original working copy for caller to play with */ @@ -4918,6 +4947,8 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) return bms_copy(relation->rd_pkattr); case INDEX_ATTR_BITMAP_IDENTITY_KEY: return idindexattrs; + case INDEX_ATTR_BITMAP_EXPR_PREDICATE: + return exprindexattrs; default: elog(ERROR, "unknown attrKind %u", attrKind); return NULL; diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h index 6a5f279..0de82fa 100644 --- a/src/include/access/amapi.h +++ b/src/include/access/amapi.h @@ -13,6 +13,7 @@ #define AMAPI_H #include "access/genam.h" +#include "access/itup.h" /* * We don't wish to include planner header files here, since most of an index @@ -137,6 +138,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan); /* restore marked scan position */ typedef void (*amrestrpos_function) (IndexScanDesc scan); +/* recheck index tuple and heap tuple match */ +typedef bool (*amrecheck_function) (Relation indexRel, IndexTuple indexTuple, + Relation heapRel, HeapTuple heapTuple); /* * API struct for an index AM. Note this must be stored in a single palloc'd @@ -196,6 +200,7 @@ typedef struct IndexAmRoutine amendscan_function amendscan; ammarkpos_function ammarkpos; /* can be NULL */ amrestrpos_function amrestrpos; /* can be NULL */ + amrecheck_function amrecheck; /* can be NULL */ } IndexAmRoutine; diff --git a/src/include/access/hash.h b/src/include/access/hash.h index 69a3873..3e14023 100644 --- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -364,4 +364,8 @@ extern void hashbucketcleanup(Relation rel, Bucket cur_bucket, bool bucket_has_garbage, IndexBulkDeleteCallback callback, void *callback_state); +/* hash.c */ +extern bool hashrecheck(Relation indexRel, IndexTuple indexTuple, + Relation heapRel, HeapTuple heapTuple); + #endif /* HASH_H */ diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 22507dc..06e22a3 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -137,9 +137,10 @@ extern bool heap_fetch(Relation relation, Snapshot snapshot, Relation stats_relation); extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, - bool *all_dead, bool first_call); + bool *all_dead, bool first_call, bool *recheck); extern bool heap_hot_search(ItemPointer tid, Relation relation, - Snapshot snapshot, bool *all_dead); + Snapshot snapshot, bool *all_dead, + bool *recheck, Buffer *buffer, HeapTuple heapTuple); extern void heap_get_latest_tid(Relation relation, Snapshot snapshot, ItemPointer tid); @@ -160,7 +161,8 @@ extern void heap_abort_speculative(Relation relation, HeapTuple tuple); extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, - HeapUpdateFailureData *hufd, LockTupleMode *lockmode); + HeapUpdateFailureData *hufd, LockTupleMode *lockmode, + Bitmapset **modified_attrsp, bool *warm_update); extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_update, @@ -175,7 +177,9 @@ extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple); extern Oid simple_heap_insert(Relation relation, HeapTuple tup); extern void simple_heap_delete(Relation relation, ItemPointer tid); extern void simple_heap_update(Relation relation, ItemPointer otid, - HeapTuple tup); + HeapTuple tup, + bool *warm_update, + Bitmapset **modified_attrs); extern void heap_sync(Relation relation); diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h index a4a1fe1..b4238e5 100644 --- a/src/include/access/heapam_xlog.h +++ b/src/include/access/heapam_xlog.h @@ -80,6 +80,7 @@ #define XLH_UPDATE_CONTAINS_NEW_TUPLE (1<<4) #define XLH_UPDATE_PREFIX_FROM_OLD (1<<5) #define XLH_UPDATE_SUFFIX_FROM_OLD (1<<6) +#define XLH_UPDATE_WARM_UPDATE (1<<7) /* convenience macro for checking whether any form of old tuple was logged */ #define XLH_UPDATE_CONTAINS_OLD \ diff --git a/src/include/access/htup.h b/src/include/access/htup.h index 870adf4..0ae223e 100644 --- a/src/include/access/htup.h +++ b/src/include/access/htup.h @@ -14,6 +14,7 @@ #ifndef HTUP_H #define HTUP_H +#include "nodes/bitmapset.h" #include "storage/itemptr.h" /* typedefs and forward declarations for structs defined in htup_details.h */ diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index fff1832..2ea4865 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -260,7 +260,8 @@ struct HeapTupleHeaderData * information stored in t_infomask2: */ #define HEAP_NATTS_MASK 0x07FF /* 11 bits for number of attributes */ -/* bits 0x0800 are available */ +#define HEAP_WARM_TUPLE 0x0800 /* This tuple is a part of a WARM chain + */ #define HEAP_LATEST_TUPLE 0x1000 /* * This is the last tuple in chain and * ip_posid points to the root line @@ -271,7 +272,7 @@ struct HeapTupleHeaderData #define HEAP_HOT_UPDATED 0x4000 /* tuple was HOT-updated */ #define HEAP_ONLY_TUPLE 0x8000 /* this is heap-only tuple */ -#define HEAP2_XACT_MASK 0xF000 /* visibility-related bits */ +#define HEAP2_XACT_MASK 0xF800 /* visibility-related bits */ /* @@ -510,6 +511,22 @@ do { \ ((tup)->t_infomask2 & HEAP_ONLY_TUPLE) != 0 \ ) +#define HeapTupleHeaderSetHeapWarmTuple(tup) \ +do { \ + (tup)->t_infomask2 |= HEAP_WARM_TUPLE; \ +} while (0) + +#define HeapTupleHeaderClearHeapWarmTuple(tup) \ +do { \ + (tup)->t_infomask2 &= ~HEAP_WARM_TUPLE; \ +} while (0) + +#define HeapTupleHeaderIsHeapWarmTuple(tup) \ +( \ + ((tup)->t_infomask2 & HEAP_WARM_TUPLE) != 0 \ +) + + #define HeapTupleHeaderSetHeapLatest(tup, offnum) \ do { \ AssertMacro(OffsetNumberIsValid(offnum)); \ @@ -763,6 +780,15 @@ struct MinimalTupleData #define HeapTupleClearHeapOnly(tuple) \ HeapTupleHeaderClearHeapOnly((tuple)->t_data) +#define HeapTupleIsHeapWarmTuple(tuple) \ + HeapTupleHeaderIsHeapWarmTuple((tuple)->t_data) + +#define HeapTupleSetHeapWarmTuple(tuple) \ + HeapTupleHeaderSetHeapWarmTuple((tuple)->t_data) + +#define HeapTupleClearHeapWarmTuple(tuple) \ + HeapTupleHeaderClearHeapWarmTuple((tuple)->t_data) + #define HeapTupleGetOid(tuple) \ HeapTupleHeaderGetOid((tuple)->t_data) diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index 011a72e..98129d6 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -750,6 +750,8 @@ extern bytea *btoptions(Datum reloptions, bool validate); extern bool btproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull); +extern bool btrecheck(Relation indexRel, IndexTuple indexTuple, + Relation heapRel, HeapTuple heapTuple); /* * prototypes for functions in nbtvalidate.c diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h index 8746045..1f5b361 100644 --- a/src/include/access/relscan.h +++ b/src/include/access/relscan.h @@ -111,7 +111,8 @@ typedef struct IndexScanDescData HeapTupleData xs_ctup; /* current heap tuple, if any */ Buffer xs_cbuf; /* current heap buffer in scan, if any */ /* NB: if xs_cbuf is not InvalidBuffer, we hold a pin on that buffer */ - bool xs_recheck; /* T means scan keys must be rechecked */ + bool xs_recheck; /* T means scan keys must be rechecked for each tuple */ + bool xs_tuple_recheck; /* T means scan keys must be rechecked for current tuple */ /* * When fetching with an ordering operator, the values of the ORDER BY diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 45605a0..5a8fb70 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -31,8 +31,12 @@ typedef struct ResultRelInfo *CatalogIndexState; extern CatalogIndexState CatalogOpenIndexes(Relation heapRel); extern void CatalogCloseIndexes(CatalogIndexState indstate); extern void CatalogIndexInsert(CatalogIndexState indstate, - HeapTuple heapTuple); -extern void CatalogUpdateIndexes(Relation heapRel, HeapTuple heapTuple); + HeapTuple heapTuple, + bool warm_update, + Bitmapset *modified_attrs); +extern void CatalogUpdateIndexes(Relation heapRel, HeapTuple heapTuple, + bool warm_update, + Bitmapset *modified_attrs); /* diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index ab12761..201e8b6 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2740,6 +2740,8 @@ DATA(insert OID = 1933 ( pg_stat_get_tuples_deleted PGNSP PGUID 12 1 0 0 0 f f DESCR("statistics: number of tuples deleted"); DATA(insert OID = 1972 ( pg_stat_get_tuples_hot_updated PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_tuples_hot_updated _null_ _null_ _null_ )); DESCR("statistics: number of tuples hot updated"); +DATA(insert OID = 3353 ( pg_stat_get_tuples_warm_updated PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_tuples_warm_updated _null_ _null_ _null_ )); +DESCR("statistics: number of tuples warm updated"); DATA(insert OID = 2878 ( pg_stat_get_live_tuples PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_live_tuples _null_ _null_ _null_ )); DESCR("statistics: number of live tuples"); DATA(insert OID = 2879 ( pg_stat_get_dead_tuples PGNSP PGUID 12 1 0 0 0 f f f f t f s r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_dead_tuples _null_ _null_ _null_ )); @@ -2892,6 +2894,8 @@ DATA(insert OID = 3042 ( pg_stat_get_xact_tuples_deleted PGNSP PGUID 12 1 0 0 DESCR("statistics: number of tuples deleted in current transaction"); DATA(insert OID = 3043 ( pg_stat_get_xact_tuples_hot_updated PGNSP PGUID 12 1 0 0 0 f f f f t f v r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_xact_tuples_hot_updated _null_ _null_ _null_ )); DESCR("statistics: number of tuples hot updated in current transaction"); +DATA(insert OID = 3354 ( pg_stat_get_xact_tuples_warm_updated PGNSP PGUID 12 1 0 0 0 f f f f t f v r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_xact_tuples_warm_updated _null_ _null_ _null_ )); +DESCR("statistics: number of tuples warm updated in current transaction"); DATA(insert OID = 3044 ( pg_stat_get_xact_blocks_fetched PGNSP PGUID 12 1 0 0 0 f f f f t f v r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_xact_blocks_fetched _null_ _null_ _null_ )); DESCR("statistics: number of blocks fetched in current transaction"); DATA(insert OID = 3045 ( pg_stat_get_xact_blocks_hit PGNSP PGUID 12 1 0 0 0 f f f f t f v r 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_xact_blocks_hit _null_ _null_ _null_ )); diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 02dbe7b..c4495a3 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -382,6 +382,7 @@ extern void UnregisterExprContextCallback(ExprContext *econtext, extern void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative); extern void ExecCloseIndices(ResultRelInfo *resultRelInfo); extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, + ItemPointer root_tid, Bitmapset *modified_attrs, EState *estate, bool noDupErr, bool *specConflict, List *arbiterIndexes); extern bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, diff --git a/src/include/executor/nodeIndexscan.h b/src/include/executor/nodeIndexscan.h index 46d6f45..2c4d884 100644 --- a/src/include/executor/nodeIndexscan.h +++ b/src/include/executor/nodeIndexscan.h @@ -37,5 +37,4 @@ extern void ExecIndexEvalRuntimeKeys(ExprContext *econtext, extern bool ExecIndexEvalArrayKeys(ExprContext *econtext, IndexArrayKeyInfo *arrayKeys, int numArrayKeys); extern bool ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys); - #endif /* NODEINDEXSCAN_H */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index f9bcdd6..07f2900 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -62,6 +62,7 @@ typedef struct IndexInfo NodeTag type; int ii_NumIndexAttrs; AttrNumber ii_KeyAttrNumbers[INDEX_MAX_KEYS]; + Bitmapset *ii_indxattrs; /* bitmap of all columns used in this index */ List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ diff --git a/src/include/pgstat.h b/src/include/pgstat.h index de8225b..ee635be 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -105,6 +105,7 @@ typedef struct PgStat_TableCounts PgStat_Counter t_tuples_updated; PgStat_Counter t_tuples_deleted; PgStat_Counter t_tuples_hot_updated; + PgStat_Counter t_tuples_warm_updated; bool t_truncated; PgStat_Counter t_delta_live_tuples; @@ -625,6 +626,7 @@ typedef struct PgStat_StatTabEntry PgStat_Counter tuples_updated; PgStat_Counter tuples_deleted; PgStat_Counter tuples_hot_updated; + PgStat_Counter tuples_warm_updated; PgStat_Counter n_live_tuples; PgStat_Counter n_dead_tuples; @@ -1177,7 +1179,7 @@ pgstat_report_wait_end(void) (pgStatBlockWriteTime += (n)) extern void pgstat_count_heap_insert(Relation rel, int n); -extern void pgstat_count_heap_update(Relation rel, bool hot); +extern void pgstat_count_heap_update(Relation rel, bool hot, bool warm); extern void pgstat_count_heap_delete(Relation rel); extern void pgstat_count_truncate(Relation rel); extern void pgstat_update_heap_dead_tuples(Relation rel, int delta); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index a1750ac..092491f 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -138,9 +138,12 @@ typedef struct RelationData /* data managed by RelationGetIndexAttrBitmap: */ Bitmapset *rd_indexattr; /* identifies columns used in indexes */ + Bitmapset *rd_exprindexattr; /* indentified columns used in expression or + predicate indexes */ Bitmapset *rd_keyattr; /* cols that can be ref'd by foreign keys */ Bitmapset *rd_pkattr; /* cols included in primary key */ Bitmapset *rd_idattr; /* included in replica identity index */ + bool rd_supportswarm;/* True if the table can be WARM updated */ PublicationActions *rd_pubactions; /* publication actions */ diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index da36b67..83a7f20 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -50,7 +50,8 @@ typedef enum IndexAttrBitmapKind INDEX_ATTR_BITMAP_ALL, INDEX_ATTR_BITMAP_KEY, INDEX_ATTR_BITMAP_PRIMARY_KEY, - INDEX_ATTR_BITMAP_IDENTITY_KEY + INDEX_ATTR_BITMAP_IDENTITY_KEY, + INDEX_ATTR_BITMAP_EXPR_PREDICATE } IndexAttrBitmapKind; extern Bitmapset *RelationGetIndexAttrBitmap(Relation relation, diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out index b01be59..37719c9 100644 --- a/src/test/regress/expected/alter_generic.out +++ b/src/test/regress/expected/alter_generic.out @@ -161,15 +161,15 @@ ALTER SERVER alt_fserv1 RENAME TO alt_fserv3; -- OK SELECT fdwname FROM pg_foreign_data_wrapper WHERE fdwname like 'alt_fdw%'; fdwname ---------- - alt_fdw2 alt_fdw3 + alt_fdw2 (2 rows) SELECT srvname FROM pg_foreign_server WHERE srvname like 'alt_fserv%'; srvname ------------ - alt_fserv2 alt_fserv3 + alt_fserv2 (2 rows) -- diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 60abcad..42d45a1 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1718,6 +1718,7 @@ pg_stat_all_tables| SELECT c.oid AS relid, pg_stat_get_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_tuples_deleted(c.oid) AS n_tup_del, pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd, + pg_stat_get_tuples_warm_updated(c.oid) AS n_tup_warm_upd, pg_stat_get_live_tuples(c.oid) AS n_live_tup, pg_stat_get_dead_tuples(c.oid) AS n_dead_tup, pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze, @@ -1861,6 +1862,7 @@ pg_stat_sys_tables| SELECT pg_stat_all_tables.relid, pg_stat_all_tables.n_tup_upd, pg_stat_all_tables.n_tup_del, pg_stat_all_tables.n_tup_hot_upd, + pg_stat_all_tables.n_tup_warm_upd, pg_stat_all_tables.n_live_tup, pg_stat_all_tables.n_dead_tup, pg_stat_all_tables.n_mod_since_analyze, @@ -1904,6 +1906,7 @@ pg_stat_user_tables| SELECT pg_stat_all_tables.relid, pg_stat_all_tables.n_tup_upd, pg_stat_all_tables.n_tup_del, pg_stat_all_tables.n_tup_hot_upd, + pg_stat_all_tables.n_tup_warm_upd, pg_stat_all_tables.n_live_tup, pg_stat_all_tables.n_dead_tup, pg_stat_all_tables.n_mod_since_analyze, @@ -1941,7 +1944,8 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_xact_tuples_deleted(c.oid) AS n_tup_del, - pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd + pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd, + pg_stat_get_xact_tuples_warm_updated(c.oid) AS n_tup_warm_upd FROM ((pg_class c LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) @@ -1957,7 +1961,8 @@ pg_stat_xact_sys_tables| SELECT pg_stat_xact_all_tables.relid, pg_stat_xact_all_tables.n_tup_ins, pg_stat_xact_all_tables.n_tup_upd, pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd + pg_stat_xact_all_tables.n_tup_hot_upd, + pg_stat_xact_all_tables.n_tup_warm_upd FROM pg_stat_xact_all_tables WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); pg_stat_xact_user_functions| SELECT p.oid AS funcid, @@ -1979,7 +1984,8 @@ pg_stat_xact_user_tables| SELECT pg_stat_xact_all_tables.relid, pg_stat_xact_all_tables.n_tup_ins, pg_stat_xact_all_tables.n_tup_upd, pg_stat_xact_all_tables.n_tup_del, - pg_stat_xact_all_tables.n_tup_hot_upd + pg_stat_xact_all_tables.n_tup_hot_upd, + pg_stat_xact_all_tables.n_tup_warm_upd FROM pg_stat_xact_all_tables WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); pg_statio_all_indexes| SELECT c.oid AS relid, diff --git a/src/test/regress/expected/warm.out b/src/test/regress/expected/warm.out new file mode 100644 index 0000000..ebbc4ca --- /dev/null +++ b/src/test/regress/expected/warm.out @@ -0,0 +1,367 @@ +CREATE TABLE updtst_tab1 (a integer unique, b int, c text, d text); +CREATE INDEX updtst_indx1 ON updtst_tab1 (b); +INSERT INTO updtst_tab1 + SELECT generate_series(1,10000), generate_series(70001, 80000), 'foo', 'bar'; +-- This should be a HOT update as non-index key is updated, but the +-- page won't have any free space, so probably a non-HOT update +UPDATE updtst_tab1 SET c = 'foo1' WHERE a = 1; +-- Next update should be a HOT update as dead space is recycled +UPDATE updtst_tab1 SET c = 'foo2' WHERE a = 1; +-- And next too +UPDATE updtst_tab1 SET c = 'foo3' WHERE a = 1; +-- Now update one of the index key columns +UPDATE updtst_tab1 SET b = b + 70000 WHERE a = 1; +-- Ensure that the correct row is fetched +SELECT * FROM updtst_tab1 WHERE a = 1; + a | b | c | d +---+--------+------+----- + 1 | 140001 | foo3 | bar +(1 row) + +SELECT * FROM updtst_tab1 WHERE b = 70001 + 70000; + a | b | c | d +---+--------+------+----- + 1 | 140001 | foo3 | bar +(1 row) + +-- Even when seqscan is disabled and indexscan is forced +SET enable_seqscan = false; +EXPLAIN SELECT * FROM updtst_tab1 WHERE b = 70001 + 70000; + QUERY PLAN +---------------------------------------------------------------------------- + Bitmap Heap Scan on updtst_tab1 (cost=4.45..47.23 rows=22 width=72) + Recheck Cond: (b = 140001) + -> Bitmap Index Scan on updtst_indx1 (cost=0.00..4.45 rows=22 width=0) + Index Cond: (b = 140001) +(4 rows) + +SELECT * FROM updtst_tab1 WHERE b = 70001 + 70000; + a | b | c | d +---+--------+------+----- + 1 | 140001 | foo3 | bar +(1 row) + +-- Check if index only scan works correctly +EXPLAIN SELECT b FROM updtst_tab1 WHERE b = 70001 + 70000; + QUERY PLAN +---------------------------------------------------------------------------- + Bitmap Heap Scan on updtst_tab1 (cost=4.45..47.23 rows=22 width=4) + Recheck Cond: (b = 140001) + -> Bitmap Index Scan on updtst_indx1 (cost=0.00..4.45 rows=22 width=0) + Index Cond: (b = 140001) +(4 rows) + +SELECT b FROM updtst_tab1 WHERE b = 70001 + 70000; + b +-------- + 140001 +(1 row) + +-- Table must be vacuumed to force index-only scan +VACUUM updtst_tab1; +EXPLAIN SELECT b FROM updtst_tab1 WHERE b = 70001 + 70000; + QUERY PLAN +-------------------------------------------------------------------------------------- + Index Only Scan using updtst_indx1 on updtst_tab1 (cost=0.29..5.16 rows=50 width=4) + Index Cond: (b = 140001) +(2 rows) + +SELECT b FROM updtst_tab1 WHERE b = 70001 + 70000; + b +-------- + 140001 +(1 row) + +SET enable_seqscan = true; +DROP TABLE updtst_tab1; +------------------ +CREATE TABLE updtst_tab2 (a integer unique, b int, c text, d text) WITH (fillfactor = 80); +CREATE INDEX updtst_indx2 ON updtst_tab2 (b); +INSERT INTO updtst_tab2 + SELECT generate_series(1,100), generate_series(701, 800), 'foo', 'bar'; +UPDATE updtst_tab2 SET b = b + 700 WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo1' WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo2' WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo3' WHERE a = 1; +UPDATE updtst_tab2 SET b = b - 700 WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo4' WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo5' WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo6' WHERE a = 1; +SELECT count(*) FROM updtst_tab2 WHERE c = 'foo'; + count +------- + 99 +(1 row) + +SELECT * FROM updtst_tab2 WHERE c = 'foo6'; + a | b | c | d +---+-----+------+----- + 1 | 701 | foo6 | bar +(1 row) + +EXPLAIN SELECT * FROM updtst_tab2 WHERE b = 701; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on updtst_tab2 (cost=4.18..12.64 rows=4 width=72) + Recheck Cond: (b = 701) + -> Bitmap Index Scan on updtst_indx2 (cost=0.00..4.18 rows=4 width=0) + Index Cond: (b = 701) +(4 rows) + +SELECT * FROM updtst_tab2 WHERE a = 1; + a | b | c | d +---+-----+------+----- + 1 | 701 | foo6 | bar +(1 row) + +SET enable_seqscan = false; +EXPLAIN SELECT * FROM updtst_tab2 WHERE b = 701; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on updtst_tab2 (cost=4.18..12.64 rows=4 width=72) + Recheck Cond: (b = 701) + -> Bitmap Index Scan on updtst_indx2 (cost=0.00..4.18 rows=4 width=0) + Index Cond: (b = 701) +(4 rows) + +SELECT * FROM updtst_tab2 WHERE b = 701; + a | b | c | d +---+-----+------+----- + 1 | 701 | foo6 | bar +(1 row) + +VACUUM updtst_tab2; +EXPLAIN SELECT b FROM updtst_tab2 WHERE b = 701; + QUERY PLAN +------------------------------------------------------------------------------------- + Index Only Scan using updtst_indx2 on updtst_tab2 (cost=0.14..4.16 rows=1 width=4) + Index Cond: (b = 701) +(2 rows) + +SELECT b FROM updtst_tab2 WHERE b = 701; + b +----- + 701 +(1 row) + +SET enable_seqscan = true; +DROP TABLE updtst_tab2; +------------------ +CREATE TABLE updtst_tab3 (a integer unique, b int, c text, d text) WITH (fillfactor = 80); +CREATE INDEX updtst_indx3 ON updtst_tab3 (b); +INSERT INTO updtst_tab3 + SELECT generate_series(1,100), generate_series(701, 800), 'foo', 'bar'; +BEGIN; +UPDATE updtst_tab3 SET c = 'foo1', b = b + 700 WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo2' WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo3' WHERE a = 1; +UPDATE updtst_tab3 SET b = b - 700 WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo4' WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo5' WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo6' WHERE a = 1; +-- Abort the transaction and ensure the original tuple is visible correctly +ROLLBACK; +BEGIN; +UPDATE updtst_tab3 SET c = 'foo11', b = b + 750 WHERE b = 701; +UPDATE updtst_tab3 SET c = 'foo12' WHERE a = 1; +UPDATE updtst_tab3 SET b = b - 30 WHERE a = 1; +COMMIT; +SELECT count(*) FROM updtst_tab3 WHERE c = 'foo'; + count +------- + 99 +(1 row) + +SELECT * FROM updtst_tab3 WHERE c = 'foo6'; + a | b | c | d +---+---+---+--- +(0 rows) + +SELECT * FROM updtst_tab3 WHERE c = 'foo12'; + a | b | c | d +---+------+-------+----- + 1 | 1421 | foo12 | bar +(1 row) + +SELECT * FROM updtst_tab3 WHERE b = 701; + a | b | c | d +---+---+---+--- +(0 rows) + +SELECT * FROM updtst_tab3 WHERE b = 1421; + a | b | c | d +---+------+-------+----- + 1 | 1421 | foo12 | bar +(1 row) + +SELECT * FROM updtst_tab3 WHERE a = 1; + a | b | c | d +---+------+-------+----- + 1 | 1421 | foo12 | bar +(1 row) + +SELECT * FROM updtst_tab3 WHERE b = 701; + a | b | c | d +---+---+---+--- +(0 rows) + +SELECT * FROM updtst_tab3 WHERE b = 1421; + a | b | c | d +---+------+-------+----- + 1 | 1421 | foo12 | bar +(1 row) + +VACUUM updtst_tab3; +EXPLAIN SELECT b FROM updtst_tab3 WHERE b = 701; + QUERY PLAN +----------------------------------------------------------- + Seq Scan on updtst_tab3 (cost=0.00..2.25 rows=1 width=4) + Filter: (b = 701) +(2 rows) + +SELECT b FROM updtst_tab3 WHERE b = 701; + b +--- +(0 rows) + +SELECT b FROM updtst_tab3 WHERE b = 1421; + b +------ + 1421 +(1 row) + +BEGIN; +UPDATE updtst_tab3 SET c = 'foo21', b = b + 700 WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo22' WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo23' WHERE a = 2; +UPDATE updtst_tab3 SET b = b - 700 WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo24' WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo25' WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo26' WHERE a = 2; +-- Abort the transaction and ensure the original tuple is visible correctly +ROLLBACK; +SET enable_seqscan = false; +BEGIN; +UPDATE updtst_tab3 SET c = 'foo21', b = b + 750 WHERE b = 702; +UPDATE updtst_tab3 SET c = 'foo22' WHERE a = 2; +UPDATE updtst_tab3 SET b = b - 30 WHERE a = 2; +COMMIT; +SELECT count(*) FROM updtst_tab3 WHERE c = 'foo'; + count +------- + 98 +(1 row) + +SELECT * FROM updtst_tab3 WHERE c = 'foo26'; + a | b | c | d +---+---+---+--- +(0 rows) + +SELECT * FROM updtst_tab3 WHERE c = 'foo22'; + a | b | c | d +---+------+-------+----- + 2 | 1422 | foo22 | bar +(1 row) + +SELECT * FROM updtst_tab3 WHERE b = 702; + a | b | c | d +---+---+---+--- +(0 rows) + +SELECT * FROM updtst_tab3 WHERE b = 1422; + a | b | c | d +---+------+-------+----- + 2 | 1422 | foo22 | bar +(1 row) + +SELECT * FROM updtst_tab3 WHERE a = 2; + a | b | c | d +---+------+-------+----- + 2 | 1422 | foo22 | bar +(1 row) + +-- Try fetching both old and new value using updtst_indx3 +SELECT * FROM updtst_tab3 WHERE b = 702; + a | b | c | d +---+---+---+--- +(0 rows) + +SELECT * FROM updtst_tab3 WHERE b = 1422; + a | b | c | d +---+------+-------+----- + 2 | 1422 | foo22 | bar +(1 row) + +VACUUM updtst_tab3; +EXPLAIN SELECT b FROM updtst_tab3 WHERE b = 702; + QUERY PLAN +------------------------------------------------------------------------------------- + Index Only Scan using updtst_indx3 on updtst_tab3 (cost=0.14..8.16 rows=1 width=4) + Index Cond: (b = 702) +(2 rows) + +SELECT b FROM updtst_tab3 WHERE b = 702; + b +--- +(0 rows) + +SELECT b FROM updtst_tab3 WHERE b = 1422; + b +------ + 1422 +(1 row) + +SET enable_seqscan = true; +DROP TABLE updtst_tab3; +------------------ +CREATE TABLE test_warm (a text unique, b text); +CREATE INDEX test_warmindx ON test_warm (lower(a)); +INSERT INTO test_warm values ('test', 'foo'); +UPDATE test_warm SET a = 'TEST'; +select *, ctid from test_warm where lower(a) = 'test'; + a | b | ctid +------+-----+------- + TEST | foo | (0,2) +(1 row) + +explain select * from test_warm where lower(a) = 'test'; + QUERY PLAN +---------------------------------------------------------------------------- + Bitmap Heap Scan on test_warm (cost=4.18..12.65 rows=4 width=64) + Recheck Cond: (lower(a) = 'test'::text) + -> Bitmap Index Scan on test_warmindx (cost=0.00..4.18 rows=4 width=0) + Index Cond: (lower(a) = 'test'::text) +(4 rows) + +select *, ctid from test_warm where lower(a) = 'test'; + a | b | ctid +------+-----+------- + TEST | foo | (0,2) +(1 row) + +select *, ctid from test_warm where a = 'test'; + a | b | ctid +---+---+------ +(0 rows) + +select *, ctid from test_warm where a = 'TEST'; + a | b | ctid +------+-----+------- + TEST | foo | (0,2) +(1 row) + +set enable_bitmapscan TO false; +explain select * from test_warm where lower(a) = 'test'; + QUERY PLAN +--------------------------------------------------------------------------------- + Index Scan using test_warmindx on test_warm (cost=0.15..20.22 rows=4 width=64) + Index Cond: (lower(a) = 'test'::text) +(2 rows) + +select *, ctid from test_warm where lower(a) = 'test'; + a | b | ctid +------+-----+------- + TEST | foo | (0,2) +(1 row) + +DROP TABLE test_warm; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index e9b2bad..a9a269b 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -42,6 +42,8 @@ test: create_type test: create_table test: create_function_2 +test: warm + # ---------- # Load huge amounts of data # We should split the data files into single files and then diff --git a/src/test/regress/sql/warm.sql b/src/test/regress/sql/warm.sql new file mode 100644 index 0000000..b73c278 --- /dev/null +++ b/src/test/regress/sql/warm.sql @@ -0,0 +1,172 @@ + +CREATE TABLE updtst_tab1 (a integer unique, b int, c text, d text); +CREATE INDEX updtst_indx1 ON updtst_tab1 (b); +INSERT INTO updtst_tab1 + SELECT generate_series(1,10000), generate_series(70001, 80000), 'foo', 'bar'; + +-- This should be a HOT update as non-index key is updated, but the +-- page won't have any free space, so probably a non-HOT update +UPDATE updtst_tab1 SET c = 'foo1' WHERE a = 1; + +-- Next update should be a HOT update as dead space is recycled +UPDATE updtst_tab1 SET c = 'foo2' WHERE a = 1; + +-- And next too +UPDATE updtst_tab1 SET c = 'foo3' WHERE a = 1; + +-- Now update one of the index key columns +UPDATE updtst_tab1 SET b = b + 70000 WHERE a = 1; + +-- Ensure that the correct row is fetched +SELECT * FROM updtst_tab1 WHERE a = 1; +SELECT * FROM updtst_tab1 WHERE b = 70001 + 70000; + +-- Even when seqscan is disabled and indexscan is forced +SET enable_seqscan = false; +EXPLAIN SELECT * FROM updtst_tab1 WHERE b = 70001 + 70000; +SELECT * FROM updtst_tab1 WHERE b = 70001 + 70000; + +-- Check if index only scan works correctly +EXPLAIN SELECT b FROM updtst_tab1 WHERE b = 70001 + 70000; +SELECT b FROM updtst_tab1 WHERE b = 70001 + 70000; + +-- Table must be vacuumed to force index-only scan +VACUUM updtst_tab1; +EXPLAIN SELECT b FROM updtst_tab1 WHERE b = 70001 + 70000; +SELECT b FROM updtst_tab1 WHERE b = 70001 + 70000; + +SET enable_seqscan = true; + +DROP TABLE updtst_tab1; + +------------------ + +CREATE TABLE updtst_tab2 (a integer unique, b int, c text, d text) WITH (fillfactor = 80); +CREATE INDEX updtst_indx2 ON updtst_tab2 (b); +INSERT INTO updtst_tab2 + SELECT generate_series(1,100), generate_series(701, 800), 'foo', 'bar'; + +UPDATE updtst_tab2 SET b = b + 700 WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo1' WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo2' WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo3' WHERE a = 1; +UPDATE updtst_tab2 SET b = b - 700 WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo4' WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo5' WHERE a = 1; +UPDATE updtst_tab2 SET c = 'foo6' WHERE a = 1; + +SELECT count(*) FROM updtst_tab2 WHERE c = 'foo'; +SELECT * FROM updtst_tab2 WHERE c = 'foo6'; + +EXPLAIN SELECT * FROM updtst_tab2 WHERE b = 701; +SELECT * FROM updtst_tab2 WHERE a = 1; + +SET enable_seqscan = false; +EXPLAIN SELECT * FROM updtst_tab2 WHERE b = 701; +SELECT * FROM updtst_tab2 WHERE b = 701; + +VACUUM updtst_tab2; +EXPLAIN SELECT b FROM updtst_tab2 WHERE b = 701; +SELECT b FROM updtst_tab2 WHERE b = 701; + +SET enable_seqscan = true; + +DROP TABLE updtst_tab2; +------------------ + +CREATE TABLE updtst_tab3 (a integer unique, b int, c text, d text) WITH (fillfactor = 80); +CREATE INDEX updtst_indx3 ON updtst_tab3 (b); +INSERT INTO updtst_tab3 + SELECT generate_series(1,100), generate_series(701, 800), 'foo', 'bar'; + +BEGIN; +UPDATE updtst_tab3 SET c = 'foo1', b = b + 700 WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo2' WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo3' WHERE a = 1; +UPDATE updtst_tab3 SET b = b - 700 WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo4' WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo5' WHERE a = 1; +UPDATE updtst_tab3 SET c = 'foo6' WHERE a = 1; + +-- Abort the transaction and ensure the original tuple is visible correctly +ROLLBACK; + +BEGIN; +UPDATE updtst_tab3 SET c = 'foo11', b = b + 750 WHERE b = 701; +UPDATE updtst_tab3 SET c = 'foo12' WHERE a = 1; +UPDATE updtst_tab3 SET b = b - 30 WHERE a = 1; +COMMIT; + +SELECT count(*) FROM updtst_tab3 WHERE c = 'foo'; +SELECT * FROM updtst_tab3 WHERE c = 'foo6'; +SELECT * FROM updtst_tab3 WHERE c = 'foo12'; + +SELECT * FROM updtst_tab3 WHERE b = 701; +SELECT * FROM updtst_tab3 WHERE b = 1421; +SELECT * FROM updtst_tab3 WHERE a = 1; + +SELECT * FROM updtst_tab3 WHERE b = 701; +SELECT * FROM updtst_tab3 WHERE b = 1421; + +VACUUM updtst_tab3; +EXPLAIN SELECT b FROM updtst_tab3 WHERE b = 701; +SELECT b FROM updtst_tab3 WHERE b = 701; +SELECT b FROM updtst_tab3 WHERE b = 1421; + +BEGIN; +UPDATE updtst_tab3 SET c = 'foo21', b = b + 700 WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo22' WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo23' WHERE a = 2; +UPDATE updtst_tab3 SET b = b - 700 WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo24' WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo25' WHERE a = 2; +UPDATE updtst_tab3 SET c = 'foo26' WHERE a = 2; + +-- Abort the transaction and ensure the original tuple is visible correctly +ROLLBACK; + +SET enable_seqscan = false; + +BEGIN; +UPDATE updtst_tab3 SET c = 'foo21', b = b + 750 WHERE b = 702; +UPDATE updtst_tab3 SET c = 'foo22' WHERE a = 2; +UPDATE updtst_tab3 SET b = b - 30 WHERE a = 2; +COMMIT; + +SELECT count(*) FROM updtst_tab3 WHERE c = 'foo'; +SELECT * FROM updtst_tab3 WHERE c = 'foo26'; +SELECT * FROM updtst_tab3 WHERE c = 'foo22'; + +SELECT * FROM updtst_tab3 WHERE b = 702; +SELECT * FROM updtst_tab3 WHERE b = 1422; +SELECT * FROM updtst_tab3 WHERE a = 2; + +-- Try fetching both old and new value using updtst_indx3 +SELECT * FROM updtst_tab3 WHERE b = 702; +SELECT * FROM updtst_tab3 WHERE b = 1422; + +VACUUM updtst_tab3; +EXPLAIN SELECT b FROM updtst_tab3 WHERE b = 702; +SELECT b FROM updtst_tab3 WHERE b = 702; +SELECT b FROM updtst_tab3 WHERE b = 1422; + +SET enable_seqscan = true; + +DROP TABLE updtst_tab3; +------------------ + +CREATE TABLE test_warm (a text unique, b text); +CREATE INDEX test_warmindx ON test_warm (lower(a)); +INSERT INTO test_warm values ('test', 'foo'); +UPDATE test_warm SET a = 'TEST'; +select *, ctid from test_warm where lower(a) = 'test'; +explain select * from test_warm where lower(a) = 'test'; +select *, ctid from test_warm where lower(a) = 'test'; +select *, ctid from test_warm where a = 'test'; +select *, ctid from test_warm where a = 'TEST'; +set enable_bitmapscan TO false; +explain select * from test_warm where lower(a) = 'test'; +select *, ctid from test_warm where lower(a) = 'test'; +DROP TABLE test_warm; + +