Skip to content

Commit 077d87a

Browse files
committed
feat: add timetravel by version number
1 parent 4a13e74 commit 077d87a

File tree

1 file changed

+54
-8
lines changed

1 file changed

+54
-8
lines changed

ffi/src/lib.rs

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use url::Url;
1313

1414
use delta_kernel::schema::Schema;
1515
use delta_kernel::snapshot::Snapshot;
16+
use delta_kernel::Version;
1617
use delta_kernel::{DeltaResult, Engine, EngineData, Table};
1718
use delta_kernel_ffi_macros::handle_descriptor;
1819

@@ -568,14 +569,31 @@ pub unsafe extern "C" fn snapshot(
568569
) -> ExternResult<Handle<SharedSnapshot>> {
569570
let url = unsafe { unwrap_and_parse_path_as_url(path) };
570571
let engine = unsafe { engine.as_ref() };
571-
snapshot_impl(url, engine).into_extern_result(&engine)
572+
snapshot_impl(url, engine, None).into_extern_result(&engine)
573+
}
574+
575+
/// Get the snapshot from the specified table at a specific version
576+
///
577+
/// # Safety
578+
///
579+
/// Caller is responsible for passing valid handles and path pointer.
580+
#[no_mangle]
581+
pub unsafe extern "C" fn snapshot_at_version(
582+
path: KernelStringSlice,
583+
engine: Handle<SharedExternEngine>,
584+
version: Version,
585+
) -> ExternResult<Handle<SharedSnapshot>> {
586+
let url = unsafe { unwrap_and_parse_path_as_url(path) };
587+
let engine = unsafe { engine.as_ref() };
588+
snapshot_impl(url, engine, version.into()).into_extern_result(&engine)
572589
}
573590

574591
fn snapshot_impl(
575592
url: DeltaResult<Url>,
576593
extern_engine: &dyn ExternEngine,
594+
version: Option<Version>,
577595
) -> DeltaResult<Handle<SharedSnapshot>> {
578-
let snapshot = Snapshot::try_new(url?, extern_engine.engine().as_ref(), None)?;
596+
let snapshot = Snapshot::try_new(url?, extern_engine.engine().as_ref(), version)?;
579597
Ok(Arc::new(snapshot).into())
580598
}
581599

@@ -776,6 +794,12 @@ mod tests {
776794
Some(ptr)
777795
}
778796

797+
// helper to recover an error from the above
798+
fn recover_error(ptr: *mut EngineError) -> EngineError {
799+
let ptr = ptr.cast();
800+
*unsafe { Box::from_raw(ptr) }
801+
}
802+
779803
// helper to recover a string from the above
780804
fn recover_string(ptr: NonNull<c_void>) -> String {
781805
let ptr = ptr.as_ptr().cast();
@@ -834,18 +858,40 @@ mod tests {
834858
let engine = engine_to_handle(Arc::new(engine), allocate_err);
835859
let path = "memory:///";
836860

837-
let snapshot =
861+
// Test getting latest snapshot
862+
let snapshot1 =
838863
unsafe { ok_or_panic(snapshot(kernel_string_slice!(path), engine.shallow_copy())) };
864+
let version1 = unsafe { version(snapshot1.shallow_copy()) };
865+
assert_eq!(version1, 0);
866+
867+
// Test getting snapshot at version
868+
let snapshot2 = unsafe {
869+
ok_or_panic(snapshot_at_version(
870+
kernel_string_slice!(path),
871+
engine.shallow_copy(),
872+
0,
873+
))
874+
};
875+
let version2 = unsafe { version(snapshot2.shallow_copy()) };
876+
assert_eq!(version2, 0);
877+
878+
// Test getting non-existent snapshot
879+
let snapshot_at_non_existent_version =
880+
unsafe { snapshot_at_version(kernel_string_slice!(path), engine.shallow_copy(), 1) };
881+
assert!(snapshot_at_non_existent_version.is_err());
882+
883+
// Avoid leaking the error by recovering it
884+
if let ExternResult::Err(e) = snapshot_at_non_existent_version {
885+
recover_error(e);
886+
}
839887

840-
let version = unsafe { version(snapshot.shallow_copy()) };
841-
assert_eq!(version, 0);
842-
843-
let table_root = unsafe { snapshot_table_root(snapshot.shallow_copy(), allocate_str) };
888+
let table_root = unsafe { snapshot_table_root(snapshot1.shallow_copy(), allocate_str) };
844889
assert!(table_root.is_some());
845890
let s = recover_string(table_root.unwrap());
846891
assert_eq!(&s, path);
847892

848-
unsafe { free_snapshot(snapshot) }
893+
unsafe { free_snapshot(snapshot1) }
894+
unsafe { free_snapshot(snapshot2) }
849895
unsafe { free_engine(engine) }
850896
Ok(())
851897
}

0 commit comments

Comments
 (0)