Skip to content

Commit 416039d

Browse files
committed
Support prepare statement
1 parent 1d0be23 commit 416039d

File tree

4 files changed

+186
-9
lines changed

4 files changed

+186
-9
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ fn main() {
2323
```
2424

2525
## TODO
26+
* NULL value
2627
* Add more apis
2728
* Windows support
2829
* Dynamic link crossdb

src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ pub enum Error {
1010
Utf8(#[from] std::str::Utf8Error),
1111
#[error("Query error: {0}, {1}")]
1212
Query(u16, String),
13+
#[error("Clear bindings error")]
14+
ClearBindings,
15+
#[error("Bind params error")]
16+
BindParams,
1317
}

src/lib.rs

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ pub use error::{Error, Result};
1818
pub use value::Value;
1919

2020
use crossdb_sys::*;
21+
use params::{IntoParams, Value as ParamValue};
2122
use std::ffi::{CStr, CString};
2223
use std::fmt::Display;
24+
mod params;
2325

2426
#[derive(Debug)]
2527
pub struct Connection {
@@ -60,23 +62,55 @@ impl Connection {
6062
}
6163

6264
pub fn begin(&self) -> bool {
65+
unsafe { xdb_begin(self.ptr) == 0 }
66+
}
67+
68+
pub fn commit(&self) -> bool {
69+
unsafe { xdb_commit(self.ptr) == 0 }
70+
}
71+
72+
pub fn rollback(&self) -> bool {
73+
unsafe { xdb_rollback(self.ptr) == 0 }
74+
}
75+
76+
// TODO: LRU cache
77+
pub fn prepare<S: AsRef<str>>(&mut self, sql: S) -> Result<Stmt> {
6378
unsafe {
64-
let status = xdb_begin(self.ptr);
65-
status as u32 == xdb_errno_e_XDB_OK
79+
let sql = CString::new(sql.as_ref())?;
80+
let ptr = xdb_stmt_prepare(self.ptr, sql.as_ptr());
81+
Ok(Stmt { ptr })
6682
}
6783
}
84+
}
6885

69-
pub fn commit(&self) -> bool {
86+
pub struct Stmt {
87+
ptr: *mut xdb_stmt_t,
88+
}
89+
90+
impl Drop for Stmt {
91+
fn drop(&mut self) {
7092
unsafe {
71-
let status = xdb_commit(self.ptr);
72-
status as u32 == xdb_errno_e_XDB_OK
93+
xdb_stmt_close(self.ptr);
7394
}
7495
}
96+
}
7597

76-
pub fn rollback(&self) -> bool {
98+
impl Stmt {
99+
pub fn exec(&self, params: impl IntoParams) -> Result<ExecResult> {
77100
unsafe {
78-
let status = xdb_rollback(self.ptr);
79-
status as u32 == xdb_errno_e_XDB_OK
101+
let ret = xdb_clear_bindings(self.ptr);
102+
if ret != 0 {
103+
return Err(Error::ClearBindings);
104+
}
105+
params.into_params()?.bind(self.ptr)?;
106+
let ptr = xdb_stmt_exec(self.ptr);
107+
let res = *ptr;
108+
if res.errcode as u32 != xdb_errno_e_XDB_OK {
109+
let msg = CStr::from_ptr(xdb_errmsg(ptr)).to_str()?.to_string();
110+
return Err(Error::Query(res.errcode, msg));
111+
}
112+
let types = ColumnType::all(&res);
113+
Ok(ExecResult { res, ptr, types })
80114
}
81115
}
82116
}
@@ -109,7 +143,7 @@ impl ExecResult {
109143
self.res.affected_rows
110144
}
111145

112-
pub fn column_name<'a>(&'a self, i: usize) -> &'a str {
146+
pub fn column_name(&self, i: usize) -> &str {
113147
unsafe {
114148
let name = xdb_column_name(self.res.col_meta, i as u16);
115149
CStr::from_ptr(name).to_str().unwrap()

src/params.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
use crate::*;
2+
3+
pub enum Value {
4+
Int(i32),
5+
Int64(i64),
6+
Float(f32),
7+
Double(f64),
8+
String(String),
9+
}
10+
11+
trait IntoValue {
12+
fn into_value(self) -> Result<Value>;
13+
}
14+
15+
macro_rules! impl_value {
16+
($t: ty, $v: ident) => {
17+
impl IntoValue for $t {
18+
fn into_value(self) -> Result<Value> {
19+
Ok(Value::$v(self as _))
20+
}
21+
}
22+
};
23+
}
24+
impl_value!(i8, Int);
25+
impl_value!(u8, Int);
26+
impl_value!(i16, Int);
27+
impl_value!(u16, Int);
28+
impl_value!(i32, Int);
29+
impl_value!(u32, Int);
30+
impl_value!(u64, Int64);
31+
impl_value!(i64, Int64);
32+
impl_value!(f32, Float);
33+
impl_value!(f64, Double);
34+
impl_value!(String, String);
35+
36+
impl IntoValue for &str {
37+
fn into_value(self) -> Result<Value> {
38+
Ok(Value::String(self.into()))
39+
}
40+
}
41+
42+
impl IntoValue for Value {
43+
fn into_value(self) -> Result<Value> {
44+
Ok(self)
45+
}
46+
}
47+
48+
pub enum Params {
49+
None,
50+
Positional(Vec<Value>),
51+
}
52+
53+
impl Params {
54+
pub(crate) unsafe fn bind(self, ptr: *mut xdb_stmt_t) -> Result<()> {
55+
if let Params::Positional(params) = self {
56+
for (i, p) in params.into_iter().enumerate() {
57+
let i = i as u16 + 1;
58+
let ret = match p {
59+
ParamValue::Int(v) => xdb_bind_int(ptr, i, v),
60+
ParamValue::Int64(v) => xdb_bind_int64(ptr, i, v),
61+
ParamValue::Float(v) => xdb_bind_float(ptr, i, v),
62+
ParamValue::Double(v) => xdb_bind_double(ptr, i, v),
63+
ParamValue::String(v) => xdb_bind_str(ptr, i, CString::new(v)?.as_ptr()),
64+
};
65+
if ret != 0 {
66+
return Err(Error::BindParams);
67+
}
68+
}
69+
}
70+
Ok(())
71+
}
72+
}
73+
74+
pub trait IntoParams {
75+
fn into_params(self) -> Result<Params>;
76+
}
77+
78+
impl IntoParams for () {
79+
fn into_params(self) -> Result<Params> {
80+
Ok(Params::None)
81+
}
82+
}
83+
84+
impl IntoParams for Params {
85+
fn into_params(self) -> Result<Params> {
86+
Ok(self)
87+
}
88+
}
89+
90+
impl<T: IntoValue> IntoParams for Vec<T> {
91+
fn into_params(self) -> Result<Params> {
92+
let mut params = Vec::with_capacity(self.len());
93+
for param in self {
94+
params.push(param.into_value()?);
95+
}
96+
Ok(Params::Positional(params))
97+
}
98+
}
99+
100+
impl<T: IntoValue + Clone> IntoParams for &[T] {
101+
fn into_params(self) -> Result<Params> {
102+
self.to_vec().into_params()
103+
}
104+
}
105+
106+
impl<T: IntoValue + Clone, const N: usize> IntoParams for &[T; N] {
107+
fn into_params(self) -> Result<Params> {
108+
self.to_vec().into_params()
109+
}
110+
}
111+
112+
// Copy from:https://github.com/tursodatabase/libsql/blob/main/libsql/src/params.rs#L206-L207
113+
macro_rules! tuple_into_params {
114+
($count:literal : $(($field:tt $ftype:ident)),* $(,)?) => {
115+
impl<$($ftype,)*> IntoParams for ($($ftype,)*) where $($ftype: IntoValue,)* {
116+
fn into_params(self) -> Result<Params> {
117+
let params = Params::Positional(vec![$(self.$field.into_value()?),*]);
118+
Ok(params)
119+
}
120+
}
121+
}
122+
}
123+
tuple_into_params!(1: (0 A));
124+
tuple_into_params!(2: (0 A), (1 B));
125+
tuple_into_params!(3: (0 A), (1 B), (2 C));
126+
tuple_into_params!(4: (0 A), (1 B), (2 C), (3 D));
127+
tuple_into_params!(5: (0 A), (1 B), (2 C), (3 D), (4 E));
128+
tuple_into_params!(6: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F));
129+
tuple_into_params!(7: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G));
130+
tuple_into_params!(8: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H));
131+
tuple_into_params!(9: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I));
132+
tuple_into_params!(10: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J));
133+
tuple_into_params!(11: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K));
134+
tuple_into_params!(12: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L));
135+
tuple_into_params!(13: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M));
136+
tuple_into_params!(14: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M), (13 N));
137+
tuple_into_params!(15: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M), (13 N), (14 O));
138+
tuple_into_params!(16: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M), (13 N), (14 O), (15 P));

0 commit comments

Comments
 (0)