@@ -417,7 +417,6 @@ def _simple_data(num_rows: int = 2) -> pa.Table:
417417
418418
419419def test_replace_table_preserves_uuid_and_clears_current_snapshot (catalog : Catalog , test_table_identifier : Identifier ) -> None :
420- """Replace preserves table_uuid and snapshot history, but clears the main ref."""
421420 _create_simple_table (catalog , test_table_identifier )
422421 original = catalog .load_table (test_table_identifier )
423422 original .append (_simple_data ())
@@ -441,9 +440,7 @@ def test_replace_table_preserves_uuid_and_clears_current_snapshot(catalog: Catal
441440@pytest .mark .parametrize (
442441 "extra_fields, expected_schema_ids, expected_current, expected_fields, expected_last_col_id" ,
443442 [
444- # Identical to the existing schema → reuse the same schema_id, no new schema appended.
445443 ([], [0 ], 0 , {"id" : 1 , "data" : 2 }, 2 ),
446- # Extra column → append a new schema; existing IDs preserved by name, new one above last_column_id.
447444 (
448445 [NestedField (field_id = 99 , name = "extra" , field_type = BooleanType (), required = False )],
449446 [0 , 1 ],
@@ -463,7 +460,6 @@ def test_replace_table_schema_id_reuse(
463460 expected_fields : dict [str , int ],
464461 expected_last_col_id : int ,
465462) -> None :
466- """A structurally identical schema reuses its schema_id; an extended schema adds a new one."""
467463 _ , base_schema = _create_simple_table (catalog , test_table_identifier )
468464 new_schema = Schema (* base_schema .fields , * extra_fields )
469465 replaced = catalog .replace_table (test_table_identifier , schema = new_schema )
@@ -475,7 +471,6 @@ def test_replace_table_schema_id_reuse(
475471
476472
477473def test_replace_table_preserves_identifier_field_ids (catalog : Catalog , test_table_identifier : Identifier ) -> None :
478- """Identifier-field IDs on the new schema are honored after reuse-by-name."""
479474 schema = Schema (
480475 NestedField (field_id = 1 , name = "id" , field_type = LongType (), required = True ),
481476 NestedField (field_id = 2 , name = "data" , field_type = StringType (), required = False ),
@@ -493,8 +488,6 @@ def test_replace_table_preserves_identifier_field_ids(catalog: Catalog, test_tab
493488
494489
495490def test_replace_table_drops_identifier_field (catalog : Catalog , test_table_identifier : Identifier ) -> None :
496- """Replacing with a schema that has no `identifier_field_ids` clears them on the table —
497- the previous identifier set is not silently carried forward."""
498491 schema_with_id = Schema (
499492 NestedField (field_id = 1 , name = "id" , field_type = LongType (), required = True ),
500493 NestedField (field_id = 2 , name = "data" , field_type = StringType (), required = False ),
@@ -510,7 +503,6 @@ def test_replace_table_drops_identifier_field(catalog: Catalog, test_table_ident
510503
511504
512505def test_replace_table_reuses_partition_spec_id (catalog : Catalog , test_table_identifier : Identifier ) -> None :
513- """An identical partition spec reuses its spec_id rather than appending a new one."""
514506 spec = PartitionSpec (PartitionField (source_id = 1 , field_id = 1000 , name = "id_part" , transform = IdentityTransform ()))
515507 _ , schema = _create_simple_table (catalog , test_table_identifier , partition_spec = spec )
516508 replaced = catalog .replace_table (test_table_identifier , schema = schema , partition_spec = spec )
@@ -519,30 +511,23 @@ def test_replace_table_reuses_partition_spec_id(catalog: Catalog, test_table_ide
519511
520512
521513def test_replace_table_with_sort_order_changes (catalog : Catalog , test_table_identifier : Identifier ) -> None :
522- """Replace can change the sort order. The new sort order is appended to the history and
523- becomes the default; a follow-up replace back to a sort already in history reuses its
524- order_id rather than appending a duplicate."""
525514 _ , schema = _create_simple_table (catalog , test_table_identifier )
526515 sort = SortOrder (SortField (source_id = 1 , transform = IdentityTransform (), direction = SortDirection .ASC ))
527516
528- # unsorted → sorted: a new order is added and becomes the default.
529517 sorted_table = catalog .replace_table (test_table_identifier , schema = schema , sort_order = sort )
530518 assert sorted_table .sort_order ().fields == sort .fields
531519 sorted_order_id = sorted_table .metadata .default_sort_order_id
532520 assert sorted_order_id != 0
533521
534- # sorted → unsorted: reuses the unsorted order_id 0 from history.
535522 unsorted_table = catalog .replace_table (test_table_identifier , schema = schema )
536523 assert unsorted_table .sort_order ().is_unsorted
537524 assert unsorted_table .metadata .default_sort_order_id == 0
538525
539- # unsorted → original sorted form: reuses the existing sorted order_id from history.
540526 replayed = catalog .replace_table (test_table_identifier , schema = schema , sort_order = sort )
541527 assert replayed .metadata .default_sort_order_id == sorted_order_id
542528
543529
544530def test_replace_table_inherits_existing_location (catalog : Catalog , test_table_identifier : Identifier ) -> None :
545- """`location=None` keeps the existing table's location."""
546531 _ , schema = _create_simple_table (catalog , test_table_identifier )
547532 existing = catalog .load_table (test_table_identifier ).metadata .location
548533 replaced = catalog .replace_table (test_table_identifier , schema = schema )
@@ -553,7 +538,6 @@ def test_replace_table_inherits_existing_location(catalog: Catalog, test_table_i
553538def test_replace_table_uses_explicit_location (
554539 catalog : Catalog , test_table_identifier : Identifier , tmp_path : Path , trailing_slash : bool
555540) -> None :
556- """An explicit `location` is used verbatim; trailing slash is stripped."""
557541 _ , schema = _create_simple_table (catalog , test_table_identifier )
558542 bare = f"file://{ tmp_path } /relocated"
559543 arg = bare + "/" if trailing_slash else bare
@@ -564,7 +548,6 @@ def test_replace_table_uses_explicit_location(
564548def test_replace_table_merges_properties_with_overrides_and_additions (
565549 catalog : Catalog , test_table_identifier : Identifier
566550) -> None :
567- """Properties are merged onto existing: new values override, untouched keys are preserved."""
568551 schema = Schema (NestedField (field_id = 1 , name = "id" , field_type = LongType (), required = False ))
569552 _create_simple_table (catalog , test_table_identifier , schema = schema , properties = {"keep" : "yes" , "override" : "old" })
570553 replaced = catalog .replace_table (test_table_identifier , schema = schema , properties = {"override" : "new" , "new_key" : "v" })
@@ -574,7 +557,6 @@ def test_replace_table_merges_properties_with_overrides_and_additions(
574557
575558
576559def test_replace_table_upgrades_format_version (catalog : Catalog , test_table_identifier : Identifier ) -> None :
577- """`properties={'format-version': '2'}` on a v1 table emits an UpgradeFormatVersion update."""
578560 _ , schema = _create_simple_table (catalog , test_table_identifier , format_version = 1 )
579561 assert catalog .load_table (test_table_identifier ).format_version == 1
580562 replaced = catalog .replace_table (test_table_identifier , schema = schema , properties = {"format-version" : "2" })
@@ -584,48 +566,40 @@ def test_replace_table_upgrades_format_version(catalog: Catalog, test_table_iden
584566
585567
586568def test_replace_table_rejects_format_version_downgrade (catalog : Catalog , test_table_identifier : Identifier ) -> None :
587- """A `format-version` lower than the existing one must be rejected to avoid silently
588- running the schema-conversion path with the wrong semantics."""
589569 _ , schema = _create_simple_table (catalog , test_table_identifier , format_version = 2 )
590570 with pytest .raises (ValueError , match = "Cannot downgrade format-version" ):
591571 catalog .replace_table (test_table_identifier , schema = schema , properties = {"format-version" : "1" })
592572
593573
594574def test_replace_table_v1_carries_forward_partition_fields_as_void (catalog : Catalog , test_table_identifier : Identifier ) -> None :
595- """v1 specs are append-only; dropped partition fields must be carried forward as VoidTransform."""
575+ """v1 specs are append-only; dropped partition fields are carried forward as VoidTransform."""
596576 spec = PartitionSpec (PartitionField (source_id = 1 , field_id = 1000 , name = "id_part" , transform = IdentityTransform ()))
597577 _ , schema = _create_simple_table (catalog , test_table_identifier , partition_spec = spec , format_version = 1 )
598578
599- # Replace with default unpartitioned spec — the old field must be preserved as void.
600579 replaced = catalog .replace_table (test_table_identifier , schema = schema )
601580 new_spec = replaced .spec ()
602581 void_field = next (f for f in new_spec .fields if f .field_id == 1000 )
603582 assert isinstance (void_field .transform , VoidTransform )
604583 assert void_field .source_id == 1
605- # Name is preserved when there's no collision in the new spec (the new spec is empty here).
606584 assert void_field .name == "id_part"
607585
608586
609587def test_replace_table_v2_does_not_carry_forward_void_field (catalog : Catalog , test_table_identifier : Identifier ) -> None :
610- """v2 specs are not append-only — a replace that drops a partition field does not
611- carry it forward (unlike v1). The new default spec contains only the new field(s)."""
588+ """v2 specs are not append-only — a dropped partition field is not carried forward (unlike v1)."""
612589 spec = PartitionSpec (PartitionField (source_id = 1 , field_id = 1000 , name = "id_part" , transform = IdentityTransform ()))
613590 _ , schema = _create_simple_table (catalog , test_table_identifier , partition_spec = spec , format_version = 2 )
614591
615- # Replace with default unpartitioned spec — the old field is gone from the default spec.
616592 replaced = catalog .replace_table (test_table_identifier , schema = schema )
617593 new_spec = replaced .spec ()
618594 assert new_spec .is_unpartitioned ()
619595 assert all (not isinstance (f .transform , VoidTransform ) for f in new_spec .fields )
620596
621597
622598def test_replace_after_format_version_upgrade (catalog : Catalog , test_table_identifier : Identifier ) -> None :
623- """A v1 table can be upgraded to v2 via replace and then re-replaced without issue."""
624599 _ , schema = _create_simple_table (catalog , test_table_identifier , format_version = 1 )
625600 upgraded = catalog .replace_table (test_table_identifier , schema = schema , properties = {"format-version" : "2" })
626601 assert upgraded .format_version == 2
627602
628- # Second replace on the now-v2 table should not re-trigger an upgrade or fail.
629603 new_schema = Schema (
630604 NestedField (field_id = 1 , name = "id" , field_type = LongType (), required = False ),
631605 NestedField (field_id = 2 , name = "data" , field_type = StringType (), required = False ),
@@ -643,7 +617,6 @@ def test_replace_table_raises_when_table_does_not_exist(catalog: Catalog, test_t
643617
644618
645619def test_replace_table_transaction_can_stage_additional_changes (catalog : Catalog , test_table_identifier : Identifier ) -> None :
646- """The transaction context lets callers stage extra updates (e.g. properties) before commit."""
647620 _ , schema = _create_simple_table (catalog , test_table_identifier )
648621 with catalog .replace_table_transaction (test_table_identifier , schema = schema ) as txn :
649622 txn .set_properties ({"staged" : "yes" })
@@ -652,12 +625,6 @@ def test_replace_table_transaction_can_stage_additional_changes(catalog: Catalog
652625
653626
654627def test_replace_table_transaction_with_write_atomic_rtas (catalog : Catalog , test_table_identifier : Identifier ) -> None :
655- """RTAS (Replace Table As Select): replace + write new data in one transaction.
656-
657- The new schema and new data must land atomically. After commit:
658- - the new snapshot is current (main ref restored by the fast_append),
659- - the new snapshot's parent is None (no lineage to the pre-replace data), and
660- - the pre-replace snapshot is preserved in history for time-travel."""
661628 _create_simple_table (catalog , test_table_identifier )
662629 catalog .load_table (test_table_identifier ).append (_simple_data (num_rows = 1 ))
663630 old_snapshot_id = catalog .load_table (test_table_identifier ).current_snapshot ().snapshot_id # type: ignore[union-attr]
@@ -680,9 +647,6 @@ def test_replace_table_transaction_with_write_atomic_rtas(catalog: Catalog, test
680647
681648
682649def test_replace_table_transaction_rolls_back_on_failure (catalog : Catalog , test_table_identifier : Identifier ) -> None :
683- """If the body of the transaction raises, no metadata change is committed.
684-
685- The table's UUID, schemas, current snapshot, and current schema id must all be unchanged."""
686650 _create_simple_table (catalog , test_table_identifier )
687651 catalog .load_table (test_table_identifier ).append (_simple_data ())
688652 before = catalog .load_table (test_table_identifier ).metadata
@@ -708,17 +672,13 @@ def run_failing_replace() -> None:
708672
709673
710674def test_concurrent_replace_table (catalog : Catalog , test_table_identifier : Identifier ) -> None :
711- """Two concurrent replace_table calls staged from the same base both adding a column
712- must fail on the second commit with an `assert-last-assigned-field-id` violation —
713- proving the `AssertLastAssignedFieldId` requirement actually guards against duplicate
714- field-id assignment under concurrent writers."""
675+ """`AssertLastAssignedFieldId` rejects the second of two replaces staged from the same base."""
715676 _create_simple_table (catalog , test_table_identifier )
716677 new_schema = Schema (
717678 NestedField (field_id = 1 , name = "id" , field_type = LongType (), required = False ),
718679 NestedField (field_id = 2 , name = "data" , field_type = StringType (), required = False ),
719680 NestedField (field_id = 3 , name = "extra" , field_type = BooleanType (), required = False ),
720681 )
721- # Both transactions build from the same base metadata with the same new schema.
722682 txn_a = catalog .replace_table_transaction (test_table_identifier , schema = new_schema )
723683 txn_b = catalog .replace_table_transaction (test_table_identifier , schema = new_schema )
724684
@@ -728,8 +688,6 @@ def test_concurrent_replace_table(catalog: Catalog, test_table_identifier: Ident
728688
729689
730690def test_replace_table_allows_subsequent_append (catalog : Catalog , test_table_identifier : Identifier ) -> None :
731- """After `replace_table` clears the current snapshot, a separate `append` produces a new
732- snapshot containing only the post-replace data — the pre-replace rows are not visible."""
733691 _ , schema = _create_simple_table (catalog , test_table_identifier )
734692 catalog .load_table (test_table_identifier ).append (_simple_data (num_rows = 3 ))
735693
0 commit comments