@@ -11,6 +11,7 @@ import { describe, expect, test } from 'vitest';
1111import { clearTestDb , connectPgPool , connectPgWire , TEST_URI } from './util.js' ;
1212import { WalStream } from '@module/replication/WalStream.js' ;
1313import { PostgresTypeCache } from '@module/types/cache.js' ;
14+ import { getServerVersion } from '@module/utils/postgres_version.js' ;
1415
1516describe ( 'pg data types' , ( ) => {
1617 async function setupTable ( db : pgwire . PgClient ) {
@@ -486,8 +487,7 @@ INSERT INTO test_data(id, time, timestamp, timestamptz) VALUES (1, '17:42:01.12'
486487 composite composite,
487488 nested_composite nested_composite,
488489 boxes box[],
489- mood mood,
490- ranges int4multirange[]
490+ mood mood
491491 );` ) ;
492492
493493 const slotName = 'test_slot' ;
@@ -504,14 +504,13 @@ INSERT INTO test_data(id, time, timestamp, timestamptz) VALUES (1, '17:42:01.12'
504504
505505 await db . query ( `
506506 INSERT INTO test_custom
507- (rating, composite, nested_composite, boxes, mood, ranges )
507+ (rating, composite, nested_composite, boxes, mood)
508508 VALUES (
509509 1,
510510 (ARRAY[2,3], 'bar'),
511511 (TRUE, (ARRAY[2,3], 'bar')),
512512 ARRAY[box(point '(1,2)', point '(3,4)'), box(point '(5, 6)', point '(7,8)')],
513- 'happy',
514- ARRAY[int4multirange(int4range(2, 4), int4range(5, 7, '(]'))]::int4multirange[]
513+ 'happy'
515514 );
516515 ` ) ;
517516
@@ -533,8 +532,7 @@ INSERT INTO test_data(id, time, timestamp, timestamptz) VALUES (1, '17:42:01.12'
533532 composite : '("{2,3}",bar)' ,
534533 nested_composite : '(t,"(""{2,3}"",bar)")' ,
535534 boxes : '["(3","4)","(1","2);(7","8)","(5","6)"]' ,
536- mood : 'happy' ,
537- ranges : '{"{[2,4),[6,8)}"}'
535+ mood : 'happy'
538536 } ) ;
539537
540538 const newFormat = applyRowContext ( transformed , new CompatibilityContext ( CompatibilityEdition . SYNC_STREAMS ) ) ;
@@ -543,7 +541,68 @@ INSERT INTO test_data(id, time, timestamp, timestamptz) VALUES (1, '17:42:01.12'
543541 composite : '{"foo":[2.0,3.0],"bar":"bar"}' ,
544542 nested_composite : '{"a":1,"b":{"foo":[2.0,3.0],"bar":"bar"}}' ,
545543 boxes : JSON . stringify ( [ '(3,4),(1,2)' , '(7,8),(5,6)' ] ) ,
546- mood : 'happy' ,
544+ mood : 'happy'
545+ } ) ;
546+ } finally {
547+ await db . end ( ) ;
548+ }
549+ } ) ;
550+
551+ test ( 'test replication - multiranges' , async ( ) => {
552+ const db = await connectPgPool ( ) ;
553+
554+ if ( ! ( await new PostgresTypeCache ( db , ( ) => getServerVersion ( db ) ) . supportsMultiRanges ( ) ) ) {
555+ // This test requires Postgres 14 or later.
556+ return ;
557+ }
558+
559+ try {
560+ await clearTestDb ( db ) ;
561+
562+ await db . query ( `CREATE TABLE test_custom(
563+ id serial primary key,
564+ ranges int4multirange[]
565+ );` ) ;
566+
567+ const slotName = 'test_slot' ;
568+
569+ await db . query ( {
570+ statement : 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1' ,
571+ params : [ { type : 'varchar' , value : slotName } ]
572+ } ) ;
573+
574+ await db . query ( {
575+ statement : `SELECT slot_name, lsn FROM pg_catalog.pg_create_logical_replication_slot($1, 'pgoutput')` ,
576+ params : [ { type : 'varchar' , value : slotName } ]
577+ } ) ;
578+
579+ await db . query ( `
580+ INSERT INTO test_custom
581+ (ranges)
582+ VALUES (
583+ ARRAY[int4multirange(int4range(2, 4), int4range(5, 7, '(]'))]::int4multirange[]
584+ );
585+ ` ) ;
586+
587+ const pg : pgwire . PgConnection = await pgwire . pgconnect ( { replication : 'database' } , TEST_URI ) ;
588+ const replicationStream = await pg . logicalReplication ( {
589+ slot : slotName ,
590+ options : {
591+ proto_version : '1' ,
592+ publication_names : 'powersync'
593+ }
594+ } ) ;
595+
596+ const [ transformed ] = await getReplicationTx ( db , replicationStream ) ;
597+ await pg . end ( ) ;
598+
599+ const oldFormat = applyRowContext ( transformed , CompatibilityContext . FULL_BACKWARDS_COMPATIBILITY ) ;
600+ expect ( oldFormat ) . toMatchObject ( {
601+ ranges : '{"{[2,4),[6,8)}"}'
602+ } ) ;
603+
604+ const newFormat = applyRowContext ( transformed , new CompatibilityContext ( CompatibilityEdition . SYNC_STREAMS ) ) ;
605+ expect ( newFormat ) . toMatchObject ( {
547606 ranges : JSON . stringify ( [
548607 [
549608 { lower : 2 , upper : 4 , lower_exclusive : 0 , upper_exclusive : 1 } ,
@@ -561,7 +620,7 @@ INSERT INTO test_data(id, time, timestamp, timestamptz) VALUES (1, '17:42:01.12'
561620 * Return all the inserts from the first transaction in the replication stream.
562621 */
563622async function getReplicationTx ( db : pgwire . PgClient , replicationStream : pgwire . ReplicationStream ) {
564- const typeCache = new PostgresTypeCache ( db ) ;
623+ const typeCache = new PostgresTypeCache ( db , ( ) => getServerVersion ( db ) ) ;
565624 await typeCache . fetchTypesForSchema ( ) ;
566625
567626 let transformed : SqliteInputRow [ ] = [ ] ;
0 commit comments