use crate::internal::ffi;
use crate::sql::types::SqlValue;
use abi_stable::derive_macro_reexports::RResult;
use abi_stable::std_types::{ROk, RVec};
use serde::de::DeserializeOwned;
use serde::Deserialize;
use std::collections::HashMap;
use tarantool::error::{BoxError, IntoBoxError, TarantoolErrorCode};
use tarantool::tuple::Tuple;
pub mod types;
pub fn query_raw(query: &str, params: Vec<SqlValue>) -> Result<Tuple, BoxError> {
let query_len = query.len();
let query_ptr = query.as_ptr();
let res = unsafe { ffi::pico_ffi_sql_query(query_ptr, query_len, RVec::from(params)) };
let ptr = match res {
ROk(v) => v,
RResult::RErr(_) => return Err(BoxError::last()),
};
let tuple = unsafe { Tuple::try_from_ptr_dont_ref(ptr) };
let tuple = tuple.expect("always non null");
Ok(tuple)
}
pub struct Query<'a> {
query: &'a str,
params: Vec<SqlValue>,
}
impl Query<'_> {
#[inline(always)]
pub fn bind<T: Into<SqlValue>>(mut self, value: T) -> Self {
self.params.push(value.into());
self
}
pub fn execute(self) -> Result<u64, BoxError> {
let tuple = query_raw(self.query, self.params)?;
#[derive(Deserialize)]
struct Output {
row_count: u64,
}
let result = tuple
.decode::<Vec<Output>>()
.map_err(|tt| tt.into_box_error())?;
let result = result.first().ok_or_else(|| {
BoxError::new(
TarantoolErrorCode::InvalidMsgpack,
"sql result should contains at least one row",
)
})?;
Ok(result.row_count)
}
pub fn fetch<T: DeserializeOwned>(self) -> Result<Vec<T>, BoxError> {
let tuple = query_raw(self.query, self.params)?;
let mut res = tuple
.decode::<Vec<HashMap<String, rmpv::Value>>>()
.map_err(|tt| tt.into_box_error())?;
let Some(mut map) = res.pop() else {
return Err(BoxError::new(
TarantoolErrorCode::InvalidMsgpack,
"fetch result array should contains at least one element",
));
};
let Some(rows) = map.remove("rows") else {
return Err(BoxError::new(
TarantoolErrorCode::InvalidMsgpack,
"fetch result map should contains `rows` key",
));
};
let data: Vec<T> = rmpv::ext::from_value(rows)
.map_err(|e| BoxError::new(TarantoolErrorCode::InvalidMsgpack, e.to_string()))?;
Ok(data)
}
}
pub fn query(query: &str) -> Query<'_> {
Query {
query,
params: vec![],
}
}