agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH v1] Minimal fix for WAIT FOR ... MODE 'standby_flush' 116+ messages / 2 participants [nested] [flat]
* [PATCH v1] Minimal fix for WAIT FOR ... MODE 'standby_flush' @ 2026-04-07 13:11 Andres Freund <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Andres Freund @ 2026-04-07 13:11 UTC (permalink / raw) The investigation into the negative test performance impact of 7e8aeb9e483 lead to discovering that there are a few issues with WAIT FOR. This commit is just a minimal fix to prevent hangs in standby_flush mode, due to WAIT FOR ... 'standby_flush' seeing a 0 LSN if a newly started walreceiver does not receive any writes, because the stanby is already caught up. There are several other issues and this is isn't necessarily the best fix. But this way we get the hangs out of the way. Reported-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/zqbppucpmkeqecfy4s5kscnru4tbk6khp3ozqz6ad2zijz354k@w4bdf4z3wqoz --- src/backend/replication/walreceiver.c | 2 -- src/backend/replication/walreceiverfuncs.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c index a437273cf9a..09fde92bfd7 100644 --- a/src/backend/replication/walreceiver.c +++ b/src/backend/replication/walreceiver.c @@ -242,8 +242,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len) SpinLockRelease(&walrcv->mutex); - pg_atomic_write_u64(&WalRcv->writtenUpto, 0); - /* Arrange to clean up at walreceiver exit */ on_shmem_exit(WalRcvDie, PointerGetDatum(&startpointTLI)); diff --git a/src/backend/replication/walreceiverfuncs.c b/src/backend/replication/walreceiverfuncs.c index 4e03e721872..bd5d47be964 100644 --- a/src/backend/replication/walreceiverfuncs.c +++ b/src/backend/replication/walreceiverfuncs.c @@ -321,6 +321,7 @@ RequestXLogStreaming(TimeLineID tli, XLogRecPtr recptr, const char *conninfo, walrcv->flushedUpto = recptr; walrcv->receivedTLI = tli; walrcv->latestChunkStart = recptr; + pg_atomic_write_u64(&walrcv->writtenUpto, recptr); } walrcv->receiveStart = recptr; walrcv->receiveStartTLI = tli; -- 2.53.0.1.gb2826b52eb --ags3ltqh73mz7p2n-- ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-06-16 11:54 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 127d4415084..2866a5e696e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2382,97 +2378,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index ec100e3eef5..fa98b8b9247 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1268,6 +1268,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
* [PATCH 2/8] Move functions to repack.c. @ 2026-07-15 08:37 Antonin Houska <[email protected]> 0 siblings, 0 replies; 116+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) In the next patches, the functions will be called from other modules. Use a separate diff for the move so that the following diffs are a bit easier to read. --- src/backend/access/heap/heapam_handler.c | 97 +----------------------- src/backend/commands/repack.c | 91 ++++++++++++++++++++++ src/include/commands/repack.h | 7 ++ 3 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 4db8a16f691..97f850ebf73 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -34,6 +34,7 @@ #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/progress.h" +#include "commands/repack.h" #include "executor/executor.h" #include "miscadmin.h" #include "pgstat.h" @@ -50,11 +51,6 @@ static void reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, RewriteState rwstate); -static void heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, - BulkInsertStateData *bistate); -static bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); -static void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, @@ -2376,97 +2372,6 @@ reform_and_rewrite_tuple(TupleTableSlot *src, TupleTableSlot *reform, heap_freetuple(newtuple); } -/* - * Insert tuple when processing REPACK CONCURRENTLY. - * - * rewriteheap.c is not used in the CONCURRENTLY case because it'd be - * difficult to do the same in the catch-up phase (as the logical - * decoding does not provide us with sufficient visibility - * information). Thus we must use heap_insert() both during the - * catch-up and here. - * - * 'reform' is a slot to use for tuple "reforming", typically to get set - * values of dropped columns to NULL. - * - * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical - * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops - * this relation, so no logical replication subscription should need the data. - * - * BulkInsertState is used because many tuples are inserted in the typical - * case. - */ -static void -heap_insert_for_repack(Relation rel, TupleTableSlot *src, - TupleTableSlot *reform, BulkInsertStateData *bistate) -{ - HeapTuple tuple; - bool shouldFree; - TupleTableSlot *slot; - - tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); - if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) - { - clear_dropped_attributes(tuple, reform); - slot = reform; - } - else - slot = src; - - /* - * clear_dropped_attributes() should have deformed the tuple, so nothing - * should depend on it now. - */ - if (shouldFree) - heap_freetuple(tuple); - - table_tuple_insert(rel, slot, GetCurrentCommandId(true), - TABLE_INSERT_NO_LOGICAL, bistate); -} - -static bool -tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) -{ - /* - * A short tuple might require values from attmissing val, so activate the - * coding unconditionally in that case. The value might legitimally be - * NULL otherwise, so this is slightly wasteful, but it probably beats - * having to test each attribute for presence of attmissingval each time. - */ - if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) - return true; - - /* Does it have dropped attributes? */ - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped && - !heap_attisnull(tuple, i + 1, tupDesc)) - return true; - } - - return false; -} - -/* - * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. - * - * Set values of dropped columns to NULL, - */ -static void -clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) -{ - TupleDesc tupDesc = reform->tts_tupleDescriptor; - - /* Assuming 'reform' is virtual, this deforms the tuple. */ - Assert(TTS_IS_VIRTUAL(reform)); - ExecForceStoreHeapTuple(tuple, reform, false); - - for (int i = 0; i < tupDesc->natts; i++) - { - if (TupleDescCompactAttr(tupDesc, i)->attisdropped) - reform->tts_isnull[i] = true; - } -} - /* * Check visibility of the tuple. */ diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 02883fe34a4..19927c15bf3 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -1288,6 +1288,97 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, return OIDNewHeap; } +/* + * Insert tuple when processing REPACK CONCURRENTLY. + * + * rewriteheap.c is not used in the CONCURRENTLY case because it'd be + * difficult to do the same in the catch-up phase (as the logical + * decoding does not provide us with sufficient visibility + * information). Thus we must use heap_insert() both during the + * catch-up and here. + * + * 'reform' is a slot to use for tuple "reforming", typically to get set + * values of dropped columns to NULL. + * + * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical + * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops + * this relation, so no logical replication subscription should need the data. + * + * BulkInsertState is used because many tuples are inserted in the typical + * case. + */ +void +heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, BulkInsertStateData *bistate) +{ + HeapTuple tuple; + bool shouldFree; + TupleTableSlot *slot; + + tuple = ExecFetchSlotHeapTuple(src, false, &shouldFree); + if (tuple_needs_reform(tuple, src->tts_tupleDescriptor)) + { + clear_dropped_attributes(tuple, reform); + slot = reform; + } + else + slot = src; + + /* + * clear_dropped_attributes() should have deformed the tuple, so nothing + * should depend on it now. + */ + if (shouldFree) + heap_freetuple(tuple); + + table_tuple_insert(rel, slot, GetCurrentCommandId(true), + TABLE_INSERT_NO_LOGICAL, bistate); +} + +bool +tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc) +{ + /* + * A short tuple might require values from attmissing val, so activate the + * coding unconditionally in that case. The value might legitimally be + * NULL otherwise, so this is slightly wasteful, but it probably beats + * having to test each attribute for presence of attmissingval each time. + */ + if (HeapTupleHeaderGetNatts(tuple->t_data) < tupDesc->natts) + return true; + + /* Does it have dropped attributes? */ + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped && + !heap_attisnull(tuple, i + 1, tupDesc)) + return true; + } + + return false; +} + +/* + * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack. + * + * Set values of dropped columns to NULL, + */ +void +clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform) +{ + TupleDesc tupDesc = reform->tts_tupleDescriptor; + + /* Assuming 'reform' is virtual, this deforms the tuple. */ + Assert(TTS_IS_VIRTUAL(reform)); + ExecForceStoreHeapTuple(tuple, reform, false); + + for (int i = 0; i < tupDesc->natts; i++) + { + if (TupleDescCompactAttr(tupDesc, i)->attisdropped) + reform->tts_isnull[i] = true; + } +} + /* * Do the physical copying of table data. * diff --git a/src/include/commands/repack.h b/src/include/commands/repack.h index 45e5440a311..27105c10591 100644 --- a/src/include/commands/repack.h +++ b/src/include/commands/repack.h @@ -15,6 +15,8 @@ #include <signal.h> +#include "access/hio.h" +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "parser/parse_node.h" #include "storage/lockdefs.h" @@ -48,6 +50,11 @@ extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode); +extern void heap_insert_for_repack(Relation rel, TupleTableSlot *src, + TupleTableSlot *reform, + BulkInsertStateData *bistate); +extern bool tuple_needs_reform(HeapTuple tuple, TupleDesc tupDesc); +extern void clear_dropped_attributes(HeapTuple tuple, TupleTableSlot *reform); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0003-Introduce-RepackDest-structure.patch ^ permalink raw reply [nested|flat] 116+ messages in thread
end of thread, other threads:[~2026-07-15 08:37 UTC | newest] Thread overview: 116+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2026-04-07 13:11 [PATCH v1] Minimal fix for WAIT FOR ... MODE 'standby_flush' Andres Freund <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-06-16 11:54 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]> 2026-07-15 08:37 [PATCH 2/8] Move functions to repack.c. Antonin Houska <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox