From c90f7b8222f093a70f286fb4139459fc5bb20df0 Mon Sep 17 00:00:00 2001 From: mazchew Date: Sun, 21 Jul 2024 16:41:23 +0800 Subject: [PATCH] add cpython compatibility for error codes and messages edits minor edit --- sqlite3/include/sqlite3.h | 12 ++++ sqlite3/src/lib.rs | 130 ++++++++++++++++++++++++++++++++++++-- sqlite3/src/util.rs | 13 ++++ 3 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 sqlite3/src/util.rs diff --git a/sqlite3/include/sqlite3.h b/sqlite3/include/sqlite3.h index 09d4283ae..9e69fb007 100644 --- a/sqlite3/include/sqlite3.h +++ b/sqlite3/include/sqlite3.h @@ -7,8 +7,12 @@ #define SQLITE_ERROR 1 +#define SQLITE_ABORT 4 + #define SQLITE_BUSY 5 +#define SQLITE_NOMEM 7 + #define SQLITE_NOTFOUND 14 #define SQLITE_MISUSE 21 @@ -17,6 +21,14 @@ #define SQLITE_DONE 101 +#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2 << 8)) + +#define SQLITE_STATE_OPEN 118 + +#define SQLITE_STATE_SICK 186 + +#define SQLITE_STATE_BUSY 109 + typedef struct sqlite3 sqlite3; typedef struct sqlite3_stmt sqlite3_stmt; diff --git a/sqlite3/src/lib.rs b/sqlite3/src/lib.rs index aad33b792..a5b307c37 100644 --- a/sqlite3/src/lib.rs +++ b/sqlite3/src/lib.rs @@ -8,20 +8,43 @@ use std::rc::Rc; pub const SQLITE_OK: ffi::c_int = 0; pub const SQLITE_ERROR: ffi::c_int = 1; +pub const SQLITE_ABORT: ffi::c_int = 4; pub const SQLITE_BUSY: ffi::c_int = 5; +pub const SQLITE_NOMEM: ffi::c_int = 7; pub const SQLITE_NOTFOUND: ffi::c_int = 14; pub const SQLITE_MISUSE: ffi::c_int = 21; pub const SQLITE_ROW: ffi::c_int = 100; pub const SQLITE_DONE: ffi::c_int = 101; +pub const SQLITE_ABORT_ROLLBACK: ffi::c_int = SQLITE_ABORT | (2 << 8); +pub const SQLITE_STATE_OPEN: u8 = 0x76; +pub const SQLITE_STATE_SICK: u8 = 0xba; +pub const SQLITE_STATE_BUSY: u8 = 0x6d; + +pub mod util; + +use util::sqlite3_safety_check_sick_or_ok; pub struct sqlite3 { pub(crate) _db: limbo_core::Database, pub(crate) conn: limbo_core::Connection, + pub(crate) err_code: ffi::c_int, + pub(crate) err_mask: ffi::c_int, + pub(crate) malloc_failed: bool, + pub(crate) e_open_state: u8, + pub(crate) p_err: *mut std::ffi::c_void, } impl sqlite3 { pub fn new(db: limbo_core::Database, conn: limbo_core::Connection) -> Self { - Self { _db: db, conn } + Self { + _db: db, + conn, + err_code: SQLITE_OK, + err_mask: 0xFFFFFFFFu32 as i32, + malloc_failed: false, + e_open_state: SQLITE_STATE_OPEN, + p_err: std::ptr::null_mut(), + } } } @@ -336,12 +359,20 @@ pub unsafe extern "C" fn sqlite3_free(_ptr: *mut std::ffi::c_void) { #[no_mangle] pub unsafe extern "C" fn sqlite3_errcode(_db: *mut sqlite3) -> ffi::c_int { - todo!(); + if !_db.is_null() && !sqlite3_safety_check_sick_or_ok(&*_db) { + return SQLITE_MISUSE; + } + + if _db.is_null() || (*_db).malloc_failed { + return SQLITE_NOMEM; + } + + (*_db).err_code & (*_db).err_mask } #[no_mangle] pub unsafe extern "C" fn sqlite3_errstr(_err: ffi::c_int) -> *const std::ffi::c_char { - todo!(); + sqlite3_errstr_impl(_err) } #[no_mangle] @@ -758,12 +789,45 @@ pub unsafe extern "C" fn sqlite3_create_window_function( #[no_mangle] pub unsafe extern "C" fn sqlite3_errmsg(_db: *mut sqlite3) -> *const std::ffi::c_char { - todo!(); + if _db.is_null() { + return sqlite3_errstr(SQLITE_NOMEM); + } + if !sqlite3_safety_check_sick_or_ok(&*_db) { + return sqlite3_errstr(SQLITE_MISUSE); + } + if (*_db).malloc_failed { + return sqlite3_errstr(SQLITE_NOMEM); + } + + let err_msg = if (*_db).err_code != SQLITE_OK { + if !(*_db).p_err.is_null() { + let cstr = (*_db).p_err as *const std::ffi::c_char; + cstr + } else { + std::ptr::null() + } + } else { + std::ptr::null() + }; + + if err_msg.is_null() { + return sqlite3_errstr((*_db).err_code); + } + + err_msg } #[no_mangle] pub unsafe extern "C" fn sqlite3_extended_errcode(_db: *mut sqlite3) -> ffi::c_int { - todo!(); + if !_db.is_null() && !sqlite3_safety_check_sick_or_ok(&*_db) { + return SQLITE_MISUSE; + } + + if _db.is_null() || (*_db).malloc_failed { + return SQLITE_NOMEM; + } + + (*_db).err_code & (*_db).err_mask } #[no_mangle] @@ -787,3 +851,59 @@ pub unsafe extern "C" fn sqlite3_libversion() -> *const std::ffi::c_char { pub unsafe extern "C" fn sqlite3_libversion_number() -> ffi::c_int { 3042000 } + +fn sqlite3_errstr_impl(rc: i32) -> *const std::ffi::c_char { + const ERROR_MESSAGES: [&str; 29] = [ + "not an error", // SQLITE_OK + "SQL logic error", // SQLITE_ERROR + "", // SQLITE_INTERNAL + "access permission denied", // SQLITE_PERM + "query aborted", // SQLITE_ABORT + "database is locked", // SQLITE_BUSY + "database table is locked", // SQLITE_LOCKED + "out of memory", // SQLITE_NOMEM + "attempt to write a readonly database", // SQLITE_READONLY + "interrupted", // SQLITE_INTERRUPT + "disk I/O error", // SQLITE_IOERR + "database disk image is malformed", // SQLITE_CORRUPT + "unknown operation", // SQLITE_NOTFOUND + "database or disk is full", // SQLITE_FULL + "unable to open database file", // SQLITE_CANTOPEN + "locking protocol", // SQLITE_PROTOCOL + "", // SQLITE_EMPTY + "database schema has changed", // SQLITE_SCHEMA + "string or blob too big", // SQLITE_TOOBIG + "constraint failed", // SQLITE_CONSTRAINT + "datatype mismatch", // SQLITE_MISMATCH + "bad parameter or other API misuse", // SQLITE_MISUSE + #[cfg(not(feature = "SQLITE_DISABLE_LFS"))] + "", // SQLITE_NOLFS + #[cfg(feature = "SQLITE_DISABLE_LFS")] + "large file support is disabled", // SQLITE_NOLFS + "authorization denied", // SQLITE_AUTH + "", // SQLITE_FORMAT + "column index out of range", // SQLITE_RANGE + "file is not a database", // SQLITE_NOTADB + "notification message", // SQLITE_NOTICE + "warning message", // SQLITE_WARNING + ]; + + const UNKNOWN_ERROR: &str = "unknown error"; + const ABORT_ROLLBACK: &str = "abort due to ROLLBACK"; + const ANOTHER_ROW_AVAILABLE: &str = "another row available"; + const NO_MORE_ROWS_AVAILABLE: &str = "no more rows available"; + + match rc { + SQLITE_ABORT_ROLLBACK => ABORT_ROLLBACK.as_ptr() as *const std::ffi::c_char, + SQLITE_ROW => ANOTHER_ROW_AVAILABLE.as_ptr() as *const std::ffi::c_char, + SQLITE_DONE => NO_MORE_ROWS_AVAILABLE.as_ptr() as *const std::ffi::c_char, + _ => { + let rc = rc & 0xff; + if rc >= 0 && rc < ERROR_MESSAGES.len() as i32 && !ERROR_MESSAGES[rc as usize].is_empty() { + ERROR_MESSAGES[rc as usize].as_ptr() as *const std::ffi::c_char + } else { + UNKNOWN_ERROR.as_ptr() as *const std::ffi::c_char + } + } + } +} diff --git a/sqlite3/src/util.rs b/sqlite3/src/util.rs new file mode 100644 index 000000000..c56d2fd20 --- /dev/null +++ b/sqlite3/src/util.rs @@ -0,0 +1,13 @@ +use crate::sqlite3; + +pub fn sqlite3_safety_check_sick_or_ok(_db: &sqlite3) -> bool { + match _db.e_open_state { + crate::SQLITE_STATE_SICK | crate::SQLITE_STATE_OPEN | crate::SQLITE_STATE_BUSY => { + true + } + _ => { + eprintln!("Invalid database state: {}", _db.e_open_state); + false + } + } +}