From 5ed5ce4a0b6b92821e49e5b5c52d141d75dd68eb Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Wed, 27 Mar 2024 00:26:36 +0800 Subject: [PATCH] fix: expand `DataValue::Utf8` so that it can correspond to `Char` and `Varchar` respectively. (#183) * fix: expand `DataValue::Utf8` so that it can correspond to `Char` and `VarChar` respectively. * fix: incomparable for char and varchar * fix: utf8 on tests --- examples/hello_world.rs | 2 +- src/binder/expr.rs | 13 +- src/execution/volcano/dml/analyze.rs | 7 +- src/execution/volcano/dql/describe.rs | 48 +++- src/execution/volcano/dql/explain.rs | 7 +- src/execution/volcano/dql/show_table.rs | 7 +- src/expression/evaluator.rs | 22 +- src/expression/value_compute.rs | 121 ++++++-- src/marcos/mod.rs | 14 +- src/optimizer/core/histogram.rs | 2 +- .../rule/normalization/column_pruning.rs | 7 +- src/storage/table_codec.rs | 4 +- src/types/mod.rs | 5 + src/types/tuple.rs | 24 +- src/types/tuple_builder.rs | 16 +- src/types/value.rs | 266 ++++++++++++------ tests/slt/describe.slt | 2 +- 17 files changed, 412 insertions(+), 155 deletions(-) diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 3b3a6795..15cf1018 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -20,7 +20,7 @@ implement_from_tuple!( } }, c2: String => |inner: &mut MyStruct, value| { - if let DataValue::Utf8(Some(val)) = value { + if let DataValue::Utf8 { value: Some(val), .. } = value { inner.c2 = val; } } diff --git a/src/binder/expr.rs b/src/binder/expr.rs index 009846ba..3e8e0275 100644 --- a/src/binder/expr.rs +++ b/src/binder/expr.rs @@ -15,7 +15,7 @@ use crate::expression::function::{FunctionSummary, ScalarFunction}; use crate::expression::{AliasType, ScalarExpression}; use crate::planner::LogicalPlan; use crate::storage::Transaction; -use crate::types::value::DataValue; +use crate::types::value::{DataValue, Utf8Type}; use crate::types::LogicalType; macro_rules! try_alias { @@ -67,7 +67,11 @@ impl<'a, T: Transaction> Binder<'a, T> { } => self.bind_cast(expr, data_type), Expr::TypedString { data_type, value } => { let logical_type = LogicalType::try_from(data_type.clone())?; - let value = DataValue::Utf8(Some(value.to_string())).cast(&logical_type)?; + let value = DataValue::Utf8 { + value: Some(value.to_string()), + ty: Utf8Type::Variable, + } + .cast(&logical_type)?; Ok(ScalarExpression::Constant(Arc::new(value))) } @@ -597,6 +601,9 @@ impl<'a, T: Transaction> Binder<'a, T> { } fn wildcard_expr() -> ScalarExpression { - ScalarExpression::Constant(Arc::new(DataValue::Utf8(Some("*".to_string())))) + ScalarExpression::Constant(Arc::new(DataValue::Utf8 { + value: Some("*".to_string()), + ty: Utf8Type::Variable, + })) } } diff --git a/src/execution/volcano/dml/analyze.rs b/src/execution/volcano/dml/analyze.rs index ef721164..ae8b6a8f 100644 --- a/src/execution/volcano/dml/analyze.rs +++ b/src/execution/volcano/dml/analyze.rs @@ -9,7 +9,7 @@ use crate::planner::LogicalPlan; use crate::storage::Transaction; use crate::types::index::IndexMetaRef; use crate::types::tuple::Tuple; -use crate::types::value::DataValue; +use crate::types::value::{DataValue, Utf8Type}; use futures_async_stream::try_stream; use itertools::Itertools; use std::fmt::Formatter; @@ -106,7 +106,10 @@ impl Analyze { let meta = StatisticsMeta::new(histogram, sketch); meta.to_file(&path)?; - values.push(Arc::new(DataValue::Utf8(Some(path.clone())))); + values.push(Arc::new(DataValue::Utf8 { + value: Some(path.clone()), + ty: Utf8Type::Variable, + })); transaction.save_table_meta(&table_name, path, meta)?; } yield Tuple { id: None, values }; diff --git a/src/execution/volcano/dql/describe.rs b/src/execution/volcano/dql/describe.rs index 247e449c..fb27ce64 100644 --- a/src/execution/volcano/dql/describe.rs +++ b/src/execution/volcano/dql/describe.rs @@ -4,16 +4,24 @@ use crate::execution::volcano::{BoxedExecutor, ReadExecutor}; use crate::planner::operator::describe::DescribeOperator; use crate::storage::Transaction; use crate::types::tuple::Tuple; -use crate::types::value::{DataValue, ValueRef}; +use crate::types::value::{DataValue, Utf8Type, ValueRef}; use futures_async_stream::try_stream; use lazy_static::lazy_static; use std::sync::Arc; lazy_static! { - static ref PRIMARY_KEY_TYPE: ValueRef = - Arc::new(DataValue::Utf8(Some(String::from("PRIMARY")))); - static ref UNIQUE_KEY_TYPE: ValueRef = Arc::new(DataValue::Utf8(Some(String::from("UNIQUE")))); - static ref EMPTY_KEY_TYPE: ValueRef = Arc::new(DataValue::Utf8(Some(String::from("EMPTY")))); + static ref PRIMARY_KEY_TYPE: ValueRef = Arc::new(DataValue::Utf8 { + value: Some(String::from("PRIMARY")), + ty: Utf8Type::Variable + }); + static ref UNIQUE_KEY_TYPE: ValueRef = Arc::new(DataValue::Utf8 { + value: Some(String::from("UNIQUE")), + ty: Utf8Type::Variable + }); + static ref EMPTY_KEY_TYPE: ValueRef = Arc::new(DataValue::Utf8 { + value: Some(String::from("EMPTY")), + ty: Utf8Type::Variable + }); } pub struct Describe { @@ -59,17 +67,27 @@ impl Describe { .map(|expr| format!("{}", expr)) .unwrap_or_else(|| "null".to_string()); let values = vec![ - Arc::new(DataValue::Utf8(Some(column.name().to_string()))), - Arc::new(DataValue::Utf8(Some(datatype.to_string()))), - Arc::new(DataValue::Utf8(Some( - datatype - .raw_len() - .map(|len| len.to_string()) - .unwrap_or_else(|| "DYNAMIC".to_string()), - ))), - Arc::new(DataValue::Utf8(Some(column.nullable.to_string()))), + Arc::new(DataValue::Utf8 { + value: Some(column.name().to_string()), + ty: Utf8Type::Variable, + }), + Arc::new(DataValue::Utf8 { + value: Some(datatype.to_string()), + ty: Utf8Type::Variable, + }), + Arc::new(DataValue::Utf8 { + value: datatype.raw_len().map(|len| len.to_string()), + ty: Utf8Type::Variable, + }), + Arc::new(DataValue::Utf8 { + value: Some(column.nullable.to_string()), + ty: Utf8Type::Variable, + }), key_fn(column), - Arc::new(DataValue::Utf8(Some(default))), + Arc::new(DataValue::Utf8 { + value: Some(default), + ty: Utf8Type::Variable, + }), ]; yield Tuple { id: None, values }; } diff --git a/src/execution/volcano/dql/explain.rs b/src/execution/volcano/dql/explain.rs index 913a38e6..f1dbb3ca 100644 --- a/src/execution/volcano/dql/explain.rs +++ b/src/execution/volcano/dql/explain.rs @@ -3,7 +3,7 @@ use crate::execution::volcano::{BoxedExecutor, ReadExecutor}; use crate::planner::LogicalPlan; use crate::storage::Transaction; use crate::types::tuple::Tuple; -use crate::types::value::DataValue; +use crate::types::value::{DataValue, Utf8Type}; use futures_async_stream::try_stream; use std::sync::Arc; @@ -26,7 +26,10 @@ impl ReadExecutor for Explain { impl Explain { #[try_stream(boxed, ok = Tuple, error = DatabaseError)] pub async fn _execute(self) { - let values = vec![Arc::new(DataValue::Utf8(Some(self.plan.explain(0))))]; + let values = vec![Arc::new(DataValue::Utf8 { + value: Some(self.plan.explain(0)), + ty: Utf8Type::Variable, + })]; yield Tuple { id: None, values }; } diff --git a/src/execution/volcano/dql/show_table.rs b/src/execution/volcano/dql/show_table.rs index 0f6d9c37..f64b6dd1 100644 --- a/src/execution/volcano/dql/show_table.rs +++ b/src/execution/volcano/dql/show_table.rs @@ -3,7 +3,7 @@ use crate::errors::DatabaseError; use crate::execution::volcano::{BoxedExecutor, ReadExecutor}; use crate::storage::Transaction; use crate::types::tuple::Tuple; -use crate::types::value::DataValue; +use crate::types::value::{DataValue, Utf8Type}; use futures_async_stream::try_stream; use std::sync::Arc; @@ -21,7 +21,10 @@ impl ShowTables { let metas = transaction.table_metas()?; for TableMeta { table_name } in metas { - let values = vec![Arc::new(DataValue::Utf8(Some(table_name.to_string())))]; + let values = vec![Arc::new(DataValue::Utf8 { + value: Some(table_name.to_string()), + ty: Utf8Type::Variable, + })]; yield Tuple { id: None, values }; } diff --git a/src/expression/evaluator.rs b/src/expression/evaluator.rs index 1082fbf4..f97a1d4c 100644 --- a/src/expression/evaluator.rs +++ b/src/expression/evaluator.rs @@ -3,7 +3,7 @@ use crate::errors::DatabaseError; use crate::expression::function::ScalarFunction; use crate::expression::{AliasType, BinaryOperator, ScalarExpression}; use crate::types::tuple::Tuple; -use crate::types::value::{DataValue, ValueRef}; +use crate::types::value::{DataValue, Utf8Type, ValueRef}; use crate::types::LogicalType; use itertools::Itertools; use lazy_static::lazy_static; @@ -23,7 +23,10 @@ macro_rules! eval_to_num { { num_i32 } else { - return Ok(Arc::new(DataValue::Utf8(None))); + return Ok(Arc::new(DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable, + })); } }; } @@ -164,7 +167,10 @@ impl ScalarExpression { from += len_i + 1; } if from > len_i { - return Ok(Arc::new(DataValue::Utf8(None))); + return Ok(Arc::new(DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable, + })); } string = string.split_off(from as usize); } @@ -174,9 +180,15 @@ impl ScalarExpression { let _ = string.split_off(for_i); } - Ok(Arc::new(DataValue::Utf8(Some(string)))) + Ok(Arc::new(DataValue::Utf8 { + value: Some(string), + ty: Utf8Type::Variable, + })) } else { - Ok(Arc::new(DataValue::Utf8(None))) + Ok(Arc::new(DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable, + })) } } ScalarExpression::Position { expr, in_expr } => { diff --git a/src/expression/value_compute.rs b/src/expression/value_compute.rs index 1b8066f1..3ceb7031 100644 --- a/src/expression/value_compute.rs +++ b/src/expression/value_compute.rs @@ -1,6 +1,6 @@ use crate::errors::DatabaseError; use crate::expression::{BinaryOperator, UnaryOperator}; -use crate::types::value::{DataValue, ValueRef}; +use crate::types::value::{DataValue, Utf8Type, ValueRef}; use crate::types::LogicalType; use regex::Regex; use std::cmp::Ordering; @@ -14,7 +14,7 @@ fn unpack_bool(value: DataValue) -> Option { fn unpack_utf8(value: DataValue) -> Option { match value { - DataValue::Utf8(inner) => inner, + DataValue::Utf8 { value: inner, .. } => inner, _ => None, } } @@ -574,7 +574,10 @@ impl DataValue { _ => None, }; - DataValue::Utf8(value) + DataValue::Utf8 { + value, + ty: Utf8Type::Variable, + } } _ => return Err(DatabaseError::UnsupportedBinaryOperator(unified_type, *op)), } @@ -648,7 +651,7 @@ impl DataValue { mod test { use crate::errors::DatabaseError; use crate::expression::BinaryOperator; - use crate::types::value::DataValue; + use crate::types::value::{DataValue, Utf8Type}; #[test] fn test_binary_op_arithmetic_plus() -> Result<(), DatabaseError> { @@ -1539,48 +1542,84 @@ mod test { fn test_binary_op_utf8_compare() -> Result<(), DatabaseError> { assert_eq!( DataValue::binary_op( - &DataValue::Utf8(Some("a".to_string())), - &DataValue::Utf8(Some("b".to_string())), + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("b".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::Gt )?, DataValue::Boolean(Some(false)) ); assert_eq!( DataValue::binary_op( - &DataValue::Utf8(Some("a".to_string())), - &DataValue::Utf8(Some("b".to_string())), + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("b".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::Lt )?, DataValue::Boolean(Some(true)) ); assert_eq!( DataValue::binary_op( - &DataValue::Utf8(Some("a".to_string())), - &DataValue::Utf8(Some("a".to_string())), + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::GtEq )?, DataValue::Boolean(Some(true)) ); assert_eq!( DataValue::binary_op( - &DataValue::Utf8(Some("a".to_string())), - &DataValue::Utf8(Some("a".to_string())), + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::LtEq )?, DataValue::Boolean(Some(true)) ); assert_eq!( DataValue::binary_op( - &DataValue::Utf8(Some("a".to_string())), - &DataValue::Utf8(Some("a".to_string())), + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::NotEq )?, DataValue::Boolean(Some(false)) ); assert_eq!( DataValue::binary_op( - &DataValue::Utf8(Some("a".to_string())), - &DataValue::Utf8(Some("a".to_string())), + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::Eq )?, DataValue::Boolean(Some(true)) @@ -1588,40 +1627,70 @@ mod test { assert_eq!( DataValue::binary_op( - &DataValue::Utf8(None), - &DataValue::Utf8(Some("a".to_string())), + &DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::Gt )?, DataValue::Boolean(None) ); assert_eq!( DataValue::binary_op( - &DataValue::Utf8(None), - &DataValue::Utf8(Some("a".to_string())), + &DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::Lt )?, DataValue::Boolean(None) ); assert_eq!( DataValue::binary_op( - &DataValue::Utf8(None), - &DataValue::Utf8(Some("a".to_string())), + &DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::GtEq )?, DataValue::Boolean(None) ); assert_eq!( DataValue::binary_op( - &DataValue::Utf8(None), - &DataValue::Utf8(Some("a".to_string())), + &DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::LtEq )?, DataValue::Boolean(None) ); assert_eq!( DataValue::binary_op( - &DataValue::Utf8(None), - &DataValue::Utf8(Some("a".to_string())), + &DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable + }, + &DataValue::Utf8 { + value: Some("a".to_string()), + ty: Utf8Type::Variable + }, &BinaryOperator::NotEq )?, DataValue::Boolean(None) diff --git a/src/marcos/mod.rs b/src/marcos/mod.rs index 5dfa4b3c..39332a1d 100644 --- a/src/marcos/mod.rs +++ b/src/marcos/mod.rs @@ -133,7 +133,7 @@ mod test { use crate::expression::BinaryOperator; use crate::expression::ScalarExpression; use crate::types::tuple::{SchemaRef, Tuple}; - use crate::types::value::{DataValue, ValueRef}; + use crate::types::value::{DataValue, Utf8Type, ValueRef}; use crate::types::LogicalType; use serde::Deserialize; use serde::Serialize; @@ -154,7 +154,10 @@ mod test { ]); let values = vec![ Arc::new(DataValue::Int32(Some(9))), - Arc::new(DataValue::Utf8(Some("LOL".to_string()))), + Arc::new(DataValue::Utf8 { + value: Some("LOL".to_string()), + ty: Utf8Type::Variable, + }), ]; (Tuple { id: None, values }, schema_ref) @@ -174,7 +177,7 @@ mod test { } }, c2: String => |inner: &mut MyStruct, value| { - if let DataValue::Utf8(Some(val)) = value { + if let DataValue::Utf8 { value: Some(val), .. } = value { inner.c2 = val; } } @@ -202,7 +205,10 @@ mod test { let sum = function.eval( &[ ScalarExpression::Constant(Arc::new(DataValue::Int8(Some(1)))), - ScalarExpression::Constant(Arc::new(DataValue::Utf8(Some("1".to_string())))), + ScalarExpression::Constant(Arc::new(DataValue::Utf8 { + value: Some("1".to_string()), + ty: Utf8Type::Variable, + })), ], &Tuple { id: None, diff --git a/src/optimizer/core/histogram.rs b/src/optimizer/core/histogram.rs index 0af568df..17cdf193 100644 --- a/src/optimizer/core/histogram.rs +++ b/src/optimizer/core/histogram.rs @@ -256,7 +256,7 @@ impl Histogram { let float_value = |value: &DataValue, prefix_len: usize| { let value = match value.logical_type() { LogicalType::Varchar(_) | LogicalType::Char(_) => match value { - DataValue::Utf8(value) => value.as_ref().map(|string| { + DataValue::Utf8 { value, .. } => value.as_ref().map(|string| { if prefix_len > string.len() { return 0.0; } diff --git a/src/optimizer/rule/normalization/column_pruning.rs b/src/optimizer/rule/normalization/column_pruning.rs index f6dcc040..e48e98e1 100644 --- a/src/optimizer/rule/normalization/column_pruning.rs +++ b/src/optimizer/rule/normalization/column_pruning.rs @@ -6,7 +6,7 @@ use crate::optimizer::core::pattern::{Pattern, PatternChildrenPredicate}; use crate::optimizer::core::rule::{MatchPattern, NormalizationRule}; use crate::optimizer::heuristic::graph::{HepGraph, HepNodeId}; use crate::planner::operator::Operator; -use crate::types::value::DataValue; +use crate::types::value::{DataValue, Utf8Type}; use crate::types::LogicalType; use itertools::Itertools; use lazy_static::lazy_static; @@ -61,7 +61,10 @@ impl ColumnPruning { Self::clear_exprs(&column_references, &mut op.agg_calls); if op.agg_calls.is_empty() && op.groupby_exprs.is_empty() { - let value = Arc::new(DataValue::Utf8(Some("*".to_string()))); + let value = Arc::new(DataValue::Utf8 { + value: Some("*".to_string()), + ty: Utf8Type::Variable, + }); // only single COUNT(*) is not depend on any column // removed all expressions from the aggregate: push a COUNT(*) op.agg_calls.push(ScalarExpression::AggCall { diff --git a/src/storage/table_codec.rs b/src/storage/table_codec.rs index b441c7aa..3994e489 100644 --- a/src/storage/table_codec.rs +++ b/src/storage/table_codec.rs @@ -227,7 +227,7 @@ impl TableCodec { ) -> Result<(Bytes, Bytes), DatabaseError> { let key = TableCodec::encode_index_key(name, index, Some(tuple_id))?; let mut bytes = Vec::new(); - tuple_id.to_raw(&mut bytes, None)?; + tuple_id.to_raw(&mut bytes)?; Ok((Bytes::from(key), Bytes::from(bytes))) } @@ -269,7 +269,7 @@ impl TableCodec { if let Some(tuple_id) = tuple_id { if matches!(index.ty, IndexType::Normal | IndexType::Composite) { - tuple_id.to_raw(&mut key_prefix, None)?; + tuple_id.to_raw(&mut key_prefix)?; } } Ok(key_prefix) diff --git a/src/types/mod.rs b/src/types/mod.rs index 173e12d6..209dcda6 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -197,6 +197,11 @@ impl LogicalType { ) { return Ok(LogicalType::DateTime); } + if let (LogicalType::Char(_), LogicalType::Varchar(len)) + | (LogicalType::Varchar(len), LogicalType::Char(_)) = (left, right) + { + return Ok(LogicalType::Varchar(*len)); + } Err(DatabaseError::Incomparable(*left, *right)) } diff --git a/src/types/tuple.rs b/src/types/tuple.rs index ef11bcdb..dff31ffb 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -125,7 +125,7 @@ impl Tuple { bytes[i / BITS_MAX_INDEX] = flip_bit(bytes[i / BITS_MAX_INDEX], i % BITS_MAX_INDEX); } else { let logical_type = types[i]; - let value_len = value.to_raw(&mut bytes, Some(logical_type))?; + let value_len = value.to_raw(&mut bytes)?; if logical_type.raw_len().is_none() { let index = bytes.len() - value_len; @@ -171,7 +171,7 @@ pub fn create_table(schema: &Schema, tuples: &[Tuple]) -> Table { mod tests { use crate::catalog::{ColumnCatalog, ColumnDesc}; use crate::types::tuple::Tuple; - use crate::types::value::DataValue; + use crate::types::value::{DataValue, Utf8Type}; use crate::types::LogicalType; use itertools::Itertools; use rust_decimal::Decimal; @@ -258,7 +258,10 @@ mod tests { values: vec![ Arc::new(DataValue::Int32(Some(0))), Arc::new(DataValue::UInt32(Some(1))), - Arc::new(DataValue::Utf8(Some("LOL".to_string()))), + Arc::new(DataValue::Utf8 { + value: Some("LOL".to_string()), + ty: Utf8Type::Variable, + }), Arc::new(DataValue::Int16(Some(1))), Arc::new(DataValue::UInt16(Some(1))), Arc::new(DataValue::Float32(Some(0.1))), @@ -269,7 +272,10 @@ mod tests { Arc::new(DataValue::Date64(Some(0))), Arc::new(DataValue::Date32(Some(0))), Arc::new(DataValue::Decimal(Some(Decimal::new(0, 3)))), - Arc::new(DataValue::Utf8(Some("K".to_string()))), + Arc::new(DataValue::Utf8 { + value: Some("K".to_string()), + ty: Utf8Type::Fixed(1), + }), ], }, Tuple { @@ -277,7 +283,10 @@ mod tests { values: vec![ Arc::new(DataValue::Int32(Some(1))), Arc::new(DataValue::UInt32(None)), - Arc::new(DataValue::Utf8(None)), + Arc::new(DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable, + }), Arc::new(DataValue::Int16(None)), Arc::new(DataValue::UInt16(None)), Arc::new(DataValue::Float32(None)), @@ -288,7 +297,10 @@ mod tests { Arc::new(DataValue::Date64(None)), Arc::new(DataValue::Date32(None)), Arc::new(DataValue::Decimal(None)), - Arc::new(DataValue::Utf8(None)), + Arc::new(DataValue::Utf8 { + value: None, + ty: Utf8Type::Fixed(1), + }), ], }, ]; diff --git a/src/types/tuple_builder.rs b/src/types/tuple_builder.rs index 82f0aef9..d9763a87 100644 --- a/src/types/tuple_builder.rs +++ b/src/types/tuple_builder.rs @@ -1,6 +1,6 @@ use crate::errors::DatabaseError; use crate::types::tuple::{Schema, Tuple}; -use crate::types::value::DataValue; +use crate::types::value::{DataValue, Utf8Type}; use std::sync::Arc; pub struct TupleBuilder<'a> { @@ -13,7 +13,10 @@ impl<'a> TupleBuilder<'a> { } pub fn build_result(message: String) -> Tuple { - let values = vec![Arc::new(DataValue::Utf8(Some(message)))]; + let values = vec![Arc::new(DataValue::Utf8 { + value: Some(message), + ty: Utf8Type::Variable, + })]; Tuple { id: None, values } } @@ -26,8 +29,13 @@ impl<'a> TupleBuilder<'a> { let mut primary_key = None; for (i, value) in row.into_iter().enumerate() { - let data_value = - Arc::new(DataValue::Utf8(Some(value.to_string())).cast(self.schema[i].datatype())?); + let data_value = Arc::new( + DataValue::Utf8 { + value: Some(value.to_string()), + ty: Utf8Type::Variable, + } + .cast(self.schema[i].datatype())?, + ); if primary_key.is_none() && self.schema[i].desc.is_primary { primary_key = Some(data_value.clone()); diff --git a/src/types/value.rs b/src/types/value.rs index ff0b1a07..9e4300d0 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -32,6 +32,12 @@ const ENCODE_MARKER: u8 = 0xFF; pub type ValueRef = Arc; +#[derive(Clone, Serialize, Deserialize)] +pub enum Utf8Type { + Variable, + Fixed(u32), +} + #[derive(Clone, Serialize, Deserialize)] pub enum DataValue { Null, @@ -46,7 +52,10 @@ pub enum DataValue { UInt16(Option), UInt32(Option), UInt64(Option), - Utf8(Option), + Utf8 { + value: Option, + ty: Utf8Type, + }, /// Date stored as a signed 32bit int days since UNIX epoch 1970-01-01 Date32(Option), /// Date stored as a signed 64bit int timestamp since UNIX epoch 1970-01-01 @@ -84,7 +93,6 @@ generate_get_option!(DataValue, u16 : UInt16(Option), u32 : UInt32(Option), u64 : UInt64(Option), - utf8 : Utf8(Option), decimal : Decimal(Option) ); @@ -127,8 +135,8 @@ impl PartialEq for DataValue { (UInt32(_), _) => false, (UInt64(v1), UInt64(v2)) => v1.eq(v2), (UInt64(_), _) => false, - (Utf8(v1), Utf8(v2)) => v1.eq(v2), - (Utf8(_), _) => false, + (Utf8 { value: v1, .. }, Utf8 { value: v2, .. }) => v1.eq(v2), + (Utf8 { .. }, _) => false, (Null, Null) => true, (Null, _) => false, (Date32(v1), Date32(v2)) => v1.eq(v2), @@ -179,8 +187,8 @@ impl PartialOrd for DataValue { (UInt32(_), _) => None, (UInt64(v1), UInt64(v2)) => v1.partial_cmp(v2), (UInt64(_), _) => None, - (Utf8(v1), Utf8(v2)) => v1.partial_cmp(v2), - (Utf8(_), _) => None, + (Utf8 { value: v1, .. }, Utf8 { value: v2, .. }) => v1.partial_cmp(v2), + (Utf8 { .. }, _) => None, (Null, Null) => Some(Ordering::Equal), (Null, _) => None, (Date32(v1), Date32(v2)) => v1.partial_cmp(v2), @@ -225,7 +233,7 @@ impl Hash for DataValue { UInt16(v) => v.hash(state), UInt32(v) => v.hash(state), UInt64(v) => v.hash(state), - Utf8(v) => v.hash(state), + Utf8 { value: v, .. } => v.hash(state), Null => 1.hash(state), Date32(v) => v.hash(state), Date64(v) => v.hash(state), @@ -240,7 +248,7 @@ impl Hash for DataValue { } } macro_rules! varchar_cast { - ($value:expr, $len:expr) => { + ($value:expr, $len:expr, $ty:expr) => { $value .map(|v| { let string_value = format!("{}", v); @@ -249,9 +257,15 @@ macro_rules! varchar_cast { return Err(DatabaseError::TooLong); } } - Ok(DataValue::Utf8(Some(string_value))) + Ok(DataValue::Utf8 { + value: Some(string_value), + ty: $ty, + }) }) - .unwrap_or(Ok(DataValue::Utf8(None))) + .unwrap_or(Ok(DataValue::Utf8 { + value: None, + ty: $ty, + })) }; } @@ -266,6 +280,17 @@ macro_rules! numeric_to_boolean { } impl DataValue { + pub fn utf8(&self) -> Option { + if let DataValue::Utf8 { + value: Some(val), .. + } = self + { + Some(val.clone()) + } else { + None + } + } + pub fn date(&self) -> Option { if let DataValue::Date32(Some(val)) = self { NaiveDate::from_num_days_from_ce_opt(*val) @@ -292,9 +317,20 @@ impl DataValue { pub(crate) fn check_len(&self, logic_type: &LogicalType) -> Result<(), DatabaseError> { let is_over_len = match (logic_type, self) { - (LogicalType::Varchar(Some(len)), DataValue::Utf8(Some(val))) => { - val.len() > *len as usize - } + ( + LogicalType::Varchar(Some(len)), + DataValue::Utf8 { + value: Some(val), + ty: Utf8Type::Variable, + }, + ) + | ( + LogicalType::Char(len), + DataValue::Utf8 { + value: Some(val), + ty: Utf8Type::Fixed(_), + }, + ) => val.len() > *len as usize, (LogicalType::Decimal(full_len, scale_len), DataValue::Decimal(Some(val))) => { if let Some(len) = full_len { if val.mantissa().ilog10() + 1 > *len as u32 { @@ -344,7 +380,7 @@ impl DataValue { DataValue::UInt16(value) => value.is_none(), DataValue::UInt32(value) => value.is_none(), DataValue::UInt64(value) => value.is_none(), - DataValue::Utf8(value) => value.is_none(), + DataValue::Utf8 { value, .. } => value.is_none(), DataValue::Date32(value) => value.is_none(), DataValue::Date64(value) => value.is_none(), DataValue::Time(value) => value.is_none(), @@ -368,7 +404,14 @@ impl DataValue { LogicalType::UBigint => DataValue::UInt64(None), LogicalType::Float => DataValue::Float32(None), LogicalType::Double => DataValue::Float64(None), - LogicalType::Char(_) | LogicalType::Varchar(_) => DataValue::Utf8(None), + LogicalType::Char(len) => DataValue::Utf8 { + value: None, + ty: Utf8Type::Fixed(*len), + }, + LogicalType::Varchar(_) => DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable, + }, LogicalType::Date => DataValue::Date32(None), LogicalType::DateTime => DataValue::Date64(None), LogicalType::Time => DataValue::Time(None), @@ -392,7 +435,14 @@ impl DataValue { LogicalType::UBigint => DataValue::UInt64(Some(0)), LogicalType::Float => DataValue::Float32(Some(0.0)), LogicalType::Double => DataValue::Float64(Some(0.0)), - LogicalType::Char(_) | LogicalType::Varchar(_) => DataValue::Utf8(Some("".to_string())), + LogicalType::Char(len) => DataValue::Utf8 { + value: Some(String::new()), + ty: Utf8Type::Fixed(*len), + }, + LogicalType::Varchar(_) => DataValue::Utf8 { + value: Some(String::new()), + ty: Utf8Type::Variable, + }, LogicalType::Date => DataValue::Date32(Some(UNIX_DATETIME.num_days_from_ce())), LogicalType::DateTime => DataValue::Date64(Some(UNIX_DATETIME.timestamp())), LogicalType::Time => DataValue::Time(Some(UNIX_TIME.num_seconds_from_midnight())), @@ -401,11 +451,7 @@ impl DataValue { } } - pub fn to_raw( - &self, - bytes: &mut Vec, - logical_type: Option, - ) -> Result { + pub fn to_raw(&self, bytes: &mut Vec) -> Result { match self { DataValue::Null => (), DataValue::Boolean(v) => { @@ -465,21 +511,24 @@ impl DataValue { return Ok(bytes.write_fixedint(*v)?); } } - DataValue::Utf8(v) => { + DataValue::Utf8 { value: v, ty } => { if let Some(v) = v { - if let Some(LogicalType::Char(len)) = logical_type { - let mut string_bytes = - format!("{:len$}", v, len = len as usize).into_bytes(); - let len = string_bytes.len(); + match ty { + Utf8Type::Variable => { + let string_bytes = v.as_bytes(); + let len = string_bytes.len(); - bytes.append(&mut string_bytes); - return Ok(len); - } else { - let string_bytes = v.as_bytes(); - let len = string_bytes.len(); + bytes.extend_from_slice(string_bytes); + return Ok(len); + } + Utf8Type::Fixed(len) => { + let mut string_bytes = + format!("{:len$}", v, len = *len as usize).into_bytes(); + let len = string_bytes.len(); - bytes.extend_from_slice(string_bytes); - return Ok(len); + bytes.append(&mut string_bytes); + return Ok(len); + } } } } @@ -548,7 +597,7 @@ impl DataValue { buf.copy_from_slice(bytes); f64::from_ne_bytes(buf) })), - LogicalType::Char(_) => { + LogicalType::Char(len) => { // https://dev.mysql.com/doc/refman/8.0/en/char.html#:~:text=If%20a%20given%20value%20is%20stored%20into%20the%20CHAR(4)%20and%20VARCHAR(4)%20columns%2C%20the%20values%20retrieved%20from%20the%20columns%20are%20not%20always%20the%20same%20because%20trailing%20spaces%20are%20removed%20from%20CHAR%20columns%20upon%20retrieval.%20The%20following%20example%20illustrates%20this%20difference%3A let value = (!bytes.is_empty()).then(|| { let last_non_zero_index = match bytes.iter().rposition(|&x| x != b' ') { @@ -557,11 +606,19 @@ impl DataValue { }; String::from_utf8(bytes[0..last_non_zero_index].to_owned()).unwrap() }); - DataValue::Utf8(value) + DataValue::Utf8 { + value, + ty: Utf8Type::Fixed(*len), + } + } + LogicalType::Varchar(_) => { + let value = + (!bytes.is_empty()).then(|| String::from_utf8(bytes.to_owned()).unwrap()); + DataValue::Utf8 { + value, + ty: Utf8Type::Variable, + } } - LogicalType::Varchar(_) => DataValue::Utf8( - (!bytes.is_empty()).then(|| String::from_utf8(bytes.to_owned()).unwrap()), - ), LogicalType::Date => { DataValue::Date32((!bytes.is_empty()).then(|| i32::decode_fixed(bytes))) } @@ -593,7 +650,14 @@ impl DataValue { DataValue::UInt16(_) => LogicalType::USmallint, DataValue::UInt32(_) => LogicalType::UInteger, DataValue::UInt64(_) => LogicalType::UBigint, - DataValue::Utf8(_) => LogicalType::Varchar(None), + DataValue::Utf8 { + ty: Utf8Type::Variable, + .. + } => LogicalType::Varchar(None), + DataValue::Utf8 { + ty: Utf8Type::Fixed(len), + .. + } => LogicalType::Char(*len), DataValue::Date32(_) => LogicalType::Date, DataValue::Date64(_) => LogicalType::DateTime, DataValue::Time(_) => LogicalType::Time, @@ -664,7 +728,7 @@ impl DataValue { DataValue::UInt16(Some(v)) => encode_u!(b, v), DataValue::UInt32(Some(v)) | DataValue::Time(Some(v)) => encode_u!(b, v), DataValue::UInt64(Some(v)) => encode_u!(b, v), - DataValue::Utf8(Some(v)) => Self::encode_bytes(b, v.as_bytes()), + DataValue::Utf8 { value: Some(v), .. } => Self::encode_bytes(b, v.as_bytes()), DataValue::Boolean(Some(v)) => b.push(if *v { b'1' } else { b'0' }), DataValue::Float32(Some(f)) => { let mut u = f.to_bits(); @@ -732,7 +796,14 @@ impl DataValue { LogicalType::UBigint => Ok(DataValue::UInt64(None)), LogicalType::Float => Ok(DataValue::Float32(None)), LogicalType::Double => Ok(DataValue::Float64(None)), - LogicalType::Char(_) | LogicalType::Varchar(_) => Ok(DataValue::Utf8(None)), + LogicalType::Char(len) => Ok(DataValue::Utf8 { + value: None, + ty: Utf8Type::Fixed(*len), + }), + LogicalType::Varchar(_) => Ok(DataValue::Utf8 { + value: None, + ty: Utf8Type::Variable, + }), LogicalType::Date => Ok(DataValue::Date32(None)), LogicalType::DateTime => Ok(DataValue::Date64(None)), LogicalType::Time => Ok(DataValue::Time(None)), @@ -752,16 +823,16 @@ impl DataValue { LogicalType::UBigint => Ok(DataValue::UInt64(value.map(|v| v.into()))), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v.into()))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), _ => Err(DatabaseError::CastFail), }, DataValue::Float32(value) => match to { LogicalType::SqlNull => Ok(DataValue::Null), LogicalType::Float => Ok(DataValue::Float32(value)), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal( value .map(|v| { @@ -779,8 +850,8 @@ impl DataValue { LogicalType::SqlNull => Ok(DataValue::Null), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v as f32))), LogicalType::Double => Ok(DataValue::Float64(value)), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal( value .map(|v| { @@ -812,8 +883,8 @@ impl DataValue { LogicalType::Bigint => Ok(DataValue::Int64(value.map(|v| v.into()))), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v.into()))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal(value.map(|v| { let mut decimal = Decimal::from(v); Self::decimal_round_i(option, &mut decimal); @@ -840,8 +911,8 @@ impl DataValue { LogicalType::Bigint => Ok(DataValue::Int64(value.map(|v| v.into()))), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v.into()))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal(value.map(|v| { let mut decimal = Decimal::from(v); Self::decimal_round_i(option, &mut decimal); @@ -867,8 +938,8 @@ impl DataValue { LogicalType::Bigint => Ok(DataValue::Int64(value.map(|v| v.into()))), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v as f32))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal(value.map(|v| { let mut decimal = Decimal::from(v); Self::decimal_round_i(option, &mut decimal); @@ -893,8 +964,8 @@ impl DataValue { LogicalType::Bigint => Ok(DataValue::Int64(value)), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v as f32))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v as f64))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal(value.map(|v| { let mut decimal = Decimal::from(v); Self::decimal_round_i(option, &mut decimal); @@ -915,8 +986,8 @@ impl DataValue { LogicalType::UBigint => Ok(DataValue::UInt64(value.map(|v| v.into()))), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v.into()))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal(value.map(|v| { let mut decimal = Decimal::from(v); Self::decimal_round_i(option, &mut decimal); @@ -935,8 +1006,8 @@ impl DataValue { LogicalType::UBigint => Ok(DataValue::UInt64(value.map(|v| v.into()))), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v.into()))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal(value.map(|v| { let mut decimal = Decimal::from(v); Self::decimal_round_i(option, &mut decimal); @@ -953,8 +1024,8 @@ impl DataValue { LogicalType::UBigint => Ok(DataValue::UInt64(value.map(|v| v.into()))), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v as f32))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal(value.map(|v| { let mut decimal = Decimal::from(v); Self::decimal_round_i(option, &mut decimal); @@ -969,8 +1040,8 @@ impl DataValue { LogicalType::UBigint => Ok(DataValue::UInt64(value)), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| v as f32))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v as f64))), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Decimal(_, option) => Ok(DataValue::Decimal(value.map(|v| { let mut decimal = Decimal::from(v); Self::decimal_round_i(option, &mut decimal); @@ -980,7 +1051,7 @@ impl DataValue { LogicalType::Boolean => numeric_to_boolean!(value), _ => Err(DatabaseError::CastFail), }, - DataValue::Utf8(value) => match to { + DataValue::Utf8 { value, .. } => match to { LogicalType::Invalid => Err(DatabaseError::CastFail), LogicalType::SqlNull => Ok(DataValue::Null), LogicalType::Boolean => Ok(DataValue::Boolean( @@ -1016,8 +1087,8 @@ impl DataValue { LogicalType::Double => Ok(DataValue::Float64( value.map(|v| f64::from_str(&v)).transpose()?, )), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), LogicalType::Date => { let option = value .map(|v| { @@ -1059,8 +1130,12 @@ impl DataValue { }, DataValue::Date32(value) => match to { LogicalType::SqlNull => Ok(DataValue::Null), - LogicalType::Char(len) => varchar_cast!(Self::format_date(value), Some(len)), - LogicalType::Varchar(len) => varchar_cast!(Self::format_date(value), len), + LogicalType::Char(len) => { + varchar_cast!(Self::format_date(value), Some(len), Utf8Type::Fixed(*len)) + } + LogicalType::Varchar(len) => { + varchar_cast!(Self::format_date(value), len, Utf8Type::Variable) + } LogicalType::Date => Ok(DataValue::Date32(value)), LogicalType::DateTime => { let option = value.and_then(|v| { @@ -1076,9 +1151,15 @@ impl DataValue { DataValue::Date64(value) => match to { LogicalType::SqlNull => Ok(DataValue::Null), LogicalType::Char(len) => { - varchar_cast!(Self::format_datetime(value), Some(len)) + varchar_cast!( + Self::format_datetime(value), + Some(len), + Utf8Type::Fixed(*len) + ) + } + LogicalType::Varchar(len) => { + varchar_cast!(Self::format_datetime(value), len, Utf8Type::Variable) } - LogicalType::Varchar(len) => varchar_cast!(Self::format_datetime(value), len), LogicalType::Date => { let option = value.and_then(|v| { NaiveDateTime::from_timestamp_opt(v, 0) @@ -1101,9 +1182,11 @@ impl DataValue { DataValue::Time(value) => match to { LogicalType::SqlNull => Ok(DataValue::Null), LogicalType::Char(len) => { - varchar_cast!(Self::format_time(value), Some(len)) + varchar_cast!(Self::format_time(value), Some(len), Utf8Type::Fixed(*len)) + } + LogicalType::Varchar(len) => { + varchar_cast!(Self::format_time(value), len, Utf8Type::Variable) } - LogicalType::Varchar(len) => varchar_cast!(Self::format_time(value), len), _ => Err(DatabaseError::CastFail), }, DataValue::Decimal(value) => match to { @@ -1111,8 +1194,8 @@ impl DataValue { LogicalType::Float => Ok(DataValue::Float32(value.and_then(|v| v.to_f32()))), LogicalType::Double => Ok(DataValue::Float64(value.and_then(|v| v.to_f64()))), LogicalType::Decimal(_, _) => Ok(DataValue::Decimal(value)), - LogicalType::Char(len) => varchar_cast!(value, Some(len)), - LogicalType::Varchar(len) => varchar_cast!(value, len), + LogicalType::Char(len) => varchar_cast!(value, Some(len), Utf8Type::Fixed(*len)), + LogicalType::Varchar(len) => varchar_cast!(value, len, Utf8Type::Variable), _ => Err(DatabaseError::CastFail), }, DataValue::Tuple(values) => match to { @@ -1132,7 +1215,15 @@ impl DataValue { return None; } - if let (DataValue::Utf8(Some(v1)), DataValue::Utf8(Some(v2))) = (self, target) { + if let ( + DataValue::Utf8 { + value: Some(v1), .. + }, + DataValue::Utf8 { + value: Some(v2), .. + }, + ) = (self, target) + { let min_len = cmp::min(v1.len(), v2.len()); let mut v1_iter = v1.get(0..min_len).unwrap().chars(); @@ -1210,7 +1301,24 @@ impl_scalar!(u8, UInt8); impl_scalar!(u16, UInt16); impl_scalar!(u32, UInt32); impl_scalar!(u64, UInt64); -impl_scalar!(String, Utf8); + +impl From for DataValue { + fn from(value: String) -> Self { + DataValue::Utf8 { + value: Some(value), + ty: Utf8Type::Variable, + } + } +} + +impl From> for DataValue { + fn from(value: Option) -> Self { + DataValue::Utf8 { + value, + ty: Utf8Type::Variable, + } + } +} impl From<&sqlparser::ast::Value> for DataValue { fn from(v: &sqlparser::ast::Value) -> Self { @@ -1278,7 +1386,7 @@ impl fmt::Display for DataValue { DataValue::UInt16(e) => format_option!(f, e)?, DataValue::UInt32(e) => format_option!(f, e)?, DataValue::UInt64(e) => format_option!(f, e)?, - DataValue::Utf8(e) => format_option!(f, e)?, + DataValue::Utf8 { value: e, .. } => format_option!(f, e)?, DataValue::Null => write!(f, "null")?, DataValue::Date32(e) => format_option!(f, e.and_then(DataValue::date_format))?, DataValue::Date64(e) => format_option!(f, e.and_then(DataValue::date_time_format))?, @@ -1317,8 +1425,8 @@ impl fmt::Debug for DataValue { DataValue::UInt16(_) => write!(f, "UInt16({})", self), DataValue::UInt32(_) => write!(f, "UInt32({})", self), DataValue::UInt64(_) => write!(f, "UInt64({})", self), - DataValue::Utf8(None) => write!(f, "Utf8({})", self), - DataValue::Utf8(Some(_)) => write!(f, "Utf8(\"{}\")", self), + DataValue::Utf8 { value: None, .. } => write!(f, "Utf8({})", self), + DataValue::Utf8 { value: Some(_), .. } => write!(f, "Utf8(\"{}\")", self), DataValue::Null => write!(f, "null"), DataValue::Date32(_) => write!(f, "Date32({})", self), DataValue::Date64(_) => write!(f, "Date64({})", self), diff --git a/tests/slt/describe.slt b/tests/slt/describe.slt index c5137ff3..d2cfb6a8 100644 --- a/tests/slt/describe.slt +++ b/tests/slt/describe.slt @@ -6,7 +6,7 @@ describe t9; ---- c1 INTEGER 4 false PRIMARY null c2 INTEGER 4 true EMPTY 0 -c3 VARCHAR DYNAMIC true UNIQUE null +c3 VARCHAR null true UNIQUE null statement ok drop table t9; \ No newline at end of file