Skip to content

Commit d2fc528

Browse files
authored
Merge 'SQLite C API improvements' from Nikita Sivukhin
This PR adds rich error codes for sqlite3 C bridge layer and also do few minor adjustments: 1. Enable indices by default 2. Return debug logs to the stderr Closes #3999
2 parents 2f078f1 + cb3ae4d commit d2fc528

File tree

1 file changed

+66
-36
lines changed

1 file changed

+66
-36
lines changed

sqlite3/src/lib.rs

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,42 @@ macro_rules! stub {
1414
};
1515
}
1616

17-
pub const SQLITE_OK: ffi::c_int = 0;
18-
pub const SQLITE_ERROR: ffi::c_int = 1;
19-
pub const SQLITE_ABORT: ffi::c_int = 4;
20-
pub const SQLITE_BUSY: ffi::c_int = 5;
21-
pub const SQLITE_NOMEM: ffi::c_int = 7;
22-
pub const SQLITE_INTERRUPT: ffi::c_int = 9;
23-
pub const SQLITE_NOTFOUND: ffi::c_int = 12;
24-
pub const SQLITE_CANTOPEN: ffi::c_int = 14;
25-
pub const SQLITE_MISUSE: ffi::c_int = 21;
26-
pub const SQLITE_RANGE: ffi::c_int = 25;
27-
pub const SQLITE_ROW: ffi::c_int = 100;
28-
pub const SQLITE_DONE: ffi::c_int = 101;
17+
/* generic error-codes */
18+
pub const SQLITE_OK: ffi::c_int = 0; /* Successful result */
19+
pub const SQLITE_ERROR: ffi::c_int = 1; /* Generic error */
20+
pub const SQLITE_INTERNAL: ffi::c_int = 2; /* Internal logic error in SQLite */
21+
pub const SQLITE_PERM: ffi::c_int = 3; /* Access permission denied */
22+
pub const SQLITE_ABORT: ffi::c_int = 4; /* Callback routine requested an abort */
23+
pub const SQLITE_BUSY: ffi::c_int = 5; /* The database file is locked */
24+
pub const SQLITE_LOCKED: ffi::c_int = 6; /* A table in the database is locked */
25+
pub const SQLITE_NOMEM: ffi::c_int = 7; /* A malloc() failed */
26+
pub const SQLITE_READONLY: ffi::c_int = 8; /* Attempt to write a readonly database */
27+
pub const SQLITE_INTERRUPT: ffi::c_int = 9; /* Operation terminated by sqlite3_interrupt()*/
28+
pub const SQLITE_IOERR: ffi::c_int = 10; /* Some kind of disk I/O error occurred */
29+
pub const SQLITE_CORRUPT: ffi::c_int = 11; /* The database disk image is malformed */
30+
pub const SQLITE_NOTFOUND: ffi::c_int = 12; /* Unknown opcode in sqlite3_file_control() */
31+
pub const SQLITE_FULL: ffi::c_int = 13; /* Insertion failed because database is full */
32+
pub const SQLITE_CANTOPEN: ffi::c_int = 14; /* Unable to open the database file */
33+
pub const SQLITE_PROTOCOL: ffi::c_int = 15; /* Database lock protocol error */
34+
pub const SQLITE_EMPTY: ffi::c_int = 16; /* Internal use only */
35+
pub const SQLITE_SCHEMA: ffi::c_int = 17; /* The database schema changed */
36+
pub const SQLITE_TOOBIG: ffi::c_int = 18; /* String or BLOB exceeds size limit */
37+
pub const SQLITE_CONSTRAINT: ffi::c_int = 19; /* Abort due to constraint violation */
38+
pub const SQLITE_MISMATCH: ffi::c_int = 20; /* Data type mismatch */
39+
pub const SQLITE_MISUSE: ffi::c_int = 21; /* Library used incorrectly */
40+
pub const SQLITE_NOLFS: ffi::c_int = 22; /* Uses OS features not supported on host */
41+
pub const SQLITE_AUTH: ffi::c_int = 23; /* Authorization denied */
42+
pub const SQLITE_FORMAT: ffi::c_int = 24; /* Not used */
43+
pub const SQLITE_RANGE: ffi::c_int = 25; /* 2nd parameter to sqlite3_bind out of range */
44+
pub const SQLITE_NOTADB: ffi::c_int = 26; /* File opened that is not a database file */
45+
pub const SQLITE_NOTICE: ffi::c_int = 27; /* Notifications from sqlite3_log() */
46+
pub const SQLITE_WARNING: ffi::c_int = 28; /* Warnings from sqlite3_log() */
47+
pub const SQLITE_ROW: ffi::c_int = 100; /* sqlite3_step() has another row ready */
48+
pub const SQLITE_DONE: ffi::c_int = 101; /* sqlite3_step() has finished executing */
49+
50+
/* extended error-codes */
2951
pub const SQLITE_ABORT_ROLLBACK: ffi::c_int = SQLITE_ABORT | (2 << 8);
52+
3053
pub const SQLITE_STATE_OPEN: u8 = 0x76;
3154
pub const SQLITE_STATE_SICK: u8 = 0xba;
3255
pub const SQLITE_STATE_BUSY: u8 = 0x6d;
@@ -39,7 +62,6 @@ pub const SQLITE_CHECKPOINT_TRUNCATE: ffi::c_int = 3;
3962
pub const SQLITE_INTEGER: ffi::c_int = 1;
4063
pub const SQLITE_FLOAT: ffi::c_int = 2;
4164
pub const SQLITE_TEXT: ffi::c_int = 3;
42-
pub const SQLITE3_TEXT: ffi::c_int = 3;
4365
pub const SQLITE_BLOB: ffi::c_int = 4;
4466
pub const SQLITE_NULL: ffi::c_int = 5;
4567

