From 76f18624e5cb1059d0d7b3187eb958befc0ccaff Mon Sep 17 00:00:00 2001 From: naisila Date: Tue, 14 Oct 2025 12:51:44 +0300 Subject: [PATCH] PG18 - use systable_inplace_update_* in UpdateStripeMetadataRow PG18 has removed heap_inplace_update(), which is crucial for citus_columnar extension because we always want to update stripe entries for columnar in-place. Relevant PG18 commit: https://github.com/postgres/postgres/commit/a07e03f heap_inplace_update() has been replaced by heap_inplace_update_and_unlock, which is used inside systable_inplace_update_finish, which is used together with systable_inplace_update_begin. This change has been back-patched up to v12, which is enough for us since the oldest version Citus supports is v15. In PG<18, a deprecated heap_inplace_update() is retained, however, let's start using the new functions because they are better, and such that we don't need to wrap these changes in PG18 version conditionals. Basically, in this commit we replace the following: SysScanDesc scanDescriptor = systable_beginscan(columnarStripes, indexId, indexOk, &dirtySnapshot, 2, scanKey); heap_inplace_update(columnarStripes, modifiedTuple); with the following: systable_inplace_update_begin(columnarStripes, indexId, indexOk, NULL, 2, scanKey, &tuple, &state); systable_inplace_update_finish(state, tuple); For more understanding, it's best to refer to an example: REL_18_0/src/backend/catalog/toasting.c#L349-L371 of how systable_inplace_update_begin and systable_inplace_update_finish are used in PG18, because they mirror the need of citus columnar. Fixes #8207 --- src/backend/columnar/columnar_metadata.c | 51 ++++++++---------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/src/backend/columnar/columnar_metadata.c b/src/backend/columnar/columnar_metadata.c index 5eb042bfd..0b4f2400c 100644 --- a/src/backend/columnar/columnar_metadata.c +++ b/src/backend/columnar/columnar_metadata.c @@ -1394,9 +1394,6 @@ static StripeMetadata * UpdateStripeMetadataRow(uint64 storageId, uint64 stripeId, uint64 fileOffset, uint64 dataLength, uint64 rowCount, uint64 chunkCount) { - SnapshotData dirtySnapshot; - InitDirtySnapshot(dirtySnapshot); - ScanKeyData scanKey[2]; ScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid, BTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId)); @@ -1410,8 +1407,11 @@ UpdateStripeMetadataRow(uint64 storageId, uint64 stripeId, uint64 fileOffset, Oid indexId = ColumnarStripePKeyIndexRelationId(); bool indexOk = OidIsValid(indexId); - SysScanDesc scanDescriptor = systable_beginscan(columnarStripes, indexId, indexOk, - &dirtySnapshot, 2, scanKey); + + void *state; + HeapTuple tuple; + systable_inplace_update_begin(columnarStripes, indexId, indexOk, NULL, + 2, scanKey, &tuple, &state); static bool loggedSlowMetadataAccessWarning = false; if (!indexOk && !loggedSlowMetadataAccessWarning) @@ -1420,8 +1420,7 @@ UpdateStripeMetadataRow(uint64 storageId, uint64 stripeId, uint64 fileOffset, loggedSlowMetadataAccessWarning = true; } - HeapTuple oldTuple = systable_getnext(scanDescriptor); - if (!HeapTupleIsValid(oldTuple)) + if (!HeapTupleIsValid(tuple)) { ereport(ERROR, (errmsg("attempted to modify an unexpected stripe, " "columnar storage with id=" UINT64_FORMAT @@ -1429,17 +1428,11 @@ UpdateStripeMetadataRow(uint64 storageId, uint64 stripeId, uint64 fileOffset, storageId, stripeId))); } - -/* - * heap_modify_tuple + heap_inplace_update only exist on PG < 18; - * on PG18 the in-place helper was removed upstream, so we skip the whole block. - */ -#if PG_VERSION_NUM < PG_VERSION_18 - /* - * heap_inplace_update already doesn't allow changing size of the original + * systable_inplace_update_finish already doesn't allow changing size of the original * tuple, so we don't allow setting any Datum's to NULL values. */ + Datum *newValues = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum)); bool *newNulls = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool)); bool *update = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool)); @@ -1454,32 +1447,20 @@ UpdateStripeMetadataRow(uint64 storageId, uint64 stripeId, uint64 fileOffset, newValues[Anum_columnar_stripe_row_count - 1] = UInt64GetDatum(rowCount); newValues[Anum_columnar_stripe_chunk_count - 1] = Int32GetDatum(chunkCount); - HeapTuple modifiedTuple = heap_modify_tuple(oldTuple, - tupleDescriptor, - newValues, - newNulls, - update); + tuple = heap_modify_tuple(tuple, + tupleDescriptor, + newValues, + newNulls, + update); - heap_inplace_update(columnarStripes, modifiedTuple); -#endif + systable_inplace_update_finish(state, tuple); - - /* - * Existing tuple now contains modifications, because we used - * heap_inplace_update(). - */ - HeapTuple newTuple = oldTuple; - - /* - * Must not pass modifiedTuple, because BuildStripeMetadata expects a real - * heap tuple with MVCC fields. - */ StripeMetadata *modifiedStripeMetadata = BuildStripeMetadata(columnarStripes, - newTuple); + tuple); CommandCounterIncrement(); - systable_endscan(scanDescriptor); + heap_freetuple(tuple); table_close(columnarStripes, AccessShareLock); pfree(newValues);