@@ -271,7 +271,8 @@ bucket_definitions:
271271 // pgoutput sends the new Relation message lazily, on the next DML rather than at ALTER time.
272272 await pool . query ( `ALTER TABLE test_mid_stream REPLICA IDENTITY FULL` ) ;
273273
274- // The next DML carries it; handleRelation must re-derive storeCurrentData without re-snapshotting.
274+ // DEFAULT->FULL widens replicaIdColumns (PK -> all columns), so the table resolves to a new
275+ // SourceTable and re-snapshots.
275276 const [ { id : id2 } ] = pgwireRows (
276277 await pool . query ( `INSERT INTO test_mid_stream (description, value) VALUES ('after', 2) RETURNING id` )
277278 ) ;
@@ -291,15 +292,17 @@ bucket_definitions:
291292 ] )
292293 ) ;
293294
294- // store_current_data is now false, snapshotComplete stays true - no re-snapshot.
295295 const after = await resolvedTable ( context , 'test_mid_stream' ) ;
296296 expect ( after ?. storeCurrentData ) . toBe ( false ) ;
297297 expect ( after ?. snapshotComplete ) . toBe ( true ) ;
298+ expect ( after ?. id ) . not . toEqual ( initial ?. id ) ;
298299 } ) ;
299300
300301 test ( 'reverting REPLICA IDENTITY FULL->DEFAULT recovers an unchanged TOAST value' , async ( ) => {
301- // No copy is kept while FULL, so after reverting to DEFAULT a partial UPDATE can't be completed
302- // from storage - the incomplete row must trigger a targeted resnapshot.
302+ // REPLICA IDENTITY FULL keeps no current_data copy. Reverting to DEFAULT narrows replicaIdColumns
303+ // (all columns -> PK), so the table resolves to a new SourceTable and re-snapshots, reading each
304+ // full row (including the unchanged TOAST description) into storage. A later partial UPDATE then
305+ // reduces against that copy.
303306 await using context = await openContext ( ) ;
304307 const { pool } = context ;
305308 await pool . query (
@@ -314,7 +317,8 @@ bucket_definitions:
314317 - SELECT id, name, description FROM test_revert
315318` ) ;
316319 await context . initializeReplication ( ) ;
317- expect ( ( await resolvedTable ( context , 'test_revert' ) ) ?. storeCurrentData ) . toBe ( false ) ;
320+ const initial = await resolvedTable ( context , 'test_revert' ) ;
321+ expect ( initial ?. storeCurrentData ) . toBe ( false ) ;
318322
319323 // Must be > 8kb after compression to be stored out-of-line (TOASTed). Random hex does not compress.
320324 const largeDescription = crypto . randomBytes ( 20_000 ) . toString ( 'hex' ) ;
@@ -330,18 +334,17 @@ bucket_definitions:
330334 // pgoutput sends the new Relation message lazily, on the next DML rather than at ALTER time.
331335 await pool . query ( `ALTER TABLE test_revert REPLICA IDENTITY DEFAULT` ) ;
332336
333- // Update only `name`: `description`'s TOAST value is omitted and DEFAULT carries only the key in
334- // the old tuple, so with no stored copy the row is incomplete -> a targeted resnapshot repairs it.
337+ // Update only `name`: under DEFAULT the unchanged TOASTed `description` is omitted from the WAL.
335338 await pool . query ( `UPDATE test_revert SET name = 'test2' WHERE id = '${ id } '` ) ;
336339
337340 data = await context . getBucketData ( 'global[]' ) ;
338341 expect ( data . findLast ( ( op ) => op . object_id === id ) ) . toMatchObject (
339342 putOp ( 'test_revert' , { id, name : 'test2' , description : largeDescription } )
340343 ) ;
341344
342- // Back to storeCurrentData=true, without a re-snapshot of the whole table.
343345 const reverted = await resolvedTable ( context , 'test_revert' ) ;
344346 expect ( reverted ?. storeCurrentData ) . toBe ( true ) ;
345347 expect ( reverted ?. snapshotComplete ) . toBe ( true ) ;
348+ expect ( reverted ?. id ) . not . toEqual ( initial ?. id ) ;
346349 } ) ;
347350}
0 commit comments