Skip to content

Commit f761334

Browse files
authored
Merge pull request #3 from wyhaya/main
Add more apis
2 parents e94482f + 416039d commit f761334

File tree

5 files changed

+223
-34
lines changed

5 files changed

+223
-34
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ fn main() {
1616
println!("Column {i}: {} {}", rst.column_name(i), rst.column_type(i));
1717
}
1818

19-
while let Some(row) = (&mut rst).next() {
19+
while let Some(row) = rst.fetch_row() {
2020
dbg!(row);
2121
}
2222
}
2323
```
2424

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

examples/basic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn main() {
88
println!("Column {i}: {} {}", rst.column_name(i), rst.column_type(i));
99
}
1010

11-
while let Some(row) = (&mut rst).next() {
11+
while let Some(row) = rst.fetch_row() {
1212
dbg!(row);
1313
}
1414
}

src/error.rs

+4
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

+78-32
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 {
@@ -54,26 +56,70 @@ impl Connection {
5456
let msg = CStr::from_ptr(xdb_errmsg(ptr)).to_str()?.to_string();
5557
return Err(Error::Query(res.errcode, msg));
5658
}
57-
Ok(ExecResult {
58-
ptr,
59-
col_meta: res.col_meta,
60-
column_count: res.col_count as usize,
61-
row_count: res.row_count as usize,
62-
column_types: ColumnType::all(&res),
63-
row_index: 0,
64-
})
59+
let types = ColumnType::all(&res);
60+
Ok(ExecResult { res, ptr, types })
61+
}
62+
}
63+
64+
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> {
78+
unsafe {
79+
let sql = CString::new(sql.as_ref())?;
80+
let ptr = xdb_stmt_prepare(self.ptr, sql.as_ptr());
81+
Ok(Stmt { ptr })
82+
}
83+
}
84+
}
85+
86+
pub struct Stmt {
87+
ptr: *mut xdb_stmt_t,
88+
}
89+
90+
impl Drop for Stmt {
91+
fn drop(&mut self) {
92+
unsafe {
93+
xdb_stmt_close(self.ptr);
94+
}
95+
}
96+
}
97+
98+
impl Stmt {
99+
pub fn exec(&self, params: impl IntoParams) -> Result<ExecResult> {
100+
unsafe {
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 })
65114
}
66115
}
67116
}
68117

69118
#[derive(Debug)]
70119
pub struct ExecResult {
120+
res: xdb_res_t,
71121
ptr: *mut xdb_res_t,
72-
col_meta: u64,
73-
column_count: usize,
74-
row_count: usize,
75-
column_types: Vec<ColumnType>,
76-
row_index: usize,
122+
types: Vec<ColumnType>,
77123
}
78124

79125
impl Drop for ExecResult {
@@ -86,41 +132,41 @@ impl Drop for ExecResult {
86132

87133
impl ExecResult {
88134
pub fn column_count(&self) -> usize {
89-
self.column_count
135+
self.res.col_count as usize
90136
}
91137

92138
pub fn row_count(&self) -> usize {
93-
self.row_count
139+
self.res.row_count as usize
140+
}
141+
142+
pub fn affected_rows(&self) -> u64 {
143+
self.res.affected_rows
94144
}
95145

96-
pub fn column_name<'a>(&'a self, i: usize) -> &'a str {
146+
pub fn column_name(&self, i: usize) -> &str {
97147
unsafe {
98-
let name = xdb_column_name(self.col_meta, i as u16);
148+
let name = xdb_column_name(self.res.col_meta, i as u16);
99149
CStr::from_ptr(name).to_str().unwrap()
100150
}
101151
}
102152

103153
pub fn column_type(&self, i: usize) -> ColumnType {
104-
self.column_types[i]
154+
self.types[i]
105155
}
106-
}
107-
108-
impl<'a> Iterator for &'a mut ExecResult {
109-
type Item = Vec<Value<'a>>;
110156

111-
fn next(&mut self) -> Option<Self::Item> {
112-
if self.row_count <= self.row_index {
113-
return None;
114-
}
115-
let mut values = Vec::with_capacity(self.column_count);
157+
pub fn fetch_row(&mut self) -> Option<Vec<Value<'_>>> {
116158
unsafe {
117-
let y = xdb_fetch_row(self.ptr);
118-
for x in 0..self.column_count {
119-
let value = Value::from_result(self.col_meta, y, x as u16, self.column_type(x));
159+
let row = xdb_fetch_row(self.ptr);
160+
if row.is_null() {
161+
return None;
162+
}
163+
let mut values = Vec::with_capacity(self.column_count());
164+
for col in 0..self.column_count() {
165+
let value =
166+
Value::from_result(self.res.col_meta, row, col as u16, self.column_type(col));
120167
values.push(value);
121168
}
169+
Some(values)
122170
}
123-
self.row_index += 1;
124-
Some(values)
125171
}
126172
}

src/params.rs

+138
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)