mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-11 19:24:21 +01:00
Adjust types in extension API
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -1260,6 +1260,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "limbo_extension"
|
||||
version = "0.0.11"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "limbo_macros"
|
||||
@@ -1294,6 +1297,7 @@ name = "limbo_uuid"
|
||||
version = "0.0.11"
|
||||
dependencies = [
|
||||
"limbo_extension",
|
||||
"log",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::{function::ExternalFunc, Database};
|
||||
pub use limbo_extension::{
|
||||
Blob as ExtBlob, TextValue as ExtTextValue, Value as ExtValue, ValueType as ExtValueType,
|
||||
};
|
||||
use limbo_extension::{ExtensionApi, ResultCode, ScalarFunction, RESULT_ERROR, RESULT_OK};
|
||||
pub use limbo_extension::{Value as ExtValue, ValueType as ExtValueType};
|
||||
use std::{
|
||||
ffi::{c_char, c_void, CStr},
|
||||
rc::Rc,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::error::LimboError;
|
||||
use crate::ext::{ExtValue, ExtValueType};
|
||||
use crate::ext::{ExtBlob, ExtTextValue, ExtValue, ExtValueType};
|
||||
use crate::storage::sqlite3_ondisk::write_varint;
|
||||
use crate::Result;
|
||||
use std::fmt::Display;
|
||||
@@ -92,37 +92,54 @@ impl Display for OwnedValue {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OwnedValue {
|
||||
pub fn to_ffi(&self) -> ExtValue {
|
||||
match self {
|
||||
Self::Null => ExtValue::null(),
|
||||
Self::Integer(i) => ExtValue::from_integer(*i),
|
||||
Self::Float(fl) => ExtValue::from_float(*fl),
|
||||
Self::Text(s) => ExtValue::from_text(s.value.to_string()),
|
||||
Self::Blob(b) => ExtValue::from_blob(b),
|
||||
Self::Agg(_) => todo!(),
|
||||
Self::Record(_) => todo!(),
|
||||
Self::Text(text) => ExtValue::from_text(text.value.to_string()),
|
||||
Self::Blob(blob) => ExtValue::from_blob(blob.to_vec()),
|
||||
Self::Agg(_) => todo!("Aggregate values not yet supported"),
|
||||
Self::Record(_) => todo!("Record values not yet supported"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_ffi(v: &ExtValue) -> Self {
|
||||
if v.value.is_null() {
|
||||
return OwnedValue::Null;
|
||||
}
|
||||
match v.value_type {
|
||||
ExtValueType::Null => OwnedValue::Null,
|
||||
ExtValueType::Integer => OwnedValue::Integer(v.integer),
|
||||
ExtValueType::Float => OwnedValue::Float(v.float),
|
||||
ExtValueType::Integer => {
|
||||
let int_ptr = v.value as *mut i64;
|
||||
let integer = unsafe { *int_ptr };
|
||||
OwnedValue::Integer(integer)
|
||||
}
|
||||
ExtValueType::Float => {
|
||||
let float_ptr = v.value as *mut f64;
|
||||
let float = unsafe { *float_ptr };
|
||||
OwnedValue::Float(float)
|
||||
}
|
||||
ExtValueType::Text => {
|
||||
if v.text.is_null() {
|
||||
if v.value.is_null() {
|
||||
OwnedValue::Null
|
||||
} else {
|
||||
OwnedValue::build_text(std::rc::Rc::new(v.text.to_string()))
|
||||
let Some(text) = ExtTextValue::from_value(v) else {
|
||||
return OwnedValue::Null;
|
||||
};
|
||||
OwnedValue::build_text(std::rc::Rc::new(unsafe { text.as_str().to_string() }))
|
||||
}
|
||||
}
|
||||
ExtValueType::Blob => {
|
||||
if v.blob.data.is_null() {
|
||||
OwnedValue::Null
|
||||
} else {
|
||||
let bytes = unsafe { std::slice::from_raw_parts(v.blob.data, v.blob.size) };
|
||||
OwnedValue::Blob(std::rc::Rc::new(bytes.to_vec()))
|
||||
}
|
||||
let blob_ptr = v.value as *mut ExtBlob;
|
||||
let blob = unsafe {
|
||||
let slice =
|
||||
std::slice::from_raw_parts((*blob_ptr).data, (*blob_ptr).size as usize);
|
||||
slice.to_vec()
|
||||
};
|
||||
OwnedValue::Blob(std::rc::Rc::new(blob))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
limbo_extension = { path = "../../limbo_extension"}
|
||||
uuid = { version = "1.11.0", features = ["v4", "v7"] }
|
||||
log = "0.4.20"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use limbo_extension::{
|
||||
declare_scalar_functions, register_extension, register_scalar_functions, Value,
|
||||
declare_scalar_functions, register_extension, register_scalar_functions, Blob, TextValue, Value,
|
||||
};
|
||||
|
||||
register_extension! {
|
||||
@@ -17,46 +17,47 @@ declare_scalar_functions! {
|
||||
let uuid = uuid::Uuid::new_v4().to_string();
|
||||
Value::from_text(uuid)
|
||||
}
|
||||
|
||||
#[args(min = 0, max = 0)]
|
||||
fn uuid4_blob(_args: &[Value]) -> Value {
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
let bytes = uuid.as_bytes();
|
||||
Value::from_blob(bytes)
|
||||
Value::from_blob(bytes.to_vec())
|
||||
}
|
||||
|
||||
#[args(min = 1, max = 1)]
|
||||
fn uuid_str(args: &[Value]) -> Value {
|
||||
if args.len() != 1 {
|
||||
return Value::null();
|
||||
}
|
||||
if args[0].value_type != limbo_extension::ValueType::Blob {
|
||||
log::debug!("uuid_str was passed a non-blob arg");
|
||||
return Value::null();
|
||||
}
|
||||
let data_ptr = args[0].blob.data;
|
||||
let size = args[0].blob.size;
|
||||
if data_ptr.is_null() || size != 16 {
|
||||
return Value::null();
|
||||
}
|
||||
let slice = unsafe{ std::slice::from_raw_parts(data_ptr, size)};
|
||||
if let Some(blob) = Blob::from_value(&args[0]) {
|
||||
let slice = unsafe{ std::slice::from_raw_parts(blob.data, blob.size as usize)};
|
||||
let parsed = uuid::Uuid::from_slice(slice).ok().map(|u| u.to_string());
|
||||
match parsed {
|
||||
Some(s) => Value::from_text(s),
|
||||
None => Value::null()
|
||||
}
|
||||
} else {
|
||||
Value::null()
|
||||
}
|
||||
}
|
||||
|
||||
#[args(min = 1, max = 1)]
|
||||
fn uuid_blob(args: &[Value]) -> Value {
|
||||
if args.len() != 1 {
|
||||
return Value::null();
|
||||
}
|
||||
if args[0].value_type != limbo_extension::ValueType::Text {
|
||||
log::debug!("uuid_blob was passed a non-text arg");
|
||||
return Value::null();
|
||||
}
|
||||
let text = args[0].text.to_string();
|
||||
match uuid::Uuid::parse_str(&text) {
|
||||
Ok(uuid) => Value::from_blob(uuid.as_bytes()),
|
||||
if let Some(text) = TextValue::from_value(&args[0]) {
|
||||
match uuid::Uuid::parse_str(unsafe {text.as_str()}) {
|
||||
Ok(uuid) => {
|
||||
Value::from_blob(uuid.as_bytes().to_vec())
|
||||
}
|
||||
Err(_) => Value::null()
|
||||
}
|
||||
} else {
|
||||
Value::null()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,3 +7,4 @@ license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.20"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
|
||||
pub type ResultCode = i32;
|
||||
@@ -76,8 +75,7 @@ macro_rules! declare_scalar_functions {
|
||||
argv: *const *const std::os::raw::c_void
|
||||
) -> $crate::Value {
|
||||
if !($min_args..=$max_args).contains(&argc) {
|
||||
println!("{}: Invalid argument count", stringify!($func_name));
|
||||
return $crate::Value::null();// TODO: error code
|
||||
return $crate::Value::null();
|
||||
}
|
||||
if argc == 0 || argv.is_null() {
|
||||
let $args: &[$crate::Value] = &[];
|
||||
@@ -103,8 +101,8 @@ macro_rules! declare_scalar_functions {
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum ValueType {
|
||||
Null,
|
||||
Integer,
|
||||
@@ -113,45 +111,20 @@ pub enum ValueType {
|
||||
Blob,
|
||||
}
|
||||
|
||||
// TODO: perf, these can be better expressed
|
||||
#[repr(C)]
|
||||
pub struct Value {
|
||||
pub value_type: ValueType,
|
||||
pub integer: i64,
|
||||
pub float: f64,
|
||||
pub text: TextValue,
|
||||
pub blob: Blob,
|
||||
pub value: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TextValue {
|
||||
text: *const c_char,
|
||||
len: usize,
|
||||
pub text: *const u8,
|
||||
pub len: u32,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TextValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.text.is_null() {
|
||||
return write!(f, "<null>");
|
||||
}
|
||||
let slice = unsafe { std::slice::from_raw_parts(self.text as *const u8, self.len) };
|
||||
match std::str::from_utf8(slice) {
|
||||
Ok(s) => write!(f, "{}", s),
|
||||
Err(e) => write!(f, "<invalid UTF-8: {:?}>", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TextValue {
|
||||
pub fn is_null(&self) -> bool {
|
||||
self.text.is_null()
|
||||
}
|
||||
|
||||
pub fn new(text: *const c_char, len: usize) -> Self {
|
||||
Self { text, len }
|
||||
}
|
||||
|
||||
pub fn null() -> Self {
|
||||
impl Default for TextValue {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
text: std::ptr::null(),
|
||||
len: 0,
|
||||
@@ -159,21 +132,49 @@ impl TextValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl TextValue {
|
||||
pub fn new(text: *const u8, len: usize) -> Self {
|
||||
Self {
|
||||
text,
|
||||
len: len as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_value(value: &Value) -> Option<&Self> {
|
||||
if value.value_type != ValueType::Text {
|
||||
return None;
|
||||
}
|
||||
unsafe { Some(&*(value.value as *const TextValue)) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// The caller must ensure that the text is a valid UTF-8 string
|
||||
pub unsafe fn as_str(&self) -> &str {
|
||||
if self.text.is_null() {
|
||||
return "";
|
||||
}
|
||||
unsafe {
|
||||
std::str::from_utf8_unchecked(std::slice::from_raw_parts(self.text, self.len as usize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Blob {
|
||||
pub data: *const u8,
|
||||
pub size: usize,
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
impl Blob {
|
||||
pub fn new(data: *const u8, size: usize) -> Self {
|
||||
pub fn new(data: *const u8, size: u64) -> Self {
|
||||
Self { data, size }
|
||||
}
|
||||
pub fn null() -> Self {
|
||||
Self {
|
||||
data: std::ptr::null(),
|
||||
size: 0,
|
||||
|
||||
pub fn from_value(value: &Value) -> Option<&Self> {
|
||||
if value.value_type != ValueType::Blob {
|
||||
return None;
|
||||
}
|
||||
unsafe { Some(&*(value.value as *const Blob)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,53 +182,65 @@ impl Value {
|
||||
pub fn null() -> Self {
|
||||
Self {
|
||||
value_type: ValueType::Null,
|
||||
integer: 0,
|
||||
float: 0.0,
|
||||
text: TextValue::null(),
|
||||
blob: Blob::null(),
|
||||
value: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_integer(value: i64) -> Self {
|
||||
let boxed = Box::new(value);
|
||||
Self {
|
||||
value_type: ValueType::Integer,
|
||||
integer: value,
|
||||
float: 0.0,
|
||||
text: TextValue::null(),
|
||||
blob: Blob::null(),
|
||||
value: Box::into_raw(boxed) as *mut c_void,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_float(value: f64) -> Self {
|
||||
let boxed = Box::new(value);
|
||||
Self {
|
||||
value_type: ValueType::Float,
|
||||
integer: 0,
|
||||
float: value,
|
||||
text: TextValue::null(),
|
||||
blob: Blob::null(),
|
||||
value: Box::into_raw(boxed) as *mut c_void,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_text(value: String) -> Self {
|
||||
let cstr = CString::new(&*value).unwrap();
|
||||
let ptr = cstr.as_ptr();
|
||||
let len = value.len();
|
||||
std::mem::forget(cstr);
|
||||
pub fn from_text(s: String) -> Self {
|
||||
let text_value = TextValue::new(s.as_ptr(), s.len());
|
||||
let boxed_text = Box::new(text_value);
|
||||
std::mem::forget(s);
|
||||
Self {
|
||||
value_type: ValueType::Text,
|
||||
integer: 0,
|
||||
float: 0.0,
|
||||
text: TextValue::new(ptr, len),
|
||||
blob: Blob::null(),
|
||||
value: Box::into_raw(boxed_text) as *mut c_void,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_blob(value: &[u8]) -> Self {
|
||||
pub fn from_blob(value: Vec<u8>) -> Self {
|
||||
let boxed = Box::new(Blob::new(value.as_ptr(), value.len() as u64));
|
||||
std::mem::forget(value);
|
||||
Self {
|
||||
value_type: ValueType::Blob,
|
||||
integer: 0,
|
||||
float: 0.0,
|
||||
text: TextValue::null(),
|
||||
blob: Blob::new(value.as_ptr(), value.len()),
|
||||
value: Box::into_raw(boxed) as *mut c_void,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn free(&mut self) {
|
||||
if self.value.is_null() {
|
||||
return;
|
||||
}
|
||||
match self.value_type {
|
||||
ValueType::Integer => {
|
||||
let _ = Box::from_raw(self.value as *mut i64);
|
||||
}
|
||||
ValueType::Float => {
|
||||
let _ = Box::from_raw(self.value as *mut f64);
|
||||
}
|
||||
ValueType::Text => {
|
||||
let _ = Box::from_raw(self.value as *mut TextValue);
|
||||
}
|
||||
ValueType::Blob => {
|
||||
let _ = Box::from_raw(self.value as *mut Blob);
|
||||
}
|
||||
ValueType::Null => {}
|
||||
}
|
||||
|
||||
self.value = std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user