1use std::collections::HashSet;
19
20use crate::spec::{SnapshotRef, TableMetadataRef};
21use crate::{Error, ErrorKind, Result};
22
23struct Ancestors {
24 next: Option<SnapshotRef>,
25 get_snapshot: Box<dyn Fn(i64) -> Option<SnapshotRef> + Send>,
26 visited: HashSet<i64>,
27}
28
29impl Iterator for Ancestors {
30 type Item = SnapshotRef;
31
32 fn next(&mut self) -> Option<Self::Item> {
33 let snapshot = self.next.take()?;
34 if !self.visited.insert(snapshot.snapshot_id()) {
36 return None;
37 }
38 self.next = snapshot
39 .parent_snapshot_id()
40 .and_then(|id| (self.get_snapshot)(id));
41 Some(snapshot)
42 }
43}
44
45pub fn ancestors_of(
50 table_metadata: &TableMetadataRef,
51 snapshot_id: i64,
52) -> impl Iterator<Item = SnapshotRef> + Send {
53 let initial = table_metadata.snapshot_by_id(snapshot_id).cloned();
54 let table_metadata = table_metadata.clone();
55 Ancestors {
56 next: initial,
57 get_snapshot: Box::new(move |id| table_metadata.snapshot_by_id(id).cloned()),
58 visited: HashSet::new(),
59 }
60}
61
62pub fn ancestors_between(
69 table_metadata: &TableMetadataRef,
70 latest_snapshot_id: i64,
71 oldest_snapshot_id: Option<i64>,
72) -> impl Iterator<Item = SnapshotRef> + Send {
73 ancestors_of(table_metadata, latest_snapshot_id).take_while(move |snapshot| {
74 oldest_snapshot_id
75 .map(|id| snapshot.snapshot_id() != id)
76 .unwrap_or(true)
77 })
78}
79
80pub fn snapshot_id_as_of_time(table_metadata: &TableMetadataRef, timestamp_ms: i64) -> Result<i64> {
112 table_metadata
113 .history()
114 .iter()
115 .enumerate()
116 .filter(|(_, entry)| entry.timestamp_ms() <= timestamp_ms)
117 .max_by(|(idx_a, a), (idx_b, b)| {
118 a.timestamp_ms()
119 .cmp(&b.timestamp_ms())
120 .then(idx_b.cmp(idx_a))
122 })
123 .map(|(_, entry)| entry.snapshot_id)
124 .ok_or_else(|| {
125 Error::new(
126 ErrorKind::DataInvalid,
127 format!("No snapshot history at or before timestamp {timestamp_ms} ms"),
128 )
129 })
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::spec::SnapshotLog;
136 use crate::test_utils::scan::TableTestFixture;
137
138 const S1: i64 = 3051729675574597004;
140 const S2: i64 = 3055729675574597004;
141 const S3: i64 = 3056729675574597004;
142 const S4: i64 = 3057729675574597004;
143 const S5: i64 = 3059729675574597004;
144
145 fn metadata() -> TableMetadataRef {
146 let fixture = TableTestFixture::new_with_deep_history();
147 std::sync::Arc::new(fixture.table.metadata().clone())
148 }
149
150 type History = [(i64, i64)];
151
152 fn metadata_with_history(history: &History) -> TableMetadataRef {
153 let mut metadata = metadata().as_ref().clone();
154 metadata.snapshot_log = history
155 .iter()
156 .map(|&(timestamp_ms, snapshot_id)| SnapshotLog {
157 timestamp_ms,
158 snapshot_id,
159 })
160 .collect();
161 metadata.into()
162 }
163
164 #[test]
165 fn test_snapshot_id_as_of_time() {
166 let cases: &[(&History, i64, i64)] = &[
167 (&[(1000, S1), (2000, S2)], 1000, S1),
168 (&[(1000, S1), (2000, S2)], 1500, S1),
169 (&[(1000, S1), (2000, S2)], 2000, S2),
170 (&[(1000, S1), (2000, S2)], 3000, S2),
171 (&[(1000, S1), (2000, S2), (3000, S1)], 3500, S1),
173 (&[(1000, S1), (2000, S2), (2000, S3)], 2000, S2),
175 (&[(1000, S1), (3000, S2), (2500, S3)], 3500, S2),
177 (&[(1000, S1), (3000, S2), (2500, S3)], 2600, S3),
178 (&[(-2000, S1), (-1000, S2)], -1500, S1),
179 (&[(i64::MIN, S1), (0, S2)], i64::MIN, S1),
181 (&[(0, S1), (i64::MAX, S2)], i64::MAX, S2),
182 ];
183 for &(history, timestamp_ms, expected) in cases {
184 let metadata = metadata_with_history(history);
185 assert_eq!(
186 snapshot_id_as_of_time(&metadata, timestamp_ms).unwrap(),
187 expected,
188 "timestamp {timestamp_ms}, history {history:?}"
189 );
190 }
191 }
192
193 #[test]
194 fn test_snapshot_id_as_of_time_without_history_at_timestamp() {
195 let cases: &[(&History, i64)] = &[
196 (&[], 1000),
197 (&[(1000, S1)], 999),
198 (&[(3000, S1)], 1500), ];
200 for &(history, timestamp_ms) in cases {
201 let metadata = metadata_with_history(history);
202 assert!(metadata.snapshot_by_id(S1).is_some());
203 let err = snapshot_id_as_of_time(&metadata, timestamp_ms).unwrap_err();
204 assert_eq!(err.kind(), ErrorKind::DataInvalid);
205 assert!(err.message().contains(×tamp_ms.to_string()));
206 }
207 }
208
209 #[test]
210 fn test_snapshot_id_as_of_time_ignores_snapshots_outside_main_history() {
211 let mut metadata = metadata().as_ref().clone();
212 metadata.snapshot_log.truncate(2);
213 assert!(metadata.snapshot_by_id(S5).is_some());
215 assert_eq!(
216 snapshot_id_as_of_time(&metadata.into(), i64::MAX).unwrap(),
217 S2
218 );
219 }
220
221 #[test]
224 fn test_ancestors_of_nonexistent_snapshot_returns_empty() {
225 let meta = metadata();
226 let ids: Vec<i64> = ancestors_of(&meta, 999).map(|s| s.snapshot_id()).collect();
227 assert!(ids.is_empty());
228 }
229
230 #[test]
231 fn test_ancestors_of_root_returns_only_root() {
232 let meta = metadata();
233 let ids: Vec<i64> = ancestors_of(&meta, S1).map(|s| s.snapshot_id()).collect();
234 assert_eq!(ids, vec![S1]);
235 }
236
237 #[test]
238 fn test_ancestors_of_leaf_returns_full_chain() {
239 let meta = metadata();
240 let ids: Vec<i64> = ancestors_of(&meta, S5).map(|s| s.snapshot_id()).collect();
241 assert_eq!(ids, vec![S5, S4, S3, S2, S1]);
242 }
243
244 #[test]
245 fn test_ancestors_of_mid_chain_returns_partial_chain() {
246 let meta = metadata();
247 let ids: Vec<i64> = ancestors_of(&meta, S3).map(|s| s.snapshot_id()).collect();
248 assert_eq!(ids, vec![S3, S2, S1]);
249 }
250
251 #[test]
252 fn test_ancestors_of_second_snapshot() {
253 let meta = metadata();
254 let ids: Vec<i64> = ancestors_of(&meta, S2).map(|s| s.snapshot_id()).collect();
255 assert_eq!(ids, vec![S2, S1]);
256 }
257
258 #[test]
261 fn test_ancestors_between_same_id_returns_empty() {
262 let meta = metadata();
263 let ids: Vec<i64> = ancestors_between(&meta, S3, Some(S3))
264 .map(|s| s.snapshot_id())
265 .collect();
266 assert!(ids.is_empty());
267 }
268
269 #[test]
270 fn test_ancestors_between_no_oldest_returns_all_ancestors() {
271 let meta = metadata();
272 let ids: Vec<i64> = ancestors_between(&meta, S5, None)
273 .map(|s| s.snapshot_id())
274 .collect();
275 assert_eq!(ids, vec![S5, S4, S3, S2, S1]);
276 }
277
278 #[test]
279 fn test_ancestors_between_excludes_oldest_snapshot() {
280 let meta = metadata();
281 let ids: Vec<i64> = ancestors_between(&meta, S5, Some(S2))
283 .map(|s| s.snapshot_id())
284 .collect();
285 assert_eq!(ids, vec![S5, S4, S3]);
286 }
287
288 #[test]
289 fn test_ancestors_between_adjacent_snapshots() {
290 let meta = metadata();
291 let ids: Vec<i64> = ancestors_between(&meta, S3, Some(S2))
293 .map(|s| s.snapshot_id())
294 .collect();
295 assert_eq!(ids, vec![S3]);
296 }
297
298 #[test]
299 fn test_ancestors_between_leaf_and_root() {
300 let meta = metadata();
301 let ids: Vec<i64> = ancestors_between(&meta, S5, Some(S1))
303 .map(|s| s.snapshot_id())
304 .collect();
305 assert_eq!(ids, vec![S5, S4, S3, S2]);
306 }
307
308 #[test]
309 fn test_ancestors_between_nonexistent_oldest_returns_full_chain() {
310 let meta = metadata();
311 let ids: Vec<i64> = ancestors_between(&meta, S5, Some(999))
313 .map(|s| s.snapshot_id())
314 .collect();
315 assert_eq!(ids, vec![S5, S4, S3, S2, S1]);
316 }
317
318 #[test]
319 fn test_ancestors_between_nonexistent_latest_returns_empty() {
320 let meta = metadata();
321 let ids: Vec<i64> = ancestors_between(&meta, 999, Some(S1))
322 .map(|s| s.snapshot_id())
323 .collect();
324 assert!(ids.is_empty());
325 }
326}