@@ -598,3 +598,64 @@ def test_push_chunked_scales_with_row_count_and_caps():
598598
599599def test_push_chunked_empty_source_pushes_nothing ():
600600 assert _count_pushes (0 , None , None ) == []
601+
602+
603+ @pytest .mark .network
604+ @pytest .mark .skipif (not os .environ .get ("OSMSG_PG_DSN" ), reason = "OSMSG_PG_DSN not set; live PG push not exercised" )
605+ def test_metadata_only_changeset_fills_stub_without_fk_abort (fresh_db ):
606+ """A long-open changeset's metadata arrives in a later, no-edit delta (its uid absent from this
607+ delta's changeset_stats). With history present the push takes the not_history branch, which must
608+ still carry that user, or the changesets FK aborts the whole push and the stub is stranded forever."""
609+ dsn = os .environ ["OSMSG_PG_DSN" ]
610+ safe_dsn = dsn .replace ("'" , "''" )
611+
612+ fresh_db .execute ("INSTALL postgres" )
613+ fresh_db .execute ("LOAD postgres" )
614+ fresh_db .execute (f"ATTACH '{ safe_dsn } ' AS pg_w (TYPE postgres)" )
615+ try :
616+ for stmt in (s .strip () for s in PG_SCHEMA .strip ().split (";" )):
617+ if stmt :
618+ fresh_db .execute (f"CALL postgres_execute('pg_w', $${ stmt } $$)" )
619+ for table in ("changeset_hashtag" , "changeset_stats" , "changesets" , "users" , "state" ):
620+ fresh_db .execute (f"CALL postgres_execute('pg_w', $$DELETE FROM { table } $$)" )
621+ # A seq_id=0 history row makes _pg_has_history() true -> the not_history branch runs.
622+ fresh_db .execute ("CALL postgres_execute('pg_w', $$INSERT INTO users VALUES (1, 'hist')$$)" )
623+ fresh_db .execute ("CALL postgres_execute('pg_w', $$INSERT INTO changesets (changeset_id, uid) VALUES (1, 1)$$)" )
624+ fresh_db .execute (
625+ "CALL postgres_execute('pg_w', $$INSERT INTO changeset_stats "
626+ "(changeset_id, seq_id, uid, nodes_created, nodes_modified, nodes_deleted, ways_created, "
627+ "ways_modified, ways_deleted, rels_created, rels_modified, rels_deleted, poi_created, poi_modified) "
628+ "VALUES (1, 0, 1, 0,0,0,0,0,0,0,0,0,0,0)$$)"
629+ )
630+ finally :
631+ fresh_db .execute ("DETACH pg_w" )
632+
633+ # Delta buffer: changeset 500 is metadata-only (uid 100, full metadata, NO stats this tick);
634+ # changeset 600 has live edits (uid 200). uid 100 is absent from this delta's changeset_stats.
635+ fresh_db .execute ("INSERT INTO users VALUES (100, 'stubuser'), (200, 'liveuser')" )
636+ fresh_db .execute (
637+ "INSERT INTO changesets VALUES "
638+ "(500, 100, '2026-08-04 02:01:51+00', ['#msf'], 'iD', NULL), "
639+ "(600, 200, '2026-08-04 03:00:00+00', ['#msf'], 'iD', NULL)"
640+ )
641+ fresh_db .execute ("INSERT INTO changeset_stats VALUES (600, 7228931, 200, 10,0,0,2,0,0,0,0,0,0,0, NULL)" )
642+
643+ to_psql (fresh_db , dsn ) # must not raise the changesets_uid_fkey violation
644+
645+ verifier = duckdb .connect (":memory:" )
646+ verifier .execute ("INSTALL postgres" )
647+ verifier .execute ("LOAD postgres" )
648+ verifier .execute (f"ATTACH '{ safe_dsn } ' AS pg_r (TYPE postgres, READ_ONLY)" )
649+ try :
650+ landed = verifier .execute ("SELECT created_at FROM pg_r.changesets WHERE changeset_id = 500" ).fetchone ()
651+ user_ok = verifier .execute ("SELECT count(*) FROM pg_r.users WHERE uid = 100" ).fetchone ()[0 ]
652+ hashtag_ok = verifier .execute (
653+ "SELECT count(*) FROM pg_r.changeset_hashtag WHERE changeset_id = 500 AND hashtag = '#msf'"
654+ ).fetchone ()[0 ]
655+ finally :
656+ verifier .execute ("DETACH pg_r" )
657+ verifier .close ()
658+
659+ assert landed is not None and landed [0 ] is not None # metadata-only changeset reached PG
660+ assert user_ok == 1 # its user was carried even without an edit row this tick
661+ assert hashtag_ok == 1 # and its #msf hashtag reached the index (the stub is no longer stranded)
0 commit comments