From e66417d8fe52002df23b3631c8ce00f9f255b0ac Mon Sep 17 00:00:00 2001 From: Xwg Date: Sun, 17 Sep 2023 04:28:29 +0800 Subject: [PATCH] feat: Refactor 'DataValue' length check logic; add support for varchar computations This commit revises the mechanism for checking limit conditions on variable length types such as varchar, moving the check from 'src/binder/insert.rs' to 'src/types/value.rs' to centralize the control. It also implements expression computation for the Udf8 data type. Furthermore, the now-unnecessary `Nvarchar` type has been removed. This overhaul enhances robustness against incorrect inputs in variable length fields and improves the computational abilities for the Utf8 data type. --- src/binder/insert.rs | 8 +-- src/binder/update.rs | 1 + src/expression/value_compute.rs | 97 ++++++++++++++++++++++++++++- src/types/mod.rs | 28 ++------- src/types/tuple.rs | 4 +- src/types/value.rs | 104 +++++++++++++++++++++++--------- 6 files changed, 183 insertions(+), 59 deletions(-) diff --git a/src/binder/insert.rs b/src/binder/insert.rs index e2507824..0a5e0b94 100644 --- a/src/binder/insert.rs +++ b/src/binder/insert.rs @@ -49,14 +49,10 @@ impl Binder { for (i, expr) in expr_row.into_iter().enumerate() { match &self.bind_expr(expr).await? { ScalarExpression::Constant(value) => { + // Check if the value length is too long + value.check_length(columns[i].datatype())?; let cast_value = DataValue::clone(value) .cast(columns[i].datatype())?; - - // Check if the value length is too long - if Some(cast_value.to_raw().len()) > columns[i].datatype().raw_len() { - return Err(BindError::InvalidTable(format!("value length is too long"))) - } - row.push(Arc::new(cast_value)) }, ScalarExpression::Unary { expr, op, .. } => { diff --git a/src/binder/update.rs b/src/binder/update.rs index 4231ba3a..92de8986 100644 --- a/src/binder/update.rs +++ b/src/binder/update.rs @@ -44,6 +44,7 @@ impl Binder { bind_table_name.as_ref() ).await? { ScalarExpression::ColumnRef(catalog) => { + value.check_length(catalog.datatype())?; columns.push(catalog); row.push(value.clone()); }, diff --git a/src/expression/value_compute.rs b/src/expression/value_compute.rs index fbb36c42..3c0b2b1d 100644 --- a/src/expression/value_compute.rs +++ b/src/expression/value_compute.rs @@ -59,6 +59,13 @@ fn unpack_date(value: DataValue) -> Option { } } +fn unpack_utf8(value: DataValue) -> Option { + match value { + DataValue::Utf8(inner) => inner, + _ => None + } +} + pub fn unary_op( value: &DataValue, op: &UnaryOperator, @@ -114,7 +121,7 @@ pub fn binary_op( ) -> Result { let unified_type = LogicalType::max_logical_type( &left.logical_type(), - &right.logical_type() + &right.logical_type(), )?; let value = match &unified_type { @@ -844,6 +851,76 @@ pub fn binary_op( _ => todo!("unsupported operator") } } + LogicalType::Varchar(None) => { + let left_value = unpack_utf8(left.clone().cast(&unified_type)?); + let right_value = unpack_utf8(right.clone().cast(&unified_type)?); + + match op { + BinaryOperator::Gt => { + let value = if let (Some(v1), Some(v2)) = (left_value, right_value) { + Some(v1 > v2) + } else { + None + }; + + DataValue::Boolean(value) + } + BinaryOperator::Lt => { + let value = if let (Some(v1), Some(v2)) = (left_value, right_value) { + Some(v1 < v2) + } else { + None + }; + + DataValue::Boolean(value) + } + BinaryOperator::GtEq => { + let value = if let (Some(v1), Some(v2)) = (left_value, right_value) { + Some(v1 >= v2) + } else { + None + }; + + DataValue::Boolean(value) + } + BinaryOperator::LtEq => { + let value = if let (Some(v1), Some(v2)) = (left_value, right_value) { + Some(v1 <= v2) + } else { + None + }; + + DataValue::Boolean(value) + } + BinaryOperator::Eq => { + let value = match (left_value, right_value) { + (Some(v1), Some(v2)) => { + Some(v1 == v2) + } + (None, None) => { + Some(true) + } + (_, _) => { + None + } + }; + + DataValue::Boolean(value) + } + BinaryOperator::NotEq => { + let value = if let (Some(v1), Some(v2)) = (left_value, right_value) { + Some(v1 != v2) + } else { + None + }; + + DataValue::Boolean(value) + } + _ => todo!("unsupported operator") + } + } + // Utf8 + _ => todo!("unsupported data type"), }; @@ -1105,4 +1182,22 @@ mod test { Ok(()) } + + #[test] + fn test_binary_op_Utf8_compare()->Result<(),TypeError>{ + assert_eq!(binary_op(&DataValue::Utf8(Some("a".to_string())), &DataValue::Utf8(Some("b".to_string())), &BinaryOperator::Gt)?, DataValue::Boolean(Some(false))); + assert_eq!(binary_op(&DataValue::Utf8(Some("a".to_string())), &DataValue::Utf8(Some("b".to_string())), &BinaryOperator::Lt)?, DataValue::Boolean(Some(true))); + assert_eq!(binary_op(&DataValue::Utf8(Some("a".to_string())), &DataValue::Utf8(Some("a".to_string())), &BinaryOperator::GtEq)?, DataValue::Boolean(Some(true))); + assert_eq!(binary_op(&DataValue::Utf8(Some("a".to_string())), &DataValue::Utf8(Some("a".to_string())), &BinaryOperator::LtEq)?, DataValue::Boolean(Some(true))); + assert_eq!(binary_op(&DataValue::Utf8(Some("a".to_string())), &DataValue::Utf8(Some("a".to_string())), &BinaryOperator::NotEq)?, DataValue::Boolean(Some(false))); + assert_eq!(binary_op(&DataValue::Utf8(Some("a".to_string())), &DataValue::Utf8(Some("a".to_string())), &BinaryOperator::Eq)?, DataValue::Boolean(Some(true))); + + assert_eq!(binary_op(&DataValue::Utf8(None), &DataValue::Utf8(Some("a".to_string())), &BinaryOperator::Gt)?, DataValue::Boolean(None)); + assert_eq!(binary_op(&DataValue::Utf8(None), &DataValue::Utf8(Some("a".to_string())), &BinaryOperator::Lt)?, DataValue::Boolean(None)); + assert_eq!(binary_op(&DataValue::Utf8(None), &DataValue::Utf8(Some("a".to_string())), &BinaryOperator::GtEq)?, DataValue::Boolean(None)); + assert_eq!(binary_op(&DataValue::Utf8(None), &DataValue::Utf8(Some("a".to_string())), &BinaryOperator::LtEq)?, DataValue::Boolean(None)); + assert_eq!(binary_op(&DataValue::Utf8(None), &DataValue::Utf8(Some("a".to_string())), &BinaryOperator::NotEq)?, DataValue::Boolean(None)); + + Ok(()) + } } diff --git a/src/types/mod.rs b/src/types/mod.rs index a99bdc9c..cb4f2fce 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -55,7 +55,6 @@ pub enum LogicalType { Float, Double, Varchar(Option), - Nvarchar(Option), Date, DateTime, } @@ -76,8 +75,8 @@ impl LogicalType { LogicalType::UBigint => Some(8), LogicalType::Float => Some(4), LogicalType::Double => Some(8), - LogicalType::Varchar(len)=> len.map(|len| len as usize), - LogicalType::Nvarchar(len) => len.map(|len| len as usize), + /// Note: The non-fixed length type's raw_len is None + LogicalType::Varchar(_)=>None, LogicalType::Date => Some(4), LogicalType::DateTime => Some(8), } @@ -158,13 +157,13 @@ impl LogicalType { if left.is_numeric() && right.is_numeric() { return LogicalType::combine_numeric_types(left, right); } - if matches!((left, right), (LogicalType::Date, LogicalType::Varchar(len)) | (LogicalType::Varchar(len), LogicalType::Date)) { + if matches!((left, right), (LogicalType::Date, LogicalType::Varchar(_)) | (LogicalType::Varchar(_), LogicalType::Date)) { return Ok(LogicalType::Date); } if matches!((left, right), (LogicalType::Date, LogicalType::DateTime) | (LogicalType::DateTime, LogicalType::Date)) { return Ok(LogicalType::DateTime); } - if matches!((left, right), (LogicalType::DateTime, LogicalType::Varchar(len)) | (LogicalType::Varchar(len), LogicalType::DateTime)) { + if matches!((left, right), (LogicalType::DateTime, LogicalType::Varchar(_)) | (LogicalType::Varchar(_), LogicalType::DateTime)) { return Ok(LogicalType::DateTime); } Err(TypeError::InternalError(format!( @@ -267,8 +266,7 @@ impl LogicalType { LogicalType::UBigint => matches!(to, LogicalType::Float | LogicalType::Double), LogicalType::Float => matches!(to, LogicalType::Double), LogicalType::Double => false, - LogicalType::Varchar(_len) => false, - LogicalType::Nvarchar(_len) => false, + LogicalType::Varchar(_) => false, LogicalType::Date => matches!(to, LogicalType::DateTime | LogicalType::Varchar(_)), LogicalType::DateTime => matches!(to, LogicalType::Date | LogicalType::Varchar(_)), } @@ -283,7 +281,6 @@ impl TryFrom for LogicalType { match value { sqlparser::ast::DataType::Char(len) | sqlparser::ast::DataType::Varchar(len)=> Ok(LogicalType::Varchar(len.map(|len| len.length as u32))), - sqlparser::ast::DataType::Nvarchar(len) => Ok(LogicalType::Nvarchar(len.map(|len| len as u32))), sqlparser::ast::DataType::Float(_) => Ok(LogicalType::Float), sqlparser::ast::DataType::Double => Ok(LogicalType::Double), sqlparser::ast::DataType::TinyInt(_) => Ok(LogicalType::Tinyint), @@ -339,19 +336,4 @@ mod test { fn test_id_generator_reset() { ID_BUF.store(0, Release) } - - - #[test] - fn test_logical_type() { - assert_eq!(LogicalType::Integer.raw_len(), Some(4)); - assert_eq!(LogicalType::UInteger.raw_len(), Some(4)); - assert_eq!(LogicalType::Bigint.raw_len(), Some(8)); - assert_eq!(LogicalType::UBigint.raw_len(), Some(8)); - assert_eq!(LogicalType::Float.raw_len(), Some(4)); - assert_eq!(LogicalType::Double.raw_len(), Some(8)); - assert_eq!(LogicalType::Varchar(Some(10)).raw_len(), Some(10)); - assert_eq!(LogicalType::Nvarchar(Some(10)).raw_len(), Some(10)); - assert_eq!(LogicalType::Date.raw_len(), Some(4)); - assert_eq!(LogicalType::DateTime.raw_len(), Some(8)); - } } diff --git a/src/types/tuple.rs b/src/types/tuple.rs index fb9dfce7..bf569c38 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -35,9 +35,11 @@ impl Tuple { if bit_index(bytes[i / BITS_MAX_INDEX], i % BITS_MAX_INDEX) { values.push(Arc::new(DataValue::none(logic_type))); } else if let Some(len) = logic_type.raw_len() { + /// fixed length (e.g.: int) values.push(Arc::new(DataValue::from_raw(&bytes[pos..pos + len], logic_type))); pos += len; } else { + /// variable length (e.g.: varchar) let len = u32::decode_fixed(&bytes[pos..pos + 4]) as usize; pos += 4; values.push(Arc::new(DataValue::from_raw(&bytes[pos..pos + len], logic_type))); @@ -133,7 +135,7 @@ mod tests { Arc::new(ColumnCatalog::new( "c3".to_string(), false, - ColumnDesc::new(LogicalType::Varchar(None), false) + ColumnDesc::new(LogicalType::Varchar(Some(2)), false) )), Arc::new(ColumnCatalog::new( "c4".to_string(), diff --git a/src/types/value.rs b/src/types/value.rs index 912b7b9e..a3063c17 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -178,8 +178,66 @@ impl Hash for DataValue { } } } +macro_rules! varchar_cast { + ($value:expr, $len:expr) => { + $value.map(|v| { + let string_value = format!("{}", v); + if let Some(len) = $len { + let len_usize = *len as usize; + if string_value.len() > len_usize { + Err(TypeError::CastFail) + } else { + Ok(DataValue::Utf8(Some(string_value))) + } + } else { + Ok(DataValue::Utf8(Some(string_value))) + } + }).unwrap_or(Ok(DataValue::Utf8(None))) + }; +} impl DataValue { + pub(crate) fn check_length(&self, logic_type: &LogicalType) -> Result<(), TypeError> { + match self { + DataValue::Boolean(_) => return Ok(()), + DataValue::Float32(_) => return Ok(()), + DataValue::Float64(_) => return Ok(()), + DataValue::Int8(_) => return Ok(()), + DataValue::Int16(_) => return Ok(()), + DataValue::Int32(_) => return Ok(()), + DataValue::Int64(_) => return Ok(()), + DataValue::UInt8(_) => return Ok(()), + DataValue::UInt16(_) => return Ok(()), + DataValue::UInt32(_) => return Ok(()), + DataValue::UInt64(_) => return Ok(()), + DataValue::Date32(_) => return Ok(()), + DataValue::Date64(_) => return Ok(()), + DataValue::Utf8(value) => { + if let LogicalType::Varchar(len) = logic_type { + if let Some(len) = len { + if value.as_ref().map(|v| v.len() > *len as usize).unwrap_or(false) { + return Err(TypeError::CastFail); + } + } + } + } + _ => { return Err(TypeError::CastFail); } + } + Ok(()) + } + + fn format_date(value: Option) -> Option { + value.and_then(|v| { + Self::date_format(v).map(|fmt| format!("{}", fmt)) + }) + } + + fn format_datetime(value: Option) -> Option { + value.and_then(|v| { + Self::date_time_format(v).map(|fmt| format!("{}", fmt)) + }) + } + pub fn is_variable(&self) -> bool { match self { DataValue::Utf8(_) => true, @@ -223,7 +281,6 @@ impl DataValue { LogicalType::Float => DataValue::Float32(None), LogicalType::Double => DataValue::Float64(None), LogicalType::Varchar(_) => DataValue::Utf8(None), - LogicalType::Nvarchar(_) => DataValue::Utf8(None), LogicalType::Date => DataValue::Date32(None), LogicalType::DateTime => DataValue::Date64(None) } @@ -245,7 +302,6 @@ impl DataValue { LogicalType::Float => DataValue::Float32(Some(0.0)), LogicalType::Double => DataValue::Float64(Some(0.0)), LogicalType::Varchar(_) => DataValue::Utf8(Some("".to_string())), - LogicalType::Nvarchar(_) => DataValue::Utf8(Some("".to_string())), LogicalType::Date => DataValue::Date32(Some(UNIX_DATETIME.num_days_from_ce())), LogicalType::DateTime => DataValue::Date64(Some(UNIX_DATETIME.timestamp())) } @@ -295,7 +351,6 @@ impl DataValue { f64::from_ne_bytes(buf) })), LogicalType::Varchar(_) => DataValue::Utf8((!bytes.is_empty()).then(|| String::from_utf8(bytes.to_owned()).unwrap())), - LogicalType::Nvarchar(_) => 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))), LogicalType::DateTime => DataValue::Date64((!bytes.is_empty()).then(|| i64::decode_fixed(bytes))), } @@ -315,7 +370,7 @@ impl DataValue { DataValue::UInt16(_) => LogicalType::USmallint, DataValue::UInt32(_) => LogicalType::UInteger, DataValue::UInt64(_) => LogicalType::UBigint, - DataValue::Utf8(_) => LogicalType::Varchar(Some(255)), + DataValue::Utf8(_) => LogicalType::Varchar(None), DataValue::Date32(_) => LogicalType::Date, DataValue::Date64(_) => LogicalType::DateTime, } @@ -335,7 +390,7 @@ impl DataValue { _ => return Err(TypeError::InvalidType), }.ok_or(TypeError::NotNull) } - + pub fn cast(self, to: &LogicalType) -> Result { match self { DataValue::Null => { @@ -354,7 +409,6 @@ impl DataValue { LogicalType::Float => Ok(DataValue::Float32(None)), LogicalType::Double => Ok(DataValue::Float64(None)), LogicalType::Varchar(_) => Ok(DataValue::Utf8(None)), - LogicalType::Nvarchar(_) => Ok(DataValue::Utf8(None)), LogicalType::Date => Ok(DataValue::Date32(None)), LogicalType::DateTime => Ok(DataValue::Date64(None)), } @@ -373,8 +427,7 @@ 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::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), - LogicalType::Nvarchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -383,7 +436,7 @@ impl DataValue { LogicalType::SqlNull => Ok(DataValue::Null), LogicalType::Float => Ok(DataValue::Float32(value)), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -391,7 +444,7 @@ impl DataValue { match to { LogicalType::SqlNull => Ok(DataValue::Null), LogicalType::Double => Ok(DataValue::Float64(value)), - LogicalType::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -408,7 +461,7 @@ 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::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -424,7 +477,7 @@ 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::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -438,7 +491,7 @@ impl DataValue { LogicalType::Integer => Ok(DataValue::Int32(value.map(|v| v.into()))), LogicalType::Bigint => Ok(DataValue::Int64(value.map(|v| v.into()))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -450,7 +503,7 @@ impl DataValue { LogicalType::UInteger => Ok(DataValue::UInt32(value.map(|v| u32::try_from(v)).transpose()?)), LogicalType::UBigint => Ok(DataValue::UInt64(value.map(|v| u64::try_from(v)).transpose()?)), LogicalType::Bigint => Ok(DataValue::Int64(value.map(|v| v.into()))), - LogicalType::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -466,7 +519,7 @@ 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::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -480,7 +533,7 @@ 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::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -491,7 +544,7 @@ impl DataValue { LogicalType::Bigint => Ok(DataValue::Int64(value.map(|v| v.into()))), LogicalType::UBigint => Ok(DataValue::UInt64(value.map(|v| v.into()))), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| v.into()))), - LogicalType::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -499,7 +552,7 @@ impl DataValue { match to { LogicalType::SqlNull => Ok(DataValue::Null), LogicalType::UBigint => Ok(DataValue::UInt64(value.map(|v| v.into()))), - LogicalType::Varchar(_) => Ok(DataValue::Utf8(value.map(|v| format!("{}", v)))), + LogicalType::Varchar(len) => varchar_cast!(value, len), _ => Err(TypeError::CastFail), } } @@ -518,8 +571,7 @@ impl DataValue { LogicalType::UBigint => Ok(DataValue::UInt64(value.map(|v| u64::from_str(&v)).transpose()?)), LogicalType::Float => Ok(DataValue::Float32(value.map(|v| f32::from_str(&v)).transpose()?)), LogicalType::Double => Ok(DataValue::Float64(value.map(|v| f64::from_str(&v)).transpose()?)), - LogicalType::Varchar(_) => Ok(DataValue::Utf8(value)), - LogicalType::Nvarchar(_) => Ok(DataValue::Utf8(value)), + LogicalType::Varchar(len) => varchar_cast!(value, len), LogicalType::Date => { let option = value.map(|v| { NaiveDate::parse_from_str(&v, DATE_FMT) @@ -545,9 +597,7 @@ impl DataValue { DataValue::Date32(value) => { match to { LogicalType::SqlNull => Ok(DataValue::Null), - LogicalType::Varchar(_) => Ok(DataValue::Utf8(value.and_then(|v| { - Self::date_format(v).map(|fmt| format!("{}", fmt)) - }))), + LogicalType::Varchar(len) => varchar_cast!(Self::format_date(value), len), LogicalType::Date => Ok(DataValue::Date32(value)), LogicalType::DateTime => { let option = value.and_then(|v| { @@ -557,16 +607,14 @@ impl DataValue { }); Ok(DataValue::Date64(option)) - }, + } _ => Err(TypeError::CastFail) } } DataValue::Date64(value) => { match to { LogicalType::SqlNull => Ok(DataValue::Null), - LogicalType::Varchar(_) => Ok(DataValue::Utf8(value.and_then(|v| { - Self::date_time_format(v).map(|fmt| format!("{}", fmt)) - }))), + LogicalType::Varchar(len) => varchar_cast!(Self::format_datetime(value), len), LogicalType::Date => { let option = value.and_then(|v| { NaiveDateTime::from_timestamp_opt(v, 0) @@ -678,7 +726,7 @@ impl fmt::Display for DataValue { } DataValue::Date64(e) => { format_option!(f, e.and_then(|s| DataValue::date_time_format(s)))? - }, + } }; Ok(()) }