1use std::collections::HashSet;
19
20use crate::spec::{SnapshotRef, TableMetadataRef};
21
22struct Ancestors {
23 next: Option<SnapshotRef>,
24 get_snapshot: Box<dyn Fn(i64) -> Option<SnapshotRef> + Send>,
25 visited: HashSet<i64>,
26}
27
28impl Iterator for Ancestors {
29 type Item = SnapshotRef;
30
31 fn next(&mut self) -> Option<Self::Item> {
32 let snapshot = self.next.take()?;
33 if !self.visited.insert(snapshot.snapshot_id()) {
35 return None;
36 }
37 self.next = snapshot
38 .parent_snapshot_id()
39 .and_then(|id| (self.get_snapshot)(id));
40 Some(snapshot)
41 }
42}
43
44pub fn ancestors_of(
49 table_metadata: &TableMetadataRef,
50 snapshot_id: i64,
51) -> impl Iterator<Item = SnapshotRef> + Send {
52 let initial = table_metadata.snapshot_by_id(snapshot_id).cloned();
53 let table_metadata = table_metadata.clone();
54 Ancestors {
55 next: initial,
56 get_snapshot: Box::new(move |id| table_metadata.snapshot_by_id(id).cloned()),
57 visited: HashSet::new(),
58 }
59}
60
61pub fn ancestors_between(
68 table_metadata: &TableMetadataRef,
69 latest_snapshot_id: i64,
70 oldest_snapshot_id: Option<i64>,
71) -> impl Iterator<Item = SnapshotRef> + Send {
72 ancestors_of(table_metadata, latest_snapshot_id).take_while(move |snapshot| {
73 oldest_snapshot_id
74 .map(|id| snapshot.snapshot_id() != id)
75 .unwrap_or(true)
76 })
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::scan::tests::TableTestFixture;
83
84 const S1: i64 = 3051729675574597004;
86 const S2: i64 = 3055729675574597004;
87 const S3: i64 = 3056729675574597004;
88 const S4: i64 = 3057729675574597004;
89 const S5: i64 = 3059729675574597004;
90
91 fn metadata() -> TableMetadataRef {
92 let fixture = TableTestFixture::new_with_deep_history();
93 std::sync::Arc::new(fixture.table.metadata().clone())
94 }
95
96 #[test]
99 fn test_ancestors_of_nonexistent_snapshot_returns_empty() {
100 let meta = metadata();
101 let ids: Vec<i64> = ancestors_of(&meta, 999).map(|s| s.snapshot_id()).collect();
102 assert!(ids.is_empty());
103 }
104
105 #[test]
106 fn test_ancestors_of_root_returns_only_root() {
107 let meta = metadata();
108 let ids: Vec<i64> = ancestors_of(&meta, S1).map(|s| s.snapshot_id()).collect();
109 assert_eq!(ids, vec![S1]);
110 }
111
112 #[test]
113 fn test_ancestors_of_leaf_returns_full_chain() {
114 let meta = metadata();
115 let ids: Vec<i64> = ancestors_of(&meta, S5).map(|s| s.snapshot_id()).collect();
116 assert_eq!(ids, vec![S5, S4, S3, S2, S1]);
117 }
118
119 #[test]
120 fn test_ancestors_of_mid_chain_returns_partial_chain() {
121 let meta = metadata();
122 let ids: Vec<i64> = ancestors_of(&meta, S3).map(|s| s.snapshot_id()).collect();
123 assert_eq!(ids, vec![S3, S2, S1]);
124 }
125
126 #[test]
127 fn test_ancestors_of_second_snapshot() {
128 let meta = metadata();
129 let ids: Vec<i64> = ancestors_of(&meta, S2).map(|s| s.snapshot_id()).collect();
130 assert_eq!(ids, vec![S2, S1]);
131 }
132
133 #[test]
136 fn test_ancestors_between_same_id_returns_empty() {
137 let meta = metadata();
138 let ids: Vec<i64> = ancestors_between(&meta, S3, Some(S3))
139 .map(|s| s.snapshot_id())
140 .collect();
141 assert!(ids.is_empty());
142 }
143
144 #[test]
145 fn test_ancestors_between_no_oldest_returns_all_ancestors() {
146 let meta = metadata();
147 let ids: Vec<i64> = ancestors_between(&meta, S5, None)
148 .map(|s| s.snapshot_id())
149 .collect();
150 assert_eq!(ids, vec![S5, S4, S3, S2, S1]);
151 }
152
153 #[test]
154 fn test_ancestors_between_excludes_oldest_snapshot() {
155 let meta = metadata();
156 let ids: Vec<i64> = ancestors_between(&meta, S5, Some(S2))
158 .map(|s| s.snapshot_id())
159 .collect();
160 assert_eq!(ids, vec![S5, S4, S3]);
161 }
162
163 #[test]
164 fn test_ancestors_between_adjacent_snapshots() {
165 let meta = metadata();
166 let ids: Vec<i64> = ancestors_between(&meta, S3, Some(S2))
168 .map(|s| s.snapshot_id())
169 .collect();
170 assert_eq!(ids, vec![S3]);
171 }
172
173 #[test]
174 fn test_ancestors_between_leaf_and_root() {
175 let meta = metadata();
176 let ids: Vec<i64> = ancestors_between(&meta, S5, Some(S1))
178 .map(|s| s.snapshot_id())
179 .collect();
180 assert_eq!(ids, vec![S5, S4, S3, S2]);
181 }
182
183 #[test]
184 fn test_ancestors_between_nonexistent_oldest_returns_full_chain() {
185 let meta = metadata();
186 let ids: Vec<i64> = ancestors_between(&meta, S5, Some(999))
188 .map(|s| s.snapshot_id())
189 .collect();
190 assert_eq!(ids, vec![S5, S4, S3, S2, S1]);
191 }
192
193 #[test]
194 fn test_ancestors_between_nonexistent_latest_returns_empty() {
195 let meta = metadata();
196 let ids: Vec<i64> = ancestors_between(&meta, 999, Some(S1))
197 .map(|s| s.snapshot_id())
198 .collect();
199 assert!(ids.is_empty());
200 }
201}