mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-04 17:04:18 +01:00
Progress on Go bindings, add prepare + query statement
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
use crate::types::ResultCode;
|
||||
use crate::rows::TursoRows;
|
||||
use crate::types::{AllocPool, ResultCode, TursoValue};
|
||||
use crate::TursoConn;
|
||||
use limbo_core::{Rows, Statement, StepResult, Value};
|
||||
use limbo_core::{Statement, StepResult};
|
||||
use std::ffi::{c_char, c_void};
|
||||
use std::num::NonZero;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn db_prepare(ctx: *mut c_void, query: *const c_char) -> *mut c_void {
|
||||
@@ -19,142 +21,119 @@ pub extern "C" fn db_prepare(ctx: *mut c_void, query: *const c_char) -> *mut c_v
|
||||
}
|
||||
}
|
||||
|
||||
struct TursoStatement<'a> {
|
||||
statement: Statement,
|
||||
conn: &'a TursoConn<'a>,
|
||||
#[no_mangle]
|
||||
pub extern "C" fn stmt_execute(
|
||||
ctx: *mut c_void,
|
||||
args_ptr: *mut TursoValue,
|
||||
arg_count: usize,
|
||||
changes: *mut i64,
|
||||
) -> ResultCode {
|
||||
if ctx.is_null() {
|
||||
return ResultCode::Error;
|
||||
}
|
||||
let stmt = TursoStatement::from_ptr(ctx);
|
||||
|
||||
let args = if !args_ptr.is_null() && arg_count > 0 {
|
||||
unsafe { std::slice::from_raw_parts(args_ptr, arg_count) }
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
let val = arg.to_value(&mut stmt.pool);
|
||||
stmt.statement.bind_at(NonZero::new(i + 1).unwrap(), val);
|
||||
}
|
||||
loop {
|
||||
match stmt.statement.step() {
|
||||
Ok(StepResult::Row(_)) => {
|
||||
// unexpected row during execution, error out.
|
||||
return ResultCode::Error;
|
||||
}
|
||||
Ok(StepResult::Done) => {
|
||||
stmt.conn.conn.total_changes();
|
||||
if !changes.is_null() {
|
||||
unsafe {
|
||||
*changes = stmt.conn.conn.total_changes();
|
||||
}
|
||||
}
|
||||
return ResultCode::Done;
|
||||
}
|
||||
Ok(StepResult::IO) => {
|
||||
let _ = stmt.conn.io.run_once();
|
||||
}
|
||||
Ok(StepResult::Busy) => {
|
||||
return ResultCode::Busy;
|
||||
}
|
||||
Ok(StepResult::Interrupt) => {
|
||||
return ResultCode::Interrupt;
|
||||
}
|
||||
Err(_) => {
|
||||
return ResultCode::Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TursoStatement<'a> {
|
||||
fn new(statement: Statement, conn: &'a TursoConn<'a>) -> Self {
|
||||
TursoStatement { statement, conn }
|
||||
#[no_mangle]
|
||||
pub extern "C" fn stmt_parameter_count(ctx: *mut c_void) -> i32 {
|
||||
if ctx.is_null() {
|
||||
return -1;
|
||||
}
|
||||
let stmt = TursoStatement::from_ptr(ctx);
|
||||
stmt.statement.parameters_count() as i32
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn stmt_query(
|
||||
ctx: *mut c_void,
|
||||
args_ptr: *mut TursoValue,
|
||||
args_count: usize,
|
||||
) -> *mut c_void {
|
||||
if ctx.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
let stmt = TursoStatement::from_ptr(ctx);
|
||||
let args = if !args_ptr.is_null() && args_count > 0 {
|
||||
unsafe { std::slice::from_raw_parts(args_ptr, args_count) }
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
let val = arg.to_value(&mut stmt.pool);
|
||||
stmt.statement.bind_at(NonZero::new(i + 1).unwrap(), val);
|
||||
}
|
||||
match stmt.statement.query() {
|
||||
Ok(rows) => {
|
||||
let stmt = unsafe { Box::from_raw(stmt) };
|
||||
TursoRows::new(rows, stmt).to_ptr()
|
||||
}
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TursoStatement<'conn> {
|
||||
pub statement: Statement,
|
||||
pub conn: &'conn mut TursoConn,
|
||||
pub pool: AllocPool,
|
||||
}
|
||||
|
||||
impl<'conn> TursoStatement<'conn> {
|
||||
pub fn new(statement: Statement, conn: &'conn mut TursoConn) -> Self {
|
||||
TursoStatement {
|
||||
statement,
|
||||
conn,
|
||||
pool: AllocPool::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn to_ptr(self) -> *mut c_void {
|
||||
Box::into_raw(Box::new(self)) as *mut c_void
|
||||
}
|
||||
fn from_ptr(ptr: *mut c_void) -> &'static mut TursoStatement<'a> {
|
||||
|
||||
fn from_ptr(ptr: *mut c_void) -> &'static mut TursoStatement<'conn> {
|
||||
if ptr.is_null() {
|
||||
panic!("Null pointer");
|
||||
}
|
||||
unsafe { &mut *(ptr as *mut TursoStatement) }
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn db_get_columns(ctx: *mut c_void) -> *const c_void {
|
||||
if ctx.is_null() {
|
||||
return std::ptr::null();
|
||||
}
|
||||
let stmt = TursoStatement::from_ptr(ctx);
|
||||
let columns = stmt.statement.columns();
|
||||
let mut column_names = Vec::new();
|
||||
for column in columns {
|
||||
column_names.push(column.name().to_string());
|
||||
}
|
||||
let c_string = std::ffi::CString::new(column_names.join(",")).unwrap();
|
||||
c_string.into_raw() as *const c_void
|
||||
}
|
||||
|
||||
struct TursoRows<'a> {
|
||||
rows: Rows<'a>,
|
||||
conn: &'a mut TursoConn<'a>,
|
||||
}
|
||||
|
||||
impl<'a> TursoRows<'a> {
|
||||
fn new(rows: Rows<'a>, conn: &'a mut TursoConn<'a>) -> Self {
|
||||
TursoRows { rows, conn }
|
||||
}
|
||||
|
||||
fn to_ptr(self) -> *mut c_void {
|
||||
Box::into_raw(Box::new(self)) as *mut c_void
|
||||
}
|
||||
|
||||
fn from_ptr(ptr: *mut c_void) -> &'static mut TursoRows<'a> {
|
||||
if ptr.is_null() {
|
||||
panic!("Null pointer");
|
||||
}
|
||||
unsafe { &mut *(ptr as *mut TursoRows) }
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rows_next(ctx: *mut c_void, rows_ptr: *mut c_void) -> ResultCode {
|
||||
if rows_ptr.is_null() || ctx.is_null() {
|
||||
return ResultCode::Error;
|
||||
}
|
||||
let rows = unsafe { &mut *(rows_ptr as *mut Rows) };
|
||||
let conn = TursoConn::from_ptr(ctx);
|
||||
|
||||
match rows.next_row() {
|
||||
Ok(StepResult::Row(row)) => {
|
||||
conn.cursor = Some(row.values);
|
||||
ResultCode::Row
|
||||
}
|
||||
Ok(StepResult::Done) => {
|
||||
// No more rows
|
||||
ResultCode::Done
|
||||
}
|
||||
Ok(StepResult::IO) => {
|
||||
let _ = conn.io.run_once();
|
||||
ResultCode::Io
|
||||
}
|
||||
Ok(StepResult::Busy) => ResultCode::Busy,
|
||||
Ok(StepResult::Interrupt) => ResultCode::Interrupt,
|
||||
Err(_) => ResultCode::Error,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rows_get_value(ctx: *mut c_void, col_idx: usize) -> *const c_char {
|
||||
if ctx.is_null() {
|
||||
return std::ptr::null();
|
||||
}
|
||||
let conn = TursoConn::from_ptr(ctx);
|
||||
|
||||
if let Some(ref cursor) = conn.cursor {
|
||||
if let Some(value) = cursor.get(col_idx) {
|
||||
let c_string = std::ffi::CString::new(value.to_string()).unwrap();
|
||||
return c_string.into_raw(); // Caller must free this pointer
|
||||
}
|
||||
}
|
||||
std::ptr::null() // No data or invalid index
|
||||
}
|
||||
|
||||
// Free the returned string
|
||||
#[no_mangle]
|
||||
pub extern "C" fn free_c_string(s: *mut c_char) {
|
||||
if !s.is_null() {
|
||||
unsafe { drop(std::ffi::CString::from_raw(s)) };
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rows_get_string(
|
||||
ctx: *mut c_void,
|
||||
rows_ptr: *mut c_void,
|
||||
col_idx: i32,
|
||||
) -> *const c_char {
|
||||
if rows_ptr.is_null() || ctx.is_null() {
|
||||
return std::ptr::null();
|
||||
}
|
||||
let _rows = unsafe { &mut *(rows_ptr as *mut Rows) };
|
||||
let conn = TursoConn::from_ptr(ctx);
|
||||
if col_idx > conn.cursor_idx as i32 || conn.cursor.is_none() {
|
||||
return std::ptr::null();
|
||||
}
|
||||
if let Some(values) = &conn.cursor {
|
||||
let value = &values[col_idx as usize];
|
||||
match value {
|
||||
Value::Text(s) => {
|
||||
return s.as_ptr() as *const i8;
|
||||
}
|
||||
_ => return std::ptr::null(),
|
||||
}
|
||||
};
|
||||
std::ptr::null()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rows_close(rows_ptr: *mut c_void) {
|
||||
if !rows_ptr.is_null() {
|
||||
let _ = unsafe { Box::from_raw(rows_ptr as *mut Rows) };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user