@@ -161,7 +183,7 @@ pub unsafe extern "C" fn sqlite3_open(
161183
Err(_) => return SQLITE_CANTOPEN,
162184
},
163185
};
164-
match turso_core::Database::open_file(io.clone(), filename_str, false, false) {
186+
match turso_core::Database::open_file(io.clone(), filename_str, false, true) {
165187
Ok(db) => {
166188
let conn = db.connect().unwrap();
167189
let filename = match filename_str {
@@ -288,9 +310,10 @@ pub unsafe extern "C" fn sqlite3_prepare_v2(
288310
};
289311
let stmt = match db.conn.prepare(sql) {
290312
Ok(stmt) => stmt,
291-
Err(_) => {
292-
db.err_code = SQLITE_ERROR;
293-
return SQLITE_ERROR;
313+
Err(err) => {
314+
let code = handle_limbo_err(err, std::ptr::null_mut());
315+
db.err_code = code;
316+
return code;
294317
}
295318
};
296319
let new_stmt = Box::leak(Box::new(sqlite3_stmt::new(raw_db, stmt)));
@@ -344,8 +367,8 @@ pub unsafe extern "C" fn sqlite3_step(stmt: *mut sqlite3_stmt) -> ffi::c_int {
344367
let db = &mut *stmt.db;
345368
loop {
346369
let _db = db.inner.lock().unwrap();
347-
if let Ok(result) = stmt.stmt.step() {
348-
match result {
370+
match stmt.stmt.step() {
371+
Ok(result) => match result {
349372
turso_core::StepResult::IO => {
350373
stmt.stmt.run_once().unwrap();
351374
continue;
@@ -360,9 +383,8 @@ pub unsafe extern "C" fn sqlite3_step(stmt: *mut sqlite3_stmt) -> ffi::c_int {
360383
return SQLITE_ROW;
361384
}
362385
turso_core::StepResult::Busy => return SQLITE_BUSY,
363-
}
364-
} else {
365-
return SQLITE_ERROR;
386+
},
387+
Err(err) => return handle_limbo_err(err, std::ptr::null_mut()),
366388
}
367389
}
368390
}
@@ -412,11 +434,7 @@ pub unsafe extern "C" fn sqlite3_exec(
412434
match db_inner.conn.execute(trimmed) {
413435
Ok(_) => continue,
414436
Err(e) => {
415-
if !err.is_null() {
416-
let err_msg = format!("SQL error: {e:?}");
417-
*err = CString::new(err_msg).unwrap().into_raw();
418-
}
419-
return SQLITE_ERROR;
437+
return handle_limbo_err(e, err);
420438
}
421439
}
422440
} else if callback.is_none() {
@@ -964,7 +982,6 @@ pub unsafe extern "C" fn sqlite3_bind_double(
964982
idx: ffi::c_int,
965983
val: f64,
966984
) -> ffi::c_int {
967-
println!("Bind Double Rust");
968985
if stmt.is_null() {
969986
return SQLITE_MISUSE;
970987
}
@@ -1801,7 +1818,6 @@ pub unsafe extern "C" fn sqlite3_wal_checkpoint_v2(
18011818
SQLITE_OK
18021819
}
18031820
Err(e) => {
1804-
println!("Checkpoint error: {e}");
18051821
if matches!(e, turso_core::LimboError::Busy) {
18061822
SQLITE_BUSY
18071823
} else {
@@ -1946,13 +1962,10 @@ pub unsafe extern "C" fn libsql_wal_disable_checkpoint(db: *mut sqlite3) -> ffi:
19461962
}
19471963

19481964
fn sqlite3_safety_check_sick_or_ok(db: &sqlite3Inner) -> bool {
1949-
match db.e_open_state {
1950-
SQLITE_STATE_SICK | SQLITE_STATE_OPEN | SQLITE_STATE_BUSY => true,
1951-
_ => {
1952-
eprintln!("Invalid database state: {}", db.e_open_state);
1953-
false
1954-
}
1955-
}
1965+
matches!(
1966+
db.e_open_state,
1967+
SQLITE_STATE_SICK | SQLITE_STATE_OPEN | SQLITE_STATE_BUSY
1968+
)
19561969
}
19571970

19581971
// https://sqlite.org/c3ref/table_column_metadata.html
@@ -2096,3 +2109,20 @@ pub unsafe extern "C" fn sqlite3_table_column_metadata(
20962109

20972110
rc
20982111
}
2112+
2113+
fn handle_limbo_err(err: LimboError, container: *mut *mut ffi::c_char) -> i32 {
2114+
if !container.is_null() {
2115+
let err_msg = format!("{err}");
2116+
unsafe { *container = CString::new(err_msg).unwrap().into_raw() };
2117+
}
2118+
match err {
2119+
LimboError::Corrupt(..) => SQLITE_CORRUPT,
2120+
LimboError::NotADB => SQLITE_NOTADB,
2121+
LimboError::Constraint(_) => SQLITE_CONSTRAINT,
2122+
LimboError::DatabaseFull(_) => SQLITE_FULL,
2123+
LimboError::TableLocked => SQLITE_LOCKED,
2124+
LimboError::ReadOnly => SQLITE_READONLY,
2125+
LimboError::Busy => SQLITE_BUSY,
2126+
_ => SQLITE_ERROR,
2127+
}
2128+
}

0 commit comments

Comments
 (0)