use std::os::raw::c_char;
use std::panic;
use crate::core::types::DataType;
use crate::core::Value;
use super::types::StoolapRows;
use super::{
STOOLAP_DONE, STOOLAP_ERROR, STOOLAP_OK, STOOLAP_ROW, STOOLAP_TYPE_BLOB, STOOLAP_TYPE_BOOLEAN,
STOOLAP_TYPE_FLOAT, STOOLAP_TYPE_INTEGER, STOOLAP_TYPE_JSON, STOOLAP_TYPE_NULL,
STOOLAP_TYPE_TEXT, STOOLAP_TYPE_TIMESTAMP,
};
fn make_text_buf(s: &str) -> Vec<u8> {
let mut buf = Vec::with_capacity(s.len() + 1);
buf.extend_from_slice(s.as_bytes());
buf.push(0); buf
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_next(rows: *mut StoolapRows) -> i32 {
let handle = match rows.as_mut() {
Some(h) => h,
None => return STOOLAP_ERROR,
};
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let rows_inner = match &mut handle.rows {
Some(r) => r,
None => return STOOLAP_DONE,
};
if handle.text_cache_dirty {
for slot in &mut handle.text_cache {
*slot = None;
}
handle.text_cache_dirty = false;
}
if rows_inner.advance() {
handle.has_row = true;
STOOLAP_ROW
} else {
handle.has_row = false;
if let Some(err) = rows_inner.error() {
handle.set_error(&err.to_string());
return STOOLAP_ERROR;
}
STOOLAP_DONE
}
}));
result.unwrap_or_else(|_| {
handle.set_error("panic during stoolap_rows_next");
STOOLAP_ERROR
})
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_count(rows: *const StoolapRows) -> i32 {
match rows.as_ref() {
Some(handle) => handle.column_names.len() as i32,
None => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_name(
rows: *const StoolapRows,
index: i32,
) -> *const c_char {
let handle = match rows.as_ref() {
Some(h) => h,
None => return std::ptr::null(),
};
if index < 0 {
return std::ptr::null();
}
match handle.column_names.get(index as usize) {
Some(cs) => cs.as_ptr(),
None => std::ptr::null(),
}
}
unsafe fn get_current_value(handle: &StoolapRows, index: i32) -> Option<&Value> {
if !handle.has_row || index < 0 {
return None;
}
let rows_inner = handle.rows.as_ref()?;
let row = rows_inner.current_row();
row.get(index as usize)
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_type(rows: *const StoolapRows, index: i32) -> i32 {
let handle = match rows.as_ref() {
Some(h) => h,
None => return STOOLAP_TYPE_NULL,
};
match get_current_value(handle, index) {
Some(val) => {
if val.is_null() {
return STOOLAP_TYPE_NULL;
}
match val.data_type() {
DataType::Null => STOOLAP_TYPE_NULL,
DataType::Integer => STOOLAP_TYPE_INTEGER,
DataType::Float => STOOLAP_TYPE_FLOAT,
DataType::Text => STOOLAP_TYPE_TEXT,
DataType::Boolean => STOOLAP_TYPE_BOOLEAN,
DataType::Timestamp => STOOLAP_TYPE_TIMESTAMP,
DataType::Json => STOOLAP_TYPE_JSON,
DataType::Vector => STOOLAP_TYPE_BLOB,
}
}
None => STOOLAP_TYPE_NULL,
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_int64(rows: *const StoolapRows, index: i32) -> i64 {
let handle = match rows.as_ref() {
Some(h) => h,
None => return 0,
};
match get_current_value(handle, index) {
Some(val) => val.as_int64().unwrap_or(0),
None => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_double(rows: *const StoolapRows, index: i32) -> f64 {
let handle = match rows.as_ref() {
Some(h) => h,
None => return 0.0,
};
match get_current_value(handle, index) {
Some(val) => val.as_float64().unwrap_or(0.0),
None => 0.0,
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_text(
rows: *mut StoolapRows,
index: i32,
out_len: *mut i64,
) -> *const c_char {
let handle = match rows.as_mut() {
Some(h) => h,
None => return std::ptr::null(),
};
if !handle.has_row || index < 0 {
return std::ptr::null();
}
let idx = index as usize;
if let Some(Some(ref cached)) = handle.text_cache.get(idx) {
if !out_len.is_null() {
*out_len = (cached.len() - 1) as i64;
}
return cached.as_ptr() as *const c_char;
}
let buf = {
let rows_inner = match &handle.rows {
Some(r) => r,
None => return std::ptr::null(),
};
let row = rows_inner.current_row();
match row.get(idx) {
Some(val) => {
if let Some(s) = val.as_str() {
Some(make_text_buf(s))
} else {
val.as_string().map(|s| make_text_buf(&s))
}
}
None => None,
}
};
match buf {
Some(b) => {
if handle.text_cache.len() <= idx {
handle.text_cache.resize_with(idx + 1, || None);
}
handle.text_cache[idx] = Some(b);
handle.text_cache_dirty = true;
let cached = handle.text_cache[idx].as_ref().unwrap();
if !out_len.is_null() {
*out_len = (cached.len() - 1) as i64;
}
cached.as_ptr() as *const c_char
}
None => std::ptr::null(),
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_bool(rows: *const StoolapRows, index: i32) -> i32 {
let handle = match rows.as_ref() {
Some(h) => h,
None => return 0,
};
match get_current_value(handle, index) {
Some(val) => {
if val.as_boolean().unwrap_or(false) {
1
} else {
0
}
}
None => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_timestamp(
rows: *const StoolapRows,
index: i32,
) -> i64 {
let handle = match rows.as_ref() {
Some(h) => h,
None => return 0,
};
match get_current_value(handle, index) {
Some(val) => match val.as_timestamp() {
Some(ts) => ts.timestamp_nanos_opt().unwrap_or(0),
None => 0,
},
None => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_blob(
rows: *const StoolapRows,
index: i32,
out_len: *mut i64,
) -> *const u8 {
if !out_len.is_null() {
*out_len = 0;
}
let handle = match rows.as_ref() {
Some(h) => h,
None => return std::ptr::null(),
};
if !handle.has_row || index < 0 {
return std::ptr::null();
}
let rows_inner = match &handle.rows {
Some(r) => r,
None => return std::ptr::null(),
};
let row = rows_inner.current_row();
match row.get(index as usize) {
Some(Value::Extension(data)) if data.first() == Some(&(DataType::Vector as u8)) => {
let payload = &data[1..];
if !out_len.is_null() {
*out_len = payload.len() as i64;
}
payload.as_ptr()
}
_ => std::ptr::null(),
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_column_is_null(rows: *const StoolapRows, index: i32) -> i32 {
let handle = match rows.as_ref() {
Some(h) => h,
None => return 1,
};
match get_current_value(handle, index) {
Some(val) => {
if val.is_null() {
1
} else {
0
}
}
None => 1,
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_affected(rows: *const StoolapRows) -> i64 {
match rows.as_ref() {
Some(handle) => handle.rows_affected,
None => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_fetch_all(
rows: *mut StoolapRows,
out_buf: *mut *mut u8,
out_len: *mut i64,
) -> i32 {
if out_buf.is_null() || out_len.is_null() {
return STOOLAP_ERROR;
}
*out_buf = std::ptr::null_mut();
*out_len = 0;
let handle = match rows.as_mut() {
Some(h) => h,
None => return STOOLAP_ERROR,
};
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let rows_inner = match &mut handle.rows {
Some(r) => r,
None => {
let col_count = handle.column_names.len() as u32;
let mut buf = Vec::with_capacity(8);
buf.extend_from_slice(&col_count.to_le_bytes());
for name in handle.column_names.iter() {
let name_bytes = name.to_bytes();
buf.extend_from_slice(&(name_bytes.len() as u16).to_le_bytes());
buf.extend_from_slice(name_bytes);
}
buf.extend_from_slice(&0u32.to_le_bytes()); let boxed = buf.into_boxed_slice();
let len = boxed.len();
let ptr = Box::into_raw(boxed) as *mut u8;
*out_buf = ptr;
*out_len = len as i64;
return STOOLAP_OK;
}
};
let mut buf = Vec::with_capacity(4096);
let col_count = handle.column_names.len() as u32;
buf.extend_from_slice(&col_count.to_le_bytes());
for name in handle.column_names.iter() {
let name_bytes = name.to_bytes();
buf.extend_from_slice(&(name_bytes.len() as u16).to_le_bytes());
buf.extend_from_slice(name_bytes);
}
let row_count_offset = buf.len();
buf.extend_from_slice(&0u32.to_le_bytes());
let mut row_count: u32 = 0;
while rows_inner.advance() {
let row = rows_inner.current_row();
for i in 0..col_count as usize {
match row.get(i) {
None => {
buf.push(0); }
Some(val) if val.is_null() => {
buf.push(0); }
Some(val) => match val.data_type() {
DataType::Null => {
buf.push(0);
}
DataType::Integer => {
buf.push(1);
let v = val.as_int64().unwrap_or(0);
buf.extend_from_slice(&v.to_le_bytes());
}
DataType::Float => {
buf.push(2);
let v = val.as_float64().unwrap_or(0.0);
buf.extend_from_slice(&v.to_le_bytes());
}
DataType::Text => {
buf.push(3);
let s = val.as_str().unwrap_or("");
buf.extend_from_slice(&(s.len() as u32).to_le_bytes());
buf.extend_from_slice(s.as_bytes());
}
DataType::Boolean => {
buf.push(4);
buf.push(if val.as_boolean().unwrap_or(false) {
1
} else {
0
});
}
DataType::Timestamp => {
buf.push(5);
let nanos = val
.as_timestamp()
.map(|ts| ts.timestamp_nanos_opt().unwrap_or(0))
.unwrap_or(0);
buf.extend_from_slice(&nanos.to_le_bytes());
}
DataType::Json => {
buf.push(6);
let s = val.as_str().unwrap_or("");
buf.extend_from_slice(&(s.len() as u32).to_le_bytes());
buf.extend_from_slice(s.as_bytes());
}
DataType::Vector => {
buf.push(7);
if let Value::Extension(data) = val {
if data.first() == Some(&(DataType::Vector as u8)) {
let payload = &data[1..];
buf.extend_from_slice(&(payload.len() as u32).to_le_bytes());
buf.extend_from_slice(payload);
} else {
buf.extend_from_slice(&0u32.to_le_bytes());
}
} else {
buf.extend_from_slice(&0u32.to_le_bytes());
}
}
},
}
}
row_count += 1;
}
buf[row_count_offset..row_count_offset + 4].copy_from_slice(&row_count.to_le_bytes());
let boxed = buf.into_boxed_slice();
let len = boxed.len();
let ptr = Box::into_raw(boxed) as *mut u8;
*out_buf = ptr;
*out_len = len as i64;
STOOLAP_OK
}));
handle.rows = None;
handle.has_row = false;
result.unwrap_or_else(|_| {
handle.set_error("panic during stoolap_rows_fetch_all");
STOOLAP_ERROR
})
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_buffer_free(buf: *mut u8, len: i64) {
if !buf.is_null() && len > 0 {
let _ = Vec::from_raw_parts(buf, len as usize, len as usize);
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_close(rows: *mut StoolapRows) {
if !rows.is_null() {
let _ = Box::from_raw(rows);
}
}
#[no_mangle]
pub unsafe extern "C" fn stoolap_rows_errmsg(rows: *const StoolapRows) -> *const c_char {
match rows.as_ref() {
Some(handle) => handle.error_ptr(),
None => super::error::empty_cstr(),
}
}