mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-10 18:54:22 +01:00
Merge 'SQLite C API improvements: add column type and column decltype' from Danawan Bimantoro
Closes #2676
This commit is contained in:
@@ -41,6 +41,13 @@
|
||||
|
||||
#define SQLITE_CHECKPOINT_TRUNCATE 3
|
||||
|
||||
#define SQLITE_INTEGER 1
|
||||
#define SQLITE_FLOAT 2
|
||||
#define SQLITE_BLOB 4
|
||||
#define SQLITE_NULL 5
|
||||
#define SQLITE_TEXT 3
|
||||
#define SQLITE3_TEXT 3
|
||||
|
||||
typedef void (*sqlite3_destructor_type)(void*);
|
||||
#define SQLITE_STATIC ((sqlite3_destructor_type)0)
|
||||
#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
|
||||
|
||||
@@ -36,6 +36,13 @@ pub const SQLITE_CHECKPOINT_FULL: ffi::c_int = 1;
|
||||
pub const SQLITE_CHECKPOINT_RESTART: ffi::c_int = 2;
|
||||
pub const SQLITE_CHECKPOINT_TRUNCATE: ffi::c_int = 3;
|
||||
|
||||
pub const SQLITE_INTEGER: ffi::c_int = 1;
|
||||
pub const SQLITE_FLOAT: ffi::c_int = 2;
|
||||
pub const SQLITE_TEXT: ffi::c_int = 3;
|
||||
pub const SQLITE3_TEXT: ffi::c_int = 3;
|
||||
pub const SQLITE_BLOB: ffi::c_int = 4;
|
||||
pub const SQLITE_NULL: ffi::c_int = 5;
|
||||
|
||||
pub struct sqlite3 {
|
||||
pub(crate) inner: Arc<Mutex<sqlite3Inner>>,
|
||||
}
|
||||
@@ -697,10 +704,22 @@ pub unsafe extern "C" fn sqlite3_bind_blob(
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_column_type(
|
||||
_stmt: *mut sqlite3_stmt,
|
||||
_idx: ffi::c_int,
|
||||
stmt: *mut sqlite3_stmt,
|
||||
idx: ffi::c_int,
|
||||
) -> ffi::c_int {
|
||||
stub!();
|
||||
let stmt = &mut *stmt;
|
||||
let row = stmt
|
||||
.stmt
|
||||
.row()
|
||||
.expect("Function should only be called after `SQLITE_ROW`");
|
||||
|
||||
match row.get::<&Value>(idx as usize) {
|
||||
Ok(turso_core::Value::Integer(_)) => SQLITE_INTEGER,
|
||||
Ok(turso_core::Value::Text(_)) => SQLITE_TEXT,
|
||||
Ok(turso_core::Value::Float(_)) => SQLITE_FLOAT,
|
||||
Ok(turso_core::Value::Blob(_)) => SQLITE_BLOB,
|
||||
_ => SQLITE_NULL,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -711,10 +730,17 @@ pub unsafe extern "C" fn sqlite3_column_count(stmt: *mut sqlite3_stmt) -> ffi::c
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_column_decltype(
|
||||
_stmt: *mut sqlite3_stmt,
|
||||
_idx: ffi::c_int,
|
||||
stmt: *mut sqlite3_stmt,
|
||||
idx: ffi::c_int,
|
||||
) -> *const ffi::c_char {
|
||||
stub!();
|
||||
let stmt = &mut *stmt;
|
||||
|
||||
if let Some(val) = stmt.stmt.get_column_type(idx as usize) {
|
||||
let c_string = CString::new(val).expect("CString::new failed");
|
||||
c_string.into_raw()
|
||||
} else {
|
||||
std::ptr::null()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -69,6 +69,8 @@ extern "C" {
|
||||
fn sqlite3_column_text(stmt: *mut sqlite3_stmt, idx: i32) -> *const libc::c_char;
|
||||
fn sqlite3_column_bytes(stmt: *mut sqlite3_stmt, idx: i32) -> i64;
|
||||
fn sqlite3_column_blob(stmt: *mut sqlite3_stmt, idx: i32) -> *const libc::c_void;
|
||||
fn sqlite3_column_type(stmt: *mut sqlite3_stmt, idx: i32) -> i32;
|
||||
fn sqlite3_column_decltype(stmt: *mut sqlite3_stmt, idx: i32) -> *const libc::c_char;
|
||||
}
|
||||
|
||||
const SQLITE_OK: i32 = 0;
|
||||
@@ -80,6 +82,12 @@ const SQLITE_CHECKPOINT_PASSIVE: i32 = 0;
|
||||
const SQLITE_CHECKPOINT_FULL: i32 = 1;
|
||||
const SQLITE_CHECKPOINT_RESTART: i32 = 2;
|
||||
const SQLITE_CHECKPOINT_TRUNCATE: i32 = 3;
|
||||
const SQLITE_INTEGER: i32 = 1;
|
||||
const SQLITE_FLOAT: i32 = 2;
|
||||
const SQLITE_TEXT: i32 = 3;
|
||||
const SQLITE3_TEXT: i32 = 3;
|
||||
const SQLITE_BLOB: i32 = 4;
|
||||
const SQLITE_NULL: i32 = 5;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
mod tests {
|
||||
@@ -647,6 +655,129 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sqlite3_column_type() {
|
||||
unsafe {
|
||||
let temp_file = tempfile::NamedTempFile::with_suffix(".db").unwrap();
|
||||
let path = std::ffi::CString::new(temp_file.path().to_str().unwrap()).unwrap();
|
||||
let mut db = std::ptr::null_mut();
|
||||
assert_eq!(sqlite3_open(path.as_ptr(), &mut db), SQLITE_OK);
|
||||
|
||||
let mut stmt = std::ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"CREATE TABLE test_types (col_int INTEGER, col_float REAL, col_text TEXT, col_blob BLOB, col_null text)".as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
std::ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_DONE);
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
|
||||
let mut stmt = std::ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"INSERT INTO test_types VALUES (123, 45.67, 'hello', x'010203', null)"
|
||||
.as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
std::ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_DONE);
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
|
||||
let mut stmt = std::ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"SELECT col_int, col_float, col_text, col_blob, col_null FROM test_types"
|
||||
.as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
std::ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_ROW);
|
||||
|
||||
assert_eq!(sqlite3_column_type(stmt, 0), SQLITE_INTEGER);
|
||||
assert_eq!(sqlite3_column_type(stmt, 1), SQLITE_FLOAT);
|
||||
assert_eq!(sqlite3_column_type(stmt, 2), SQLITE_TEXT);
|
||||
assert_eq!(sqlite3_column_type(stmt, 3), SQLITE_BLOB);
|
||||
assert_eq!(sqlite3_column_type(stmt, 4), SQLITE_NULL);
|
||||
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sqlite3_column_decltype() {
|
||||
unsafe {
|
||||
let temp_file = tempfile::NamedTempFile::with_suffix(".db").unwrap();
|
||||
let path = std::ffi::CString::new(temp_file.path().to_str().unwrap()).unwrap();
|
||||
let mut db = std::ptr::null_mut();
|
||||
assert_eq!(sqlite3_open(path.as_ptr(), &mut db), SQLITE_OK);
|
||||
|
||||
let mut stmt = std::ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"CREATE TABLE test_decltype (col_int INTEGER, col_float REAL, col_text TEXT, col_blob BLOB, col_null NULL)".as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
std::ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_DONE);
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
|
||||
let mut stmt = std::ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"SELECT col_int, col_float, col_text, col_blob, col_null FROM test_decltype"
|
||||
.as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
std::ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
|
||||
let expected = [
|
||||
Some("INTEGER"),
|
||||
Some("REAL"),
|
||||
Some("TEXT"),
|
||||
Some("BLOB"),
|
||||
None,
|
||||
];
|
||||
|
||||
for i in 0..sqlite3_column_count(stmt) {
|
||||
let decl = sqlite3_column_decltype(stmt, i);
|
||||
|
||||
if decl.is_null() {
|
||||
assert!(expected[i as usize].is_none());
|
||||
} else {
|
||||
let s = std::ffi::CStr::from_ptr(decl)
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
assert_eq!(Some(s.as_str()), expected[i as usize]);
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sqlite3"))]
|
||||
mod libsql_ext {
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ void test_sqlite3_last_insert_rowid();
|
||||
void test_sqlite3_bind_text();
|
||||
void test_sqlite3_bind_text2();
|
||||
void test_sqlite3_bind_blob();
|
||||
void test_sqlite3_column_type();
|
||||
void test_sqlite3_column_decltype();
|
||||
|
||||
int allocated = 0;
|
||||
|
||||
@@ -31,7 +33,8 @@ int main(void)
|
||||
test_sqlite3_bind_text();
|
||||
test_sqlite3_bind_text2();
|
||||
test_sqlite3_bind_blob();
|
||||
|
||||
test_sqlite3_column_type();
|
||||
test_sqlite3_column_decltype();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -482,3 +485,83 @@ void test_sqlite3_bind_blob()
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
void test_sqlite3_column_type()
|
||||
{
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open(":memory:", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE test_column_type (col_int INTEGER, col_float REAL, col_text TEXT, col_blob BLOB, col_null TEXT);",
|
||||
NULL, NULL, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"INSERT INTO test_column_type VALUES (42, 3.14, 'hello', x'010203', NULL);",
|
||||
NULL, NULL, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_prepare_v2(db,
|
||||
"SELECT col_int, col_float, col_text, col_blob, col_null FROM test_column_type;",
|
||||
-1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
assert(rc == SQLITE_ROW);
|
||||
|
||||
for (int i = 0; i < sqlite3_column_count(stmt); i++) {
|
||||
int type = sqlite3_column_type(stmt, i);
|
||||
switch (i) {
|
||||
case 0: assert(type == SQLITE_INTEGER); break;
|
||||
case 1: assert(type == SQLITE_FLOAT); break;
|
||||
case 2: assert(type == SQLITE_TEXT); break;
|
||||
case 3: assert(type == SQLITE_BLOB); break;
|
||||
case 4: assert(type == SQLITE_NULL); break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("sqlite3_column_type test completed!\n");
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
void test_sqlite3_column_decltype()
|
||||
{
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open(":memory:", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE test_decltype (col_int INTEGER, col_float REAL, col_text TEXT, col_blob BLOB, col_null NULL);",
|
||||
NULL, NULL, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_prepare_v2(db,
|
||||
"SELECT col_int, col_float, col_text, col_blob, col_null FROM test_decltype;",
|
||||
-1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
const char* expected[] = { "INTEGER", "REAL", "TEXT", "BLOB", NULL};
|
||||
|
||||
for (int i = 0; i < sqlite3_column_count(stmt); i++) {
|
||||
const char* decl = sqlite3_column_decltype(stmt, i);
|
||||
if (decl == NULL) {
|
||||
assert(expected[i] == NULL);
|
||||
} else {
|
||||
assert(strcmp(decl, expected[i]) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
printf("sqlite3_column_decltype test completed!\n");
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user