mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-20 08:34:19 +01:00
Merge 'SQLite C API improvements: add basic bind and column functions' from Danawan Bimantoro
Add support for more of the SQLite C API. Bind functions: * bind_parameter_count * bind_parameter_name * bind_null * bind_int * bind_int64 * bind_double Column functions: * column_count * column_name * column_int * column_double * last_insert_rowid Closes #2494
This commit is contained in:
@@ -44,6 +44,8 @@
|
||||
typedef struct sqlite3 sqlite3;
|
||||
|
||||
typedef struct sqlite3_stmt sqlite3_stmt;
|
||||
typedef int64_t sqlite3_int64;
|
||||
typedef sqlite3_int64 sqlite_int64;
|
||||
|
||||
typedef int (*exec_callback)(void *context, int n_column, char **argv, char **colv);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use std::ffi::{self, CStr, CString};
|
||||
use std::num::NonZero;
|
||||
use tracing::trace;
|
||||
use turso_core::{CheckpointMode, LimboError, Value};
|
||||
|
||||
@@ -22,6 +23,7 @@ pub const SQLITE_INTERRUPT: ffi::c_int = 9;
|
||||
pub const SQLITE_NOTFOUND: ffi::c_int = 12;
|
||||
pub const SQLITE_CANTOPEN: ffi::c_int = 14;
|
||||
pub const SQLITE_MISUSE: ffi::c_int = 21;
|
||||
pub const SQLITE_RANGE: ffi::c_int = 25;
|
||||
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);
|
||||
@@ -313,8 +315,10 @@ pub unsafe extern "C" fn sqlite3_reset(stmt: *mut sqlite3_stmt) -> ffi::c_int {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_changes(_db: *mut sqlite3) -> ffi::c_int {
|
||||
stub!();
|
||||
pub unsafe extern "C" fn sqlite3_changes(db: *mut sqlite3) -> ffi::c_int {
|
||||
let db: &mut sqlite3 = &mut *db;
|
||||
let inner = db.inner.lock().unwrap();
|
||||
inner.conn.changes() as ffi::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -355,13 +359,17 @@ pub unsafe extern "C" fn sqlite3_get_autocommit(_db: *mut sqlite3) -> ffi::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_total_changes(_db: *mut sqlite3) -> ffi::c_int {
|
||||
stub!();
|
||||
pub unsafe extern "C" fn sqlite3_total_changes(db: *mut sqlite3) -> ffi::c_int {
|
||||
let db: &mut sqlite3 = &mut *db;
|
||||
let inner = db.inner.lock().unwrap();
|
||||
inner.conn.total_changes() as ffi::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_last_insert_rowid(_db: *mut sqlite3) -> i64 {
|
||||
stub!();
|
||||
pub unsafe extern "C" fn sqlite3_last_insert_rowid(db: *mut sqlite3) -> ffi::c_int {
|
||||
let db: &mut sqlite3 = &mut *db;
|
||||
let inner = db.inner.lock().unwrap();
|
||||
inner.conn.last_insert_rowid() as ffi::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -476,42 +484,91 @@ pub unsafe extern "C" fn sqlite3_data_count(stmt: *mut sqlite3_stmt) -> ffi::c_i
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_bind_parameter_count(_stmt: *mut sqlite3_stmt) -> ffi::c_int {
|
||||
stub!();
|
||||
pub unsafe extern "C" fn sqlite3_bind_parameter_count(stmt: *mut sqlite3_stmt) -> ffi::c_int {
|
||||
let stmt = &*stmt;
|
||||
stmt.stmt.parameters_count() as ffi::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_bind_parameter_name(
|
||||
_stmt: *mut sqlite3_stmt,
|
||||
_idx: ffi::c_int,
|
||||
stmt: *mut sqlite3_stmt,
|
||||
idx: ffi::c_int,
|
||||
) -> *const ffi::c_char {
|
||||
stub!();
|
||||
let stmt = &*stmt;
|
||||
let index = NonZero::new_unchecked(idx as usize);
|
||||
|
||||
if let Some(val) = stmt.stmt.parameters().name(index) {
|
||||
let c_string = CString::new(val).expect("CString::new failed");
|
||||
c_string.into_raw()
|
||||
} else {
|
||||
std::ptr::null()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_bind_null(
|
||||
_stmt: *mut sqlite3_stmt,
|
||||
_idx: ffi::c_int,
|
||||
pub unsafe extern "C" fn sqlite3_bind_null(stmt: *mut sqlite3_stmt, idx: ffi::c_int) -> ffi::c_int {
|
||||
if stmt.is_null() {
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
if idx <= 0 {
|
||||
return SQLITE_RANGE;
|
||||
}
|
||||
let stmt = &mut *stmt;
|
||||
|
||||
stmt.stmt
|
||||
.bind_at(NonZero::new_unchecked(idx as usize), Value::Null);
|
||||
SQLITE_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_bind_int(
|
||||
stmt: *mut sqlite3_stmt,
|
||||
idx: ffi::c_int,
|
||||
val: i64,
|
||||
) -> ffi::c_int {
|
||||
stub!();
|
||||
sqlite3_bind_int64(stmt, idx, val)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_bind_int64(
|
||||
_stmt: *mut sqlite3_stmt,
|
||||
_idx: ffi::c_int,
|
||||
_val: i64,
|
||||
stmt: *mut sqlite3_stmt,
|
||||
idx: ffi::c_int,
|
||||
val: i64,
|
||||
) -> ffi::c_int {
|
||||
stub!();
|
||||
if stmt.is_null() {
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
if idx <= 0 {
|
||||
return SQLITE_RANGE;
|
||||
}
|
||||
let stmt = &mut *stmt;
|
||||
|
||||
stmt.stmt
|
||||
.bind_at(NonZero::new_unchecked(idx as usize), Value::Integer(val));
|
||||
|
||||
SQLITE_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_bind_double(
|
||||
_stmt: *mut sqlite3_stmt,
|
||||
_idx: ffi::c_int,
|
||||
_val: f64,
|
||||
stmt: *mut sqlite3_stmt,
|
||||
idx: ffi::c_int,
|
||||
val: f64,
|
||||
) -> ffi::c_int {
|
||||
stub!();
|
||||
println!("Bind Double Rust");
|
||||
if stmt.is_null() {
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
if idx <= 0 {
|
||||
return SQLITE_RANGE;
|
||||
}
|
||||
let stmt = &mut *stmt;
|
||||
|
||||
stmt.stmt
|
||||
.bind_at(NonZero::new_unchecked(idx as usize), Value::Float(val));
|
||||
|
||||
SQLITE_OK
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -545,8 +602,9 @@ pub unsafe extern "C" fn sqlite3_column_type(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_column_count(_stmt: *mut sqlite3_stmt) -> ffi::c_int {
|
||||
stub!();
|
||||
pub unsafe extern "C" fn sqlite3_column_count(stmt: *mut sqlite3_stmt) -> ffi::c_int {
|
||||
let stmt = &mut *stmt;
|
||||
stmt.stmt.num_columns() as ffi::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -559,10 +617,16 @@ pub unsafe extern "C" fn sqlite3_column_decltype(
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_column_name(
|
||||
_stmt: *mut sqlite3_stmt,
|
||||
_idx: ffi::c_int,
|
||||
stmt: *mut sqlite3_stmt,
|
||||
idx: ffi::c_int,
|
||||
) -> *const ffi::c_char {
|
||||
stub!();
|
||||
let idx = idx.try_into().unwrap();
|
||||
let stmt = &mut *stmt;
|
||||
|
||||
let binding = stmt.stmt.get_column_name(idx).into_owned();
|
||||
let val = binding.as_str();
|
||||
let c_string = CString::new(val).expect("CString::new failed");
|
||||
c_string.into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -578,8 +642,19 @@ pub unsafe extern "C" fn sqlite3_column_int64(stmt: *mut sqlite3_stmt, idx: ffi:
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_column_double(_stmt: *mut sqlite3_stmt, _idx: ffi::c_int) -> f64 {
|
||||
stub!();
|
||||
pub unsafe extern "C" fn sqlite3_column_int(stmt: *mut sqlite3_stmt, idx: ffi::c_int) -> i64 {
|
||||
sqlite3_column_int64(stmt, idx)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_column_double(stmt: *mut sqlite3_stmt, idx: ffi::c_int) -> f64 {
|
||||
let idx = idx.try_into().unwrap();
|
||||
let stmt = &mut *stmt;
|
||||
let row = stmt
|
||||
.stmt
|
||||
.row()
|
||||
.expect("Function should only be called after `SQLITE_ROW`");
|
||||
row.get(idx).unwrap()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
10
sqlite3/tests/Makefile
Normal file
10
sqlite3/tests/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
CC=gcc
|
||||
CFLAGS=-Wall -Wextra -g -I../include
|
||||
TARGET=sqlite3-tests
|
||||
SRC=sqlite3_tests.c
|
||||
|
||||
all:
|
||||
$(CC) $(CFLAGS) -o $(TARGET) $(SRC) $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
@@ -1,6 +1,5 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
#[repr(C)]
|
||||
@@ -46,6 +45,12 @@ extern "C" {
|
||||
frame_len: u32,
|
||||
) -> i32;
|
||||
fn libsql_wal_disable_checkpoint(db: *mut sqlite3) -> i32;
|
||||
fn sqlite3_column_int(stmt: *mut sqlite3_stmt, idx: i32) -> i64;
|
||||
fn sqlite3_bind_int(stmt: *mut sqlite3_stmt, idx: i32, val: i64) -> i32;
|
||||
fn sqlite3_bind_parameter_count(stmt: *mut sqlite3_stmt) -> i32;
|
||||
fn sqlite3_bind_parameter_name(stmt: *mut sqlite3_stmt, idx: i32) -> *const libc::c_char;
|
||||
fn sqlite3_column_name(stmt: *mut sqlite3_stmt, idx: i32) -> *const libc::c_char;
|
||||
fn sqlite3_last_insert_rowid(db: *mut sqlite3) -> i32;
|
||||
}
|
||||
|
||||
const SQLITE_OK: i32 = 0;
|
||||
@@ -204,6 +209,179 @@ mod tests {
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_sqlite3_bind_int() {
|
||||
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 = ptr::null_mut();
|
||||
assert_eq!(sqlite3_open(path.as_ptr(), &mut db), SQLITE_OK);
|
||||
|
||||
let mut stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"CREATE TABLE test_bind (id INTEGER PRIMARY KEY, value INTEGER)".as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_DONE);
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
|
||||
let mut stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"INSERT INTO test_bind (value) VALUES (?)".as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_bind_int(stmt, 1, 42), SQLITE_OK);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_DONE);
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
|
||||
let mut stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"SELECT value FROM test_bind LIMIT 1".as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_ROW);
|
||||
assert_eq!(sqlite3_column_int(stmt, 0), 42);
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_sqlite3_bind_parameter_name_and_count() {
|
||||
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 = ptr::null_mut();
|
||||
assert_eq!(sqlite3_open(path.as_ptr(), &mut db), SQLITE_OK);
|
||||
|
||||
let mut stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"CREATE TABLE test_params (id INTEGER PRIMARY KEY, value TEXT)".as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_DONE);
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
|
||||
let mut stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
c"INSERT INTO test_params (id, value) VALUES (?1, ?2)".as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
|
||||
let param_count = sqlite3_bind_parameter_count(stmt);
|
||||
assert_eq!(param_count, 2);
|
||||
|
||||
println!("parameter count {param_count}");
|
||||
let name1 = sqlite3_bind_parameter_name(stmt, 1);
|
||||
assert!(!name1.is_null());
|
||||
let name1_str = std::ffi::CStr::from_ptr(name1).to_str().unwrap();
|
||||
assert_eq!(name1_str, "?1");
|
||||
|
||||
let name2 = sqlite3_bind_parameter_name(stmt, 2);
|
||||
assert!(!name2.is_null());
|
||||
let name2_str = std::ffi::CStr::from_ptr(name2).to_str().unwrap();
|
||||
assert_eq!(name2_str, "?2");
|
||||
|
||||
let invalid_name = sqlite3_bind_parameter_name(stmt, 99);
|
||||
assert!(invalid_name.is_null());
|
||||
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sqlite3_last_insert_rowid() {
|
||||
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_rowid (value INTEGER)".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_rowid (value) VALUES (6)".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 last_rowid = sqlite3_last_insert_rowid(db);
|
||||
assert!(last_rowid > 0);
|
||||
println!("last insert rowid: {last_rowid}");
|
||||
let query = format!("SELECT value FROM test_rowid WHERE rowid = {last_rowid}");
|
||||
let query_cstring = std::ffi::CString::new(query).unwrap();
|
||||
|
||||
let mut stmt = std::ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
query_cstring.as_ptr(),
|
||||
-1,
|
||||
&mut stmt,
|
||||
std::ptr::null_mut(),
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_ROW);
|
||||
let value_int = sqlite3_column_int(stmt, 0);
|
||||
assert_eq!(value_int, 6);
|
||||
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sqlite3"))]
|
||||
mod libsql_ext {
|
||||
|
||||
311
sqlite3/tests/sqlite3_tests.c
Normal file
311
sqlite3/tests/sqlite3_tests.c
Normal file
@@ -0,0 +1,311 @@
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void test_sqlite3_changes();
|
||||
void test_sqlite3_bind_int64();
|
||||
void test_sqlite3_bind_double();
|
||||
void test_sqlite3_bind_parameter_name();
|
||||
void test_sqlite3_bind_parameter_count();
|
||||
void test_sqlite3_column_name();
|
||||
void test_sqlite3_last_insert_rowid();
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_sqlite3_changes();
|
||||
test_sqlite3_bind_int64();
|
||||
test_sqlite3_bind_double();
|
||||
test_sqlite3_bind_parameter_name();
|
||||
test_sqlite3_bind_parameter_count();
|
||||
test_sqlite3_column_name();
|
||||
test_sqlite3_last_insert_rowid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void test_sqlite3_changes()
|
||||
{
|
||||
sqlite3 *db;
|
||||
char *err_msg = NULL;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open("../../testing/testing.db", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS turso_test_changes (id INTEGER PRIMARY KEY, name TEXT);",
|
||||
NULL, NULL, &err_msg);
|
||||
assert(rc == SQLITE_OK);
|
||||
if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; }
|
||||
|
||||
rc = sqlite3_exec(db, "DELETE FROM turso_test_changes;", NULL, NULL, &err_msg);
|
||||
assert(rc == SQLITE_OK);
|
||||
if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; }
|
||||
|
||||
sqlite3_close(db);
|
||||
rc = sqlite3_open("../../testing/testing.db", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
assert(sqlite3_changes(db) == 0);
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"INSERT INTO turso_test_changes (name) VALUES ('abc');",
|
||||
NULL, NULL, &err_msg);
|
||||
assert(rc == SQLITE_OK);
|
||||
if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; }
|
||||
assert(sqlite3_changes(db) == 1);
|
||||
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"INSERT INTO turso_test_changes (name) VALUES ('def'),('ghi'),('jkl');",
|
||||
NULL, NULL, &err_msg);
|
||||
assert(rc == SQLITE_OK);
|
||||
if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; }
|
||||
assert(sqlite3_changes(db) == 3);
|
||||
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
|
||||
void test_sqlite3_bind_int64()
|
||||
{
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
char *err_msg = NULL;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open("../../testing/testing.db", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS turso_test_int64 (id INTEGER PRIMARY KEY, value INTEGER);",
|
||||
NULL, NULL, &err_msg);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; }
|
||||
rc = sqlite3_exec(db, "DELETE FROM turso_test_int64;", NULL, NULL, &err_msg);
|
||||
assert(rc == SQLITE_OK);
|
||||
if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; }
|
||||
|
||||
rc = sqlite3_prepare_v2(db, "INSERT INTO turso_test_int64 (value) VALUES (?);", -1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
sqlite_int64 big_value = (sqlite_int64)9223372036854775807LL;
|
||||
rc = sqlite3_bind_int64(stmt, 1, big_value);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
assert(rc == SQLITE_DONE);
|
||||
|
||||
rc = sqlite3_finalize(stmt);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, "SELECT value FROM turso_test_int64 LIMIT 1;", -1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
assert(rc == SQLITE_ROW);
|
||||
|
||||
sqlite_int64 fetched = sqlite3_column_int64(stmt, 0);
|
||||
assert(fetched == big_value);
|
||||
|
||||
printf("Inserted value: %lld, Fetched value: %lld\n", (long long)big_value, (long long)fetched);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
void test_sqlite3_bind_double()
|
||||
{
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
char *err_msg = NULL;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open("../../testing/testing.db", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS turso_test_double (id INTEGER PRIMARY KEY, value REAL);",
|
||||
NULL, NULL, &err_msg);
|
||||
assert(rc == SQLITE_OK);
|
||||
if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; }
|
||||
|
||||
rc = sqlite3_exec(db, "DELETE FROM turso_test_double;", NULL, NULL, &err_msg);
|
||||
assert(rc == SQLITE_OK);
|
||||
if (err_msg) { sqlite3_free(err_msg); err_msg = NULL; }
|
||||
|
||||
rc = sqlite3_prepare_v2(db, "INSERT INTO turso_test_double (value) VALUES (?);", -1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
double big_value = 1234567890.123456;
|
||||
rc = sqlite3_bind_double(stmt, 1, big_value);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
assert(rc == SQLITE_DONE);
|
||||
|
||||
rc = sqlite3_finalize(stmt);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, "SELECT value FROM turso_test_double LIMIT 1;", -1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
assert(rc == SQLITE_ROW);
|
||||
|
||||
double fetched = sqlite3_column_double(stmt, 0);
|
||||
assert(fabs(fetched - big_value) < 1e-9);
|
||||
|
||||
printf("Inserted value: %.15f, Fetched value: %.15f\n", big_value, fetched);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
|
||||
void test_sqlite3_bind_parameter_name() {
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open(":memory:", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
const char *sql = "INSERT INTO test_parameter_name (value) VALUES (:val);";
|
||||
rc = sqlite3_exec(db, "CREATE TABLE test_parameter_name (id INTEGER PRIMARY KEY, value INTEGER);", NULL, NULL, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
const char *param_name = sqlite3_bind_parameter_name(stmt, 1);
|
||||
assert(param_name != NULL);
|
||||
printf("Parameter name: %s\n", param_name);
|
||||
assert(strcmp(param_name, ":val") == 0);
|
||||
|
||||
const char *invalid_name = sqlite3_bind_parameter_name(stmt, 99);
|
||||
assert(invalid_name == NULL);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
|
||||
void test_sqlite3_bind_parameter_count() {
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open(":memory:", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_exec(db, "CREATE TABLE test_parameter_count (id INTEGER PRIMARY KEY, value TEXT);", NULL, NULL, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
const char *sql = "INSERT INTO test_parameter_count (id, value) VALUES (?1, ?2);";
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
int param_count = sqlite3_bind_parameter_count(stmt);
|
||||
printf("Parameter count: %d\n", param_count);
|
||||
assert(param_count == 2);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, "SELECT * FROM test_parameter_count;", -1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
param_count = sqlite3_bind_parameter_count(stmt);
|
||||
printf("Parameter count (no params): %d\n", param_count);
|
||||
assert(param_count == 0);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
void test_sqlite3_column_name() {
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open(":memory:", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE test_column_name (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);",
|
||||
NULL, NULL, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
const char *sql = "SELECT id, name AS full_name, age FROM test_column_name;";
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
int col_count = sqlite3_column_count(stmt);
|
||||
assert(col_count == 3);
|
||||
|
||||
const char *col0 = sqlite3_column_name(stmt, 0);
|
||||
const char *col1 = sqlite3_column_name(stmt, 1);
|
||||
const char *col2 = sqlite3_column_name(stmt, 2);
|
||||
|
||||
printf("Column 0 name: %s\n", col0);
|
||||
printf("Column 1 name: %s\n", col1);
|
||||
printf("Column 2 name: %s\n", col2);
|
||||
|
||||
assert(strcmp(col0, "id") == 0);
|
||||
assert(strcmp(col1, "full_name") == 0);
|
||||
assert(strcmp(col2, "age") == 0);
|
||||
|
||||
const char *invalid_col = sqlite3_column_name(stmt, 99);
|
||||
assert(invalid_col == NULL);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
|
||||
void test_sqlite3_last_insert_rowid() {
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open(":memory:", &db);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_exec(db,
|
||||
"CREATE TABLE test_last_insert (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);",
|
||||
NULL, NULL, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_prepare_v2(db,
|
||||
"INSERT INTO test_last_insert (name) VALUES ('first');",
|
||||
-1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
assert(rc == SQLITE_DONE);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
sqlite3_int64 rowid1 = sqlite3_last_insert_rowid(db);
|
||||
printf("first: %lld\n", (long long)rowid1);
|
||||
assert(rowid1 == 1);
|
||||
|
||||
rc = sqlite3_prepare_v2(db,
|
||||
"INSERT INTO test_last_insert (name) VALUES ('second');",
|
||||
-1, &stmt, NULL);
|
||||
assert(rc == SQLITE_OK);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
assert(rc == SQLITE_DONE);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
sqlite3_int64 rowid2 = sqlite3_last_insert_rowid(db);
|
||||
printf("second: %lld\n", (long long)rowid2);
|
||||
assert(rowid2 == 2);
|
||||
|
||||
sqlite3_close(db);
|
||||
}
|
||||
Reference in New Issue
Block a user