@@ -13,8 +13,7 @@ use url::Url;
1313
1414use delta_kernel:: schema:: Schema ;
1515use delta_kernel:: snapshot:: Snapshot ;
16- use delta_kernel:: Version ;
17- use delta_kernel:: { DeltaResult , Engine , EngineData } ;
16+ use delta_kernel:: { DeltaResult , Engine , EngineData , LogPath , Version } ;
1817use delta_kernel_ffi_macros:: handle_descriptor;
1918
2019// cbindgen doesn't understand our use of feature flags here, and by default it parses `mod handle`
@@ -43,6 +42,8 @@ use error::{AllocateError, AllocateErrorFn, ExternResult, IntoExternResult};
4342pub mod expressions;
4443#[ cfg( feature = "tracing" ) ]
4544pub mod ffi_tracing;
45+ #[ cfg( feature = "catalog-managed" ) ]
46+ pub mod log_path;
4647pub mod scan;
4748pub mod schema;
4849
@@ -596,10 +597,36 @@ pub unsafe extern "C" fn snapshot(
596597) -> ExternResult < Handle < SharedSnapshot > > {
597598 let url = unsafe { unwrap_and_parse_path_as_url ( path) } ;
598599 let engine = unsafe { engine. as_ref ( ) } ;
599- snapshot_impl ( url, engine, None ) . into_extern_result ( & engine)
600+ snapshot_impl ( url, engine, None , Vec :: new ( ) ) . into_extern_result ( & engine)
600601}
601602
602- /// Get the snapshot from the specified table at a specific version
603+ /// Get the latest snapshot from the specified table with optional log tail
604+ ///
605+ /// # Safety
606+ ///
607+ /// Caller is responsible for passing valid handles and path pointer.
608+ /// The log_paths array and its contents must remain valid for the duration of this call.
609+ #[ cfg( feature = "catalog-managed" ) ]
610+ #[ no_mangle]
611+ pub unsafe extern "C" fn snapshot_with_log_tail (
612+ path : KernelStringSlice ,
613+ engine : Handle < SharedExternEngine > ,
614+ log_paths : log_path:: LogPathArray ,
615+ ) -> ExternResult < Handle < SharedSnapshot > > {
616+ let url = unsafe { unwrap_and_parse_path_as_url ( path) } ;
617+ let engine_ref = unsafe { engine. as_ref ( ) } ;
618+
619+ // Convert LogPathArray to Vec<LogPath>
620+ let log_tail = match unsafe { log_paths. log_paths ( ) } {
621+ Ok ( paths) => paths,
622+ Err ( err) => return Err ( err) . into_extern_result ( & engine_ref) ,
623+ } ;
624+
625+ snapshot_impl ( url, engine_ref, None , log_tail) . into_extern_result ( & engine_ref)
626+ }
627+
628+ /// Get the snapshot from the specified table at a specific version. Note this is only safe for
629+ /// non-catalog-managed tables.
603630///
604631/// # Safety
605632///
@@ -612,21 +639,52 @@ pub unsafe extern "C" fn snapshot_at_version(
612639) -> ExternResult < Handle < SharedSnapshot > > {
613640 let url = unsafe { unwrap_and_parse_path_as_url ( path) } ;
614641 let engine = unsafe { engine. as_ref ( ) } ;
615- snapshot_impl ( url, engine, version. into ( ) ) . into_extern_result ( & engine)
642+ snapshot_impl ( url, engine, version. into ( ) , Vec :: new ( ) ) . into_extern_result ( & engine)
643+ }
644+
645+ /// Get the snapshot from the specified table at a specific version with log tail.
646+ ///
647+ /// # Safety
648+ ///
649+ /// Caller is responsible for passing valid handles and path pointer.
650+ /// The log_tail array and its contents must remain valid for the duration of this call.
651+ #[ cfg( feature = "catalog-managed" ) ]
652+ #[ no_mangle]
653+ pub unsafe extern "C" fn snapshot_at_version_with_log_tail (
654+ path : KernelStringSlice ,
655+ engine : Handle < SharedExternEngine > ,
656+ version : Version ,
657+ log_tail : log_path:: LogPathArray ,
658+ ) -> ExternResult < Handle < SharedSnapshot > > {
659+ let url = unsafe { unwrap_and_parse_path_as_url ( path) } ;
660+ let engine_ref = unsafe { engine. as_ref ( ) } ;
661+
662+ // Convert LogPathArray to Vec<LogPath>
663+ let log_tail = match unsafe { log_tail. log_paths ( ) } {
664+ Ok ( paths) => paths,
665+ Err ( err) => return Err ( err) . into_extern_result ( & engine_ref) ,
666+ } ;
667+
668+ snapshot_impl ( url, engine_ref, version. into ( ) , log_tail) . into_extern_result ( & engine_ref)
616669}
617670
618671fn snapshot_impl (
619672 url : DeltaResult < Url > ,
620673 extern_engine : & dyn ExternEngine ,
621674 version : Option < Version > ,
675+ #[ allow( unused_variables) ] log_tail : Vec < LogPath > ,
622676) -> DeltaResult < Handle < SharedSnapshot > > {
623- let builder = Snapshot :: builder_for ( url?) ;
624- let builder = if let Some ( v) = version {
625- // TODO: should we include a `with_version_opt` method for the builder?
626- builder. at_version ( v)
627- } else {
628- builder
629- } ;
677+ let mut builder = Snapshot :: builder_for ( url?) ;
678+
679+ if let Some ( v) = version {
680+ builder = builder. at_version ( v) ;
681+ }
682+
683+ #[ cfg( feature = "catalog-managed" ) ]
684+ if !log_tail. is_empty ( ) {
685+ builder = builder. with_log_tail ( log_tail) ;
686+ }
687+
630688 let snapshot = builder. build ( extern_engine. engine ( ) . as_ref ( ) ) ?;
631689 Ok ( snapshot. into ( ) )
632690}
@@ -964,4 +1022,65 @@ mod tests {
9641022 unsafe { free_engine ( engine) }
9651023 Ok ( ( ) )
9661024 }
1025+
1026+ #[ cfg( feature = "catalog-managed" ) ]
1027+ #[ tokio:: test]
1028+ async fn test_snapshot_log_tail ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
1029+ use test_utils:: add_staged_commit;
1030+ let storage = Arc :: new ( InMemory :: new ( ) ) ;
1031+ add_commit (
1032+ storage. as_ref ( ) ,
1033+ 0 ,
1034+ actions_to_string ( vec ! [ TestAction :: Metadata ] ) ,
1035+ )
1036+ . await ?;
1037+ let commit1 = add_staged_commit (
1038+ storage. as_ref ( ) ,
1039+ 1 ,
1040+ actions_to_string ( vec ! [ TestAction :: Add ( "path1" . into( ) ) ] ) ,
1041+ )
1042+ . await ?;
1043+ let engine = DefaultEngine :: new ( storage. clone ( ) , Arc :: new ( TokioBackgroundExecutor :: new ( ) ) ) ;
1044+ let engine = engine_to_handle ( Arc :: new ( engine) , allocate_err) ;
1045+ let path = "memory:///" ;
1046+
1047+ let commit1_path = format ! (
1048+ "{}_delta_log/_staged_commits/{}" ,
1049+ path,
1050+ commit1. filename( ) . unwrap( )
1051+ ) ;
1052+ let log_path =
1053+ log_path:: FfiLogPath :: new ( kernel_string_slice ! ( commit1_path) , 123456789 , 100 ) ;
1054+ let log_tail = [ log_path] ;
1055+ let log_tail = log_path:: LogPathArray {
1056+ ptr : log_tail. as_ptr ( ) ,
1057+ len : log_tail. len ( ) ,
1058+ } ;
1059+ let snapshot = unsafe {
1060+ ok_or_panic ( snapshot_with_log_tail (
1061+ kernel_string_slice ! ( path) ,
1062+ engine. shallow_copy ( ) ,
1063+ log_tail. clone ( ) ,
1064+ ) )
1065+ } ;
1066+ let snapshot_version = unsafe { version ( snapshot. shallow_copy ( ) ) } ;
1067+ assert_eq ! ( snapshot_version, 1 ) ;
1068+
1069+ // Test getting snapshot at version
1070+ let snapshot2 = unsafe {
1071+ ok_or_panic ( snapshot_at_version_with_log_tail (
1072+ kernel_string_slice ! ( path) ,
1073+ engine. shallow_copy ( ) ,
1074+ 1 ,
1075+ log_tail,
1076+ ) )
1077+ } ;
1078+ let snapshot_version = unsafe { version ( snapshot. shallow_copy ( ) ) } ;
1079+ assert_eq ! ( snapshot_version, 1 ) ;
1080+
1081+ unsafe { free_snapshot ( snapshot) }
1082+ unsafe { free_snapshot ( snapshot2) }
1083+ unsafe { free_engine ( engine) }
1084+ Ok ( ( ) )
1085+ }
9671086}
0 commit comments