From faf27fe56738f5fb3f499f1c6bd7945f572a9a91 Mon Sep 17 00:00:00 2001 From: Kould <2435992353@qq.com> Date: Tue, 26 Mar 2024 00:07:08 +0800 Subject: [PATCH] Feat: add Function: `current_date` & add Type: `LogicalType::Time` (#181) * feat: support 'current_date' * feat: support `DataType::Time` & perf `Tuple::serialize_to` * fix: into_pg_type add `LogicalType::Time` * fix: encode_tuples add `LogicalType::Time` * doc: add `Time` for DataTypes --- README.md | 1 + src/bin/server.rs | 2 + src/db.rs | 5 +- src/expression/value_compute.rs | 9 ++ src/lib.rs | 1 + src/marcos/mod.rs | 2 + src/optimizer/core/histogram.rs | 5 +- src/storage/table_codec.rs | 8 +- src/types/mod.rs | 32 +++++- src/types/tuple.rs | 16 +-- src/types/value.rs | 190 +++++++++++++++++++++++++++----- src/udf/current_date.rs | 18 +++ src/udf/mod.rs | 1 + tests/slt/basic_test.slt | 15 +++ tests/slt/sql_2016/E141_07.slt | 7 +- tests/slt/sql_2016/F051_06.slt | 9 +- 16 files changed, 272 insertions(+), 49 deletions(-) create mode 100644 src/udf/current_date.rs create mode 100644 src/udf/mod.rs diff --git a/README.md b/README.md index a8ef7159..1a03ef85 100755 --- a/README.md +++ b/README.md @@ -182,6 +182,7 @@ let fnck_sql = DataBaseBuilder::path("./data") - Varchar - Date - DateTime + - Time - Tuple ## Roadmap diff --git a/src/bin/server.rs b/src/bin/server.rs index c2e222e9..70747931 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -203,6 +203,7 @@ fn encode_tuples<'a>(schema: &Schema, tuples: Vec) -> PgWireResult encoder.encode_field(&value.date()), LogicalType::DateTime => encoder.encode_field(&value.datetime()), + LogicalType::Time => encoder.encode_field(&value.time()), LogicalType::Decimal(_, _) => todo!(), _ => unreachable!(), }?; @@ -227,6 +228,7 @@ fn into_pg_type(data_type: &LogicalType) -> PgWireResult { LogicalType::Varchar(_) => Type::VARCHAR, LogicalType::Date | LogicalType::DateTime => Type::DATE, LogicalType::Char(_) => Type::CHAR, + LogicalType::Time => Type::TIME, LogicalType::Decimal(_, _) => todo!(), _ => { return Err(PgWireError::UserError(Box::new(ErrorInfo::new( diff --git a/src/db.rs b/src/db.rs index b76e54fb..3d243a2c 100644 --- a/src/db.rs +++ b/src/db.rs @@ -18,6 +18,7 @@ use crate::planner::LogicalPlan; use crate::storage::kip::KipStorage; use crate::storage::{Storage, Transaction}; use crate::types::tuple::{SchemaRef, Tuple}; +use crate::udf::current_date::CurrentDate; pub(crate) type Functions = HashMap>; @@ -47,7 +48,9 @@ impl DataBaseBuilder { self } - pub async fn build(self) -> Result, DatabaseError> { + pub async fn build(mut self) -> Result, DatabaseError> { + self = self.register_function(CurrentDate::new()); + let storage = KipStorage::new(self.path).await?; Ok(Database { diff --git a/src/expression/value_compute.rs b/src/expression/value_compute.rs index 32c07c84..1b8066f1 100644 --- a/src/expression/value_compute.rs +++ b/src/expression/value_compute.rs @@ -335,6 +335,15 @@ impl DataValue { &unified_type ) } + LogicalType::Time => { + numeric_binary_compute!( + DataValue::Time, + self.clone(), + right.clone(), + op, + &unified_type + ) + } LogicalType::Decimal(_, _) => { let left_value = self.clone().cast(&unified_type)?; let right_value = right.clone().cast(&unified_type)?; diff --git a/src/lib.rs b/src/lib.rs index 0d125f5c..13f943c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,3 +19,4 @@ pub mod parser; pub mod planner; pub mod storage; pub mod types; +mod udf; diff --git a/src/marcos/mod.rs b/src/marcos/mod.rs index 98379465..5dfa4b3c 100644 --- a/src/marcos/mod.rs +++ b/src/marcos/mod.rs @@ -74,6 +74,7 @@ macro_rules! function { } impl $struct_name { + #[allow(unused_mut)] pub(crate) fn new() -> Arc { let function_name = stringify!($function_name).to_lowercase(); @@ -93,6 +94,7 @@ macro_rules! function { #[typetag::serde] impl ScalarFunctionImpl for $struct_name { + #[allow(unused_variables, clippy::redundant_closure_call)] fn eval(&self, args: &[ScalarExpression], tuple: &Tuple, schema: &[ColumnRef]) -> Result { let mut _index = 0; diff --git a/src/optimizer/core/histogram.rs b/src/optimizer/core/histogram.rs index b74b36d7..0af568df 100644 --- a/src/optimizer/core/histogram.rs +++ b/src/optimizer/core/histogram.rs @@ -279,13 +279,16 @@ impl Histogram { }), _ => unreachable!(), }, - LogicalType::Date | LogicalType::DateTime => match value { + LogicalType::Date | LogicalType::DateTime | LogicalType::Time => match value { DataValue::Date32(value) => DataValue::Int32(*value) .cast(&LogicalType::Double)? .double(), DataValue::Date64(value) => DataValue::Int64(*value) .cast(&LogicalType::Double)? .double(), + DataValue::Time(value) => DataValue::UInt32(*value) + .cast(&LogicalType::Double)? + .double(), _ => unreachable!(), }, diff --git a/src/storage/table_codec.rs b/src/storage/table_codec.rs index 1cc182ad..b441c7aa 100644 --- a/src/storage/table_codec.rs +++ b/src/storage/table_codec.rs @@ -152,7 +152,7 @@ impl TableCodec { let tuple_id = tuple.id.clone().ok_or(DatabaseError::PrimaryKeyNotFound)?; let key = Self::encode_tuple_key(table_name, &tuple_id)?; - Ok((Bytes::from(key), Bytes::from(tuple.serialize_to(types)))) + Ok((Bytes::from(key), Bytes::from(tuple.serialize_to(types)?))) } pub fn encode_tuple_key( @@ -226,8 +226,10 @@ impl TableCodec { tuple_id: &TupleId, ) -> 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)?; - Ok((Bytes::from(key), Bytes::from(tuple_id.to_raw(None)))) + Ok((Bytes::from(key), Bytes::from(bytes))) } fn _encode_index_key(name: &str, index: &Index) -> Result, DatabaseError> { @@ -267,7 +269,7 @@ impl TableCodec { if let Some(tuple_id) = tuple_id { if matches!(index.ty, IndexType::Normal | IndexType::Composite) { - key_prefix.append(&mut tuple_id.to_raw(None)); + tuple_id.to_raw(&mut key_prefix, None)?; } } Ok(key_prefix) diff --git a/src/types/mod.rs b/src/types/mod.rs index 361a447c..173e12d6 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -10,7 +10,7 @@ use std::any::TypeId; use std::cmp; use crate::errors::DatabaseError; -use sqlparser::ast::{CharLengthUnits, CharacterLength, ExactNumberInfo}; +use sqlparser::ast::{CharLengthUnits, CharacterLength, ExactNumberInfo, TimezoneInfo}; use strum_macros::AsRefStr; pub type ColumnId = u32; @@ -38,6 +38,7 @@ pub enum LogicalType { Varchar(Option), Date, DateTime, + Time, // decimal (precision, scale) Decimal(Option, Option), Tuple, @@ -100,6 +101,7 @@ impl LogicalType { LogicalType::Decimal(_, _) => Some(16), LogicalType::Date => Some(4), LogicalType::DateTime => Some(8), + LogicalType::Time => Some(4), LogicalType::Invalid | LogicalType::Tuple => unreachable!(), } } @@ -297,8 +299,12 @@ impl LogicalType { ), LogicalType::DateTime => matches!( to, - LogicalType::Date | LogicalType::Varchar(_) | LogicalType::Char(_) + LogicalType::Date + | LogicalType::Time + | LogicalType::Varchar(_) + | LogicalType::Char(_) ), + LogicalType::Time => matches!(to, LogicalType::Varchar(_) | LogicalType::Char(_)), LogicalType::Decimal(_, _) | LogicalType::Tuple => false, } } @@ -356,7 +362,27 @@ impl TryFrom for LogicalType { sqlparser::ast::DataType::UnsignedBigInt(_) => Ok(LogicalType::UBigint), sqlparser::ast::DataType::Boolean => Ok(LogicalType::Boolean), sqlparser::ast::DataType::Date => Ok(LogicalType::Date), - sqlparser::ast::DataType::Datetime(_) => Ok(LogicalType::DateTime), + sqlparser::ast::DataType::Datetime(precision) => { + if precision.is_some() { + return Err(DatabaseError::UnsupportedStmt( + "time's precision".to_string(), + )); + } + Ok(LogicalType::DateTime) + } + sqlparser::ast::DataType::Time(precision, info) => { + if precision.is_some() { + return Err(DatabaseError::UnsupportedStmt( + "time's precision".to_string(), + )); + } + if !matches!(info, TimezoneInfo::None) { + return Err(DatabaseError::UnsupportedStmt( + "time's time zone".to_string(), + )); + } + Ok(LogicalType::Time) + } sqlparser::ast::DataType::Decimal(info) | sqlparser::ast::DataType::Dec(info) => { match info { ExactNumberInfo::None => Ok(Self::Decimal(None, None)), diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 0cced1ae..ef11bcdb 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -1,4 +1,5 @@ use crate::catalog::ColumnRef; +use crate::errors::DatabaseError; use crate::types::value::{DataValue, ValueRef}; use crate::types::LogicalType; use comfy_table::{Cell, Table}; @@ -108,7 +109,7 @@ impl Tuple { /// e.g.: bits(u8)..|data_0(len for utf8_1)|utf8_0|data_1| /// Tips: all len is u32 - pub fn serialize_to(&self, types: &[LogicalType]) -> Vec { + pub fn serialize_to(&self, types: &[LogicalType]) -> Result, DatabaseError> { assert_eq!(self.values.len(), types.len()); fn flip_bit(bits: u8, i: usize) -> u8 { @@ -124,16 +125,17 @@ 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 mut value_bytes = value.to_raw(Some(logical_type)); + let value_len = value.to_raw(&mut bytes, Some(logical_type))?; if logical_type.raw_len().is_none() { - bytes.append(&mut (value_bytes.len() as u32).encode_fixed_vec()); + let index = bytes.len() - value_len; + + bytes.splice(index..index, (value_len as u32).encode_fixed_vec()); } - bytes.append(&mut value_bytes); } } - bytes + Ok(bytes) } } @@ -300,13 +302,13 @@ mod tests { &types, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], &columns, - &tuples[0].serialize_to(&types), + &tuples[0].serialize_to(&types).unwrap(), ); let tuple_1 = Tuple::deserialize_from( &types, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], &columns, - &tuples[1].serialize_to(&types), + &tuples[1].serialize_to(&types).unwrap(), ); assert_eq!(tuples[0], tuple_0); diff --git a/src/types/value.rs b/src/types/value.rs index dc2e704f..ff0b1a07 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -1,6 +1,6 @@ use chrono::format::{DelayedFormat, StrftimeItems}; -use chrono::{Datelike, NaiveDate, NaiveDateTime}; -use integer_encoding::FixedInt; +use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike}; +use integer_encoding::{FixedInt, FixedIntWriter}; use lazy_static::lazy_static; use rust_decimal::Decimal; use std::cmp::Ordering; @@ -20,10 +20,12 @@ use super::LogicalType; lazy_static! { pub static ref NULL_VALUE: ValueRef = Arc::new(DataValue::Null); static ref UNIX_DATETIME: NaiveDateTime = NaiveDateTime::from_timestamp_opt(0, 0).unwrap(); + static ref UNIX_TIME: NaiveTime = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); } pub const DATE_FMT: &str = "%Y-%m-%d"; pub const DATE_TIME_FMT: &str = "%Y-%m-%d %H:%M:%S"; +pub const TIME_FMT: &str = "%H:%M:%S"; const ENCODE_GROUP_SIZE: usize = 8; const ENCODE_MARKER: u8 = 0xFF; @@ -49,6 +51,7 @@ pub enum DataValue { Date32(Option), /// Date stored as a signed 64bit int timestamp since UNIX epoch 1970-01-01 Date64(Option), + Time(Option), Decimal(Option), Tuple(Option>), } @@ -132,6 +135,8 @@ impl PartialEq for DataValue { (Date32(_), _) => false, (Date64(v1), Date64(v2)) => v1.eq(v2), (Date64(_), _) => false, + (Time(v1), Time(v2)) => v1.eq(v2), + (Time(_), _) => false, (Decimal(v1), Decimal(v2)) => v1.eq(v2), (Decimal(_), _) => false, (Tuple(values_1), Tuple(values_2)) => values_1.eq(values_2), @@ -182,6 +187,8 @@ impl PartialOrd for DataValue { (Date32(_), _) => None, (Date64(v1), Date64(v2)) => v1.partial_cmp(v2), (Date64(_), _) => None, + (Time(v1), Time(v2)) => v1.partial_cmp(v2), + (Time(_), _) => None, (Decimal(v1), Decimal(v2)) => v1.partial_cmp(v2), (Decimal(_), _) => None, (Tuple(_), _) => None, @@ -222,6 +229,7 @@ impl Hash for DataValue { Null => 1.hash(state), Date32(v) => v.hash(state), Date64(v) => v.hash(state), + Time(v) => v.hash(state), Decimal(v) => v.hash(state), Tuple(values) => { for v in values { @@ -274,6 +282,14 @@ impl DataValue { } } + pub fn time(&self) -> Option { + if let DataValue::Time(Some(val)) = self { + NaiveTime::from_num_seconds_from_midnight_opt(*val, 0) + } else { + None + } + } + 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))) => { @@ -310,6 +326,10 @@ impl DataValue { value.and_then(|v| Self::date_time_format(v).map(|fmt| format!("{}", fmt))) } + fn format_time(value: Option) -> Option { + value.and_then(|v| Self::time_format(v).map(|fmt| format!("{}", fmt))) + } + pub fn is_null(&self) -> bool { match self { DataValue::Null => true, @@ -327,6 +347,7 @@ impl DataValue { DataValue::Utf8(value) => value.is_none(), DataValue::Date32(value) => value.is_none(), DataValue::Date64(value) => value.is_none(), + DataValue::Time(value) => value.is_none(), DataValue::Decimal(value) => value.is_none(), DataValue::Tuple(value) => value.is_none(), } @@ -350,6 +371,7 @@ impl DataValue { LogicalType::Char(_) | LogicalType::Varchar(_) => DataValue::Utf8(None), LogicalType::Date => DataValue::Date32(None), LogicalType::DateTime => DataValue::Date64(None), + LogicalType::Time => DataValue::Time(None), LogicalType::Decimal(_, _) => DataValue::Decimal(None), LogicalType::Tuple => DataValue::Tuple(None), } @@ -373,37 +395,118 @@ impl DataValue { LogicalType::Char(_) | LogicalType::Varchar(_) => DataValue::Utf8(Some("".to_string())), 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())), LogicalType::Decimal(_, _) => DataValue::Decimal(Some(Decimal::new(0, 0))), LogicalType::Tuple => DataValue::Tuple(Some(vec![])), } } - pub fn to_raw(&self, logical_type: Option) -> Vec { + pub fn to_raw( + &self, + bytes: &mut Vec, + logical_type: Option, + ) -> Result { match self { - DataValue::Null => None, - DataValue::Boolean(v) => v.map(|v| vec![v as u8]), - DataValue::Float32(v) => v.map(|v| v.to_ne_bytes().to_vec()), - DataValue::Float64(v) => v.map(|v| v.to_ne_bytes().to_vec()), - DataValue::Int8(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::Int16(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::Int32(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::Int64(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::UInt8(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::UInt16(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::UInt32(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::UInt64(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::Utf8(v) => v.clone().map(|mut v| { - if let Some(LogicalType::Char(len)) = logical_type { - v = format!("{:len$}", v, len = len as usize); + DataValue::Null => (), + DataValue::Boolean(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v as u8)?); + } + } + DataValue::Float32(v) => { + if let Some(v) = v { + bytes.extend_from_slice(&v.to_ne_bytes()); + return Ok(4); + } + } + DataValue::Float64(v) => { + if let Some(v) = v { + bytes.extend_from_slice(&v.to_ne_bytes()); + return Ok(8); + } + } + DataValue::Int8(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::Int16(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); } - v.into_bytes() - }), - DataValue::Date32(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::Date64(v) => v.map(|v| v.encode_fixed_vec()), - DataValue::Decimal(v) => v.map(|v| v.serialize().to_vec()), + } + DataValue::Int32(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::Int64(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::UInt8(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::UInt16(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::UInt32(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::UInt64(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::Utf8(v) => { + 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(); + + 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); + } + } + } + DataValue::Date32(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::Date64(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::Time(v) => { + if let Some(v) = v { + return Ok(bytes.write_fixedint(*v)?); + } + } + DataValue::Decimal(v) => { + if let Some(v) = v { + bytes.extend_from_slice(&v.serialize()); + return Ok(16); + } + } DataValue::Tuple(_) => unreachable!(), } - .unwrap_or(vec![]) + Ok(0) } pub fn from_raw(bytes: &[u8], ty: &LogicalType) -> Self { @@ -465,6 +568,9 @@ impl DataValue { LogicalType::DateTime => { DataValue::Date64((!bytes.is_empty()).then(|| i64::decode_fixed(bytes))) } + LogicalType::Time => { + DataValue::Time((!bytes.is_empty()).then(|| u32::decode_fixed(bytes))) + } LogicalType::Decimal(_, _) => DataValue::Decimal( (!bytes.is_empty()) .then(|| Decimal::deserialize(<[u8; 16]>::try_from(bytes).unwrap())), @@ -490,6 +596,7 @@ impl DataValue { DataValue::Utf8(_) => LogicalType::Varchar(None), DataValue::Date32(_) => LogicalType::Date, DataValue::Date64(_) => LogicalType::DateTime, + DataValue::Time(_) => LogicalType::Time, DataValue::Decimal(_) => LogicalType::Decimal(None, None), DataValue::Tuple(_) => LogicalType::Tuple, } @@ -555,7 +662,7 @@ impl DataValue { } DataValue::UInt8(Some(v)) => encode_u!(b, v), DataValue::UInt16(Some(v)) => encode_u!(b, v), - DataValue::UInt32(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::Boolean(Some(v)) => b.push(if *v { b'1' } else { b'0' }), @@ -628,6 +735,7 @@ impl DataValue { LogicalType::Char(_) | LogicalType::Varchar(_) => Ok(DataValue::Utf8(None)), LogicalType::Date => Ok(DataValue::Date32(None)), LogicalType::DateTime => Ok(DataValue::Date64(None)), + LogicalType::Time => Ok(DataValue::Time(None)), LogicalType::Decimal(_, _) => Ok(DataValue::Decimal(None)), LogicalType::Tuple => Ok(DataValue::Tuple(None)), }, @@ -934,6 +1042,16 @@ impl DataValue { Ok(DataValue::Date64(option)) } + LogicalType::Time => { + let option = value + .map(|v| { + NaiveTime::parse_from_str(&v, TIME_FMT) + .map(|time| time.num_seconds_from_midnight()) + }) + .transpose()?; + + Ok(DataValue::Time(option)) + } LogicalType::Decimal(_, _) => Ok(DataValue::Decimal( value.map(|v| Decimal::from_str(&v)).transpose()?, )), @@ -970,6 +1088,22 @@ impl DataValue { Ok(DataValue::Date32(option)) } LogicalType::DateTime => Ok(DataValue::Date64(value)), + LogicalType::Time => { + let option = value.and_then(|v| { + NaiveDateTime::from_timestamp_opt(v, 0) + .map(|date_time| date_time.time().num_seconds_from_midnight()) + }); + + Ok(DataValue::Time(option)) + } + _ => Err(DatabaseError::CastFail), + }, + DataValue::Time(value) => match to { + LogicalType::SqlNull => Ok(DataValue::Null), + LogicalType::Char(len) => { + varchar_cast!(Self::format_time(value), Some(len)) + } + LogicalType::Varchar(len) => varchar_cast!(Self::format_time(value), len), _ => Err(DatabaseError::CastFail), }, DataValue::Decimal(value) => match to { @@ -1040,6 +1174,10 @@ impl DataValue { NaiveDateTime::from_timestamp_opt(v, 0).map(|date_time| date_time.format(DATE_TIME_FMT)) } + fn time_format<'a>(v: u32) -> Option>> { + NaiveTime::from_num_seconds_from_midnight_opt(v, 0).map(|time| time.format(TIME_FMT)) + } + fn decimal_format(v: &Decimal) -> String { v.to_string() } @@ -1144,6 +1282,7 @@ impl fmt::Display for DataValue { 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))?, + DataValue::Time(e) => format_option!(f, e.and_then(DataValue::time_format))?, DataValue::Decimal(e) => format_option!(f, e.as_ref().map(DataValue::decimal_format))?, DataValue::Tuple(e) => { write!(f, "(")?; @@ -1183,6 +1322,7 @@ impl fmt::Debug for DataValue { DataValue::Null => write!(f, "null"), DataValue::Date32(_) => write!(f, "Date32({})", self), DataValue::Date64(_) => write!(f, "Date64({})", self), + DataValue::Time(_) => write!(f, "Time({})", self), DataValue::Decimal(_) => write!(f, "Decimal({})", self), DataValue::Tuple(_) => write!(f, "Tuple({})", self), } diff --git a/src/udf/current_date.rs b/src/udf/current_date.rs new file mode 100644 index 00000000..73bbab1b --- /dev/null +++ b/src/udf/current_date.rs @@ -0,0 +1,18 @@ +use crate::catalog::ColumnRef; +use crate::errors::DatabaseError; +use crate::expression::function::FuncMonotonicity; +use crate::expression::function::FunctionSummary; +use crate::expression::function::ScalarFunctionImpl; +use crate::expression::ScalarExpression; +use crate::function; +use crate::types::tuple::Tuple; +use crate::types::value::DataValue; +use crate::types::LogicalType; +use chrono::{Datelike, Local}; +use serde::Deserialize; +use serde::Serialize; +use std::sync::Arc; + +function!(CurrentDate::current_date() -> LogicalType::Date => (|| { + Ok(DataValue::Date32(Some(Local::now().num_days_from_ce()))) +})); diff --git a/src/udf/mod.rs b/src/udf/mod.rs new file mode 100644 index 00000000..718107a6 --- /dev/null +++ b/src/udf/mod.rs @@ -0,0 +1 @@ +pub(crate) mod current_date; diff --git a/tests/slt/basic_test.slt b/tests/slt/basic_test.slt index b584facf..30c7966d 100644 --- a/tests/slt/basic_test.slt +++ b/tests/slt/basic_test.slt @@ -28,6 +28,21 @@ select DATE '2001-02-16' ---- 2001-02-16 +query T +select DATETIME '2001-02-16 01:02:03' +---- +2001-02-16 01:02:03 + +query T +select TIME '01:02:03' +---- +01:02:03 + +query T +select cast(DATETIME '2001-02-16 02:03:03' as TIME) +---- +02:03:03 + subtest NullType statement ok diff --git a/tests/slt/sql_2016/E141_07.slt b/tests/slt/sql_2016/E141_07.slt index bbd2777c..b3684508 100644 --- a/tests/slt/sql_2016/E141_07.slt +++ b/tests/slt/sql_2016/E141_07.slt @@ -4,9 +4,8 @@ # statement ok # CREATE TABLE TABLE_E141_07_01_01 ( A NAME DEFAULT CURRENT_CATALOG ) -# TODO: CURRENT_DATE -# statement ok -# CREATE TABLE TABLE_E141_07_02_01 ( A DATE DEFAULT CURRENT_DATE ) +statement ok +CREATE TABLE TABLE_E141_07_02_01 ( ID INT PRIMARY KEY, A DATE DEFAULT CURRENT_DATE ) # statement ok # CREATE TABLE TABLE_E141_07_04_01 ( A NAME DEFAULT CURRENT_ROLE ) @@ -24,7 +23,7 @@ # statement ok # CREATE TABLE TABLE_E141_07_09_01 ( A NAME DEFAULT USER ) -# TODO: TIME & WITH TIME ZONE & CURRENT_TIMESTAMP +# TODO: WITH TIME ZONE & CURRENT_TIMESTAMP # statement ok # CREATE TABLE TABLE_E141_07_10_01 ( A TIME WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) diff --git a/tests/slt/sql_2016/F051_06.slt b/tests/slt/sql_2016/F051_06.slt index 3c53e6f2..02d77a5e 100644 --- a/tests/slt/sql_2016/F051_06.slt +++ b/tests/slt/sql_2016/F051_06.slt @@ -1,7 +1,6 @@ # F051-06: CURRENT_DATE -# TODO: CURRENT_DATE -# query B -# SELECT CURRENT_DATE = CURRENT_DATE -# ---- -# true +query B +SELECT CURRENT_DATE = CURRENT_DATE +---- +true