Skip to content

Commit 3b2b6a0

Browse files
committed
Add tests
1 parent dc56ff7 commit 3b2b6a0

1 file changed

Lines changed: 142 additions & 0 deletions

File tree

  • crates/iceberg/src/scan/incremental

‎crates/iceberg/src/scan/incremental/tests.rs‎

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,3 +943,145 @@ async fn test_incremental_fixture_complex() {
943943
])
944944
.await;
945945
}
946+
947+
#[tokio::test]
948+
async fn test_incremental_scan_edge_cases() {
949+
// This test covers several edge cases:
950+
// 1. Multiple data files added in separate snapshots
951+
// 2. Deletes spread across multiple data files
952+
// 3. Partial deletes from multiple files
953+
// 4. Cross-file delete operations in a single snapshot
954+
let fixture = IncrementalTestFixture::new(vec![
955+
// Snapshot 1: Empty starting point
956+
Operation::Add(vec![], "empty.parquet".to_string()),
957+
// Snapshot 2: Add file A with 3 rows
958+
Operation::Add(
959+
vec![(1, "a1".to_string()), (2, "a2".to_string()), (3, "a3".to_string())],
960+
"file-a.parquet".to_string(),
961+
),
962+
// Snapshot 3: Add file B with 4 rows
963+
Operation::Add(
964+
vec![
965+
(10, "b1".to_string()),
966+
(20, "b2".to_string()),
967+
(30, "b3".to_string()),
968+
(40, "b4".to_string()),
969+
],
970+
"file-b.parquet".to_string(),
971+
),
972+
// Snapshot 4: Partial delete from file A (delete middle row n=2)
973+
Operation::Delete(vec![(1, "file-a.parquet".to_string())]),
974+
// Snapshot 5: Partial delete from file B (delete first and last rows n=10,40)
975+
Operation::Delete(vec![
976+
(0, "file-b.parquet".to_string()),
977+
(3, "file-b.parquet".to_string()),
978+
]),
979+
// Snapshot 6: Add file C with 2 rows
980+
Operation::Add(
981+
vec![(100, "c1".to_string()), (200, "c2".to_string())],
982+
"file-c.parquet".to_string(),
983+
),
984+
// Snapshot 7: Delete from multiple files in one snapshot (cross-file deletes)
985+
Operation::Delete(vec![
986+
(0, "file-a.parquet".to_string()), // n=1
987+
(1, "file-b.parquet".to_string()), // n=20
988+
(0, "file-c.parquet".to_string()), // n=100
989+
]),
990+
])
991+
.await;
992+
993+
// Verify we have 7 snapshots
994+
let n_snapshots = fixture.table.metadata().snapshots().count();
995+
assert_eq!(n_snapshots, 7);
996+
997+
let file_a_path = format!("{}/data/file-a.parquet", fixture.table_location);
998+
let file_b_path = format!("{}/data/file-b.parquet", fixture.table_location);
999+
1000+
// Test 1: Scan from snapshot 1 to 4
1001+
// Should see: file-a (1,2,3), file-b (10,20,30,40) added, then (2) deleted from file-a
1002+
// BUT: The row n=2 was added AFTER snapshot 1, so it won't show as a delete!
1003+
// Net: appends (1,3) from file-a (n=2 added then deleted = net zero), (10,20,30,40) from file-b
1004+
// No deletes (n=2 was added and deleted between snapshots 1 and 4)
1005+
fixture
1006+
.verify_incremental_scan(
1007+
1,
1008+
4,
1009+
vec![
1010+
(1, "a1"),
1011+
(3, "a3"),
1012+
(10, "b1"),
1013+
(20, "b2"),
1014+
(30, "b3"),
1015+
(40, "b4"),
1016+
],
1017+
vec![], // No deletes - n=2 was added and deleted between snapshots
1018+
)
1019+
.await;
1020+
1021+
// Test 2: Scan from snapshot 4 to 6
1022+
// Snapshot 4: has file-a (1,3) and file-b (10,20,30,40)
1023+
// Snapshot 5: deletes positions 0,3 from file-b (n=10,40)
1024+
// Snapshot 6: adds file-c (100,200)
1025+
// Net: appends (100,200) from file-c; deletes pos 0,3 from file-b
1026+
fixture
1027+
.verify_incremental_scan(
1028+
4,
1029+
6,
1030+
vec![(100, "c1"), (200, "c2")],
1031+
vec![
1032+
(0, file_b_path.as_str()), // n=10
1033+
(3, file_b_path.as_str()), // n=40
1034+
],
1035+
)
1036+
.await;
1037+
1038+
// Test 3: Scan from snapshot 2 to 7
1039+
// This tests the full lifecycle: multiple adds, partial deletes, more adds, cross-file deletes
1040+
// Starting at snapshot 2: file-a (1,2,3) exists
1041+
// File-b is added in snapshot 3 (after snapshot 2)
1042+
// File-c is added in snapshot 6 (after snapshot 2)
1043+
// By snapshot 7: file-a has (3) at position 2, file-b has (30), file-c has (200)
1044+
//
1045+
// Net appends: file-b (30) and file-c (200) were added after snapshot 2
1046+
// Net deletes: positions 0,1 from file-a (n=1,2) existed at snapshot 2 and were deleted
1047+
// Note: (3) from file-a already existed at snapshot 2, so it's not a net append!
1048+
fixture
1049+
.verify_incremental_scan(
1050+
2,
1051+
7,
1052+
vec![(30, "b3"), (200, "c2")],
1053+
vec![
1054+
(0, file_a_path.as_str()), // n=1
1055+
(1, file_a_path.as_str()), // n=2
1056+
],
1057+
)
1058+
.await;
1059+
1060+
// Test 4: Scan from snapshot 5 to 6
1061+
// Simple test: just adding a new file
1062+
// Snapshot 5 state: file-a (1,3), file-b (20,30)
1063+
// Snapshot 6: adds file-c (100,200)
1064+
// Net: appends (100,200) from file-c, no deletes
1065+
fixture
1066+
.verify_incremental_scan(
1067+
5,
1068+
6,
1069+
vec![(100, "c1"), (200, "c2")],
1070+
vec![],
1071+
)
1072+
.await;
1073+
1074+
// Test 5: Scan from snapshot 3 to 4
1075+
// Tests a single delete operation
1076+
// State at 3: file-a (1,2,3), file-b (10,20,30,40)
1077+
// State at 4: file-a (1,3), file-b (10,20,30,40)
1078+
// Net: no appends, 1 delete (position 1, n=2) from file-a
1079+
fixture
1080+
.verify_incremental_scan(
1081+
3,
1082+
4,
1083+
vec![],
1084+
vec![(1, file_a_path.as_str())], // n=2
1085+
)
1086+
.await;
1087+
}

0 commit comments

Comments
 (0)