@@ -8,13 +8,13 @@ use std::ffi::{c_char, c_int, c_void, CStr, CString};
88use std:: ptr;
99use std:: slice;
1010use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
11- use std:: sync:: { Arc , OnceLock } ;
11+ use std:: sync:: { Arc , Mutex , OnceLock } ;
1212
1313use libsqlite3_sys:: * ;
1414use tokio:: runtime:: Handle ;
1515
1616use crate :: kv;
17- use crate :: sqlite_kv:: { KvGetResult , SqliteKv } ;
17+ use crate :: sqlite_kv:: { KvGetResult , SqliteKv , SqliteKvError } ;
1818
1919// MARK: Panic Guard
2020
@@ -158,12 +158,62 @@ struct VfsContext {
158158 actor_id : String ,
159159 main_file_name : String ,
160160 read_cache_enabled : bool ,
161+ last_error : Mutex < Option < String > > ,
161162 rt_handle : Handle ,
162163 io_methods : Box < sqlite3_io_methods > ,
163164 vfs_metrics : Arc < VfsMetrics > ,
164165}
165166
166167impl VfsContext {
168+ fn clear_last_error ( & self ) {
169+ match self . last_error . lock ( ) {
170+ Ok ( mut last_error) => {
171+ * last_error = None ;
172+ }
173+ Err ( err) => {
174+ tracing:: warn!( %err, "native sqlite last_error mutex poisoned" ) ;
175+ }
176+ }
177+ }
178+
179+ fn set_last_error ( & self , message : String ) {
180+ match self . last_error . lock ( ) {
181+ Ok ( mut last_error) => {
182+ * last_error = Some ( message) ;
183+ }
184+ Err ( err) => {
185+ tracing:: warn!( %err, "native sqlite last_error mutex poisoned" ) ;
186+ }
187+ }
188+ }
189+
190+ fn clone_last_error ( & self ) -> Option < String > {
191+ match self . last_error . lock ( ) {
192+ Ok ( last_error) => last_error. clone ( ) ,
193+ Err ( err) => {
194+ tracing:: warn!( %err, "native sqlite last_error mutex poisoned" ) ;
195+ None
196+ }
197+ }
198+ }
199+
200+ fn take_last_error ( & self ) -> Option < String > {
201+ match self . last_error . lock ( ) {
202+ Ok ( mut last_error) => last_error. take ( ) ,
203+ Err ( err) => {
204+ tracing:: warn!( %err, "native sqlite last_error mutex poisoned" ) ;
205+ None
206+ }
207+ }
208+ }
209+
210+ fn report_kv_error ( & self , err : SqliteKvError ) -> String {
211+ let message = err. to_string ( ) ;
212+ self . set_last_error ( message. clone ( ) ) ;
213+ self . kv . on_error ( & self . actor_id , & err) ;
214+ message
215+ }
216+
167217 fn resolve_file_tag ( & self , path : & str ) -> Option < u8 > {
168218 if path == self . main_file_name {
169219 return Some ( kv:: FILE_TAG_MAIN ) ;
@@ -187,7 +237,10 @@ impl VfsContext {
187237 let result = self
188238 . rt_handle
189239 . block_on ( self . kv . batch_get ( & self . actor_id , keys) )
190- . map_err ( |e| e. to_string ( ) ) ;
240+ . map_err ( |err| self . report_kv_error ( err) ) ;
241+ if result. is_ok ( ) {
242+ self . clear_last_error ( ) ;
243+ }
191244 let elapsed = start. elapsed ( ) ;
192245 tracing:: debug!(
193246 op = %format_args!( "get({key_count}keys)" ) ,
@@ -203,7 +256,10 @@ impl VfsContext {
203256 let result = self
204257 . rt_handle
205258 . block_on ( self . kv . batch_put ( & self . actor_id , keys, values) )
206- . map_err ( |e| e. to_string ( ) ) ;
259+ . map_err ( |err| self . report_kv_error ( err) ) ;
260+ if result. is_ok ( ) {
261+ self . clear_last_error ( ) ;
262+ }
207263 let elapsed = start. elapsed ( ) ;
208264 tracing:: debug!(
209265 op = %format_args!( "put({key_count}keys)" ) ,
@@ -219,7 +275,10 @@ impl VfsContext {
219275 let result = self
220276 . rt_handle
221277 . block_on ( self . kv . batch_delete ( & self . actor_id , keys) )
222- . map_err ( |e| e. to_string ( ) ) ;
278+ . map_err ( |err| self . report_kv_error ( err) ) ;
279+ if result. is_ok ( ) {
280+ self . clear_last_error ( ) ;
281+ }
223282 let elapsed = start. elapsed ( ) ;
224283 tracing:: debug!(
225284 op = %format_args!( "del({key_count}keys)" ) ,
@@ -234,7 +293,10 @@ impl VfsContext {
234293 let result = self
235294 . rt_handle
236295 . block_on ( self . kv . delete_range ( & self . actor_id , start, end) )
237- . map_err ( |e| e. to_string ( ) ) ;
296+ . map_err ( |err| self . report_kv_error ( err) ) ;
297+ if result. is_ok ( ) {
298+ self . clear_last_error ( ) ;
299+ }
238300 let elapsed = start_time. elapsed ( ) ;
239301 tracing:: debug!(
240302 op = "delRange" ,
@@ -574,7 +636,10 @@ unsafe extern "C" fn kv_io_write(
574636 let chunk_key = kv:: get_chunk_key( file. file_tag, chunk_idx as u32 ) . to_vec( ) ;
575637 let cached_chunk = if needs_existing && ctx. read_cache_enabled {
576638 let state = get_file_state( file. state) ;
577- state. read_cache. get( chunk_key. as_slice( ) ) . cloned( )
639+ state
640+ . read_cache
641+ . as_ref( )
642+ . and_then( |read_cache| read_cache. get( chunk_key. as_slice( ) ) . cloned( ) )
578643 } else {
579644 None
580645 } ;
@@ -616,7 +681,7 @@ unsafe extern "C" fn kv_io_write(
616681 let existing_chunk = plan. cached_chunk. as_deref( ) . or_else( || {
617682 plan. existing_chunk_index
618683 . and_then( |idx| existing_chunks. get( idx) )
619- . and_then( |value| value. as_ref ( ) )
684+ . and_then( |value| value. as_deref ( ) )
620685 } ) ;
621686
622687 let mut new_chunk = if let Some ( existing_chunk) = existing_chunk {
@@ -1164,11 +1229,30 @@ unsafe extern "C" fn kv_vfs_current_time(_p_vfs: *mut sqlite3_vfs, p_time_out: *
11641229}
11651230
11661231unsafe extern "C" fn kv_vfs_get_last_error (
1167- _p_vfs : * mut sqlite3_vfs ,
1168- _n_byte : c_int ,
1169- _z_err_msg : * mut c_char ,
1232+ p_vfs : * mut sqlite3_vfs ,
1233+ n_byte : c_int ,
1234+ z_err_msg : * mut c_char ,
11701235) -> c_int {
1171- vfs_catch_unwind ! ( SQLITE_IOERR , SQLITE_OK )
1236+ vfs_catch_unwind ! ( SQLITE_IOERR , {
1237+ if n_byte <= 0 || z_err_msg. is_null( ) {
1238+ return 0 ;
1239+ }
1240+
1241+ let ctx = get_vfs_ctx( p_vfs) ;
1242+ let last_error = ctx. clone_last_error( ) ;
1243+ let Some ( message) = last_error else {
1244+ * z_err_msg = 0 ;
1245+ return 0 ;
1246+ } ;
1247+
1248+ let bytes = message. as_bytes( ) ;
1249+ let max_len = ( n_byte as usize ) . saturating_sub( 1 ) ;
1250+ let copy_len = bytes. len( ) . min( max_len) ;
1251+ let dst = z_err_msg. cast:: <u8 >( ) ;
1252+ ptr:: copy_nonoverlapping( bytes. as_ptr( ) , dst, copy_len) ;
1253+ * dst. add( copy_len) = 0u8 ;
1254+ 0
1255+ } )
11721256}
11731257
11741258// MARK: KvVfs
@@ -1183,6 +1267,10 @@ unsafe impl Send for KvVfs {}
11831267unsafe impl Sync for KvVfs { }
11841268
11851269impl KvVfs {
1270+ fn take_last_kv_error ( & self ) -> Option < String > {
1271+ unsafe { ( * self . ctx_ptr ) . take_last_error ( ) }
1272+ }
1273+
11861274 pub fn register (
11871275 name : & str ,
11881276 kv : Arc < dyn SqliteKv > ,
@@ -1210,6 +1298,7 @@ impl KvVfs {
12101298 actor_id : actor_id. clone ( ) ,
12111299 main_file_name : actor_id,
12121300 read_cache_enabled : read_cache_enabled ( ) ,
1301+ last_error : Mutex :: new ( None ) ,
12131302 rt_handle,
12141303 io_methods : Box :: new ( io_methods) ,
12151304 vfs_metrics,
@@ -1279,6 +1368,10 @@ impl NativeDatabase {
12791368 pub fn as_ptr ( & self ) -> * mut sqlite3 {
12801369 self . db
12811370 }
1371+
1372+ pub fn take_last_kv_error ( & self ) -> Option < String > {
1373+ self . _vfs . take_last_kv_error ( )
1374+ }
12821375}
12831376
12841377impl Drop for NativeDatabase {
@@ -1291,6 +1384,18 @@ impl Drop for NativeDatabase {
12911384 }
12921385}
12931386
1387+ fn sqlite_error_message ( db : * mut sqlite3 ) -> String {
1388+ unsafe {
1389+ if db. is_null ( ) {
1390+ "unknown sqlite error" . to_string ( )
1391+ } else {
1392+ CStr :: from_ptr ( sqlite3_errmsg ( db) )
1393+ . to_string_lossy ( )
1394+ . into_owned ( )
1395+ }
1396+ }
1397+ }
1398+
12941399pub fn open_database ( vfs : KvVfs , file_name : & str ) -> Result < NativeDatabase , String > {
12951400 let c_name = CString :: new ( file_name) . map_err ( |err| err. to_string ( ) ) ?;
12961401 let mut db: * mut sqlite3 = ptr:: null_mut ( ) ;
@@ -1304,12 +1409,13 @@ pub fn open_database(vfs: KvVfs, file_name: &str) -> Result<NativeDatabase, Stri
13041409 )
13051410 } ;
13061411 if rc != SQLITE_OK {
1412+ let message = sqlite_error_message ( db) ;
13071413 if !db. is_null ( ) {
13081414 unsafe {
13091415 sqlite3_close ( db) ;
13101416 }
13111417 }
1312- return Err ( format ! ( "sqlite3_open_v2 failed with code {rc}" ) ) ;
1418+ return Err ( format ! ( "sqlite3_open_v2 failed with code {rc}: {message} " ) ) ;
13131419 }
13141420
13151421 for pragma in & [
@@ -1324,10 +1430,11 @@ pub fn open_database(vfs: KvVfs, file_name: &str) -> Result<NativeDatabase, Stri
13241430 let rc =
13251431 unsafe { sqlite3_exec ( db, c_sql. as_ptr ( ) , None , ptr:: null_mut ( ) , ptr:: null_mut ( ) ) } ;
13261432 if rc != SQLITE_OK {
1433+ let message = sqlite_error_message ( db) ;
13271434 unsafe {
13281435 sqlite3_close ( db) ;
13291436 }
1330- return Err ( format ! ( "{pragma} failed with code {rc}" ) ) ;
1437+ return Err ( format ! ( "{pragma} failed with code {rc}: {message} " ) ) ;
13311438 }
13321439 }
13331440
0 commit comments