From 2c31c6888c6bf8b9ce7f06f6745fb90993fb41cb Mon Sep 17 00:00:00 2001 From: Denis Smirnov <sd@picodata.io> Date: Tue, 20 Jun 2023 15:29:36 +0700 Subject: [PATCH] feat: reduce memory allocations on msgpack serialization --- .../src/api/calculate_bucket_id.rs | 6 +- .../test_app/test/integration/api_test.lua | 2 +- sbroad-core/src/backend/sql/ir.rs | 7 +- sbroad-core/src/backend/sql/space.rs | 21 +- sbroad-core/src/executor/engine.rs | 4 + sbroad-core/src/executor/engine/helpers.rs | 99 ++++----- sbroad-core/src/executor/engine/mock.rs | 10 +- sbroad-core/src/executor/result.rs | 4 +- sbroad-core/src/executor/result/tests.rs | 22 +- sbroad-core/src/executor/tests.rs | 110 ++++----- sbroad-core/src/executor/tests/between.rs | 10 +- sbroad-core/src/executor/tests/bucket_id.rs | 14 +- .../src/executor/tests/empty_motion.rs | 6 +- sbroad-core/src/executor/tests/not_eq.rs | 10 +- sbroad-core/src/executor/tests/not_in.rs | 6 +- sbroad-core/src/ir/value.rs | 208 ++++++++++++------ 16 files changed, 291 insertions(+), 248 deletions(-) diff --git a/sbroad-cartridge/src/api/calculate_bucket_id.rs b/sbroad-cartridge/src/api/calculate_bucket_id.rs index dbf2c8dffa..40e1f2f61e 100644 --- a/sbroad-cartridge/src/api/calculate_bucket_id.rs +++ b/sbroad-cartridge/src/api/calculate_bucket_id.rs @@ -9,7 +9,7 @@ use serde::{de::Deserializer, Deserialize, Serialize}; use crate::api::helper::load_config; use crate::api::COORDINATOR_ENGINE; use sbroad::executor::engine::{Router, Vshard}; -use sbroad::ir::value::{EncodedValue, Value}; +use sbroad::ir::value::{LuaValue, Value}; use sbroad::log::tarantool_error; #[derive(Debug, Default, PartialEq, Eq)] @@ -29,7 +29,7 @@ impl<'de> Deserialize<'de> for ArgsMap { { #[derive(Deserialize)] #[serde(rename = "FunctionArgs")] - struct StructHelper(HashMap<String, EncodedValue>, String); + struct StructHelper(HashMap<String, LuaValue>, String); let mut struct_helper = StructHelper::deserialize(deserializer)?; let rec: HashMap<String, Value> = struct_helper @@ -62,7 +62,7 @@ impl<'de> Deserialize<'de> for ArgsTuple { { #[derive(Deserialize)] #[serde(rename = "FunctionArgs")] - struct StructHelper(Vec<EncodedValue>, String); + struct StructHelper(Vec<LuaValue>, String); let mut struct_helper = StructHelper::deserialize(deserializer)?; let rec: Vec<Value> = struct_helper.0.drain(..).map(Value::from).collect(); diff --git a/sbroad-cartridge/test_app/test/integration/api_test.lua b/sbroad-cartridge/test_app/test/integration/api_test.lua index 25eff7b808..f5da5c5e45 100644 --- a/sbroad-cartridge/test_app/test/integration/api_test.lua +++ b/sbroad-cartridge/test_app/test/integration/api_test.lua @@ -214,7 +214,7 @@ g.test_query_errored = function() local _, err = api:call("sbroad.execute", { [[SELECT * FROM "testing_space" where "id" = ?]], {invalid_type_param} }) t.assert_equals( tostring(err), "Sbroad Error: pattern with parameters parsing error: " .. - [[Decode(Syntax("data did not match any variant of untagged enum EncodedValue"))]] + [[Decode(Syntax("data did not match any variant of untagged enum LuaValue"))]] ) -- check err when params lenght is less then amount of sign `?` diff --git a/sbroad-core/src/backend/sql/ir.rs b/sbroad-core/src/backend/sql/ir.rs index 207de27a66..19e9cc1bcd 100644 --- a/sbroad-core/src/backend/sql/ir.rs +++ b/sbroad-core/src/backend/sql/ir.rs @@ -13,7 +13,7 @@ use crate::executor::bucket::Buckets; use crate::executor::ir::ExecutionPlan; use crate::ir::expression::Expression; use crate::ir::operator::Relational; -use crate::ir::value::{EncodedValue, Value}; +use crate::ir::value::{LuaValue, Value}; use crate::ir::Node; use crate::otm::{ child_span, current_id, deserialize_context, force_trace, get_tracer, inject_context, query_id, @@ -59,7 +59,7 @@ impl TryFrom<FunctionArgs> for PatternWithParams { #[derive(Deserialize, Serialize)] pub struct EncodedPatternWithParams( String, - Option<Vec<EncodedValue>>, + Option<Vec<LuaValue>>, Option<HashMap<String, String>>, Option<String>, bool, @@ -67,8 +67,7 @@ pub struct EncodedPatternWithParams( impl From<PatternWithParams> for EncodedPatternWithParams { fn from(mut value: PatternWithParams) -> Self { - let encoded_params: Vec<EncodedValue> = - value.params.drain(..).map(EncodedValue::from).collect(); + let encoded_params: Vec<LuaValue> = value.params.drain(..).map(LuaValue::from).collect(); EncodedPatternWithParams( value.pattern, Some(encoded_params), diff --git a/sbroad-core/src/backend/sql/space.rs b/sbroad-core/src/backend/sql/space.rs index 65180fe2dc..9b76e077f3 100644 --- a/sbroad-core/src/backend/sql/space.rs +++ b/sbroad-core/src/backend/sql/space.rs @@ -8,7 +8,7 @@ mod prod_imports { pub use crate::error; pub use crate::errors::{Action, Entity}; pub use crate::ir::relation::Column; - pub use crate::ir::value::EncodedValue; + pub use crate::ir::value::{EncodedValue, Value}; pub use tarantool::index::{FieldType, IndexOptions, IndexType, Part}; pub use tarantool::space::{Field, Space, SpaceCreateOptions}; pub use tarantool::tuple::Tuple; @@ -97,24 +97,13 @@ impl TmpSpace { } } for (idx, tuples) in vtable.get_tuples_with_buckets(buckets).iter().enumerate() { - let mut extended_value: Vec<EncodedValue> = Vec::with_capacity(tuples.len() + 1); + let mut data: Vec<EncodedValue> = Vec::with_capacity(tuples.len() + 1); for (v, c) in tuples.iter().zip(vtable.get_columns().iter()) { let casted_value = v.cast(&c.r#type)?; - extended_value.push(EncodedValue::from(casted_value)); + data.push(casted_value); } - extended_value.push(EncodedValue::Unsigned(idx as u64)); - let data = match rmp_serde::to_vec(&extended_value) { - Ok(data) => data, - Err(e) => { - cleanup(space); - return Err(SbroadError::FailedTo( - Action::Serialize, - Some(Entity::Value), - format!("to tuple buffer (msgpack bytes): {e}"), - )); - } - }; - let tuple = match Tuple::try_from_slice(data.as_slice()) { + data.push(EncodedValue::from(Value::Unsigned(idx as u64))); + let tuple = match Tuple::new(&data) { Ok(tuple) => tuple, Err(e) => { cleanup(space); diff --git a/sbroad-core/src/executor/engine.rs b/sbroad-core/src/executor/engine.rs index ed1de01764..d66d3115d8 100644 --- a/sbroad-core/src/executor/engine.rs +++ b/sbroad-core/src/executor/engine.rs @@ -259,5 +259,9 @@ pub trait Vshard { fn get_random_bucket(&self) -> Buckets; /// Determine shard for query execution by sharding key value + /// + /// # Errors + /// - Internal error. Under normal conditions we should always return + /// bucket id successfully. fn determine_bucket_id(&self, s: &[&Value]) -> u64; } diff --git a/sbroad-core/src/executor/engine/helpers.rs b/sbroad-core/src/executor/engine/helpers.rs index 2eed3b9457..8f249cd0c3 100644 --- a/sbroad-core/src/executor/engine/helpers.rs +++ b/sbroad-core/src/executor/engine/helpers.rs @@ -38,7 +38,7 @@ use crate::{ relation::{Column, ColumnRole, Type}, transformation::redistribution::{MotionKey, MotionPolicy}, tree::Snapshot, - value::{EncodedValue, Value}, + value::{MsgPackValue, Value}, Node, Plan, }, }; @@ -343,73 +343,54 @@ pub fn execute_dml( } // Check if the virtual table have been dispatched (case 2) or built locally (case 1). - if let Some(vtables) = &mut optional.exec_plan.vtables { - if let Some(mut vtable) = vtables.mut_map().remove(&insert_child_id) { - let space = Space::find(&space_name).ok_or_else(|| { - SbroadError::Invalid(Entity::Space, Some(format!("space {space_name} not found"))) - })?; - // There are no long-living references to the virtual table on the storage - // (we can have the ones only on the router while dispatching subplans). - // So we should never produce any memory copy here with `Rc::make_mut()`. - let vtable = Rc::make_mut(&mut vtable); - let mut tuples = std::mem::take(vtable.get_mut_tuples()); - start_transaction(|| -> Result<(), SbroadError> { - for (bucket_id, positions) in vtable.get_mut_index().drain() { - for pos in positions { - let mut vt_tuple = { - let tuple = tuples.get_mut(pos).ok_or_else(|| { + let vtable = optional.exec_plan.get_motion_vtable(insert_child_id)?; + let space = Space::find(&space_name).ok_or_else(|| { + SbroadError::Invalid(Entity::Space, Some(format!("space {space_name} not found"))) + })?; + start_transaction(|| -> Result<(), SbroadError> { + for (bucket_id, positions) in vtable.get_index().iter() { + for pos in positions { + let vt_tuple = vtable.get_tuples().get(*pos).ok_or_else(|| { + SbroadError::Invalid( + Entity::VirtualTable, + Some(format!( + "tuple at position {pos} not found in virtual table" + )), + ) + })?; + let mut insert_tuple = Vec::with_capacity(builder.len()); + for command in &builder { + // We don't produce any additional allocations as `MsgPackValue` keeps + // a reference to the original value. The only allocation is for message + // pack serialization, but it is unavoidable. + match command { + TupleBuilderCommands::TakePosition(pos) => { + let value = vt_tuple.get(*pos).ok_or_else(|| { SbroadError::Invalid( - Entity::VirtualTable, + Entity::Tuple, Some(format!( - "tuple at position {pos} not found in virtual table" + "column at position {pos} not found in virtual table" )), ) })?; - std::mem::take(tuple) - }; - let mut insert_tuple = Vec::with_capacity(builder.len()); - for command in &builder { - match command { - TupleBuilderCommands::TakePosition(pos) => { - let value = { - let value = vt_tuple.get_mut(*pos).ok_or_else(|| - SbroadError::Invalid( - Entity::Tuple, - Some(format!( - "column at position {pos} not found in virtual table" - )) - ))?; - std::mem::take(value) - }; - insert_tuple.push(EncodedValue::from(value)); - } - TupleBuilderCommands::SetValue(value) => { - insert_tuple.push(EncodedValue::from(value.clone())); - } - TupleBuilderCommands::CalculateBucketId(_) => { - insert_tuple.push(EncodedValue::Unsigned(bucket_id)); - } - } + insert_tuple.push(MsgPackValue::from(value)); + } + TupleBuilderCommands::SetValue(value) => { + insert_tuple.push(MsgPackValue::from(value)); + } + TupleBuilderCommands::CalculateBucketId(_) => { + insert_tuple.push(MsgPackValue::Unsigned(bucket_id)); } - space.insert(&insert_tuple).map_err(|e| { - SbroadError::FailedTo( - Action::Insert, - Some(Entity::Space), - format!("{e}"), - ) - })?; - result.row_count += 1; } } - Ok(()) - })?; + space.insert(&insert_tuple).map_err(|e| { + SbroadError::FailedTo(Action::Insert, Some(Entity::Space), format!("{e}")) + })?; + result.row_count += 1; + } } - } else { - return Err(SbroadError::NotFound( - Entity::VirtualTable, - "in the execution plan while executiong DML on the storage".into(), - )); - } + Ok(()) + })?; Ok(result) } diff --git a/sbroad-core/src/executor/engine/mock.rs b/sbroad-core/src/executor/engine/mock.rs index 466b929001..1c57083619 100644 --- a/sbroad-core/src/executor/engine/mock.rs +++ b/sbroad-core/src/executor/engine/mock.rs @@ -25,7 +25,7 @@ use crate::ir::function::Function; use crate::ir::helpers::RepeatableState; use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type}; use crate::ir::tree::Snapshot; -use crate::ir::value::{EncodedValue, Value}; +use crate::ir::value::{LuaValue, Value}; use crate::ir::Plan; use super::helpers::normalize_name_from_sql; @@ -832,8 +832,8 @@ fn exec_on_some(bucket: u64, query: &str) -> ProducerResult { let mut result = ProducerResult::new(); result.rows.push(vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String(String::from(query)), + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String(String::from(query)), ]); result @@ -843,8 +843,8 @@ fn exec_on_all(query: &str) -> ProducerResult { let mut result = ProducerResult::new(); result.rows.push(vec![ - EncodedValue::String(String::from("Execute query on all buckets")), - EncodedValue::String(String::from(query)), + LuaValue::String(String::from("Execute query on all buckets")), + LuaValue::String(String::from(query)), ]); result diff --git a/sbroad-core/src/executor/result.rs b/sbroad-core/src/executor/result.rs index 7cab30d638..7798555d22 100644 --- a/sbroad-core/src/executor/result.rs +++ b/sbroad-core/src/executor/result.rs @@ -10,10 +10,10 @@ use crate::executor::vtable::VirtualTable; use crate::ir::operator::Relational; use crate::ir::relation::{Column, ColumnRole, Type}; use crate::ir::tree::traversal::{PostOrder, REL_CAPACITY}; -use crate::ir::value::{EncodedValue, Value}; +use crate::ir::value::{LuaValue, Value}; use crate::ir::Plan; -type ExecutorTuple = Vec<EncodedValue>; +type ExecutorTuple = Vec<LuaValue>; #[derive(LuaRead, Debug, Deserialize, PartialEq, Eq, Clone)] pub struct MetadataColumn { diff --git a/sbroad-core/src/executor/result/tests.rs b/sbroad-core/src/executor/result/tests.rs index 2bb10a9ea9..326aaa6a84 100644 --- a/sbroad-core/src/executor/result/tests.rs +++ b/sbroad-core/src/executor/result/tests.rs @@ -13,9 +13,9 @@ fn box_execute_result_serialize() { MetadataColumn::new("count".into(), "unsigned".into()), ], rows: vec![vec![ - EncodedValue::Integer(1), - EncodedValue::String("теÑÑ‚".into()), - EncodedValue::Unsigned(1), + LuaValue::Integer(1), + LuaValue::String("теÑÑ‚".into()), + LuaValue::Unsigned(1), ]], }; @@ -58,16 +58,16 @@ fn convert_to_vtable() { ], rows: vec![ vec![ - EncodedValue::Integer(1), - EncodedValue::String("теÑÑ‚".into()), - EncodedValue::Unsigned(1), - EncodedValue::Decimal(decimal!(1.5)), + LuaValue::Integer(1), + LuaValue::String("теÑÑ‚".into()), + LuaValue::Unsigned(1), + LuaValue::Decimal(decimal!(1.5)), ], vec![ - EncodedValue::Integer(2), - EncodedValue::String("теÑÑ‚2".into()), - EncodedValue::Unsigned(5), - EncodedValue::Decimal(decimal!(2.0)), + LuaValue::Integer(2), + LuaValue::String("теÑÑ‚2".into()), + LuaValue::Unsigned(5), + LuaValue::Decimal(decimal!(2.0)), ], ], }; diff --git a/sbroad-core/src/executor/tests.rs b/sbroad-core/src/executor/tests.rs index 0a8d3e5100..5b663afd15 100644 --- a/sbroad-core/src/executor/tests.rs +++ b/sbroad-core/src/executor/tests.rs @@ -9,7 +9,7 @@ use crate::ir::operator::Relational; use crate::ir::relation::{Column, ColumnRole, Type}; use crate::ir::transformation::redistribution::MotionPolicy; -use crate::ir::value::{EncodedValue, Value}; +use crate::ir::value::{LuaValue, Value}; use super::*; @@ -30,8 +30,8 @@ fn shard_query() { let param1 = Value::from(1_u64); let bucket = coordinator.determine_bucket_id(&[¶m1]); expected.rows.push(vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"SELECT "test_space"."FIRST_NAME" FROM "test_space""#, @@ -69,8 +69,8 @@ fn shard_union_query() { let param1 = Value::from(1_u64); let bucket = query.coordinator.determine_bucket_id(&[¶m1]); expected.rows.push(vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}{} {} {}{} {}", r#"SELECT "t3"."id""#, @@ -108,8 +108,8 @@ fn map_reduce_query() { let bucket = query.coordinator.determine_bucket_id(&[¶m1, ¶m457]); expected.rows.push(vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String( String::from( PatternWithParams::new( format!( @@ -166,8 +166,8 @@ fn linker_test() { expected.rows.extend(vec![ vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket3}]")), - EncodedValue::String( + LuaValue::String(format!("Execute query on a bucket [{bucket3}]")), + LuaValue::String( String::from( PatternWithParams::new( format!( @@ -181,8 +181,8 @@ fn linker_test() { ), ], vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket2}]")), - EncodedValue::String( + LuaValue::String(format!("Execute query on a bucket [{bucket2}]")), + LuaValue::String( String::from( PatternWithParams::new( format!( @@ -250,8 +250,8 @@ fn union_linker_test() { expected.rows.extend(vec![ vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket3}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket3}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}{} {} {} {} {} {} {}{} {}", r#"SELECT "t1"."id", "t1"."FIRST_NAME""#, @@ -270,8 +270,8 @@ fn union_linker_test() { ))), ], vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket2}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket2}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}{} {} {} {} {} {} {}{} {}", r#"SELECT "t1"."id", "t1"."FIRST_NAME""#, @@ -349,8 +349,8 @@ WHERE "t3"."id" = 2 AND "t8"."identification_number" = 2"#; let bucket2 = query.coordinator.determine_bucket_id(&[¶m2]); expected.rows.extend(vec![vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket2}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket2}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{}, {}, {} {}{} {} {} {} {} {} {}{} {} {}{} {} {}", r#"SELECT "t3"."id""#, @@ -437,8 +437,8 @@ fn join_linker2_test() { let bucket1 = query.coordinator.determine_bucket_id(&[¶m1]); expected.rows.extend(vec![vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket1}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket1}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {} {} {} {}", r#"SELECT "t1"."id" FROM (SELECT"#, @@ -509,8 +509,8 @@ fn join_linker3_test() { let bucket1 = query.coordinator.determine_bucket_id(&[¶m1]); expected.rows.extend(vec![vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket1}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket1}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {} {} {}", r#"SELECT "t2"."id1" FROM"#, @@ -604,8 +604,8 @@ fn join_linker4_test() { expected.rows.extend(vec![ vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket2}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket2}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {} {} {} {} {}", r#"SELECT "T1"."id" FROM (SELECT"#, @@ -620,8 +620,8 @@ fn join_linker4_test() { ))), ], vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket1}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket1}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {} {} {} {} {}", r#"SELECT "T1"."id" FROM (SELECT"#, @@ -712,8 +712,8 @@ on q."f" = "t1"."a""#; let mut expected = ProducerResult::new(); expected.rows.extend(vec![vec![ - EncodedValue::String("Execute query on all buckets".to_string()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".to_string()), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {} {}", r#"SELECT "t1"."a", "t1"."b", "Q"."f", "Q"."B" FROM"#, @@ -783,8 +783,8 @@ fn anonymous_col_index_test() { let bucket3 = query.coordinator.determine_bucket_id(&[¶m3]); expected.rows.extend(vec![ vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket3}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket3}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {} {} {} {} {} {} {} {}", "SELECT", @@ -802,8 +802,8 @@ fn anonymous_col_index_test() { ))), ], vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket2}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket2}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {} {} {} {} {} {} {} {}", "SELECT", @@ -842,8 +842,8 @@ fn sharding_column1_test() { let param1 = Value::from(1_u64); let bucket = query.coordinator.determine_bucket_id(&[¶m1]); expected.rows.push(vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {}", r#"SELECT "test_space"."id", "test_space"."sysFrom","#, @@ -873,8 +873,8 @@ fn sharding_column2_test() { let param1 = Value::from(1_u64); let bucket = query.coordinator.determine_bucket_id(&[¶m1]); expected.rows.push(vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {}", r#"SELECT "test_space"."id", "test_space"."sysFrom","#, @@ -936,8 +936,8 @@ fn insert1_test() { expected.rows.extend(vec![ vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket1}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket1}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"INSERT INTO "t" ("b")"#, @@ -949,8 +949,8 @@ fn insert1_test() { ))), ], vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket2}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket2}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"INSERT INTO "t" ("b")"#, @@ -986,8 +986,8 @@ fn insert2_test() { let bucket = query.coordinator.determine_bucket_id(&[¶m1, ¶m2]); expected.rows.extend(vec![vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", // We don't generate SQL for insertion, so we just check a storage plan @@ -1055,8 +1055,8 @@ fn insert3_test() { expected.rows.extend(vec![ vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket1}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket1}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"INSERT INTO "t" ("b", "a")"#, @@ -1068,8 +1068,8 @@ fn insert3_test() { ))), ], vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket2}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket2}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"INSERT INTO "t" ("b", "a")"#, @@ -1106,8 +1106,8 @@ fn insert4_test() { let bucket = query.coordinator.determine_bucket_id(&[¶m1, ¶m2]); expected.rows.extend(vec![vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", // We don't generate SQL for insertion, so we just check a storage plan @@ -1170,8 +1170,8 @@ fn insert5_test() { let bucket = query.coordinator.determine_bucket_id(&[¶m1, ¶m2]); expected.rows.extend(vec![vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"INSERT INTO "t" ("b", "a")"#, @@ -1278,8 +1278,8 @@ fn insert8_test() { let bucket = query.coordinator.determine_bucket_id(&[¶m1, ¶m2]); expected.rows.extend(vec![vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String( String::from( PatternWithParams::new( format!( @@ -1365,8 +1365,8 @@ pub(crate) fn broadcast_check(sql: &str, pattern: &str, params: Vec<Value>) { let mut expected = ProducerResult::new(); expected.rows.push(vec![ - EncodedValue::String("Execute query on all buckets".to_string()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".to_string()), + LuaValue::String(String::from(PatternWithParams::new( pattern.to_string(), params, ))), @@ -1424,8 +1424,8 @@ fn groupby_linker_test() { let mut expected = ProducerResult::new(); for buc in buckets { expected.rows.extend(vec![vec![ - EncodedValue::String(format!("Execute query on a bucket [{buc}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{buc}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {}", r#"SELECT "column_12" as "ii" FROM"#, diff --git a/sbroad-core/src/executor/tests/between.rs b/sbroad-core/src/executor/tests/between.rs index 95a4c78019..a6b689fb9d 100644 --- a/sbroad-core/src/executor/tests/between.rs +++ b/sbroad-core/src/executor/tests/between.rs @@ -7,7 +7,7 @@ use crate::executor::vtable::VirtualTable; use crate::ir::relation::{Column, ColumnRole, Type}; use crate::ir::transformation::redistribution::tests::get_motion_id; use crate::ir::transformation::redistribution::MotionPolicy; -use crate::ir::value::{EncodedValue, Value}; +use crate::ir::value::{LuaValue, Value}; use super::*; @@ -52,8 +52,8 @@ fn between1_test() { // Validate the result. let mut expected = ProducerResult::new(); expected.rows.extend(vec![vec![ - EncodedValue::String("Execute query on all buckets".to_string()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".to_string()), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {}", r#"SELECT "t"."identification_number" FROM "hash_testing" as "t""#, @@ -110,8 +110,8 @@ fn between2_test() { // Validate the result. let mut expected = ProducerResult::new(); expected.rows.extend(vec![vec![ - EncodedValue::String("Execute query on all buckets".to_string()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".to_string()), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {}", r#"SELECT "t"."identification_number" FROM "hash_testing" as "t""#, diff --git a/sbroad-core/src/executor/tests/bucket_id.rs b/sbroad-core/src/executor/tests/bucket_id.rs index ef19271f03..22658dcdb7 100644 --- a/sbroad-core/src/executor/tests/bucket_id.rs +++ b/sbroad-core/src/executor/tests/bucket_id.rs @@ -3,7 +3,7 @@ use pretty_assertions::assert_eq; use crate::backend::sql::ir::PatternWithParams; use crate::executor::engine::mock::RouterRuntimeMock; use crate::executor::result::ProducerResult; -use crate::ir::value::{EncodedValue, Value}; +use crate::ir::value::{LuaValue, Value}; use super::*; @@ -22,8 +22,8 @@ fn bucket1_test() { let mut expected = ProducerResult::new(); expected.rows.push(vec![ - EncodedValue::String("Execute query on all buckets".to_string()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".to_string()), + LuaValue::String(String::from(PatternWithParams::new( r#"SELECT "t1"."a", "t1"."b", "t1"."bucket_id" FROM "t1""#.to_string(), vec![], ))), @@ -50,8 +50,8 @@ fn bucket2_test() { let bucket = query.coordinator.determine_bucket_id(&[¶m1, ¶m2]); expected.rows.push(vec![ - EncodedValue::String(format!("Execute query on a bucket [{bucket}]")), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String(format!("Execute query on a bucket [{bucket}]")), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"SELECT "t1"."a", "t1"."bucket_id", "t1"."b" FROM "t1""#, @@ -78,8 +78,8 @@ fn bucket3_test() { let mut expected = ProducerResult::new(); expected.rows.push(vec![ - EncodedValue::String("Execute query on all buckets".to_string()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".to_string()), + LuaValue::String(String::from(PatternWithParams::new( r#"SELECT "t1"."a", "t1"."b", "FUNC" (?) as "COL_1" FROM "t1""#.to_string(), vec![Value::from("111".to_string())], ))), diff --git a/sbroad-core/src/executor/tests/empty_motion.rs b/sbroad-core/src/executor/tests/empty_motion.rs index 69fb287258..7524aa0af6 100644 --- a/sbroad-core/src/executor/tests/empty_motion.rs +++ b/sbroad-core/src/executor/tests/empty_motion.rs @@ -6,7 +6,7 @@ use crate::executor::result::ProducerResult; use crate::executor::vtable::VirtualTable; use crate::ir::relation::{Column, ColumnRole, Type}; use crate::ir::transformation::redistribution::MotionPolicy; -use crate::ir::value::{EncodedValue, Value}; +use crate::ir::value::{LuaValue, Value}; use super::*; @@ -60,8 +60,8 @@ fn empty_motion1_test() { let mut expected = ProducerResult::new(); expected.rows.extend(vec![vec![ - EncodedValue::String("Execute query on all buckets".into()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".into()), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {} {} {} {} {} {} {} {} {} {} {} {} {}", r#"SELECT "Q"."a", "Q"."b" FROM"#, diff --git a/sbroad-core/src/executor/tests/not_eq.rs b/sbroad-core/src/executor/tests/not_eq.rs index ee3c9e67ea..ca1ed7ea58 100644 --- a/sbroad-core/src/executor/tests/not_eq.rs +++ b/sbroad-core/src/executor/tests/not_eq.rs @@ -7,7 +7,7 @@ use crate::executor::vtable::VirtualTable; use crate::ir::relation::{Column, ColumnRole, Type}; use crate::ir::transformation::redistribution::tests::get_motion_id; use crate::ir::transformation::redistribution::MotionPolicy; -use crate::ir::value::{EncodedValue, Value}; +use crate::ir::value::{LuaValue, Value}; use super::*; @@ -36,8 +36,8 @@ fn not_eq1_test() { // Validate the result. let mut expected = ProducerResult::new(); expected.rows.extend(vec![vec![ - EncodedValue::String("Execute query on all buckets".to_string()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".to_string()), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"SELECT "t"."identification_number" FROM "hash_testing" as "t""#, @@ -91,8 +91,8 @@ fn not_eq2_test() { // Validate the result. let mut expected = ProducerResult::new(); expected.rows.extend(vec![vec![ - EncodedValue::String("Execute query on all buckets".to_string()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".to_string()), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"SELECT "t"."identification_number" FROM "hash_testing" as "t""#, diff --git a/sbroad-core/src/executor/tests/not_in.rs b/sbroad-core/src/executor/tests/not_in.rs index eedbd3ba33..e35128fec3 100644 --- a/sbroad-core/src/executor/tests/not_in.rs +++ b/sbroad-core/src/executor/tests/not_in.rs @@ -7,7 +7,7 @@ use crate::executor::vtable::VirtualTable; use crate::ir::relation::{Column, ColumnRole, Type}; use crate::ir::transformation::redistribution::tests::get_motion_id; use crate::ir::transformation::redistribution::MotionPolicy; -use crate::ir::value::{EncodedValue, Value}; +use crate::ir::value::{LuaValue, Value}; use super::*; @@ -53,8 +53,8 @@ fn not_in1_test() { // Validate the result. let mut expected = ProducerResult::new(); expected.rows.extend(vec![vec![ - EncodedValue::String("Execute query on all buckets".to_string()), - EncodedValue::String(String::from(PatternWithParams::new( + LuaValue::String("Execute query on all buckets".to_string()), + LuaValue::String(String::from(PatternWithParams::new( format!( "{} {}", r#"SELECT "t"."identification_number" FROM "hash_testing" as "t""#, diff --git a/sbroad-core/src/ir/value.rs b/sbroad-core/src/ir/value.rs index 3349fb28f6..a1e5a625a0 100644 --- a/sbroad-core/src/ir/value.rs +++ b/sbroad-core/src/ir/value.rs @@ -634,15 +634,17 @@ impl Value { } } - /// Cast a value to a different type. + /// Cast a value to a different type and wrap into encoded value. + /// If the target type is the same as the current type, the value + /// is returned by reference. Otherwise, the value is cloned. /// /// # Errors /// - the value cannot be cast to the given type. #[allow(clippy::too_many_lines)] - pub fn cast(&self, column_type: &Type) -> Result<Value, SbroadError> { + pub fn cast(&self, column_type: &Type) -> Result<EncodedValue, SbroadError> { match column_type { Type::Array => match self { - Value::Null => Ok(Value::Null), + Value::Null => Ok(Value::Null.into()), _ => Err(SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), @@ -650,8 +652,8 @@ impl Value { )), }, Type::Boolean => match self { - Value::Boolean(_) => Ok(self.clone()), - Value::Null => Ok(Value::Null), + Value::Boolean(_) => Ok(self.into()), + Value::Null => Ok(Value::Null.into()), _ => Err(SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), @@ -659,7 +661,7 @@ impl Value { )), }, Type::Decimal => match self { - Value::Decimal(_) => Ok(self.clone()), + Value::Decimal(_) => Ok(self.into()), Value::Double(v) => Ok(Value::Decimal( Decimal::from_str(&format!("{v}")).map_err(|e| { SbroadError::FailedTo( @@ -668,10 +670,11 @@ impl Value { format!("{e:?}"), ) })?, - )), - Value::Integer(v) => Ok(Value::Decimal(Decimal::from(*v))), - Value::Unsigned(v) => Ok(Value::Decimal(Decimal::from(*v))), - Value::Null => Ok(Value::Null), + ) + .into()), + Value::Integer(v) => Ok(Value::Decimal(Decimal::from(*v)).into()), + Value::Unsigned(v) => Ok(Value::Decimal(Decimal::from(*v)).into()), + Value::Null => Ok(Value::Null.into()), _ => Err(SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), @@ -679,11 +682,11 @@ impl Value { )), }, Type::Double => match self { - Value::Double(_) => Ok(self.clone()), - Value::Decimal(v) => Ok(Value::Double(Double::from_str(&format!("{v}"))?)), - Value::Integer(v) => Ok(Value::Double(Double::from(*v))), - Value::Unsigned(v) => Ok(Value::Double(Double::from(*v))), - Value::Null => Ok(Value::Null), + Value::Double(_) => Ok(self.into()), + Value::Decimal(v) => Ok(Value::Double(Double::from_str(&format!("{v}"))?).into()), + Value::Integer(v) => Ok(Value::Double(Double::from(*v)).into()), + Value::Unsigned(v) => Ok(Value::Double(Double::from(*v)).into()), + Value::Null => Ok(Value::Null.into()), _ => Err(SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), @@ -691,18 +694,20 @@ impl Value { )), }, Type::Integer => match self { - Value::Integer(_) => Ok(self.clone()), + Value::Integer(_) => Ok(self.into()), Value::Decimal(v) => Ok(Value::Integer(v.to_i64().ok_or_else(|| { SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), format!("{self:?} into integer"), ) - })?)), + })?) + .into()), Value::Double(v) => v .to_string() .parse::<i64>() .map(Value::Integer) + .map(EncodedValue::from) .map_err(|e| { SbroadError::FailedTo(Action::Serialize, Some(Entity::Value), e.to_string()) }), @@ -712,8 +717,9 @@ impl Value { Some(Entity::Value), format!("u64 {v} into i64: {e}"), ) - })?)), - Value::Null => Ok(Value::Null), + })?) + .into()), + Value::Null => Ok(Value::Null.into()), _ => Err(SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), @@ -726,11 +732,11 @@ impl Value { Some(Entity::Value), format!("{self:?} into scalar"), )), - _ => Ok(self.clone()), + _ => Ok(self.into()), }, Type::String => match self { - Value::String(_) => Ok(self.clone()), - Value::Null => Ok(Value::Null), + Value::String(_) => Ok(self.into()), + Value::Null => Ok(Value::Null.into()), _ => Err(SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), @@ -739,9 +745,9 @@ impl Value { }, Type::Number => match self { Value::Integer(_) | Value::Decimal(_) | Value::Double(_) | Value::Unsigned(_) => { - Ok(self.clone()) + Ok(self.into()) } - Value::Null => Ok(Value::Null), + Value::Null => Ok(Value::Null.into()), _ => Err(SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), @@ -749,34 +755,36 @@ impl Value { )), }, Type::Unsigned => match self { - Value::Unsigned(_) => Ok(self.clone()), + Value::Unsigned(_) => Ok(self.into()), Value::Integer(v) => Ok(Value::Unsigned(u64::try_from(*v).map_err(|e| { SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), format!("i64 {v} into u64: {e}"), ) - })?)), + })?) + .into()), Value::Decimal(v) => Ok(Value::Unsigned(v.to_u64().ok_or_else(|| { SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), format!("{self:?} into unsigned"), ) - })?)), - Value::Double(v) => { - v.to_string() - .parse::<u64>() - .map(Value::Unsigned) - .map_err(|_| { - SbroadError::FailedTo( - Action::Serialize, - Some(Entity::Value), - format!("{self:?} into unsigned"), - ) - }) - } - Value::Null => Ok(Value::Null), + })?) + .into()), + Value::Double(v) => v + .to_string() + .parse::<u64>() + .map(Value::Unsigned) + .map(EncodedValue::from) + .map_err(|_| { + SbroadError::FailedTo( + Action::Serialize, + Some(Entity::Value), + format!("{self:?} into unsigned"), + ) + }), + Value::Null => Ok(Value::Null.into()), _ => Err(SbroadError::FailedTo( Action::Serialize, Some(Entity::Value), @@ -807,10 +815,72 @@ impl ToHashString for Value { } } +/// A helper enum to encode values into `MessagePack`. +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum EncodedValue<'v> { + Ref(MsgPackValue<'v>), + Owned(LuaValue), +} + +impl<'v> From<MsgPackValue<'v>> for EncodedValue<'v> { + fn from(value: MsgPackValue<'v>) -> Self { + EncodedValue::Ref(value) + } +} + +impl From<LuaValue> for EncodedValue<'_> { + fn from(value: LuaValue) -> Self { + EncodedValue::Owned(value) + } +} + +impl<'v> From<&'v Value> for EncodedValue<'v> { + fn from(value: &'v Value) -> Self { + EncodedValue::from(MsgPackValue::from(value)) + } +} + +impl From<Value> for EncodedValue<'_> { + fn from(value: Value) -> Self { + EncodedValue::from(LuaValue::from(value)) + } +} + +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum MsgPackValue<'v> { + Boolean(&'v bool), + Decimal(&'v Decimal), + Double(&'v f64), + Integer(&'v i64), + Unsigned(&'v u64), + String(&'v String), + Tuple(&'v Tuple), + Null(()), +} + +impl<'v> From<&'v Value> for MsgPackValue<'v> { + fn from(value: &'v Value) -> Self { + match value { + Value::Boolean(v) => MsgPackValue::Boolean(v), + Value::Decimal(v) => MsgPackValue::Decimal(v), + Value::Double(v) => MsgPackValue::Double(&v.value), + Value::Integer(v) => MsgPackValue::Integer(v), + Value::Null => MsgPackValue::Null(()), + Value::String(v) => MsgPackValue::String(v), + Value::Tuple(v) => MsgPackValue::Tuple(v), + Value::Unsigned(v) => MsgPackValue::Unsigned(v), + } + } +} + #[allow(clippy::module_name_repetitions)] #[derive(Clone, LuaRead, PartialEq, Debug, Deserialize, Serialize)] #[serde(untagged)] -pub enum EncodedValue { +pub enum LuaValue { Boolean(bool), Decimal(Decimal), Double(f64), @@ -821,44 +891,44 @@ pub enum EncodedValue { Null(()), } -impl fmt::Display for EncodedValue { +impl fmt::Display for LuaValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - EncodedValue::Boolean(v) => write!(f, "{v}"), - EncodedValue::Decimal(v) => fmt::Display::fmt(v, f), - EncodedValue::Double(v) => write!(f, "{v}"), - EncodedValue::Integer(v) => write!(f, "{v}"), - EncodedValue::Unsigned(v) => write!(f, "{v}"), - EncodedValue::String(v) => write!(f, "'{v}'"), - EncodedValue::Tuple(v) => write!(f, "{v}"), - EncodedValue::Null(_) => write!(f, "NULL"), + LuaValue::Boolean(v) => write!(f, "{v}"), + LuaValue::Decimal(v) => fmt::Display::fmt(v, f), + LuaValue::Double(v) => write!(f, "{v}"), + LuaValue::Integer(v) => write!(f, "{v}"), + LuaValue::Unsigned(v) => write!(f, "{v}"), + LuaValue::String(v) => write!(f, "'{v}'"), + LuaValue::Tuple(v) => write!(f, "{v}"), + LuaValue::Null(_) => write!(f, "NULL"), } } } -impl From<Value> for EncodedValue { +impl From<Value> for LuaValue { fn from(value: Value) -> Self { match value { - Value::Boolean(v) => EncodedValue::Boolean(v), - Value::Decimal(v) => EncodedValue::Decimal(v), - Value::Double(v) => EncodedValue::Double(v.value), - Value::Integer(v) => EncodedValue::Integer(v), - Value::Null => EncodedValue::Null(()), - Value::String(v) => EncodedValue::String(v), - Value::Tuple(v) => EncodedValue::Tuple(v), - Value::Unsigned(v) => EncodedValue::Unsigned(v), + Value::Boolean(v) => LuaValue::Boolean(v), + Value::Decimal(v) => LuaValue::Decimal(v), + Value::Double(v) => LuaValue::Double(v.value), + Value::Integer(v) => LuaValue::Integer(v), + Value::Null => LuaValue::Null(()), + Value::String(v) => LuaValue::String(v), + Value::Tuple(v) => LuaValue::Tuple(v), + Value::Unsigned(v) => LuaValue::Unsigned(v), } } } -impl From<EncodedValue> for Value { +impl From<LuaValue> for Value { #[allow(clippy::cast_possible_truncation)] - fn from(value: EncodedValue) -> Self { + fn from(value: LuaValue) -> Self { match value { - EncodedValue::Unsigned(v) => Value::Unsigned(v), - EncodedValue::Integer(v) => Value::Integer(v), - EncodedValue::Decimal(v) => Value::Decimal(v), - EncodedValue::Double(v) => { + LuaValue::Unsigned(v) => Value::Unsigned(v), + LuaValue::Integer(v) => Value::Integer(v), + LuaValue::Decimal(v) => Value::Decimal(v), + LuaValue::Double(v) => { if v.is_nan() { Value::Null } else if v.is_subnormal() @@ -870,10 +940,10 @@ impl From<EncodedValue> for Value { Value::Integer(v as i64) } } - EncodedValue::Boolean(v) => Value::Boolean(v), - EncodedValue::String(v) => Value::String(v), - EncodedValue::Tuple(v) => Value::Tuple(v), - EncodedValue::Null(_) => Value::Null, + LuaValue::Boolean(v) => Value::Boolean(v), + LuaValue::String(v) => Value::String(v), + LuaValue::Tuple(v) => Value::Tuple(v), + LuaValue::Null(_) => Value::Null, } } } -- GitLab