mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-23 00:45:37 +01:00
Create ext directory for outside funcs, add uuid to ext dir
This commit is contained in:
@@ -22,6 +22,7 @@ json = [
|
||||
"dep:pest",
|
||||
"dep:pest_derive",
|
||||
]
|
||||
uuid = ["dep:uuid"]
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
io-uring = "0.6.1"
|
||||
|
||||
30
core/ext/mod.rs
Normal file
30
core/ext/mod.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
#[cfg(feature = "uuid")]
|
||||
mod uuid;
|
||||
#[cfg(feature = "uuid")]
|
||||
pub use uuid::{exec_ts_from_uuid7, exec_uuid, exec_uuidblob, exec_uuidstr, UuidFunc};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ExtFunc {
|
||||
#[cfg(feature = "uuid")]
|
||||
Uuid(UuidFunc),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ExtFunc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
#[cfg(feature = "uuid")]
|
||||
ExtFunc::Uuid(uuidfn) => write!(f, "{}", uuidfn),
|
||||
_ => write!(f, "unknown"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtFunc {
|
||||
pub fn resolve_function(name: &str, num_args: usize) -> Result<ExtFunc, ()> {
|
||||
match name {
|
||||
#[cfg(feature = "uuid")]
|
||||
name => UuidFunc::resolve_function(name, num_args),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
334
core/ext/uuid.rs
Normal file
334
core/ext/uuid.rs
Normal file
@@ -0,0 +1,334 @@
|
||||
use super::ExtFunc;
|
||||
use crate::{types::OwnedValue, LimboError};
|
||||
use std::rc::Rc;
|
||||
use uuid::{ContextV7, Timestamp, Uuid};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum UuidFunc {
|
||||
Uuid4Str,
|
||||
Uuid4,
|
||||
Uuid7,
|
||||
Uuid7TS,
|
||||
UuidStr,
|
||||
UuidBlob,
|
||||
}
|
||||
|
||||
impl UuidFunc {
|
||||
pub fn resolve_function(name: &str, num_args: usize) -> Result<ExtFunc, ()> {
|
||||
match name {
|
||||
"uuid4_str" => Ok(ExtFunc::Uuid(UuidFunc::Uuid4Str)),
|
||||
"uuid4" => Ok(ExtFunc::Uuid(UuidFunc::Uuid4)),
|
||||
"uuid7" if num_args < 2 => Ok(ExtFunc::Uuid(UuidFunc::Uuid7)),
|
||||
"uuid_str" if num_args == 1 => Ok(ExtFunc::Uuid(UuidFunc::UuidStr)),
|
||||
"uuid_blob" if num_args == 1 => Ok(ExtFunc::Uuid(UuidFunc::UuidBlob)),
|
||||
"uuid7_timestamp_ms" if num_args == 1 => Ok(ExtFunc::Uuid(UuidFunc::Uuid7TS)),
|
||||
// postgres_compatability
|
||||
"gen_random_uuid" => Ok(ExtFunc::Uuid(UuidFunc::Uuid4Str)),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UuidFunc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
UuidFunc::Uuid4Str => write!(f, "uuid4_str"),
|
||||
UuidFunc::Uuid4 => write!(f, "uuid4"),
|
||||
UuidFunc::Uuid7 => write!(f, "uuid7"),
|
||||
UuidFunc::Uuid7TS => write!(f, "uuid7_timestamp_ms"),
|
||||
UuidFunc::UuidStr => write!(f, "uuid_str"),
|
||||
UuidFunc::UuidBlob => write!(f, "uuid_blob"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_uuid(var: &UuidFunc, sec: Option<&OwnedValue>) -> crate::Result<OwnedValue> {
|
||||
match var {
|
||||
UuidFunc::Uuid4 => Ok(OwnedValue::Blob(Rc::new(
|
||||
Uuid::new_v4().into_bytes().to_vec(),
|
||||
))),
|
||||
UuidFunc::Uuid4Str => Ok(OwnedValue::Text(Rc::new(Uuid::new_v4().to_string()))),
|
||||
UuidFunc::Uuid7 => {
|
||||
let uuid = match sec {
|
||||
Some(OwnedValue::Integer(ref seconds)) => {
|
||||
let ctx = ContextV7::new();
|
||||
if *seconds < 0 {
|
||||
// not valid unix timestamp, error or null?
|
||||
return Ok(OwnedValue::Null);
|
||||
}
|
||||
Uuid::new_v7(Timestamp::from_unix(ctx, *seconds as u64, 0))
|
||||
}
|
||||
_ => Uuid::now_v7(),
|
||||
};
|
||||
Ok(OwnedValue::Blob(Rc::new(uuid.into_bytes().to_vec())))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_uuidstr(reg: &OwnedValue) -> crate::Result<OwnedValue> {
|
||||
match reg {
|
||||
OwnedValue::Blob(blob) => {
|
||||
let uuid = Uuid::from_slice(blob).map_err(|e| LimboError::ParseError(e.to_string()))?;
|
||||
Ok(OwnedValue::Text(Rc::new(uuid.to_string())))
|
||||
}
|
||||
OwnedValue::Text(val) => {
|
||||
let uuid = Uuid::parse_str(val).map_err(|e| LimboError::ParseError(e.to_string()))?;
|
||||
Ok(OwnedValue::Text(Rc::new(uuid.to_string())))
|
||||
}
|
||||
OwnedValue::Null => Ok(OwnedValue::Null),
|
||||
_ => Err(LimboError::ParseError(
|
||||
"Invalid argument type for UUID function".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_uuidblob(reg: &OwnedValue) -> crate::Result<OwnedValue> {
|
||||
match reg {
|
||||
OwnedValue::Text(val) => {
|
||||
let uuid = Uuid::parse_str(val).map_err(|e| LimboError::ParseError(e.to_string()))?;
|
||||
Ok(OwnedValue::Blob(Rc::new(uuid.as_bytes().to_vec())))
|
||||
}
|
||||
OwnedValue::Blob(blob) => {
|
||||
let uuid = Uuid::from_slice(blob).map_err(|e| LimboError::ParseError(e.to_string()))?;
|
||||
Ok(OwnedValue::Blob(Rc::new(uuid.as_bytes().to_vec())))
|
||||
}
|
||||
OwnedValue::Null => Ok(OwnedValue::Null),
|
||||
_ => Err(LimboError::ParseError(
|
||||
"Invalid argument type for UUID function".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_ts_from_uuid7(reg: &OwnedValue) -> OwnedValue {
|
||||
let uuid = match reg {
|
||||
OwnedValue::Blob(blob) => {
|
||||
Uuid::from_slice(blob).map_err(|e| LimboError::ParseError(e.to_string()))
|
||||
}
|
||||
OwnedValue::Text(val) => {
|
||||
Uuid::parse_str(val).map_err(|e| LimboError::ParseError(e.to_string()))
|
||||
}
|
||||
_ => Err(LimboError::ParseError(
|
||||
"Invalid argument type for UUID function".to_string(),
|
||||
)),
|
||||
};
|
||||
match uuid {
|
||||
Ok(uuid) => OwnedValue::Integer(uuid_to_unix(uuid.as_bytes()) as i64),
|
||||
// display error? sqlean seems to set value to null
|
||||
Err(_) => OwnedValue::Null,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn uuid_to_unix(uuid: &[u8; 16]) -> u64 {
|
||||
((uuid[0] as u64) << 40)
|
||||
| ((uuid[1] as u64) << 32)
|
||||
| ((uuid[2] as u64) << 24)
|
||||
| ((uuid[3] as u64) << 16)
|
||||
| ((uuid[4] as u64) << 8)
|
||||
| (uuid[5] as u64)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "uuid")]
|
||||
pub mod test {
|
||||
use super::UuidFunc;
|
||||
use crate::types::OwnedValue;
|
||||
#[test]
|
||||
fn test_exec_uuid_v4blob() {
|
||||
use super::exec_uuid;
|
||||
use uuid::Uuid;
|
||||
let func = UuidFunc::Uuid4;
|
||||
let owned_val = exec_uuid(&func, None);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 4);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v4str() {
|
||||
use super::{exec_uuid, UuidFunc};
|
||||
use uuid::Uuid;
|
||||
let func = UuidFunc::Uuid4Str;
|
||||
let owned_val = exec_uuid(&func, None);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Text(v4str)) => {
|
||||
assert_eq!(v4str.len(), 36);
|
||||
let uuid = Uuid::parse_str(&v4str);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 4);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_now() {
|
||||
use super::{exec_uuid, UuidFunc};
|
||||
use uuid::Uuid;
|
||||
let func = UuidFunc::Uuid7;
|
||||
let owned_val = exec_uuid(&func, None);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_with_input() {
|
||||
use super::{exec_uuid, UuidFunc};
|
||||
use uuid::Uuid;
|
||||
let func = UuidFunc::Uuid7;
|
||||
let owned_val = exec_uuid(&func, Some(&OwnedValue::Integer(946702800)));
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_now_to_timestamp() {
|
||||
use super::{exec_ts_from_uuid7, exec_uuid, UuidFunc};
|
||||
use uuid::Uuid;
|
||||
let func = UuidFunc::Uuid7;
|
||||
let owned_val = exec_uuid(&func, None);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(ref blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
let result = exec_ts_from_uuid7(&owned_val.expect("uuid7"));
|
||||
if let OwnedValue::Integer(ref ts) = result {
|
||||
let unixnow = (std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
* 1000) as i64;
|
||||
assert!(*ts >= unixnow - 1000);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_to_timestamp() {
|
||||
use super::{exec_ts_from_uuid7, exec_uuid, UuidFunc};
|
||||
use uuid::Uuid;
|
||||
let func = UuidFunc::Uuid7;
|
||||
let owned_val = exec_uuid(&func, Some(&OwnedValue::Integer(946702800)));
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(ref blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
let result = exec_ts_from_uuid7(&owned_val.expect("uuid7"));
|
||||
assert_eq!(result, OwnedValue::Integer(946702800 * 1000));
|
||||
if let OwnedValue::Integer(ts) = result {
|
||||
let time = chrono::DateTime::from_timestamp(ts / 1000, 0);
|
||||
assert_eq!(
|
||||
time.unwrap(),
|
||||
"2000-01-01T05:00:00Z"
|
||||
.parse::<chrono::DateTime<chrono::Utc>>()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v4_str_to_blob() {
|
||||
use super::{exec_uuid, exec_uuidblob, UuidFunc};
|
||||
use uuid::Uuid;
|
||||
let owned_val = exec_uuidblob(
|
||||
&exec_uuid(&UuidFunc::Uuid4Str, None).expect("uuid v4 string to generate"),
|
||||
);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 4);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_str_to_blob() {
|
||||
use super::{exec_uuid, exec_uuidblob, exec_uuidstr, UuidFunc};
|
||||
use uuid::Uuid;
|
||||
// convert a v7 blob to a string then back to a blob
|
||||
let owned_val = exec_uuidblob(
|
||||
&exec_uuidstr(&exec_uuid(&UuidFunc::Uuid7, None).expect("uuid v7 blob to generate"))
|
||||
.expect("uuid v7 string to generate"),
|
||||
);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v4_blob_to_str() {
|
||||
use super::{exec_uuid, exec_uuidstr, UuidFunc};
|
||||
use uuid::Uuid;
|
||||
// convert a v4 blob to a string
|
||||
let owned_val =
|
||||
exec_uuidstr(&exec_uuid(&UuidFunc::Uuid4, None).expect("uuid v7 blob to generate"));
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Text(v4str)) => {
|
||||
assert_eq!(v4str.len(), 36);
|
||||
let uuid = Uuid::parse_str(&v4str);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 4);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_blob_to_str() {
|
||||
use super::{exec_uuid, exec_uuidstr};
|
||||
use uuid::Uuid;
|
||||
// convert a v7 blob to a string
|
||||
let owned_val = exec_uuidstr(
|
||||
&exec_uuid(&UuidFunc::Uuid7, Some(&OwnedValue::Integer(123456789)))
|
||||
.expect("uuid v7 blob to generate"),
|
||||
);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Text(v7str)) => {
|
||||
assert_eq!(v7str.len(), 36);
|
||||
let uuid = Uuid::parse_str(&v7str);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::ext::ExtFunc;
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
|
||||
#[cfg(feature = "json")]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum JsonFunc {
|
||||
@@ -91,12 +91,6 @@ pub enum ScalarFunc {
|
||||
ZeroBlob,
|
||||
LastInsertRowid,
|
||||
Replace,
|
||||
Uuid4,
|
||||
Uuid4Str,
|
||||
UuidStr,
|
||||
UuidBlob,
|
||||
Uuid7,
|
||||
Uuid7TS,
|
||||
}
|
||||
|
||||
impl Display for ScalarFunc {
|
||||
@@ -142,12 +136,6 @@ impl Display for ScalarFunc {
|
||||
ScalarFunc::ZeroBlob => "zeroblob".to_string(),
|
||||
ScalarFunc::LastInsertRowid => "last_insert_rowid".to_string(),
|
||||
ScalarFunc::Replace => "replace".to_string(),
|
||||
ScalarFunc::Uuid4 => "uuid4".to_string(),
|
||||
ScalarFunc::UuidStr => "uuid_str".to_string(),
|
||||
ScalarFunc::UuidBlob => "uuid_blob".to_string(),
|
||||
ScalarFunc::Uuid7 => "uuid7".to_string(),
|
||||
ScalarFunc::Uuid4Str => "uuid4_str".to_string(),
|
||||
ScalarFunc::Uuid7TS => "uuid7_timestamp_ms".to_string(),
|
||||
};
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
@@ -268,13 +256,14 @@ impl Display for MathFunc {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Func {
|
||||
Agg(AggFunc),
|
||||
Scalar(ScalarFunc),
|
||||
Math(MathFunc),
|
||||
#[cfg(feature = "json")]
|
||||
Json(JsonFunc),
|
||||
Extention(ExtFunc),
|
||||
}
|
||||
|
||||
impl Display for Func {
|
||||
@@ -285,6 +274,7 @@ impl Display for Func {
|
||||
Func::Math(math_func) => write!(f, "{}", math_func),
|
||||
#[cfg(feature = "json")]
|
||||
Func::Json(json_func) => write!(f, "{}", json_func),
|
||||
Func::Extention(ext_func) => write!(f, "{}", ext_func),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,14 +327,6 @@ impl Func {
|
||||
"typeof" => Ok(Func::Scalar(ScalarFunc::Typeof)),
|
||||
"last_insert_rowid" => Ok(Func::Scalar(ScalarFunc::LastInsertRowid)),
|
||||
"unicode" => Ok(Func::Scalar(ScalarFunc::Unicode)),
|
||||
"uuid4_str" => Ok(Func::Scalar(ScalarFunc::Uuid4Str)),
|
||||
"uuid4" => Ok(Func::Scalar(ScalarFunc::Uuid4)),
|
||||
"uuid7" => Ok(Func::Scalar(ScalarFunc::Uuid7)),
|
||||
"uuid_str" => Ok(Func::Scalar(ScalarFunc::UuidStr)),
|
||||
"uuid_blob" => Ok(Func::Scalar(ScalarFunc::UuidBlob)),
|
||||
"uuid7_timestamp_ms" => Ok(Func::Scalar(ScalarFunc::Uuid7TS)),
|
||||
// postgres_compatability
|
||||
"gen_random_uuid" => Ok(Func::Scalar(ScalarFunc::Uuid4Str)),
|
||||
"quote" => Ok(Func::Scalar(ScalarFunc::Quote)),
|
||||
"sqlite_version" => Ok(Func::Scalar(ScalarFunc::SqliteVersion)),
|
||||
"replace" => Ok(Func::Scalar(ScalarFunc::Replace)),
|
||||
@@ -386,7 +368,10 @@ impl Func {
|
||||
"tan" => Ok(Func::Math(MathFunc::Tan)),
|
||||
"tanh" => Ok(Func::Math(MathFunc::Tanh)),
|
||||
"trunc" => Ok(Func::Math(MathFunc::Trunc)),
|
||||
_ => Err(()),
|
||||
_ => match ExtFunc::resolve_function(name, arg_count) {
|
||||
Ok(ext_func) => Ok(Func::Extention(ext_func)),
|
||||
Err(_) => Err(()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
mod error;
|
||||
mod ext;
|
||||
mod function;
|
||||
mod io;
|
||||
#[cfg(feature = "json")]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use sqlite3_parser::ast::{self, UnaryOperator};
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
use crate::ext::{ExtFunc, UuidFunc};
|
||||
#[cfg(feature = "json")]
|
||||
use crate::function::JsonFunc;
|
||||
use crate::function::{AggFunc, Func, FuncCtx, MathFuncArity, ScalarFunc};
|
||||
@@ -1194,10 +1196,7 @@ pub fn translate_expr(
|
||||
| ScalarFunc::RandomBlob
|
||||
| ScalarFunc::Sign
|
||||
| ScalarFunc::Soundex
|
||||
| ScalarFunc::ZeroBlob
|
||||
| ScalarFunc::UuidStr
|
||||
| ScalarFunc::UuidBlob
|
||||
| ScalarFunc::Uuid7TS => {
|
||||
| ScalarFunc::ZeroBlob => {
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() != 1 {
|
||||
crate::bail_parse_error!(
|
||||
@@ -1229,39 +1228,7 @@ pub fn translate_expr(
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
ScalarFunc::Uuid7 => {
|
||||
let args = match args {
|
||||
Some(args) if args.len() > 1 => crate::bail_parse_error!(
|
||||
"{} function with more than 1 argument",
|
||||
srf.to_string()
|
||||
),
|
||||
Some(args) => args,
|
||||
None => &vec![],
|
||||
};
|
||||
let mut start_reg = None;
|
||||
if let Some(arg) = args.first() {
|
||||
let reg = program.alloc_register();
|
||||
start_reg = Some(reg);
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
arg,
|
||||
reg,
|
||||
precomputed_exprs_to_registers,
|
||||
)?;
|
||||
if let ast::Expr::Literal(_) = arg {
|
||||
program.mark_last_insn_constant()
|
||||
}
|
||||
}
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: start_reg.unwrap_or(target_register),
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
ScalarFunc::Random | ScalarFunc::Uuid4 | ScalarFunc::Uuid4Str => {
|
||||
ScalarFunc::Random => {
|
||||
if args.is_some() {
|
||||
crate::bail_parse_error!(
|
||||
"{} function with arguments",
|
||||
@@ -1648,6 +1615,92 @@ pub fn translate_expr(
|
||||
}
|
||||
}
|
||||
}
|
||||
Func::Extention(ext_func) => match ext_func {
|
||||
#[cfg(feature = "uuid")]
|
||||
ExtFunc::Uuid(ref uuid_fn) => match uuid_fn {
|
||||
UuidFunc::UuidStr | UuidFunc::UuidBlob | UuidFunc::Uuid7TS => {
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() != 1 {
|
||||
crate::bail_parse_error!(
|
||||
"{} function with not exactly 1 argument",
|
||||
ext_func.to_string()
|
||||
);
|
||||
}
|
||||
args
|
||||
} else {
|
||||
crate::bail_parse_error!(
|
||||
"{} function with no arguments",
|
||||
ext_func.to_string()
|
||||
);
|
||||
};
|
||||
|
||||
let regs = program.alloc_register();
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[0],
|
||||
regs,
|
||||
precomputed_exprs_to_registers,
|
||||
)?;
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: regs,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
UuidFunc::Uuid4 | UuidFunc::Uuid4Str => {
|
||||
if args.is_some() {
|
||||
crate::bail_parse_error!(
|
||||
"{} function with arguments",
|
||||
ext_func.to_string()
|
||||
);
|
||||
}
|
||||
let regs = program.alloc_register();
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: regs,
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
UuidFunc::Uuid7 => {
|
||||
let args = match args {
|
||||
Some(args) if args.len() > 1 => crate::bail_parse_error!(
|
||||
"{} function with more than 1 argument",
|
||||
ext_func.to_string()
|
||||
),
|
||||
Some(args) => args,
|
||||
None => &vec![],
|
||||
};
|
||||
let mut start_reg = None;
|
||||
if let Some(arg) = args.first() {
|
||||
let reg = program.alloc_register();
|
||||
start_reg = Some(reg);
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
arg,
|
||||
reg,
|
||||
precomputed_exprs_to_registers,
|
||||
)?;
|
||||
if let ast::Expr::Literal(_) = arg {
|
||||
program.mark_last_insn_constant()
|
||||
}
|
||||
}
|
||||
program.emit_insn(Insn::Function {
|
||||
constant_mask: 0,
|
||||
start_reg: start_reg.unwrap_or(target_register),
|
||||
dest: target_register,
|
||||
func: func_ctx,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
},
|
||||
_ => unreachable!("{ext_func} not implemented yet"),
|
||||
},
|
||||
Func::Math(math_func) => match math_func.arity() {
|
||||
MathFuncArity::Nullary => {
|
||||
if args.is_some() {
|
||||
|
||||
347
core/vdbe/mod.rs
347
core/vdbe/mod.rs
@@ -24,7 +24,9 @@ pub mod sorter;
|
||||
mod datetime;
|
||||
|
||||
use crate::error::{LimboError, SQLITE_CONSTRAINT_PRIMARYKEY};
|
||||
use crate::function::{AggFunc, FuncCtx, MathFunc, MathFuncArity, ScalarFunc};
|
||||
#[cfg(feature = "uuid")]
|
||||
use crate::ext::{exec_ts_from_uuid7, exec_uuid, exec_uuidblob, exec_uuidstr, ExtFunc, UuidFunc};
|
||||
use crate::function::{AggFunc, Func, FuncCtx, MathFunc, MathFuncArity, ScalarFunc};
|
||||
use crate::pseudo::PseudoCursor;
|
||||
use crate::schema::Table;
|
||||
use crate::storage::sqlite3_ondisk::DatabaseHeader;
|
||||
@@ -46,10 +48,10 @@ use regex::Regex;
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt::Display;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use uuid::{ContextV7, Timestamp, Uuid};
|
||||
|
||||
pub type BranchOffset = i64;
|
||||
use macros::Description;
|
||||
pub type CursorID = usize;
|
||||
@@ -2397,10 +2399,7 @@ impl Program {
|
||||
| ScalarFunc::RandomBlob
|
||||
| ScalarFunc::Sign
|
||||
| ScalarFunc::Soundex
|
||||
| ScalarFunc::ZeroBlob
|
||||
| ScalarFunc::UuidStr
|
||||
| ScalarFunc::UuidBlob
|
||||
| ScalarFunc::Uuid7TS => {
|
||||
| ScalarFunc::ZeroBlob => {
|
||||
let reg_value = state.registers[*start_reg].borrow_mut();
|
||||
let result = match scalar_func {
|
||||
ScalarFunc::Sign => exec_sign(reg_value),
|
||||
@@ -2415,26 +2414,10 @@ impl Program {
|
||||
ScalarFunc::RandomBlob => Some(exec_randomblob(reg_value)),
|
||||
ScalarFunc::ZeroBlob => Some(exec_zeroblob(reg_value)),
|
||||
ScalarFunc::Soundex => Some(exec_soundex(reg_value)),
|
||||
ScalarFunc::UuidStr => Some(exec_uuidstr(reg_value)?),
|
||||
ScalarFunc::UuidBlob => Some(exec_uuidblob(reg_value)?),
|
||||
ScalarFunc::Uuid7TS => Some(exec_ts_from_uuid7(reg_value)),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
state.registers[*dest] = result.unwrap_or(OwnedValue::Null);
|
||||
}
|
||||
ScalarFunc::Uuid7 => match arg_count {
|
||||
0 => {
|
||||
state.registers[*dest] =
|
||||
exec_uuid(scalar_func, None).unwrap_or(OwnedValue::Null);
|
||||
}
|
||||
1 => {
|
||||
let reg_value = state.registers[*start_reg].borrow();
|
||||
state.registers[*dest] =
|
||||
exec_uuid(scalar_func, Some(reg_value))
|
||||
.unwrap_or(OwnedValue::Null);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
ScalarFunc::Hex => {
|
||||
let reg_value = state.registers[*start_reg].borrow_mut();
|
||||
let result = exec_hex(reg_value);
|
||||
@@ -2449,9 +2432,6 @@ impl Program {
|
||||
ScalarFunc::Random => {
|
||||
state.registers[*dest] = exec_random();
|
||||
}
|
||||
ScalarFunc::Uuid4 | ScalarFunc::Uuid4Str => {
|
||||
state.registers[*dest] = exec_uuid(scalar_func, None)?;
|
||||
}
|
||||
ScalarFunc::Trim => {
|
||||
let reg_value = state.registers[*start_reg].clone();
|
||||
let pattern_value = state.registers.get(*start_reg + 1).cloned();
|
||||
@@ -2555,6 +2535,38 @@ impl Program {
|
||||
state.registers[*dest] = exec_replace(source, pattern, replacement);
|
||||
}
|
||||
},
|
||||
Func::Extention(extfn) => match extfn {
|
||||
#[cfg(feature = "uuid")]
|
||||
ExtFunc::Uuid(uuidfn) => match uuidfn {
|
||||
UuidFunc::Uuid4 | UuidFunc::Uuid4Str => {
|
||||
state.registers[*dest] = exec_uuid(uuidfn, None)?
|
||||
}
|
||||
UuidFunc::Uuid7 => match arg_count {
|
||||
0 => {
|
||||
state.registers[*dest] =
|
||||
exec_uuid(uuidfn, None).unwrap_or(OwnedValue::Null);
|
||||
}
|
||||
1 => {
|
||||
let reg_value = state.registers[*start_reg].borrow();
|
||||
state.registers[*dest] = exec_uuid(uuidfn, Some(reg_value))
|
||||
.unwrap_or(OwnedValue::Null);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => {
|
||||
// remaining accept 1 arg
|
||||
let reg_value = state.registers[*start_reg].borrow();
|
||||
state.registers[*dest] = match uuidfn {
|
||||
UuidFunc::Uuid7TS => Some(exec_ts_from_uuid7(reg_value)),
|
||||
UuidFunc::UuidStr => exec_uuidstr(reg_value).ok(),
|
||||
UuidFunc::UuidBlob => exec_uuidblob(reg_value).ok(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.unwrap_or(OwnedValue::Null);
|
||||
}
|
||||
},
|
||||
_ => unreachable!(), // when more extension types are added
|
||||
},
|
||||
crate::function::Func::Math(math_func) => match math_func.arity() {
|
||||
MathFuncArity::Nullary => match math_func {
|
||||
MathFunc::Pi => {
|
||||
@@ -3117,93 +3129,6 @@ fn exec_random() -> OwnedValue {
|
||||
OwnedValue::Integer(random_number)
|
||||
}
|
||||
|
||||
fn exec_uuid(var: &ScalarFunc, sec: Option<&OwnedValue>) -> Result<OwnedValue> {
|
||||
match var {
|
||||
ScalarFunc::Uuid4 => Ok(OwnedValue::Blob(Rc::new(
|
||||
Uuid::new_v4().into_bytes().to_vec(),
|
||||
))),
|
||||
ScalarFunc::Uuid4Str => Ok(OwnedValue::Text(Rc::new(Uuid::new_v4().to_string()))),
|
||||
ScalarFunc::Uuid7 => {
|
||||
let uuid = match sec {
|
||||
Some(OwnedValue::Integer(ref seconds)) => {
|
||||
let ctx = ContextV7::new();
|
||||
if *seconds < 0 {
|
||||
// not valid unix timestamp, error or null?
|
||||
return Ok(OwnedValue::Null);
|
||||
}
|
||||
Uuid::new_v7(Timestamp::from_unix(ctx, *seconds as u64, 0))
|
||||
}
|
||||
_ => Uuid::now_v7(),
|
||||
};
|
||||
Ok(OwnedValue::Blob(Rc::new(uuid.into_bytes().to_vec())))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_uuidstr(reg: &OwnedValue) -> Result<OwnedValue> {
|
||||
match reg {
|
||||
OwnedValue::Blob(blob) => {
|
||||
let uuid = Uuid::from_slice(blob).map_err(|e| LimboError::ParseError(e.to_string()))?;
|
||||
Ok(OwnedValue::Text(Rc::new(uuid.to_string())))
|
||||
}
|
||||
OwnedValue::Text(val) => {
|
||||
let uuid = Uuid::parse_str(val).map_err(|e| LimboError::ParseError(e.to_string()))?;
|
||||
Ok(OwnedValue::Text(Rc::new(uuid.to_string())))
|
||||
}
|
||||
OwnedValue::Null => Ok(OwnedValue::Null),
|
||||
_ => Err(LimboError::ParseError(
|
||||
"Invalid argument type for UUID function".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_uuidblob(reg: &OwnedValue) -> Result<OwnedValue> {
|
||||
match reg {
|
||||
OwnedValue::Text(val) => {
|
||||
let uuid = Uuid::parse_str(val).map_err(|e| LimboError::ParseError(e.to_string()))?;
|
||||
Ok(OwnedValue::Blob(Rc::new(uuid.as_bytes().to_vec())))
|
||||
}
|
||||
OwnedValue::Blob(blob) => {
|
||||
let uuid = Uuid::from_slice(blob).map_err(|e| LimboError::ParseError(e.to_string()))?;
|
||||
Ok(OwnedValue::Blob(Rc::new(uuid.as_bytes().to_vec())))
|
||||
}
|
||||
OwnedValue::Null => Ok(OwnedValue::Null),
|
||||
_ => Err(LimboError::ParseError(
|
||||
"Invalid argument type for UUID function".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_ts_from_uuid7(reg: &OwnedValue) -> OwnedValue {
|
||||
let uuid = match reg {
|
||||
OwnedValue::Blob(blob) => {
|
||||
Uuid::from_slice(blob).map_err(|e| LimboError::ParseError(e.to_string()))
|
||||
}
|
||||
OwnedValue::Text(val) => {
|
||||
Uuid::parse_str(val).map_err(|e| LimboError::ParseError(e.to_string()))
|
||||
}
|
||||
_ => Err(LimboError::ParseError(
|
||||
"Invalid argument type for UUID function".to_string(),
|
||||
)),
|
||||
};
|
||||
match uuid {
|
||||
Ok(uuid) => OwnedValue::Integer(uuid_to_unix(uuid.as_bytes()) as i64),
|
||||
// display error? sqlean seems to set value to null
|
||||
Err(_) => OwnedValue::Null,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn uuid_to_unix(uuid: &[u8; 16]) -> u64 {
|
||||
((uuid[0] as u64) << 40)
|
||||
| ((uuid[1] as u64) << 32)
|
||||
| ((uuid[2] as u64) << 24)
|
||||
| ((uuid[3] as u64) << 16)
|
||||
| ((uuid[4] as u64) << 8)
|
||||
| (uuid[5] as u64)
|
||||
}
|
||||
|
||||
fn exec_randomblob(reg: &OwnedValue) -> OwnedValue {
|
||||
let length = match reg {
|
||||
OwnedValue::Integer(i) => *i,
|
||||
@@ -4970,202 +4895,4 @@ mod tests {
|
||||
expected_str
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v4blob() {
|
||||
use super::{exec_uuid, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
let func = ScalarFunc::Uuid4;
|
||||
let owned_val = exec_uuid(&func, None);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 4);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v4str() {
|
||||
use super::{exec_uuid, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
let func = ScalarFunc::Uuid4Str;
|
||||
let owned_val = exec_uuid(&func, None);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Text(v4str)) => {
|
||||
assert_eq!(v4str.len(), 36);
|
||||
let uuid = Uuid::parse_str(&v4str);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 4);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_now() {
|
||||
use super::{exec_uuid, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
let func = ScalarFunc::Uuid7;
|
||||
let owned_val = exec_uuid(&func, None);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_with_input() {
|
||||
use super::{exec_uuid, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
let func = ScalarFunc::Uuid7;
|
||||
let owned_val = exec_uuid(&func, Some(&OwnedValue::Integer(946702800)));
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_now_to_timestamp() {
|
||||
use super::{exec_ts_from_uuid7, exec_uuid, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
let func = ScalarFunc::Uuid7;
|
||||
let owned_val = exec_uuid(&func, None);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(ref blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
let result = exec_ts_from_uuid7(&owned_val.expect("uuid7"));
|
||||
if let OwnedValue::Integer(ref ts) = result {
|
||||
let unixnow = (std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
* 1000) as i64;
|
||||
assert!(*ts >= unixnow - 1000);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_to_timestamp() {
|
||||
use super::{exec_ts_from_uuid7, exec_uuid, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
let func = ScalarFunc::Uuid7;
|
||||
let owned_val = exec_uuid(&func, Some(&OwnedValue::Integer(946702800)));
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(ref blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
let result = exec_ts_from_uuid7(&owned_val.expect("uuid7"));
|
||||
assert_eq!(result, OwnedValue::Integer(946702800 * 1000));
|
||||
if let OwnedValue::Integer(ts) = result {
|
||||
let time = chrono::DateTime::from_timestamp(ts / 1000, 0);
|
||||
assert_eq!(
|
||||
time.unwrap(),
|
||||
"2000-01-01T05:00:00Z"
|
||||
.parse::<chrono::DateTime<chrono::Utc>>()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v4_str_to_blob() {
|
||||
use super::{exec_uuid, exec_uuidblob, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
let owned_val = exec_uuidblob(
|
||||
&exec_uuid(&ScalarFunc::Uuid4Str, None).expect("uuid v4 string to generate"),
|
||||
);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 4);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_str_to_blob() {
|
||||
use super::{exec_uuid, exec_uuidblob, exec_uuidstr, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
// convert a v7 blob to a string then back to a blob
|
||||
let owned_val = exec_uuidblob(
|
||||
&exec_uuidstr(&exec_uuid(&ScalarFunc::Uuid7, None).expect("uuid v7 blob to generate"))
|
||||
.expect("uuid v7 string to generate"),
|
||||
);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Blob(blob)) => {
|
||||
assert_eq!(blob.len(), 16);
|
||||
let uuid = Uuid::from_slice(&blob);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v4_blob_to_str() {
|
||||
use super::{exec_uuid, exec_uuidstr, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
// convert a v4 blob to a string
|
||||
let owned_val =
|
||||
exec_uuidstr(&exec_uuid(&ScalarFunc::Uuid4, None).expect("uuid v7 blob to generate"));
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Text(v4str)) => {
|
||||
assert_eq!(v4str.len(), 36);
|
||||
let uuid = Uuid::parse_str(&v4str);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 4);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exec_uuid_v7_blob_to_str() {
|
||||
use super::{exec_uuid, exec_uuidstr, ScalarFunc};
|
||||
use uuid::Uuid;
|
||||
// convert a v7 blob to a string
|
||||
let owned_val = exec_uuidstr(
|
||||
&exec_uuid(&ScalarFunc::Uuid7, Some(&OwnedValue::Integer(123456789)))
|
||||
.expect("uuid v7 blob to generate"),
|
||||
);
|
||||
match owned_val {
|
||||
Ok(OwnedValue::Text(v7str)) => {
|
||||
assert_eq!(v7str.len(), 36);
|
||||
let uuid = Uuid::parse_str(&v7str);
|
||||
assert!(uuid.is_ok());
|
||||
assert_eq!(uuid.unwrap().get_version_num(), 7);
|
||||
}
|
||||
_ => panic!("exec_uuid did not return a Blob variant"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user