diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5018b8fd3..6c9ae53bb 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -43,7 +43,7 @@ jobs: --branch \ --ignore-not-existing \ --ignore "/*" \ - --ignore "examples/*.rs" \ + --ignore "pkg/rust/examples/*.rs" \ --ignore "cli/src/{cli,helper,lib,main}.rs" \ --excl-line "#\\[derive\\(" \ -o ./coverage/lcov.info diff --git a/Cargo.toml b/Cargo.toml index 309740ec1..775b412a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,8 @@ default-members = [ "test-suite", "utils", ] + +# ref. https://github.com/rustwasm/wasm-pack/issues/1111 +# enable this only for gluesql-js build +# [profile.release] +# opt-level = "s" diff --git a/cli/src/print.rs b/cli/src/print.rs index b169ae34c..29673d684 100644 --- a/cli/src/print.rs +++ b/cli/src/print.rs @@ -1,9 +1,6 @@ use { crate::command::{SetOption, ShowOption}, - gluesql_core::{ - ast::ToSql, - prelude::{Payload, PayloadVariable}, - }, + gluesql_core::prelude::{Payload, PayloadVariable}, std::{ fmt::Display, fs::File, @@ -105,6 +102,14 @@ impl<'a, W: Write> Print { }; match payload { + Payload::Create => self.write("Table created")?, + Payload::DropTable => self.write("Table dropped")?, + Payload::AlterTable => self.write("Table altered")?, + Payload::CreateIndex => self.write("Index created")?, + Payload::DropIndex => self.write("Index dropped")?, + Payload::Commit => self.write("Commit completed")?, + Payload::Rollback => self.write("Rollback completed")?, + Payload::StartTransaction => self.write("Transaction started")?, Payload::Insert(n) => affected(*n, "inserted")?, Payload::Delete(n) => affected(*n, "deleted")?, Payload::Update(n) => affected(*n, "updated")?, @@ -125,18 +130,6 @@ impl<'a, W: Write> Print { let table = self.build_table(table); self.write(table)?; } - Payload::ShowIndexes(indexes) => { - let mut table = self.get_table(vec!["Index Name", "Order", "Description"]); - for index in indexes { - table.add_record([ - index.name.to_owned(), - index.order.to_string(), - index.expr.to_sql(), - ]); - } - let table = self.build_table(table); - self.write(table)?; - } Payload::Select { labels, rows } => { let PrintOption { tabular, @@ -179,7 +172,6 @@ impl<'a, W: Write> Print { } } } - _ => {} }; Ok(()) @@ -262,10 +254,6 @@ mod tests { use { super::Print, crate::command::{SetOption, ShowOption}, - gluesql_core::{ - ast::{BinaryOperator, Expr}, - data::{SchemaIndex, SchemaIndexOrd}, - }, }; #[test] @@ -323,6 +311,14 @@ mod tests { }; } + test!(&Payload::Create, "Table created"); + test!(&Payload::DropTable, "Table dropped"); + test!(&Payload::AlterTable, "Table altered"); + test!(&Payload::CreateIndex, "Index created"); + test!(&Payload::DropIndex, "Index dropped"); + test!(&Payload::Commit, "Commit completed"); + test!(&Payload::Rollback, "Rollback completed"); + test!(&Payload::StartTransaction, "Transaction started"); test!(&Payload::Insert(0), "0 row inserted"); test!(&Payload::Insert(1), "1 row inserted"); test!(&Payload::Insert(7), "7 rows inserted"); @@ -426,36 +422,6 @@ mod tests { | 5 | kim | TRUE |" ); - test!( - &Payload::ShowIndexes(vec![ - SchemaIndex { - name: "id_ndx".to_owned(), - order: SchemaIndexOrd::Asc, - expr: Expr::Identifier("id".to_owned()) - }, - SchemaIndex { - name: "name_ndx".to_owned(), - order: SchemaIndexOrd::Desc, - expr: Expr::Identifier("name".to_owned()) - }, - SchemaIndex { - name: "expr_ndx".to_owned(), - order: SchemaIndexOrd::Both, - expr: Expr::BinaryOp { - left: Box::new(Expr::Identifier("expr1".to_owned())), - op: BinaryOperator::Minus, - right: Box::new(Expr::Identifier("expr2".to_owned())) - } - } - ],), - " -| Index Name | Order | Description | -|------------|-------|---------------| -| id_ndx | ASC | id | -| name_ndx | DESC | name | -| expr_ndx | BOTH | expr1 - expr2 |" - ); - test!( &Payload::ShowColumns(vec![ ("id".to_owned(), DataType::Int), diff --git a/cli/tests/dump.rs b/cli/tests/dump.rs index 817849041..c4732e6eb 100644 --- a/cli/tests/dump.rs +++ b/cli/tests/dump.rs @@ -1,9 +1,6 @@ use { gluesql_cli::dump_database, - gluesql_core::{ - prelude::Glue, - store::{Store, Transaction}, - }, + gluesql_core::prelude::Glue, gluesql_sled_storage::{sled, SledStorage}, std::{fs::File, io::Read, path::PathBuf}, }; @@ -63,9 +60,6 @@ async fn dump_and_import() { source_glue.execute(sql).unwrap(); } - let sql = "SELECT * FROM Foo JOIN Bar;"; - let source_data = source_glue.execute(sql).unwrap(); - let source_storage = dump_database(source_glue.storage.unwrap(), dump_path.clone()).unwrap(); let data_path = "tmp/target"; @@ -83,14 +77,17 @@ async fn dump_and_import() { target_glue.execute(sql).unwrap(); } + let mut source_glue = Glue::new(source_storage); + + // schemas should be identical + let sql = "SELECT OBJECT_TYPE, OBJECT_NAME FROM GLUE_OBJECTS"; + let source_data = source_glue.execute(sql).unwrap(); let target_data = target_glue.execute(sql).unwrap(); assert_eq!(source_data, target_data); - let (source_storage, _) = source_storage.begin(true).await.unwrap(); - let source_schemas = source_storage.fetch_all_schemas().await.unwrap(); - - let (target_storage, _) = target_glue.storage.unwrap().begin(true).await.unwrap(); - let target_schemas = target_storage.fetch_all_schemas().await.unwrap(); - - assert_eq!(source_schemas, target_schemas); + // data should be identical + let sql = "SELECT * FROM Foo JOIN Bar;"; + let source_data = source_glue.execute(sql).unwrap(); + let target_data = target_glue.execute(sql).unwrap(); + assert_eq!(source_data, target_data); } diff --git a/core/src/ast/data_type.rs b/core/src/ast/data_type.rs index 6f7e25795..9a78757d7 100644 --- a/core/src/ast/data_type.rs +++ b/core/src/ast/data_type.rs @@ -11,6 +11,7 @@ pub enum DataType { Int, Int128, Uint8, + Uint16, Float, Text, Bytea, diff --git a/core/src/ast/ddl.rs b/core/src/ast/ddl.rs index aeb775c15..594f50a62 100644 --- a/core/src/ast/ddl.rs +++ b/core/src/ast/ddl.rs @@ -26,13 +26,7 @@ pub enum AlterTableOperation { pub struct ColumnDef { pub name: String, pub data_type: DataType, - pub options: Vec, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct ColumnOptionDef { - pub name: Option, - pub option: ColumnOption, + pub options: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -81,7 +75,7 @@ impl ToSql for ColumnDef { { let options = options .iter() - .map(|ColumnOptionDef { option, .. }| option.to_sql()) + .map(|option| option.to_sql()) .collect::>() .join(" "); @@ -108,7 +102,7 @@ impl ToSql for ColumnOption { #[cfg(test)] mod tests { - use crate::ast::{AstLiteral, ColumnDef, ColumnOption, ColumnOptionDef, DataType, Expr, ToSql}; + use crate::ast::{AstLiteral, ColumnDef, ColumnOption, DataType, Expr, ToSql}; #[test] fn to_sql_column_def() { @@ -117,10 +111,7 @@ mod tests { ColumnDef { name: "name".to_owned(), data_type: DataType::Text, - options: vec![ColumnOptionDef { - name: None, - option: ColumnOption::Unique { is_primary: false } - }] + options: vec![ColumnOption::Unique { is_primary: false }] } .to_sql() ); @@ -130,10 +121,7 @@ mod tests { ColumnDef { name: "accepted".to_owned(), data_type: DataType::Boolean, - options: vec![ColumnOptionDef { - name: None, - option: ColumnOption::Null - }] + options: vec![ColumnOption::Null] } .to_sql() ); @@ -144,14 +132,8 @@ mod tests { name: "id".to_owned(), data_type: DataType::Int, options: vec![ - ColumnOptionDef { - name: None, - option: ColumnOption::NotNull - }, - ColumnOptionDef { - name: None, - option: ColumnOption::Unique { is_primary: true } - } + ColumnOption::NotNull, + ColumnOption::Unique { is_primary: true } ] } .to_sql() @@ -162,10 +144,9 @@ mod tests { ColumnDef { name: "accepted".to_owned(), data_type: DataType::Boolean, - options: vec![ColumnOptionDef { - name: None, - option: ColumnOption::Default(Expr::Literal(AstLiteral::Boolean(false))) - }] + options: vec![ColumnOption::Default(Expr::Literal(AstLiteral::Boolean( + false + )))] } .to_sql() ); diff --git a/core/src/ast/function.rs b/core/src/ast/function.rs index a8194849b..63d3a073c 100644 --- a/core/src/ast/function.rs +++ b/core/src/ast/function.rs @@ -38,6 +38,10 @@ pub enum Function { }, Ceil(Expr), Concat(Vec), + ConcatWs { + separator: Expr, + exprs: Vec, + }, IfNull { expr: Expr, then: Expr, @@ -180,6 +184,14 @@ impl ToSql for Function { .join(", "); format!("CONCAT({items})") } + Function::ConcatWs { separator, exprs } => { + let exprs = exprs + .iter() + .map(ToSql::to_sql) + .collect::>() + .join(", "); + format!("CONCAT_WS({}, {})", separator.to_sql(), exprs) + } Function::IfNull { expr, then } => { format!("IFNULL({}, {})", expr.to_sql(), then.to_sql()) } @@ -462,6 +474,19 @@ mod tests { .to_sql() ); + assert_eq!( + "CONCAT_WS(-, Tic, tac, toe)", + &Expr::Function(Box::new(Function::ConcatWs { + separator: Expr::Identifier("-".to_owned()), + exprs: vec![ + Expr::Identifier("Tic".to_owned()), + Expr::Identifier("tac".to_owned()), + Expr::Identifier("toe".to_owned()) + ] + })) + .to_sql() + ); + assert_eq!( "IFNULL(updated_at, created_at)", &Expr::Function(Box::new(Function::IfNull { diff --git a/core/src/ast/mod.rs b/core/src/ast/mod.rs index bd4234dc4..7dc7dd46b 100644 --- a/core/src/ast/mod.rs +++ b/core/src/ast/mod.rs @@ -240,9 +240,9 @@ mod tests { use { crate::ast::{ - Assignment, AstLiteral, BinaryOperator, ColumnDef, ColumnOption, ColumnOptionDef, - DataType, Expr, Query, Select, SelectItem, SetExpr, Statement, TableFactor, - TableWithJoins, ToSql, Values, Variable, + Assignment, AstLiteral, BinaryOperator, ColumnDef, ColumnOption, DataType, Expr, Query, + Select, SelectItem, SetExpr, Statement, TableFactor, TableWithJoins, ToSql, Values, + Variable, }, bigdecimal::BigDecimal, std::str::FromStr, @@ -374,10 +374,7 @@ mod tests { ColumnDef { name: "num".to_owned(), data_type: DataType::Int, - options: vec![ColumnOptionDef { - name: None, - option: ColumnOption::Null - }] + options: vec![ColumnOption::Null] }, ColumnDef { name: "name".to_owned(), @@ -461,12 +458,9 @@ mod tests { column_def: ColumnDef { name: "amount".to_owned(), data_type: DataType::Int, - options: vec![ColumnOptionDef { - name: None, - option: ColumnOption::Default(Expr::Literal(AstLiteral::Number( - BigDecimal::from_str("10").unwrap() - ))) - }] + options: vec![ColumnOption::Default(Expr::Literal(AstLiteral::Number( + BigDecimal::from_str("10").unwrap() + )))] } } } diff --git a/core/src/ast/query.rs b/core/src/ast/query.rs index 793f595bc..7f5fb709d 100644 --- a/core/src/ast/query.rs +++ b/core/src/ast/query.rs @@ -84,6 +84,7 @@ pub enum Dictionary { GlueTables, GlueTableColumns, GlueIndexes, + GlueObjects, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] diff --git a/core/src/ast_builder/assignment.rs b/core/src/ast_builder/assignment.rs index e36d52ee4..88a06825a 100644 --- a/core/src/ast_builder/assignment.rs +++ b/core/src/ast_builder/assignment.rs @@ -7,21 +7,21 @@ use crate::{ }; #[derive(Clone)] -pub enum AssignmentNode { - Expr(String, ExprNode), +pub enum AssignmentNode<'a> { + Expr(String, ExprNode<'a>), Text(String), } -impl From<&str> for AssignmentNode { +impl<'a> From<&str> for AssignmentNode<'a> { fn from(expr: &str) -> Self { Self::Text(expr.to_owned()) } } -impl TryFrom for Assignment { +impl<'a> TryFrom> for Assignment { type Error = Error; - fn try_from(node: AssignmentNode) -> Result { + fn try_from(node: AssignmentNode<'a>) -> Result { match node { AssignmentNode::Text(expr) => { let expr = parse_assignment(expr) diff --git a/core/src/ast_builder/delete.rs b/core/src/ast_builder/delete.rs index 6893dbf0b..7bf15cf1f 100644 --- a/core/src/ast_builder/delete.rs +++ b/core/src/ast_builder/delete.rs @@ -7,12 +7,12 @@ use { }; #[derive(Clone)] -pub struct DeleteNode { +pub struct DeleteNode<'a> { table_name: String, - filter_expr: Option, + filter_expr: Option>, } -impl DeleteNode { +impl<'a> DeleteNode<'a> { pub fn new(table_name: String) -> Self { Self { table_name, @@ -20,14 +20,14 @@ impl DeleteNode { } } - pub fn filter>(mut self, expr: T) -> Self { + pub fn filter>>(mut self, expr: T) -> Self { self.filter_expr = Some(expr.into()); self } } -impl Build for DeleteNode { +impl<'a> Build for DeleteNode<'a> { fn build(self) -> Result { let table_name = self.table_name; let selection = self.filter_expr.map(Expr::try_from).transpose()?; diff --git a/core/src/ast_builder/expr/aggregate.rs b/core/src/ast_builder/expr/aggregate.rs index 58f12d830..c35902453 100644 --- a/core/src/ast_builder/expr/aggregate.rs +++ b/core/src/ast_builder/expr/aggregate.rs @@ -9,38 +9,38 @@ use { }; #[derive(Clone)] -pub enum AggregateNode { - Count(CountArgExprNode), - Sum(ExprNode), - Min(ExprNode), - Max(ExprNode), - Avg(ExprNode), - Variance(ExprNode), - Stdev(ExprNode), +pub enum AggregateNode<'a> { + Count(CountArgExprNode<'a>), + Sum(ExprNode<'a>), + Min(ExprNode<'a>), + Max(ExprNode<'a>), + Avg(ExprNode<'a>), + Variance(ExprNode<'a>), + Stdev(ExprNode<'a>), } #[derive(Clone)] -pub enum CountArgExprNode { +pub enum CountArgExprNode<'a> { Text(String), - Expr(ExprNode), + Expr(ExprNode<'a>), } -impl From<&str> for CountArgExprNode { +impl<'a> From<&'a str> for CountArgExprNode<'a> { fn from(count_arg_str: &str) -> Self { Self::Text(count_arg_str.to_owned()) } } -impl From for CountArgExprNode { - fn from(expr_node: ExprNode) -> Self { +impl<'a> From> for CountArgExprNode<'a> { + fn from(expr_node: ExprNode<'a>) -> Self { Self::Expr(expr_node) } } -impl TryFrom for CountArgExpr { +impl<'a> TryFrom> for CountArgExpr { type Error = Error; - fn try_from(count_expr_node: CountArgExprNode) -> Result { + fn try_from(count_expr_node: CountArgExprNode<'a>) -> Result { match count_expr_node { CountArgExprNode::Text(s) if &s == "*" => Ok(CountArgExpr::Wildcard), CountArgExprNode::Text(s) => { @@ -53,10 +53,10 @@ impl TryFrom for CountArgExpr { } } -impl TryFrom for Aggregate { +impl<'a> TryFrom> for Aggregate { type Error = Error; - fn try_from(aggr_node: AggregateNode) -> Result { + fn try_from(aggr_node: AggregateNode<'a>) -> Result { match aggr_node { AggregateNode::Count(count_arg_expr_node) => { count_arg_expr_node.try_into().map(Aggregate::Count) @@ -71,7 +71,7 @@ impl TryFrom for Aggregate { } } -impl ExprNode { +impl<'a> ExprNode<'a> { pub fn count(self) -> Self { count(self) } @@ -101,31 +101,31 @@ impl ExprNode { } } -pub fn count>(expr: T) -> ExprNode { +pub fn count<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Aggregate(Box::new(AggregateNode::Count(expr.into()))) } -pub fn sum>(expr: T) -> ExprNode { +pub fn sum<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Aggregate(Box::new(AggregateNode::Sum(expr.into()))) } -pub fn min>(expr: T) -> ExprNode { +pub fn min<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Aggregate(Box::new(AggregateNode::Min(expr.into()))) } -pub fn max>(expr: T) -> ExprNode { +pub fn max<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Aggregate(Box::new(AggregateNode::Max(expr.into()))) } -pub fn avg>(expr: T) -> ExprNode { +pub fn avg<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Aggregate(Box::new(AggregateNode::Avg(expr.into()))) } -pub fn variance>(expr: T) -> ExprNode { +pub fn variance<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Aggregate(Box::new(AggregateNode::Variance(expr.into()))) } -pub fn stdev>(expr: T) -> ExprNode { +pub fn stdev<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Aggregate(Box::new(AggregateNode::Stdev(expr.into()))) } diff --git a/core/src/ast_builder/expr/between.rs b/core/src/ast_builder/expr/between.rs index 9afb2ad8b..3e52d1830 100644 --- a/core/src/ast_builder/expr/between.rs +++ b/core/src/ast_builder/expr/between.rs @@ -1,6 +1,6 @@ use super::ExprNode; -impl ExprNode { +impl<'a> ExprNode<'a> { pub fn between, U: Into>(self, low: T, high: U) -> Self { Self::Between { expr: Box::new(self), diff --git a/core/src/ast_builder/expr/binary_op.rs b/core/src/ast_builder/expr/binary_op.rs index 49af432eb..b9fc9146b 100644 --- a/core/src/ast_builder/expr/binary_op.rs +++ b/core/src/ast_builder/expr/binary_op.rs @@ -1,6 +1,6 @@ use {super::ExprNode, crate::ast::BinaryOperator}; -impl ExprNode { +impl<'a> ExprNode<'a> { fn binary_op>(self, op: BinaryOperator, other: T) -> Self { Self::BinaryOp { left: Box::new(self), diff --git a/core/src/ast_builder/expr/case.rs b/core/src/ast_builder/expr/case.rs index cab15748b..13b73436f 100644 --- a/core/src/ast_builder/expr/case.rs +++ b/core/src/ast_builder/expr/case.rs @@ -1,23 +1,27 @@ use super::ExprNode; -impl ExprNode { - pub fn case(self) -> CaseNode { +impl<'a> ExprNode<'a> { + pub fn case(self) -> CaseNode<'a> { CaseNode { operand: Some(Box::new(self)), } } } -pub fn case() -> CaseNode { +pub fn case() -> CaseNode<'static> { CaseNode { operand: None } } -pub struct CaseNode { - operand: Option>, +pub struct CaseNode<'a> { + operand: Option>>, } -impl CaseNode { - pub fn when_then, T: Into>(self, when: W, then: T) -> WhenThenNode { +impl<'a> CaseNode<'a> { + pub fn when_then>, T: Into>>( + self, + when: W, + then: T, + ) -> WhenThenNode<'a> { WhenThenNode { prev_node: self, when_then: vec![(when.into(), then.into())], @@ -25,18 +29,22 @@ impl CaseNode { } } -pub struct WhenThenNode { - prev_node: CaseNode, - when_then: Vec<(ExprNode, ExprNode)>, +pub struct WhenThenNode<'a> { + prev_node: CaseNode<'a>, + when_then: Vec<(ExprNode<'a>, ExprNode<'a>)>, } -impl WhenThenNode { - pub fn when_then, T: Into>(mut self, when: W, then: T) -> Self { +impl<'a> WhenThenNode<'a> { + pub fn when_then>, T: Into>>( + mut self, + when: W, + then: T, + ) -> Self { self.when_then.push((when.into(), then.into())); self } - pub fn or_else>(self, else_result: T) -> ExprNode { + pub fn or_else>>(self, else_result: T) -> ExprNode<'a> { ExprNode::Case { operand: self.prev_node.operand, when_then: self.when_then, @@ -44,7 +52,7 @@ impl WhenThenNode { } } - pub fn end(self) -> ExprNode { + pub fn end(self) -> ExprNode<'a> { ExprNode::Case { operand: self.prev_node.operand, when_then: self.when_then, diff --git a/core/src/ast_builder/expr/exists.rs b/core/src/ast_builder/expr/exists.rs index 79266aa3d..bc069d5ac 100644 --- a/core/src/ast_builder/expr/exists.rs +++ b/core/src/ast_builder/expr/exists.rs @@ -1,13 +1,13 @@ use {super::ExprNode, crate::ast_builder::QueryNode}; -pub fn exists>(query: T) -> ExprNode { +pub fn exists<'a, T: Into>>(query: T) -> ExprNode<'a> { ExprNode::Exists { subquery: Box::new(query.into()), negated: false, } } -pub fn not_exists>(query: T) -> ExprNode { +pub fn not_exists<'a, T: Into>>(query: T) -> ExprNode<'a> { ExprNode::Exists { subquery: Box::new(query.into()), negated: true, diff --git a/core/src/ast_builder/expr/function.rs b/core/src/ast_builder/expr/function.rs index aa30aeaf2..3f3d53a60 100644 --- a/core/src/ast_builder/expr/function.rs +++ b/core/src/ast_builder/expr/function.rs @@ -8,129 +8,133 @@ use { }; #[derive(Clone)] -pub enum FunctionNode { - Abs(ExprNode), - Upper(ExprNode), +pub enum FunctionNode<'a> { + Abs(ExprNode<'a>), + Upper(ExprNode<'a>), IfNull { - expr: ExprNode, - then: ExprNode, + expr: ExprNode<'a>, + then: ExprNode<'a>, }, - Ceil(ExprNode), - Round(ExprNode), - Floor(ExprNode), - Asin(ExprNode), - Acos(ExprNode), - Atan(ExprNode), - Sin(ExprNode), - Cos(ExprNode), - Tan(ExprNode), + Ceil(ExprNode<'a>), + Round(ExprNode<'a>), + Floor(ExprNode<'a>), + Asin(ExprNode<'a>), + Acos(ExprNode<'a>), + Atan(ExprNode<'a>), + Sin(ExprNode<'a>), + Cos(ExprNode<'a>), + Tan(ExprNode<'a>), Pi, Now, Left { - expr: ExprNode, - size: ExprNode, + expr: ExprNode<'a>, + size: ExprNode<'a>, }, Log { - antilog: ExprNode, - base: ExprNode, + antilog: ExprNode<'a>, + base: ExprNode<'a>, }, - Log2(ExprNode), - Log10(ExprNode), - Ln(ExprNode), + Log2(ExprNode<'a>), + Log10(ExprNode<'a>), + Ln(ExprNode<'a>), Right { - expr: ExprNode, - size: ExprNode, + expr: ExprNode<'a>, + size: ExprNode<'a>, }, - Reverse(ExprNode), - Sign(ExprNode), + Reverse(ExprNode<'a>), + Sign(ExprNode<'a>), Power { - expr: ExprNode, - power: ExprNode, + expr: ExprNode<'a>, + power: ExprNode<'a>, }, - Sqrt(ExprNode), + Sqrt(ExprNode<'a>), Gcd { - left: ExprNode, - right: ExprNode, + left: ExprNode<'a>, + right: ExprNode<'a>, }, Lcm { - left: ExprNode, - right: ExprNode, + left: ExprNode<'a>, + right: ExprNode<'a>, }, GenerateUuid, Repeat { - expr: ExprNode, - num: ExprNode, + expr: ExprNode<'a>, + num: ExprNode<'a>, }, - Exp(ExprNode), + Exp(ExprNode<'a>), Lpad { - expr: ExprNode, - size: ExprNode, - fill: Option, + expr: ExprNode<'a>, + size: ExprNode<'a>, + fill: Option>, }, Rpad { - expr: ExprNode, - size: ExprNode, - fill: Option, + expr: ExprNode<'a>, + size: ExprNode<'a>, + fill: Option>, + }, + Degrees(ExprNode<'a>), + Radians(ExprNode<'a>), + Concat(ExprList<'a>), + ConcatWs { + separator: ExprNode<'a>, + exprs: ExprList<'a>, }, - Degrees(ExprNode), - Radians(ExprNode), - Concat(ExprList), Substr { - expr: ExprNode, - start: ExprNode, - count: Option, + expr: ExprNode<'a>, + start: ExprNode<'a>, + count: Option>, }, Ltrim { - expr: ExprNode, - chars: Option, + expr: ExprNode<'a>, + chars: Option>, }, Rtrim { - expr: ExprNode, - chars: Option, + expr: ExprNode<'a>, + chars: Option>, }, Div { - dividend: ExprNode, - divisor: ExprNode, + dividend: ExprNode<'a>, + divisor: ExprNode<'a>, }, Mod { - dividend: ExprNode, - divisor: ExprNode, + dividend: ExprNode<'a>, + divisor: ExprNode<'a>, }, Format { - expr: ExprNode, - format: ExprNode, + expr: ExprNode<'a>, + format: ExprNode<'a>, }, ToDate { - expr: ExprNode, - format: ExprNode, + expr: ExprNode<'a>, + format: ExprNode<'a>, }, ToTimestamp { - expr: ExprNode, - format: ExprNode, + expr: ExprNode<'a>, + format: ExprNode<'a>, }, ToTime { - expr: ExprNode, - format: ExprNode, + expr: ExprNode<'a>, + format: ExprNode<'a>, }, - Lower(ExprNode), + Lower(ExprNode<'a>), Position { - from_expr: ExprNode, - sub_expr: ExprNode, + from_expr: ExprNode<'a>, + sub_expr: ExprNode<'a>, }, Cast { - expr: ExprNode, + expr: ExprNode<'a>, data_type: DataTypeNode, }, Extract { field: DateTimeField, - expr: ExprNode, + expr: ExprNode<'a>, }, } -impl TryFrom for Function { +impl<'a> TryFrom> for Function { type Error = Error; - fn try_from(func_node: FunctionNode) -> Result { + fn try_from(func_node: FunctionNode<'a>) -> Result { match func_node { FunctionNode::Abs(expr_node) => expr_node.try_into().map(Function::Abs), FunctionNode::Upper(expr_node) => expr_node.try_into().map(Function::Upper), @@ -206,6 +210,11 @@ impl TryFrom for Function { Ok(Function::Rpad { expr, size, fill }) } FunctionNode::Concat(expr_list) => expr_list.try_into().map(Function::Concat), + FunctionNode::ConcatWs { separator, exprs } => { + let separator = separator.try_into()?; + let exprs = exprs.try_into()?; + Ok(Function::ConcatWs { separator, exprs }) + } FunctionNode::Degrees(expr) => expr.try_into().map(Function::Degrees), FunctionNode::Radians(expr) => expr.try_into().map(Function::Radians), FunctionNode::Exp(expr) => expr.try_into().map(Function::Exp), @@ -279,263 +288,278 @@ impl TryFrom for Function { } } -impl ExprNode { - pub fn abs(self) -> ExprNode { +impl<'a> ExprNode<'a> { + pub fn abs(self) -> ExprNode<'a> { abs(self) } - pub fn upper(self) -> ExprNode { + pub fn upper(self) -> ExprNode<'a> { upper(self) } - pub fn lower(self) -> ExprNode { + pub fn lower(self) -> ExprNode<'a> { lower(self) } - pub fn ifnull>(self, another: T) -> ExprNode { + pub fn ifnull>>(self, another: T) -> ExprNode<'a> { ifnull(self, another) } - pub fn ceil(self) -> ExprNode { + pub fn ceil(self) -> ExprNode<'a> { ceil(self) } - pub fn round(self) -> ExprNode { + pub fn round(self) -> ExprNode<'a> { round(self) } - pub fn floor(self) -> ExprNode { + pub fn floor(self) -> ExprNode<'a> { floor(self) } - pub fn asin(self) -> ExprNode { + pub fn asin(self) -> ExprNode<'a> { asin(self) } - pub fn acos(self) -> ExprNode { + pub fn acos(self) -> ExprNode<'a> { acos(self) } - pub fn atan(self) -> ExprNode { + pub fn atan(self) -> ExprNode<'a> { atan(self) } - pub fn sin(self) -> ExprNode { + pub fn sin(self) -> ExprNode<'a> { sin(self) } - pub fn cos(self) -> ExprNode { + pub fn cos(self) -> ExprNode<'a> { cos(self) } - pub fn tan(self) -> ExprNode { + pub fn tan(self) -> ExprNode<'a> { tan(self) } - pub fn left>(self, size: T) -> Self { + pub fn left>>(self, size: T) -> Self { left(self, size) } - pub fn log>(self, base: T) -> ExprNode { + pub fn log>>(self, base: T) -> ExprNode<'a> { log(self, base) } - pub fn log2(self) -> ExprNode { + pub fn log2(self) -> ExprNode<'a> { log2(self) } - pub fn log10(self) -> ExprNode { + pub fn log10(self) -> ExprNode<'a> { log10(self) } - pub fn ln(self) -> ExprNode { + pub fn ln(self) -> ExprNode<'a> { ln(self) } - pub fn right>(self, size: T) -> Self { + pub fn right>>(self, size: T) -> Self { right(self, size) } - pub fn reverse(self) -> ExprNode { + pub fn reverse(self) -> ExprNode<'a> { reverse(self) } - pub fn sign(self) -> ExprNode { + pub fn sign(self) -> ExprNode<'a> { sign(self) } - pub fn power>(self, pwr: T) -> ExprNode { + pub fn power>>(self, pwr: T) -> ExprNode<'a> { power(self, pwr) } - pub fn sqrt(self) -> ExprNode { + pub fn sqrt(self) -> ExprNode<'a> { sqrt(self) } - pub fn gcd>(self, right: T) -> ExprNode { + pub fn gcd>>(self, right: T) -> ExprNode<'a> { gcd(self, right) } - pub fn lcm>(self, right: T) -> ExprNode { + pub fn lcm>>(self, right: T) -> ExprNode<'a> { lcm(self, right) } - pub fn repeat>(self, num: T) -> ExprNode { + pub fn repeat>>(self, num: T) -> ExprNode<'a> { repeat(self, num) } - pub fn degrees(self) -> ExprNode { + pub fn degrees(self) -> ExprNode<'a> { degrees(self) } - pub fn radians(self) -> ExprNode { + pub fn radians(self) -> ExprNode<'a> { radians(self) } - pub fn lpad>(self, size: T, fill: Option) -> ExprNode { + pub fn lpad>>(self, size: T, fill: Option>) -> ExprNode<'a> { lpad(self, size, fill) } - pub fn rpad>(self, size: T, fill: Option) -> ExprNode { + pub fn rpad>>(self, size: T, fill: Option>) -> ExprNode<'a> { rpad(self, size, fill) } - pub fn exp(self) -> ExprNode { + pub fn exp(self) -> ExprNode<'a> { exp(self) } - pub fn substr>(self, start: T, count: Option) -> ExprNode { + pub fn substr>>( + self, + start: T, + count: Option>, + ) -> ExprNode<'a> { substr(self, start, count) } - pub fn rtrim(self, chars: Option) -> ExprNode { + pub fn rtrim(self, chars: Option>) -> ExprNode<'a> { rtrim(self, chars) } - pub fn ltrim(self, chars: Option) -> ExprNode { + pub fn ltrim(self, chars: Option>) -> ExprNode<'a> { ltrim(self, chars) } - pub fn format>(self, fmt: T) -> ExprNode { + pub fn format>>(self, fmt: T) -> ExprNode<'a> { format(self, fmt) } - pub fn to_date>(self, format: T) -> ExprNode { + pub fn to_date>>(self, format: T) -> ExprNode<'a> { to_date(self, format) } - pub fn to_timestamp>(self, format: T) -> ExprNode { + pub fn to_timestamp>>(self, format: T) -> ExprNode<'a> { to_timestamp(self, format) } - pub fn to_time>(self, format: T) -> ExprNode { + pub fn to_time>>(self, format: T) -> ExprNode<'a> { to_time(self, format) } - pub fn position>(self, format: T) -> ExprNode { + pub fn position>>(self, format: T) -> ExprNode<'a> { position(self, format) } - pub fn cast>(self, data_type: T) -> ExprNode { + pub fn cast>(self, data_type: T) -> ExprNode<'a> { cast(self, data_type) } - pub fn extract(self, field: DateTimeField) -> ExprNode { + pub fn extract(self, field: DateTimeField) -> ExprNode<'a> { extract(field, self) } } -pub fn abs>(expr: T) -> ExprNode { +pub fn abs<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Abs(expr.into()))) } -pub fn upper>(expr: T) -> ExprNode { +pub fn upper<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Upper(expr.into()))) } -pub fn lower>(expr: T) -> ExprNode { +pub fn lower<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Lower(expr.into()))) } -pub fn ifnull, U: Into>(expr: T, then: U) -> ExprNode { +pub fn ifnull<'a, T: Into>, U: Into>>(expr: T, then: U) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::IfNull { expr: expr.into(), then: then.into(), })) } -pub fn ceil>(expr: T) -> ExprNode { +pub fn ceil<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Ceil(expr.into()))) } -pub fn round>(expr: T) -> ExprNode { +pub fn round<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Round(expr.into()))) } -pub fn concat>(expr: T) -> ExprNode { +pub fn concat<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Concat(expr.into()))) } -pub fn floor>(expr: T) -> ExprNode { + +pub fn concat_ws<'a, T: Into>, U: Into>>( + separator: T, + exprs: U, +) -> ExprNode<'a> { + ExprNode::Function(Box::new(FunctionNode::ConcatWs { + separator: separator.into(), + exprs: exprs.into(), + })) +} + +pub fn floor<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Floor(expr.into()))) } -pub fn asin>(expr: T) -> ExprNode { +pub fn asin<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Asin(expr.into()))) } -pub fn acos>(expr: T) -> ExprNode { +pub fn acos<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Acos(expr.into()))) } -pub fn atan>(expr: T) -> ExprNode { +pub fn atan<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Atan(expr.into()))) } -pub fn sin>(expr: T) -> ExprNode { +pub fn sin<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Sin(expr.into()))) } -pub fn cos>(expr: T) -> ExprNode { +pub fn cos<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Cos(expr.into()))) } -pub fn tan>(expr: T) -> ExprNode { +pub fn tan<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Tan(expr.into()))) } -pub fn pi() -> ExprNode { +pub fn pi<'a>() -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Pi)) } -pub fn generate_uuid() -> ExprNode { +pub fn generate_uuid<'a>() -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::GenerateUuid)) } -pub fn now() -> ExprNode { +pub fn now<'a>() -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Now)) } -pub fn left, U: Into>(expr: T, size: U) -> ExprNode { +pub fn left<'a, T: Into>, U: Into>>(expr: T, size: U) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Left { expr: expr.into(), size: size.into(), })) } -pub fn log, U: Into>(antilog: T, base: U) -> ExprNode { +pub fn log<'a, T: Into>, U: Into>>(antilog: T, base: U) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Log { antilog: antilog.into(), base: base.into(), })) } -pub fn log2>(expr: T) -> ExprNode { +pub fn log2<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Log2(expr.into()))) } -pub fn log10>(expr: T) -> ExprNode { +pub fn log10<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Log10(expr.into()))) } -pub fn ln>(expr: T) -> ExprNode { +pub fn ln<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Ln(expr.into()))) } -pub fn right, U: Into>(expr: T, size: U) -> ExprNode { +pub fn right<'a, T: Into>, U: Into>>(expr: T, size: U) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Right { expr: expr.into(), size: size.into(), })) } -pub fn reverse>(expr: T) -> ExprNode { +pub fn reverse<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Reverse(expr.into()))) } -pub fn sign>(expr: T) -> ExprNode { +pub fn sign<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Sign(expr.into()))) } -pub fn power, U: Into>(expr: T, power: U) -> ExprNode { +pub fn power<'a, T: Into>, U: Into>>(expr: T, power: U) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Power { expr: expr.into(), power: power.into(), })) } -pub fn sqrt>(expr: V) -> ExprNode { +pub fn sqrt<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Sqrt(expr.into()))) } -pub fn gcd, U: Into>(left: T, right: U) -> ExprNode { +pub fn gcd<'a, T: Into>, U: Into>>(left: T, right: U) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Gcd { left: left.into(), right: right.into(), })) } -pub fn lcm, U: Into>(left: T, right: U) -> ExprNode { +pub fn lcm<'a, T: Into>, U: Into>>(left: T, right: U) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Lcm { left: left.into(), right: right.into(), })) } -pub fn repeat, V: Into>(expr: T, num: V) -> ExprNode { +pub fn repeat<'a, T: Into>, V: Into>>(expr: T, num: V) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Repeat { expr: expr.into(), num: num.into(), })) } -pub fn lpad, U: Into>( +pub fn lpad<'a, T: Into>, U: Into>>( expr: T, size: U, - fill: Option, -) -> ExprNode { + fill: Option>, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Lpad { expr: expr.into(), size: size.into(), @@ -543,11 +567,11 @@ pub fn lpad, U: Into>( })) } -pub fn rpad, U: Into>( +pub fn rpad<'a, T: Into>, U: Into>>( expr: T, size: U, - fill: Option, -) -> ExprNode { + fill: Option>, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Rpad { expr: expr.into(), size: size.into(), @@ -555,22 +579,22 @@ pub fn rpad, U: Into>( })) } -pub fn degrees>(expr: V) -> ExprNode { +pub fn degrees<'a, V: Into>>(expr: V) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Degrees(expr.into()))) } -pub fn radians>(expr: V) -> ExprNode { +pub fn radians<'a, V: Into>>(expr: V) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Radians(expr.into()))) } -pub fn exp>(expr: V) -> ExprNode { +pub fn exp<'a, V: Into>>(expr: V) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Exp(expr.into()))) } -pub fn substr, U: Into>( +pub fn substr<'a, T: Into>, U: Into>>( expr: T, start: U, - count: Option, -) -> ExprNode { + count: Option>, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Substr { expr: expr.into(), start: start.into(), @@ -578,77 +602,101 @@ pub fn substr, U: Into>( })) } -pub fn ltrim>(expr: T, chars: Option) -> ExprNode { +pub fn ltrim<'a, T: Into>>(expr: T, chars: Option>) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Ltrim { expr: expr.into(), chars, })) } -pub fn rtrim>(expr: T, chars: Option) -> ExprNode { +pub fn rtrim<'a, T: Into>>(expr: T, chars: Option>) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Rtrim { expr: expr.into(), chars, })) } -pub fn divide, U: Into>(dividend: T, divisor: U) -> ExprNode { +pub fn divide<'a, T: Into>, U: Into>>( + dividend: T, + divisor: U, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Div { dividend: dividend.into(), divisor: divisor.into(), })) } -pub fn modulo, U: Into>(dividend: T, divisor: U) -> ExprNode { +pub fn modulo<'a, T: Into>, U: Into>>( + dividend: T, + divisor: U, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Mod { dividend: dividend.into(), divisor: divisor.into(), })) } -pub fn format, T: Into>(expr: D, format: T) -> ExprNode { +pub fn format<'a, D: Into>, T: Into>>( + expr: D, + format: T, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Format { expr: expr.into(), format: format.into(), })) } -pub fn to_date, U: Into>(expr: T, format: U) -> ExprNode { +pub fn to_date<'a, T: Into>, U: Into>>( + expr: T, + format: U, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::ToDate { expr: expr.into(), format: format.into(), })) } -pub fn to_timestamp, U: Into>(expr: T, format: U) -> ExprNode { +pub fn to_timestamp<'a, T: Into>, U: Into>>( + expr: T, + format: U, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::ToTimestamp { expr: expr.into(), format: format.into(), })) } -pub fn to_time, U: Into>(expr: T, format: U) -> ExprNode { +pub fn to_time<'a, T: Into>, U: Into>>( + expr: T, + format: U, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::ToTime { expr: expr.into(), format: format.into(), })) } -pub fn position, U: Into>(from_expr: T, sub_expr: U) -> ExprNode { +pub fn position<'a, T: Into>, U: Into>>( + from_expr: T, + sub_expr: U, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Position { from_expr: from_expr.into(), sub_expr: sub_expr.into(), })) } -pub fn cast, U: Into>(expr: T, data_type: U) -> ExprNode { +pub fn cast<'a, T: Into>, U: Into>( + expr: T, + data_type: U, +) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Cast { expr: expr.into(), data_type: data_type.into(), })) } -pub fn extract>(field: DateTimeField, expr: T) -> ExprNode { +pub fn extract<'a, T: Into>>(field: DateTimeField, expr: T) -> ExprNode<'a> { ExprNode::Function(Box::new(FunctionNode::Extract { field, expr: expr.into(), @@ -660,11 +708,11 @@ mod tests { use crate::{ ast::DateTimeField, ast_builder::{ - abs, acos, asin, atan, cast, ceil, col, concat, cos, date, degrees, divide, exp, expr, - extract, floor, format, gcd, generate_uuid, ifnull, lcm, left, ln, log, log10, log2, - lower, lpad, ltrim, modulo, now, num, pi, position, power, radians, repeat, reverse, - right, round, rpad, rtrim, sign, sin, sqrt, substr, tan, test_expr, text, time, - timestamp, to_date, to_time, to_timestamp, upper, + abs, acos, asin, atan, cast, ceil, col, concat, concat_ws, cos, date, degrees, divide, + exp, expr, extract, floor, format, gcd, generate_uuid, ifnull, lcm, left, ln, log, + log10, log2, lower, lpad, ltrim, modulo, now, num, pi, position, power, radians, + repeat, reverse, right, round, rpad, rtrim, sign, sin, sqrt, substr, tan, test_expr, + text, time, timestamp, to_date, to_time, to_timestamp, upper, }, prelude::DataType, }; @@ -985,6 +1033,17 @@ mod tests { test_expr(actual, expected); } + #[test] + fn function_concat_ws() { + let actual = concat_ws(text(","), vec![text("Glue"), text("SQL"), text("Go")]); + let expected = "CONCAT_WS(',', 'Glue', 'SQL', 'Go')"; + test_expr(actual, expected); + + let actual = concat_ws(text(","), vec!["Glue", "SQL", "Go"]); + let expected = "CONCAT_WS(',', Glue, SQL, Go)"; + test_expr(actual, expected); + } + #[test] fn function_lpad() { let actual = lpad(text("GlueSQL"), num(10), Some(text("Go"))); diff --git a/core/src/ast_builder/expr/in_list.rs b/core/src/ast_builder/expr/in_list.rs index abb3a3cec..3ebdc3468 100644 --- a/core/src/ast_builder/expr/in_list.rs +++ b/core/src/ast_builder/expr/in_list.rs @@ -8,33 +8,33 @@ use { }; #[derive(Clone)] -pub enum InListNode { - InList(Vec), - Query(Box), +pub enum InListNode<'a> { + InList(Vec>), + Query(Box>), Text(String), } -impl From> for InListNode { - fn from(list: Vec) -> Self { +impl<'a> From>> for InListNode<'a> { + fn from(list: Vec>) -> Self { InListNode::InList(list) } } -impl From<&str> for InListNode { +impl<'a> From<&str> for InListNode<'a> { fn from(query: &str) -> Self { InListNode::Text(query.to_owned()) } } -impl From for InListNode { - fn from(node: QueryNode) -> Self { +impl<'a> From> for InListNode<'a> { + fn from(node: QueryNode<'a>) -> Self { InListNode::Query(Box::new(node)) } } macro_rules! impl_from_select_nodes { ($type: path) => { - impl From<$type> for InListNode { + impl<'a> From<$type> for InListNode<'a> { fn from(list: $type) -> Self { InListNode::Query(Box::new(list.into())) } @@ -43,21 +43,21 @@ macro_rules! impl_from_select_nodes { } impl_from_select_nodes!(SelectNode); -impl_from_select_nodes!(JoinNode); -impl_from_select_nodes!(JoinConstraintNode); -impl_from_select_nodes!(HashJoinNode); -impl_from_select_nodes!(GroupByNode); -impl_from_select_nodes!(HavingNode); -impl_from_select_nodes!(FilterNode); -impl_from_select_nodes!(LimitNode); -impl_from_select_nodes!(LimitOffsetNode); -impl_from_select_nodes!(OffsetNode); -impl_from_select_nodes!(OffsetLimitNode); -impl_from_select_nodes!(ProjectNode); -impl_from_select_nodes!(OrderByNode); - -impl ExprNode { - pub fn in_list>(self, value: T) -> Self { +impl_from_select_nodes!(JoinNode<'a>); +impl_from_select_nodes!(JoinConstraintNode<'a>); +impl_from_select_nodes!(HashJoinNode<'a>); +impl_from_select_nodes!(GroupByNode<'a>); +impl_from_select_nodes!(HavingNode<'a>); +impl_from_select_nodes!(FilterNode<'a>); +impl_from_select_nodes!(LimitNode<'a>); +impl_from_select_nodes!(LimitOffsetNode<'a>); +impl_from_select_nodes!(OffsetNode<'a>); +impl_from_select_nodes!(OffsetLimitNode<'a>); +impl_from_select_nodes!(ProjectNode<'a>); +impl_from_select_nodes!(OrderByNode<'a>); + +impl<'a> ExprNode<'a> { + pub fn in_list>>(self, value: T) -> Self { Self::InList { expr: Box::new(self), list: Box::new(value.into()), @@ -65,7 +65,7 @@ impl ExprNode { } } - pub fn not_in_list>(self, value: T) -> Self { + pub fn not_in_list>>(self, value: T) -> Self { Self::InList { expr: Box::new(self), list: Box::new(value.into()), diff --git a/core/src/ast_builder/expr/is_null.rs b/core/src/ast_builder/expr/is_null.rs index 976d7918c..063d892a9 100644 --- a/core/src/ast_builder/expr/is_null.rs +++ b/core/src/ast_builder/expr/is_null.rs @@ -1,6 +1,6 @@ use super::ExprNode; -impl ExprNode { +impl<'a> ExprNode<'a> { pub fn is_null(self) -> Self { Self::IsNull(Box::new(self)) } diff --git a/core/src/ast_builder/expr/like.rs b/core/src/ast_builder/expr/like.rs index 4b2873e4c..36e0f4aa8 100644 --- a/core/src/ast_builder/expr/like.rs +++ b/core/src/ast_builder/expr/like.rs @@ -1,6 +1,6 @@ use super::ExprNode; -impl ExprNode { +impl<'a> ExprNode<'a> { pub fn like>(self, pattern: T) -> Self { Self::Like { expr: Box::new(self), diff --git a/core/src/ast_builder/expr/mod.rs b/core/src/ast_builder/expr/mod.rs index 9656382e8..7f962e224 100644 --- a/core/src/ast_builder/expr/mod.rs +++ b/core/src/ast_builder/expr/mod.rs @@ -29,79 +29,97 @@ use { bigdecimal::BigDecimal, function::FunctionNode, in_list::InListNode, + std::borrow::Cow, }; #[derive(Clone)] -pub enum ExprNode { - Expr(Expr), - SqlExpr(String), - Identifier(String), - CompoundIdentifier { - alias: String, - ident: String, +pub enum ExprNode<'a> { + Expr(Cow<'a, Expr>), + SqlExpr(Cow<'a, str>), + Identifier(Cow<'a, str>), + QuotedString(Cow<'a, str>), + TypedString { + data_type: DataType, + value: Cow<'a, str>, }, Between { - expr: Box, + expr: Box>, negated: bool, - low: Box, - high: Box, + low: Box>, + high: Box>, }, Like { - expr: Box, + expr: Box>, negated: bool, - pattern: Box, + pattern: Box>, }, ILike { - expr: Box, + expr: Box>, negated: bool, - pattern: Box, + pattern: Box>, }, BinaryOp { - left: Box, + left: Box>, op: BinaryOperator, - right: Box, + right: Box>, }, UnaryOp { op: UnaryOperator, - expr: Box, + expr: Box>, }, - IsNull(Box), - IsNotNull(Box), + IsNull(Box>), + IsNotNull(Box>), InList { - expr: Box, - list: Box, + expr: Box>, + list: Box>, negated: bool, }, - Nested(Box), - Function(Box), - Aggregate(Box), + Nested(Box>), + Function(Box>), + Aggregate(Box>), Exists { - subquery: Box, + subquery: Box>, negated: bool, }, - Subquery(Box), + Subquery(Box>), Case { - operand: Option>, - when_then: Vec<(ExprNode, ExprNode)>, - else_result: Option>, + operand: Option>>, + when_then: Vec<(ExprNode<'a>, ExprNode<'a>)>, + else_result: Option>>, }, } -impl TryFrom for Expr { +impl<'a> TryFrom> for Expr { type Error = Error; - fn try_from(expr_node: ExprNode) -> Result { + fn try_from(expr_node: ExprNode<'a>) -> Result { match expr_node { - ExprNode::Expr(expr) => Ok(expr), + ExprNode::Expr(expr) => Ok(expr.into_owned()), ExprNode::SqlExpr(expr) => { let expr = parse_expr(expr)?; translate_expr(&expr) } - ExprNode::Identifier(ident) => Ok(Expr::Identifier(ident)), - ExprNode::CompoundIdentifier { alias, ident } => { - Ok(Expr::CompoundIdentifier { alias, ident }) + ExprNode::Identifier(value) => { + let idents = value.as_ref().split('.').collect::>(); + + Ok(match idents.as_slice() { + [alias, ident] => Expr::CompoundIdentifier { + alias: alias.to_string(), + ident: ident.to_string(), + }, + _ => Expr::Identifier(value.into_owned()), + }) + } + ExprNode::QuotedString(value) => { + let value = value.into_owned(); + + Ok(Expr::Literal(AstLiteral::QuotedString(value))) } + ExprNode::TypedString { data_type, value } => Ok(Expr::TypedString { + data_type, + value: value.into_owned(), + }), ExprNode::Between { expr, negated, @@ -256,82 +274,88 @@ impl TryFrom for Expr { } } -impl From<&str> for ExprNode { - fn from(expr: &str) -> Self { - ExprNode::SqlExpr(expr.to_owned()) +impl<'a> From<&'a str> for ExprNode<'a> { + fn from(expr: &'a str) -> Self { + ExprNode::SqlExpr(Cow::Borrowed(expr)) } } -impl From for ExprNode { +impl<'a> From for ExprNode<'a> { + fn from(expr: String) -> Self { + ExprNode::SqlExpr(Cow::Owned(expr)) + } +} + +impl<'a> From for ExprNode<'a> { fn from(n: i64) -> Self { - ExprNode::Expr(Expr::Literal(AstLiteral::Number(BigDecimal::from(n)))) + ExprNode::Expr(Cow::Owned(Expr::Literal(AstLiteral::Number( + BigDecimal::from(n), + )))) } } -impl From for ExprNode { +impl<'a> From for ExprNode<'a> { fn from(b: bool) -> Self { - ExprNode::Expr(Expr::Literal(AstLiteral::Boolean(b))) + ExprNode::Expr(Cow::Owned(Expr::Literal(AstLiteral::Boolean(b)))) } } -impl From for ExprNode { - fn from(node: QueryNode) -> Self { +impl<'a> From> for ExprNode<'a> { + fn from(node: QueryNode<'a>) -> Self { ExprNode::Subquery(Box::new(node)) } } -impl From for ExprNode { +impl<'a> From for ExprNode<'a> { fn from(expr: Expr) -> Self { - ExprNode::Expr(expr) + ExprNode::Expr(Cow::Owned(expr)) } } -pub fn expr(value: &str) -> ExprNode { - ExprNode::from(value) +impl<'a> From<&'a Expr> for ExprNode<'a> { + fn from(expr: &'a Expr) -> Self { + ExprNode::Expr(Cow::Borrowed(expr)) + } } -pub fn col(value: &str) -> ExprNode { - let idents = value.split('.').collect::>(); +pub fn expr<'a, T: Into>>(value: T) -> ExprNode<'a> { + ExprNode::SqlExpr(value.into()) +} - match idents.as_slice() { - [alias, ident] => ExprNode::CompoundIdentifier { - alias: alias.to_string(), - ident: ident.to_string(), - }, - _ => ExprNode::Identifier(value.to_owned()), - } +pub fn col<'a, T: Into>>(value: T) -> ExprNode<'a> { + ExprNode::Identifier(value.into()) } -pub fn num(value: i64) -> ExprNode { +pub fn num<'a>(value: i64) -> ExprNode<'a> { ExprNode::from(value) } -pub fn text(value: &str) -> ExprNode { - ExprNode::Expr(Expr::Literal(AstLiteral::QuotedString(value.to_owned()))) +pub fn text<'a, T: Into>>(value: T) -> ExprNode<'a> { + ExprNode::QuotedString(value.into()) } -pub fn date(date: &str) -> ExprNode { - ExprNode::Expr(Expr::TypedString { +pub fn date<'a, T: Into>>(date: T) -> ExprNode<'a> { + ExprNode::TypedString { data_type: DataType::Date, - value: date.to_owned(), - }) + value: date.into(), + } } -pub fn timestamp(timestamp: &str) -> ExprNode { - ExprNode::Expr(Expr::TypedString { +pub fn timestamp<'a, T: Into>>(timestamp: T) -> ExprNode<'a> { + ExprNode::TypedString { data_type: DataType::Timestamp, - value: timestamp.to_owned(), - }) + value: timestamp.into(), + } } -pub fn time(time: &str) -> ExprNode { - ExprNode::Expr(Expr::TypedString { +pub fn time<'a, T: Into>>(time: T) -> ExprNode<'a> { + ExprNode::TypedString { data_type: DataType::Time, - value: time.to_owned(), - }) + value: time.into(), + } } -pub fn subquery>(query_node: T) -> ExprNode { +pub fn subquery<'a, T: Into>>(query_node: T) -> ExprNode<'a> { ExprNode::Subquery(Box::new(query_node.into())) } @@ -353,6 +377,10 @@ mod tests { let expected = "id IS NOT NULL"; test_expr(actual, expected); + let actual: ExprNode = String::from("1 + 10)").into(); + let expected = "1 + 10"; + test_expr(actual, expected); + let actual: ExprNode = 1024.into(); let expected = "1024"; test_expr(actual, expected); @@ -365,9 +393,13 @@ mod tests { let expected = "(SELECT id FROM Foo)"; test_expr(actual, expected); - let actual: ExprNode = Expr::Identifier("id".to_owned()).into(); + let expr = Expr::Identifier("id".to_owned()); + let actual: ExprNode = (&expr).into(); let expected = "id"; test_expr(actual, expected); + + let actual: ExprNode = expr.into(); + test_expr(actual, expected); } #[test] diff --git a/core/src/ast_builder/expr/nested.rs b/core/src/ast_builder/expr/nested.rs index 137a3b326..5fd84460b 100644 --- a/core/src/ast_builder/expr/nested.rs +++ b/core/src/ast_builder/expr/nested.rs @@ -1,12 +1,12 @@ use super::ExprNode; -impl ExprNode { +impl<'a> ExprNode<'a> { pub fn nested(self) -> Self { nested(self) } } -pub fn nested>(expr: T) -> ExprNode { +pub fn nested<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::Nested(Box::new(expr.into())) } diff --git a/core/src/ast_builder/expr/unary_op.rs b/core/src/ast_builder/expr/unary_op.rs index a72796fd1..166a3d619 100644 --- a/core/src/ast_builder/expr/unary_op.rs +++ b/core/src/ast_builder/expr/unary_op.rs @@ -1,6 +1,6 @@ use {super::ExprNode, crate::ast::UnaryOperator}; -impl ExprNode { +impl<'a> ExprNode<'a> { pub fn plus(self) -> Self { plus(self) } @@ -16,28 +16,28 @@ impl ExprNode { } } -pub fn plus>(expr: T) -> ExprNode { +pub fn plus<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::UnaryOp { op: UnaryOperator::Plus, expr: Box::new(expr.into()), } } -pub fn minus>(expr: T) -> ExprNode { +pub fn minus<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::UnaryOp { op: UnaryOperator::Minus, expr: Box::new(expr.into()), } } -pub fn not>(expr: T) -> ExprNode { +pub fn not<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::UnaryOp { op: UnaryOperator::Not, expr: Box::new(expr.into()), } } -pub fn factorial>(expr: T) -> ExprNode { +pub fn factorial<'a, T: Into>>(expr: T) -> ExprNode<'a> { ExprNode::UnaryOp { op: UnaryOperator::Factorial, expr: Box::new(expr.into()), diff --git a/core/src/ast_builder/expr_list.rs b/core/src/ast_builder/expr_list.rs index 78176290e..c2316319c 100644 --- a/core/src/ast_builder/expr_list.rs +++ b/core/src/ast_builder/expr_list.rs @@ -6,45 +6,128 @@ use { result::{Error, Result}, translate::translate_expr, }, + std::borrow::Cow, }; #[derive(Clone)] -pub enum ExprList { - Text(String), - Exprs(Vec), +pub enum ExprList<'a> { + Text(Cow<'a, str>), + Exprs(Cow<'a, [ExprNode<'a>]>), } -impl From<&str> for ExprList { - fn from(exprs: &str) -> Self { - ExprList::Text(exprs.to_owned()) +impl<'a> From<&'a str> for ExprList<'a> { + fn from(exprs: &'a str) -> Self { + ExprList::Text(Cow::Borrowed(exprs)) } } -impl From> for ExprList { - fn from(exprs: Vec) -> Self { - ExprList::Exprs(exprs) +impl<'a> From for ExprList<'a> { + fn from(exprs: String) -> Self { + ExprList::Text(Cow::from(exprs)) } } -impl From> for ExprList { - fn from(exprs: Vec<&str>) -> Self { - ExprList::Exprs(exprs.into_iter().map(Into::into).collect()) +impl<'a, T: Into>> From> for ExprList<'a> { + fn from(exprs: Vec) -> Self { + ExprList::Exprs(Cow::Owned(exprs.into_iter().map(Into::into).collect())) } } -impl TryFrom for Vec { +impl<'a, T: Into> + Copy> From<&'a Vec> for ExprList<'a> { + fn from(exprs: &'a Vec) -> Self { + exprs.as_slice().into() + } +} + +impl<'a, T: Into> + Copy> From<&'a [T]> for ExprList<'a> { + fn from(exprs: &'a [T]) -> Self { + ExprList::Exprs(Cow::Owned(exprs.iter().map(|&v| v.into()).collect())) + } +} + +impl<'a> From<&'a [ExprNode<'a>]> for ExprList<'a> { + fn from(exprs: &'a [ExprNode<'a>]) -> Self { + ExprList::Exprs(Cow::Borrowed(exprs)) + } +} + +impl<'a> TryFrom> for Vec { type Error = Error; - fn try_from(expr_list: ExprList) -> Result { + fn try_from(expr_list: ExprList<'a>) -> Result { match expr_list { ExprList::Text(exprs) => parse_comma_separated_exprs(exprs)? .iter() .map(translate_expr) .collect::>>(), - ExprList::Exprs(nodes) => nodes - .into_iter() - .map(TryInto::try_into) - .collect::>>(), + ExprList::Exprs(exprs) => { + let exprs = exprs.into_owned(); + + exprs + .into_iter() + .map(TryInto::try_into) + .collect::>>() + } } } } + +#[cfg(test)] +mod tests { + use { + super::ExprList, + crate::{ + ast::Expr, ast_builder::col, parse_sql::parse_comma_separated_exprs, result::Result, + translate::translate_expr, + }, + }; + + fn test(actual: ExprList, expected: &str) { + let parsed = parse_comma_separated_exprs(expected).expect(expected); + let expected = parsed + .iter() + .map(translate_expr) + .collect::>>(); + + assert_eq!(actual.try_into(), expected); + } + + #[test] + fn into_expr_list() { + let actual: ExprList = "1, a * 2, b".into(); + let expected = "1, a * 2, b"; + test(actual, expected); + + let actual: ExprList = String::from("'hello' || 'world', col1").into(); + let expected = "'hello' || 'world', col1"; + test(actual, expected); + + // Vec> + let actual: ExprList = vec!["id", "name"].into(); + let expected = "id, name"; + test(actual, expected); + + // Vec + let actual: ExprList = vec![col("id"), col("name")].into(); + let expected = "id, name"; + test(actual, expected); + + // &Vec + let actual = vec!["id", "name"]; + let actual: ExprList = (&actual).into(); + let expected = "id, name"; + test(actual, expected); + + // &[Into] + let actual = vec!["rate / 10", "col1"]; + let actual: ExprList = actual.as_slice().into(); + let expected = "rate / 10, col1"; + test(actual, expected); + + // &[ExprNode] + let actual = vec![col("id"), col("name")]; + let actual: ExprList = actual.as_slice().into(); + let expected = "id, name"; + test(actual, expected); + } +} diff --git a/core/src/ast_builder/index.rs b/core/src/ast_builder/index.rs index 15daa1fdf..c01ca8edc 100644 --- a/core/src/ast_builder/index.rs +++ b/core/src/ast_builder/index.rs @@ -8,14 +8,14 @@ use { use super::OrderByExprNode; #[derive(Clone)] -pub struct CreateIndexNode { +pub struct CreateIndexNode<'a> { name: String, table_name: String, - column: OrderByExprNode, + column: OrderByExprNode<'a>, } -impl CreateIndexNode { - pub fn new(table_name: String, name: String, column: OrderByExprNode) -> Self { +impl<'a> CreateIndexNode<'a> { + pub fn new(table_name: String, name: String, column: OrderByExprNode<'a>) -> Self { Self { table_name, name, @@ -24,7 +24,7 @@ impl CreateIndexNode { } } -impl Build for CreateIndexNode { +impl<'a> Build for CreateIndexNode<'a> { fn build(self) -> Result { let table_name = self.table_name; let name = self.name; diff --git a/core/src/ast_builder/insert.rs b/core/src/ast_builder/insert.rs index 31271cb56..0dc1c631f 100644 --- a/core/src/ast_builder/insert.rs +++ b/core/src/ast_builder/insert.rs @@ -25,7 +25,7 @@ impl InsertNode { self } - pub fn values>(self, values: Vec) -> InsertSourceNode { + pub fn values<'a, T: Into>>(self, values: Vec) -> InsertSourceNode<'a> { let values: Vec = values.into_iter().map(Into::into).collect(); InsertSourceNode { @@ -34,7 +34,7 @@ impl InsertNode { } } - pub fn as_select>(self, query: T) -> InsertSourceNode { + pub fn as_select<'a, T: Into>>(self, query: T) -> InsertSourceNode<'a> { InsertSourceNode { insert_node: self, source: query.into(), @@ -43,12 +43,12 @@ impl InsertNode { } #[derive(Clone)] -pub struct InsertSourceNode { +pub struct InsertSourceNode<'a> { insert_node: InsertNode, - source: QueryNode, + source: QueryNode<'a>, } -impl Build for InsertSourceNode { +impl<'a> Build for InsertSourceNode<'a> { fn build(self) -> Result { let table_name = self.insert_node.table_name; let columns = self.insert_node.columns; diff --git a/core/src/ast_builder/mod.rs b/core/src/ast_builder/mod.rs index 8fa508b3a..3babfa175 100644 --- a/core/src/ast_builder/mod.rs +++ b/core/src/ast_builder/mod.rs @@ -70,10 +70,10 @@ pub use {index::CreateIndexNode, index::DropIndexNode}; pub use expr::{ aggregate::{avg, count, max, min, stdev, sum, variance, AggregateNode}, function::{ - abs, acos, asin, atan, cast, ceil, concat, cos, degrees, divide, exp, extract, floor, - format, gcd, generate_uuid, ifnull, lcm, left, ln, log, log10, log2, lower, lpad, ltrim, - modulo, now, pi, position, power, radians, repeat, reverse, right, round, rpad, rtrim, - sign, sin, sqrt, substr, tan, to_date, to_time, to_timestamp, upper, FunctionNode, + abs, acos, asin, atan, cast, ceil, concat, concat_ws, cos, degrees, divide, exp, extract, + floor, format, gcd, generate_uuid, ifnull, lcm, left, ln, log, log10, log2, lower, lpad, + ltrim, modulo, now, pi, position, power, radians, repeat, reverse, right, round, rpad, + rtrim, sign, sin, sqrt, substr, tan, to_date, to_time, to_timestamp, upper, FunctionNode, }, }; diff --git a/core/src/ast_builder/order_by_expr.rs b/core/src/ast_builder/order_by_expr.rs index 8a83f759d..8547a94fd 100644 --- a/core/src/ast_builder/order_by_expr.rs +++ b/core/src/ast_builder/order_by_expr.rs @@ -9,27 +9,27 @@ use { }; #[derive(Clone)] -pub enum OrderByExprNode { +pub enum OrderByExprNode<'a> { Text(String), - Expr(ExprNode), + Expr(ExprNode<'a>), } -impl From<&str> for OrderByExprNode { +impl<'a> From<&str> for OrderByExprNode<'a> { fn from(expr: &str) -> Self { Self::Text(expr.to_owned()) } } -impl From for OrderByExprNode { - fn from(expr_node: ExprNode) -> Self { +impl<'a> From> for OrderByExprNode<'a> { + fn from(expr_node: ExprNode<'a>) -> Self { Self::Expr(expr_node) } } -impl TryFrom for OrderByExpr { +impl<'a> TryFrom> for OrderByExpr { type Error = Error; - fn try_from(node: OrderByExprNode) -> Result { + fn try_from(node: OrderByExprNode<'a>) -> Result { match node { OrderByExprNode::Text(expr) => { let expr = parse_order_by_expr(expr).and_then(|op| translate_order_by_expr(&op))?; diff --git a/core/src/ast_builder/order_by_expr_list.rs b/core/src/ast_builder/order_by_expr_list.rs index e241d6222..cb1ff259c 100644 --- a/core/src/ast_builder/order_by_expr_list.rs +++ b/core/src/ast_builder/order_by_expr_list.rs @@ -9,33 +9,33 @@ use { }; #[derive(Clone)] -pub enum OrderByExprList { +pub enum OrderByExprList<'a> { Text(String), - OrderByExprs(Vec), + OrderByExprs(Vec>), } -impl From<&str> for OrderByExprList { +impl<'a> From<&str> for OrderByExprList<'a> { fn from(exprs: &str) -> Self { OrderByExprList::Text(exprs.to_owned()) } } -impl From> for OrderByExprList { +impl<'a> From> for OrderByExprList<'a> { fn from(exprs: Vec<&str>) -> Self { OrderByExprList::OrderByExprs(exprs.into_iter().map(Into::into).collect()) } } -impl From for OrderByExprList { - fn from(expr_node: ExprNode) -> Self { +impl<'a> From> for OrderByExprList<'a> { + fn from(expr_node: ExprNode<'a>) -> Self { OrderByExprList::OrderByExprs(vec![expr_node.into()]) } } -impl TryFrom for Vec { +impl<'a> TryFrom> for Vec { type Error = Error; - fn try_from(order_by_exprs: OrderByExprList) -> Result { + fn try_from(order_by_exprs: OrderByExprList<'a>) -> Result { match order_by_exprs { OrderByExprList::Text(exprs) => parse_order_by_exprs(exprs)? .iter() diff --git a/core/src/ast_builder/query.rs b/core/src/ast_builder/query.rs index 5099155b9..7e56e14ca 100644 --- a/core/src/ast_builder/query.rs +++ b/core/src/ast_builder/query.rs @@ -13,41 +13,46 @@ use { }; #[derive(Clone)] -pub enum QueryNode { +pub enum QueryNode<'a> { Text(String), - Values(Vec), + Values(Vec>), SelectNode(SelectNode), - JoinNode(JoinNode), - JoinConstraintNode(JoinConstraintNode), - HashJoinNode(HashJoinNode), - GroupByNode(GroupByNode), - HavingNode(HavingNode), - LimitNode(LimitNode), - LimitOffsetNode(LimitOffsetNode), - OffsetNode(OffsetNode), - OffsetLimitNode(OffsetLimitNode), - FilterNode(FilterNode), - ProjectNode(ProjectNode), - OrderByNode(OrderByNode), + JoinNode(JoinNode<'a>), + JoinConstraintNode(JoinConstraintNode<'a>), + HashJoinNode(HashJoinNode<'a>), + GroupByNode(GroupByNode<'a>), + HavingNode(HavingNode<'a>), + LimitNode(LimitNode<'a>), + LimitOffsetNode(LimitOffsetNode<'a>), + OffsetNode(OffsetNode<'a>), + OffsetLimitNode(OffsetLimitNode<'a>), + FilterNode(FilterNode<'a>), + ProjectNode(ProjectNode<'a>), + OrderByNode(OrderByNode<'a>), } -impl From<&str> for QueryNode { +impl<'a> From<&str> for QueryNode<'a> { fn from(query: &str) -> Self { Self::Text(query.to_owned()) } } +impl<'a> From for QueryNode<'a> { + fn from(node: SelectNode) -> Self { + QueryNode::SelectNode(node) + } +} + macro_rules! impl_from_select_nodes { ($type: ident) => { - impl From<$type> for QueryNode { - fn from(node: $type) -> Self { + impl<'a> From<$type<'a>> for QueryNode<'a> { + fn from(node: $type<'a>) -> Self { QueryNode::$type(node) } } }; } -impl_from_select_nodes!(SelectNode); impl_from_select_nodes!(JoinNode); impl_from_select_nodes!(JoinConstraintNode); impl_from_select_nodes!(HashJoinNode); @@ -61,10 +66,10 @@ impl_from_select_nodes!(OffsetLimitNode); impl_from_select_nodes!(ProjectNode); impl_from_select_nodes!(OrderByNode); -impl TryFrom for Query { +impl<'a> TryFrom> for Query { type Error = Error; - fn try_from(query_node: QueryNode) -> Result { + fn try_from(query_node: QueryNode<'a>) -> Result { match query_node { QueryNode::Text(query_node) => { return parse_query(query_node).and_then(|item| translate_query(&item)); diff --git a/core/src/ast_builder/select/filter.rs b/core/src/ast_builder/select/filter.rs index de0712bcc..0135ff895 100644 --- a/core/src/ast_builder/select/filter.rs +++ b/core/src/ast_builder/select/filter.rs @@ -10,14 +10,14 @@ use { }; #[derive(Clone)] -pub enum PrevNode { +pub enum PrevNode<'a> { Select(SelectNode), - Join(Box), - JoinConstraint(Box), - HashJoin(Box), + Join(Box>), + JoinConstraint(Box>), + HashJoin(Box>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::Select(node) => node.prebuild(), @@ -28,71 +28,71 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { - fn from(node: JoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinNode<'a>) -> Self { PrevNode::Join(Box::new(node)) } } -impl From for PrevNode { - fn from(node: JoinConstraintNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinConstraintNode<'a>) -> Self { PrevNode::JoinConstraint(Box::new(node)) } } -impl From for PrevNode { - fn from(node: HashJoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HashJoinNode<'a>) -> Self { PrevNode::HashJoin(Box::new(node)) } } -impl From for PrevNode { +impl<'a> From for PrevNode<'a> { fn from(node: SelectNode) -> Self { PrevNode::Select(node) } } #[derive(Clone)] -pub struct FilterNode { - prev_node: PrevNode, - filter_expr: ExprNode, +pub struct FilterNode<'a> { + prev_node: PrevNode<'a>, + filter_expr: ExprNode<'a>, } -impl FilterNode { - pub fn new, T: Into>(prev_node: N, expr: T) -> Self { +impl<'a> FilterNode<'a> { + pub fn new>, T: Into>>(prev_node: N, expr: T) -> Self { Self { prev_node: prev_node.into(), filter_expr: expr.into(), } } - pub fn filter>(mut self, expr: T) -> Self { + pub fn filter>>(mut self, expr: T) -> Self { let exprs = self.filter_expr; self.filter_expr = exprs.and(expr); self } - pub fn offset>(self, expr: T) -> OffsetNode { + pub fn offset>>(self, expr: T) -> OffsetNode<'a> { OffsetNode::new(self, expr) } - pub fn limit>(self, expr: T) -> LimitNode { + pub fn limit>>(self, expr: T) -> LimitNode<'a> { LimitNode::new(self, expr) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } - pub fn group_by>(self, expr_list: T) -> GroupByNode { + pub fn group_by>>(self, expr_list: T) -> GroupByNode<'a> { GroupByNode::new(self, expr_list) } - pub fn order_by>(self, order_by_exprs: T) -> OrderByNode { + pub fn order_by>>(self, order_by_exprs: T) -> OrderByNode<'a> { OrderByNode::new(self, order_by_exprs) } } -impl Prebuild for FilterNode { +impl<'a> Prebuild for FilterNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.filter = Some(self.filter_expr.try_into()?); diff --git a/core/src/ast_builder/select/group_by.rs b/core/src/ast_builder/select/group_by.rs index 896c02986..eba2cd19e 100644 --- a/core/src/ast_builder/select/group_by.rs +++ b/core/src/ast_builder/select/group_by.rs @@ -11,15 +11,15 @@ use { }; #[derive(Clone)] -pub enum PrevNode { +pub enum PrevNode<'a> { Select(SelectNode), - Join(Box), - JoinConstraint(Box), - HashJoin(Box), - Filter(FilterNode), + Join(Box>), + JoinConstraint(Box>), + HashJoin(Box>), + Filter(FilterNode<'a>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::Select(node) => node.prebuild(), @@ -31,72 +31,72 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { +impl<'a> From for PrevNode<'a> { fn from(node: SelectNode) -> Self { PrevNode::Select(node) } } -impl From for PrevNode { - fn from(node: JoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinNode<'a>) -> Self { PrevNode::Join(Box::new(node)) } } -impl From for PrevNode { - fn from(node: JoinConstraintNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinConstraintNode<'a>) -> Self { PrevNode::JoinConstraint(Box::new(node)) } } -impl From for PrevNode { - fn from(node: HashJoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HashJoinNode<'a>) -> Self { PrevNode::HashJoin(Box::new(node)) } } -impl From for PrevNode { - fn from(node: FilterNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: FilterNode<'a>) -> Self { PrevNode::Filter(node) } } #[derive(Clone)] -pub struct GroupByNode { - prev_node: PrevNode, - expr_list: ExprList, +pub struct GroupByNode<'a> { + prev_node: PrevNode<'a>, + expr_list: ExprList<'a>, } -impl GroupByNode { - pub fn new, T: Into>(prev_node: N, expr_list: T) -> Self { +impl<'a> GroupByNode<'a> { + pub fn new>, T: Into>>(prev_node: N, expr_list: T) -> Self { Self { prev_node: prev_node.into(), expr_list: expr_list.into(), } } - pub fn having>(self, expr: T) -> HavingNode { + pub fn having>>(self, expr: T) -> HavingNode<'a> { HavingNode::new(self, expr) } - pub fn offset>(self, expr: T) -> OffsetNode { + pub fn offset>>(self, expr: T) -> OffsetNode<'a> { OffsetNode::new(self, expr) } - pub fn limit>(self, expr: T) -> LimitNode { + pub fn limit>>(self, expr: T) -> LimitNode<'a> { LimitNode::new(self, expr) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } - pub fn order_by>(self, expr_list: T) -> OrderByNode { + pub fn order_by>>(self, expr_list: T) -> OrderByNode<'a> { OrderByNode::new(self, expr_list) } } -impl Prebuild for GroupByNode { +impl<'a> Prebuild for GroupByNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.group_by = self.expr_list.try_into()?; diff --git a/core/src/ast_builder/select/having.rs b/core/src/ast_builder/select/having.rs index 156d7546c..a75328bcb 100644 --- a/core/src/ast_builder/select/having.rs +++ b/core/src/ast_builder/select/having.rs @@ -10,11 +10,11 @@ use { }; #[derive(Clone)] -pub enum PrevNode { - GroupBy(GroupByNode), +pub enum PrevNode<'a> { + GroupBy(GroupByNode<'a>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::GroupBy(node) => node.prebuild(), @@ -22,44 +22,44 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { - fn from(node: GroupByNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: GroupByNode<'a>) -> Self { PrevNode::GroupBy(node) } } #[derive(Clone)] -pub struct HavingNode { - prev_node: PrevNode, - expr: ExprNode, +pub struct HavingNode<'a> { + prev_node: PrevNode<'a>, + expr: ExprNode<'a>, } -impl HavingNode { - pub fn new, T: Into>(prev_node: N, expr: T) -> Self { +impl<'a> HavingNode<'a> { + pub fn new>, T: Into>>(prev_node: N, expr: T) -> Self { Self { prev_node: prev_node.into(), expr: expr.into(), } } - pub fn offset>(self, expr: T) -> OffsetNode { + pub fn offset>>(self, expr: T) -> OffsetNode<'a> { OffsetNode::new(self, expr) } - pub fn limit>(self, expr: T) -> LimitNode { + pub fn limit>>(self, expr: T) -> LimitNode<'a> { LimitNode::new(self, expr) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } - pub fn order_by>(self, expr_list: T) -> OrderByNode { + pub fn order_by>>(self, expr_list: T) -> OrderByNode<'a> { OrderByNode::new(self, expr_list) } } -impl Prebuild for HavingNode { +impl<'a> Prebuild for HavingNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.having = Some(self.expr.try_into()?); diff --git a/core/src/ast_builder/select/join/hash_join.rs b/core/src/ast_builder/select/join/hash_join.rs index 7c18da04f..1f04730f5 100644 --- a/core/src/ast_builder/select/join/hash_join.rs +++ b/core/src/ast_builder/select/join/hash_join.rs @@ -12,16 +12,16 @@ use { }; #[derive(Clone)] -pub struct HashJoinNode { - join_node: JoinNode, - key_expr: ExprNode, - value_expr: ExprNode, - filter_expr: Option, +pub struct HashJoinNode<'a> { + join_node: JoinNode<'a>, + key_expr: ExprNode<'a>, + value_expr: ExprNode<'a>, + filter_expr: Option>, } -impl HashJoinNode { - pub fn new, U: Into>( - join_node: JoinNode, +impl<'a> HashJoinNode<'a> { + pub fn new>, U: Into>>( + join_node: JoinNode<'a>, key_expr: T, value_expr: U, ) -> Self { @@ -33,7 +33,7 @@ impl HashJoinNode { } } - pub fn hash_filter>(mut self, expr: T) -> Self { + pub fn hash_filter>>(mut self, expr: T) -> Self { let expr = expr.into(); let filter_expr = match self.filter_expr { Some(filter_expr) => filter_expr.and(expr), @@ -44,15 +44,15 @@ impl HashJoinNode { self } - pub fn on>(self, expr: T) -> JoinConstraintNode { + pub fn on>>(self, expr: T) -> JoinConstraintNode<'a> { JoinConstraintNode::new(self, expr) } - pub fn join(self, table_name: &str) -> JoinNode { + pub fn join(self, table_name: &str) -> JoinNode<'a> { JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Inner) } - pub fn join_as(self, table_name: &str, alias: &str) -> JoinNode { + pub fn join_as(self, table_name: &str, alias: &str) -> JoinNode<'a> { JoinNode::new( self, table_name.to_owned(), @@ -61,11 +61,11 @@ impl HashJoinNode { ) } - pub fn left_join(self, table_name: &str) -> JoinNode { + pub fn left_join(self, table_name: &str) -> JoinNode<'a> { JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Left) } - pub fn left_join_as(self, table_name: &str, alias: &str) -> JoinNode { + pub fn left_join_as(self, table_name: &str, alias: &str) -> JoinNode<'a> { JoinNode::new( self, table_name.to_owned(), @@ -74,27 +74,27 @@ impl HashJoinNode { ) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } - pub fn group_by>(self, expr_list: T) -> GroupByNode { + pub fn group_by>>(self, expr_list: T) -> GroupByNode<'a> { GroupByNode::new(self, expr_list) } - pub fn offset>(self, expr: T) -> OffsetNode { + pub fn offset>>(self, expr: T) -> OffsetNode<'a> { OffsetNode::new(self, expr) } - pub fn limit>(self, expr: T) -> LimitNode { + pub fn limit>>(self, expr: T) -> LimitNode<'a> { LimitNode::new(self, expr) } - pub fn filter>(self, expr: T) -> FilterNode { + pub fn filter>>(self, expr: T) -> FilterNode<'a> { FilterNode::new(self, expr) } - pub fn order_by>(self, order_by_exprs: T) -> OrderByNode { + pub fn order_by>>(self, order_by_exprs: T) -> OrderByNode<'a> { OrderByNode::new(self, order_by_exprs) } @@ -108,7 +108,7 @@ impl HashJoinNode { } } -impl Prebuild for HashJoinNode { +impl<'a> Prebuild for HashJoinNode<'a> { fn prebuild(self) -> Result { let (mut select_data, relation, join_operator) = self.join_node.prebuild_for_hash_join()?; let join_executor = build_join_executor(self.key_expr, self.value_expr, self.filter_expr)?; diff --git a/core/src/ast_builder/select/join/join_constraint.rs b/core/src/ast_builder/select/join/join_constraint.rs index a9c37a68f..547083fd2 100644 --- a/core/src/ast_builder/select/join/join_constraint.rs +++ b/core/src/ast_builder/select/join/join_constraint.rs @@ -12,12 +12,12 @@ use { }; #[derive(Clone)] -pub enum PrevNode { - Join(JoinNode), - HashJoin(HashJoinNode), +pub enum PrevNode<'a> { + Join(Box>), + HashJoin(Box>), } -impl PrevNode { +impl<'a> PrevNode<'a> { fn prebuild_for_constraint(self) -> Result { match self { PrevNode::Join(node) => node.prebuild_for_constraint(), @@ -26,37 +26,37 @@ impl PrevNode { } } -impl From for PrevNode { - fn from(node: JoinNode) -> Self { - PrevNode::Join(node) +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinNode<'a>) -> Self { + PrevNode::Join(Box::new(node)) } } -impl From for PrevNode { - fn from(node: HashJoinNode) -> Self { - PrevNode::HashJoin(node) +impl<'a> From> for PrevNode<'a> { + fn from(node: HashJoinNode<'a>) -> Self { + PrevNode::HashJoin(Box::new(node)) } } #[derive(Clone)] -pub struct JoinConstraintNode { - prev_node: PrevNode, - expr: ExprNode, +pub struct JoinConstraintNode<'a> { + prev_node: PrevNode<'a>, + expr: ExprNode<'a>, } -impl JoinConstraintNode { - pub fn new, T: Into>(prev_node: N, expr: T) -> Self { +impl<'a> JoinConstraintNode<'a> { + pub fn new>, T: Into>>(prev_node: N, expr: T) -> Self { Self { prev_node: prev_node.into(), expr: expr.into(), } } - pub fn join(self, table_name: &str) -> JoinNode { + pub fn join(self, table_name: &str) -> JoinNode<'a> { JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Inner) } - pub fn join_as(self, table_name: &str, alias: &str) -> JoinNode { + pub fn join_as(self, table_name: &str, alias: &str) -> JoinNode<'a> { JoinNode::new( self, table_name.to_owned(), @@ -65,11 +65,11 @@ impl JoinConstraintNode { ) } - pub fn left_join(self, table_name: &str) -> JoinNode { + pub fn left_join(self, table_name: &str) -> JoinNode<'a> { JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Left) } - pub fn left_join_as(self, table_name: &str, alias: &str) -> JoinNode { + pub fn left_join_as(self, table_name: &str, alias: &str) -> JoinNode<'a> { JoinNode::new( self, table_name.to_owned(), @@ -78,32 +78,32 @@ impl JoinConstraintNode { ) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } - pub fn group_by>(self, expr_list: T) -> GroupByNode { + pub fn group_by>>(self, expr_list: T) -> GroupByNode<'a> { GroupByNode::new(self, expr_list) } - pub fn offset>(self, expr: T) -> OffsetNode { + pub fn offset>>(self, expr: T) -> OffsetNode<'a> { OffsetNode::new(self, expr) } - pub fn limit>(self, expr: T) -> LimitNode { + pub fn limit>>(self, expr: T) -> LimitNode<'a> { LimitNode::new(self, expr) } - pub fn filter>(self, expr: T) -> FilterNode { + pub fn filter>>(self, expr: T) -> FilterNode<'a> { FilterNode::new(self, expr) } - pub fn order_by>(self, order_by_exprs: T) -> OrderByNode { + pub fn order_by>>(self, order_by_exprs: T) -> OrderByNode<'a> { OrderByNode::new(self, order_by_exprs) } } -impl Prebuild for JoinConstraintNode { +impl<'a> Prebuild for JoinConstraintNode<'a> { fn prebuild(self) -> Result { let JoinConstraintData { mut node_data, diff --git a/core/src/ast_builder/select/join/root.rs b/core/src/ast_builder/select/join/root.rs index d74beb34e..e05af7e23 100644 --- a/core/src/ast_builder/select/join/root.rs +++ b/core/src/ast_builder/select/join/root.rs @@ -13,14 +13,14 @@ use { }; #[derive(Clone)] -pub enum PrevNode { +pub enum PrevNode<'a> { Select(SelectNode), - Join(Box), - JoinConstraint(Box), - HashJoin(Box), + Join(Box>), + JoinConstraint(Box>), + HashJoin(Box>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::Select(node) => node.prebuild(), @@ -31,39 +31,39 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { +impl<'a> From for PrevNode<'a> { fn from(node: SelectNode) -> Self { PrevNode::Select(node) } } -impl From for PrevNode { - fn from(node: JoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinNode<'a>) -> Self { PrevNode::Join(Box::new(node)) } } -impl From for PrevNode { - fn from(node: JoinConstraintNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinConstraintNode<'a>) -> Self { PrevNode::JoinConstraint(Box::new(node)) } } -impl From for PrevNode { - fn from(node: HashJoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HashJoinNode<'a>) -> Self { PrevNode::HashJoin(Box::new(node)) } } #[derive(Clone)] -pub struct JoinNode { - prev_node: PrevNode, +pub struct JoinNode<'a> { + prev_node: PrevNode<'a>, relation: TableFactor, join_operator_type: JoinOperatorType, } -impl JoinNode { - pub fn new>( +impl<'a> JoinNode<'a> { + pub fn new>>( prev_node: N, name: String, alias: Option, @@ -90,15 +90,15 @@ impl JoinNode { } } - pub fn on>(self, expr: T) -> JoinConstraintNode { + pub fn on>>(self, expr: T) -> JoinConstraintNode<'a> { JoinConstraintNode::new(self, expr) } - pub fn join(self, table_name: &str) -> JoinNode { + pub fn join(self, table_name: &str) -> JoinNode<'a> { JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Inner) } - pub fn join_as(self, table_name: &str, alias: &str) -> JoinNode { + pub fn join_as(self, table_name: &str, alias: &str) -> JoinNode<'a> { JoinNode::new( self, table_name.to_owned(), @@ -107,11 +107,11 @@ impl JoinNode { ) } - pub fn left_join(self, table_name: &str) -> JoinNode { + pub fn left_join(self, table_name: &str) -> JoinNode<'a> { JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Left) } - pub fn left_join_as(self, table_name: &str, alias: &str) -> JoinNode { + pub fn left_join_as(self, table_name: &str, alias: &str) -> JoinNode<'a> { JoinNode::new( self, table_name.to_owned(), @@ -120,35 +120,35 @@ impl JoinNode { ) } - pub fn hash_executor, U: Into>( + pub fn hash_executor>, U: Into>>( self, key_expr: T, value_expr: U, - ) -> HashJoinNode { + ) -> HashJoinNode<'a> { HashJoinNode::new(self, key_expr, value_expr) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } - pub fn group_by>(self, expr_list: T) -> GroupByNode { + pub fn group_by>>(self, expr_list: T) -> GroupByNode<'a> { GroupByNode::new(self, expr_list) } - pub fn offset>(self, expr: T) -> OffsetNode { + pub fn offset>>(self, expr: T) -> OffsetNode<'a> { OffsetNode::new(self, expr) } - pub fn limit>(self, expr: T) -> LimitNode { + pub fn limit>>(self, expr: T) -> LimitNode<'a> { LimitNode::new(self, expr) } - pub fn filter>(self, expr: T) -> FilterNode { + pub fn filter>>(self, expr: T) -> FilterNode<'a> { FilterNode::new(self, expr) } - pub fn order_by>(self, order_by_exprs: T) -> OrderByNode { + pub fn order_by>>(self, order_by_exprs: T) -> OrderByNode<'a> { OrderByNode::new(self, order_by_exprs) } @@ -169,7 +169,7 @@ impl JoinNode { } } -impl Prebuild for JoinNode { +impl<'a> Prebuild for JoinNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.joins.push(Join { diff --git a/core/src/ast_builder/select/limit.rs b/core/src/ast_builder/select/limit.rs index 8a9d0c80f..7b9526680 100644 --- a/core/src/ast_builder/select/limit.rs +++ b/core/src/ast_builder/select/limit.rs @@ -10,18 +10,18 @@ use { }; #[derive(Clone)] -pub enum PrevNode { +pub enum PrevNode<'a> { Select(SelectNode), - GroupBy(GroupByNode), - Having(HavingNode), - Join(Box), - JoinConstraint(Box), - HashJoin(HashJoinNode), - Filter(FilterNode), - OrderBy(OrderByNode), + GroupBy(GroupByNode<'a>), + Having(HavingNode<'a>), + Join(Box>), + JoinConstraint(Box>), + HashJoin(HashJoinNode<'a>), + Filter(FilterNode<'a>), + OrderBy(OrderByNode<'a>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::Select(node) => node.prebuild(), @@ -36,78 +36,78 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { +impl<'a> From for PrevNode<'a> { fn from(node: SelectNode) -> Self { PrevNode::Select(node) } } -impl From for PrevNode { - fn from(node: GroupByNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: GroupByNode<'a>) -> Self { PrevNode::GroupBy(node) } } -impl From for PrevNode { - fn from(node: HavingNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HavingNode<'a>) -> Self { PrevNode::Having(node) } } -impl From for PrevNode { - fn from(node: JoinConstraintNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinConstraintNode<'a>) -> Self { PrevNode::JoinConstraint(Box::new(node)) } } -impl From for PrevNode { - fn from(node: JoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinNode<'a>) -> Self { PrevNode::Join(Box::new(node)) } } -impl From for PrevNode { - fn from(node: HashJoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HashJoinNode<'a>) -> Self { PrevNode::HashJoin(node) } } -impl From for PrevNode { - fn from(node: FilterNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: FilterNode<'a>) -> Self { PrevNode::Filter(node) } } -impl From for PrevNode { - fn from(node: OrderByNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: OrderByNode<'a>) -> Self { PrevNode::OrderBy(node) } } #[derive(Clone)] -pub struct LimitNode { - prev_node: PrevNode, - expr: ExprNode, +pub struct LimitNode<'a> { + prev_node: PrevNode<'a>, + expr: ExprNode<'a>, } -impl LimitNode { - pub fn new, T: Into>(prev_node: N, expr: T) -> Self { +impl<'a> LimitNode<'a> { + pub fn new>, T: Into>>(prev_node: N, expr: T) -> Self { Self { prev_node: prev_node.into(), expr: expr.into(), } } - pub fn offset>(self, expr: T) -> LimitOffsetNode { + pub fn offset>>(self, expr: T) -> LimitOffsetNode<'a> { LimitOffsetNode::new(self, expr) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } } -impl Prebuild for LimitNode { +impl<'a> Prebuild for LimitNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.limit = Some(self.expr.try_into()?); diff --git a/core/src/ast_builder/select/limit_offset.rs b/core/src/ast_builder/select/limit_offset.rs index ef5a8b6bc..0436d5db8 100644 --- a/core/src/ast_builder/select/limit_offset.rs +++ b/core/src/ast_builder/select/limit_offset.rs @@ -7,11 +7,11 @@ use { }; #[derive(Clone)] -pub enum PrevNode { - Limit(LimitNode), +pub enum PrevNode<'a> { + Limit(LimitNode<'a>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::Limit(node) => node.prebuild(), @@ -19,32 +19,32 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { - fn from(node: LimitNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: LimitNode<'a>) -> Self { PrevNode::Limit(node) } } #[derive(Clone)] -pub struct LimitOffsetNode { - prev_node: PrevNode, - expr: ExprNode, +pub struct LimitOffsetNode<'a> { + prev_node: PrevNode<'a>, + expr: ExprNode<'a>, } -impl LimitOffsetNode { - pub fn new, T: Into>(prev_node: N, expr: T) -> Self { +impl<'a> LimitOffsetNode<'a> { + pub fn new>, T: Into>>(prev_node: N, expr: T) -> Self { Self { prev_node: prev_node.into(), expr: expr.into(), } } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } } -impl Prebuild for LimitOffsetNode { +impl<'a> Prebuild for LimitOffsetNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.offset = Some(self.expr.try_into()?); diff --git a/core/src/ast_builder/select/offset.rs b/core/src/ast_builder/select/offset.rs index f9639c945..67548fb87 100644 --- a/core/src/ast_builder/select/offset.rs +++ b/core/src/ast_builder/select/offset.rs @@ -10,18 +10,18 @@ use { }; #[derive(Clone)] -pub enum PrevNode { +pub enum PrevNode<'a> { Select(SelectNode), - GroupBy(GroupByNode), - Having(HavingNode), - Join(Box), - JoinConstraint(Box), - HashJoin(HashJoinNode), - Filter(FilterNode), - OrderBy(OrderByNode), + GroupBy(GroupByNode<'a>), + Having(HavingNode<'a>), + Join(Box>), + JoinConstraint(Box>), + HashJoin(HashJoinNode<'a>), + Filter(FilterNode<'a>), + OrderBy(OrderByNode<'a>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::Select(node) => node.prebuild(), @@ -36,78 +36,78 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { +impl<'a> From for PrevNode<'a> { fn from(node: SelectNode) -> Self { PrevNode::Select(node) } } -impl From for PrevNode { - fn from(node: GroupByNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: GroupByNode<'a>) -> Self { PrevNode::GroupBy(node) } } -impl From for PrevNode { - fn from(node: HavingNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HavingNode<'a>) -> Self { PrevNode::Having(node) } } -impl From for PrevNode { - fn from(node: JoinConstraintNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinConstraintNode<'a>) -> Self { PrevNode::JoinConstraint(Box::new(node)) } } -impl From for PrevNode { - fn from(node: JoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinNode<'a>) -> Self { PrevNode::Join(Box::new(node)) } } -impl From for PrevNode { - fn from(node: HashJoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HashJoinNode<'a>) -> Self { PrevNode::HashJoin(node) } } -impl From for PrevNode { - fn from(node: FilterNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: FilterNode<'a>) -> Self { PrevNode::Filter(node) } } -impl From for PrevNode { - fn from(node: OrderByNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: OrderByNode<'a>) -> Self { PrevNode::OrderBy(node) } } #[derive(Clone)] -pub struct OffsetNode { - prev_node: PrevNode, - expr: ExprNode, +pub struct OffsetNode<'a> { + prev_node: PrevNode<'a>, + expr: ExprNode<'a>, } -impl OffsetNode { - pub fn new, T: Into>(prev_node: N, expr: T) -> Self { +impl<'a> OffsetNode<'a> { + pub fn new>, T: Into>>(prev_node: N, expr: T) -> Self { Self { prev_node: prev_node.into(), expr: expr.into(), } } - pub fn limit>(self, expr: T) -> OffsetLimitNode { + pub fn limit>>(self, expr: T) -> OffsetLimitNode<'a> { OffsetLimitNode::new(self, expr) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } } -impl Prebuild for OffsetNode { +impl<'a> Prebuild for OffsetNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.offset = Some(self.expr.try_into()?); diff --git a/core/src/ast_builder/select/offset_limit.rs b/core/src/ast_builder/select/offset_limit.rs index ef02a54c8..61a57969c 100644 --- a/core/src/ast_builder/select/offset_limit.rs +++ b/core/src/ast_builder/select/offset_limit.rs @@ -7,11 +7,11 @@ use { }; #[derive(Clone)] -pub enum PrevNode { - Offset(OffsetNode), +pub enum PrevNode<'a> { + Offset(OffsetNode<'a>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::Offset(node) => node.prebuild(), @@ -19,32 +19,32 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { - fn from(node: OffsetNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: OffsetNode<'a>) -> Self { PrevNode::Offset(node) } } #[derive(Clone)] -pub struct OffsetLimitNode { - prev_node: PrevNode, - expr: ExprNode, +pub struct OffsetLimitNode<'a> { + prev_node: PrevNode<'a>, + expr: ExprNode<'a>, } -impl OffsetLimitNode { - pub fn new, T: Into>(prev_node: N, expr: T) -> Self { +impl<'a> OffsetLimitNode<'a> { + pub fn new>, T: Into>>(prev_node: N, expr: T) -> Self { Self { prev_node: prev_node.into(), expr: expr.into(), } } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } } -impl Prebuild for OffsetLimitNode { +impl<'a> Prebuild for OffsetLimitNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.limit = Some(self.expr.try_into()?); diff --git a/core/src/ast_builder/select/order_by.rs b/core/src/ast_builder/select/order_by.rs index 7aa3bb926..c795598ee 100644 --- a/core/src/ast_builder/select/order_by.rs +++ b/core/src/ast_builder/select/order_by.rs @@ -11,17 +11,17 @@ use { }; #[derive(Clone)] -pub enum PrevNode { +pub enum PrevNode<'a> { Select(SelectNode), - Having(HavingNode), - GroupBy(GroupByNode), - Filter(FilterNode), - JoinNode(JoinNode), - JoinConstraint(JoinConstraintNode), - HashJoin(HashJoinNode), + Having(HavingNode<'a>), + GroupBy(GroupByNode<'a>), + Filter(FilterNode<'a>), + JoinNode(JoinNode<'a>), + JoinConstraint(JoinConstraintNode<'a>), + HashJoin(Box>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::Select(node) => node.prebuild(), @@ -35,76 +35,79 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { +impl<'a> From for PrevNode<'a> { fn from(node: SelectNode) -> Self { PrevNode::Select(node) } } -impl From for PrevNode { - fn from(node: HavingNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HavingNode<'a>) -> Self { PrevNode::Having(node) } } -impl From for PrevNode { - fn from(node: GroupByNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: GroupByNode<'a>) -> Self { PrevNode::GroupBy(node) } } -impl From for PrevNode { - fn from(node: FilterNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: FilterNode<'a>) -> Self { PrevNode::Filter(node) } } -impl From for PrevNode { - fn from(node: JoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinNode<'a>) -> Self { PrevNode::JoinNode(node) } } -impl From for PrevNode { - fn from(node: JoinConstraintNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinConstraintNode<'a>) -> Self { PrevNode::JoinConstraint(node) } } -impl From for PrevNode { - fn from(node: HashJoinNode) -> Self { - PrevNode::HashJoin(node) +impl<'a> From> for PrevNode<'a> { + fn from(node: HashJoinNode<'a>) -> Self { + PrevNode::HashJoin(Box::new(node)) } } #[derive(Clone)] -pub struct OrderByNode { - prev_node: PrevNode, - expr_list: OrderByExprList, +pub struct OrderByNode<'a> { + prev_node: PrevNode<'a>, + expr_list: OrderByExprList<'a>, } -impl OrderByNode { - pub fn new, T: Into>(prev_node: N, expr_list: T) -> Self { +impl<'a> OrderByNode<'a> { + pub fn new>, T: Into>>( + prev_node: N, + expr_list: T, + ) -> Self { Self { prev_node: prev_node.into(), expr_list: expr_list.into(), } } - pub fn offset>(self, expr: T) -> OffsetNode { + pub fn offset>>(self, expr: T) -> OffsetNode<'a> { OffsetNode::new(self, expr) } - pub fn limit>(self, expr: T) -> LimitNode { + pub fn limit>>(self, expr: T) -> LimitNode<'a> { LimitNode::new(self, expr) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } } -impl Prebuild for OrderByNode { +impl<'a> Prebuild for OrderByNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.order_by = self.expr_list.try_into()?; @@ -164,7 +167,7 @@ mod tests { .select() .group_by("city") .having("COUNT(name) < 100") - .order_by(ExprNode::Identifier("name".to_owned())) + .order_by(ExprNode::Identifier("name".into())) .limit(3) .offset(2) .build(); diff --git a/core/src/ast_builder/select/project.rs b/core/src/ast_builder/select/project.rs index cfd87eb80..fbf0d262d 100644 --- a/core/src/ast_builder/select/project.rs +++ b/core/src/ast_builder/select/project.rs @@ -11,22 +11,22 @@ use { }; #[derive(Clone)] -pub enum PrevNode { +pub enum PrevNode<'a> { Select(SelectNode), - GroupBy(GroupByNode), - Having(HavingNode), - Limit(LimitNode), - LimitOffset(LimitOffsetNode), - Offset(OffsetNode), - OffsetLimit(OffsetLimitNode), - Join(Box), - JoinConstraint(Box), - HashJoin(HashJoinNode), - Filter(FilterNode), - OrderBy(OrderByNode), + GroupBy(GroupByNode<'a>), + Having(HavingNode<'a>), + Limit(LimitNode<'a>), + LimitOffset(LimitOffsetNode<'a>), + Offset(OffsetNode<'a>), + OffsetLimit(OffsetLimitNode<'a>), + Join(Box>), + JoinConstraint(Box>), + HashJoin(HashJoinNode<'a>), + Filter(FilterNode<'a>), + OrderBy(OrderByNode<'a>), } -impl Prebuild for PrevNode { +impl<'a> Prebuild for PrevNode<'a> { fn prebuild(self) -> Result { match self { Self::Select(node) => node.prebuild(), @@ -45,100 +45,103 @@ impl Prebuild for PrevNode { } } -impl From for PrevNode { +impl<'a> From for PrevNode<'a> { fn from(node: SelectNode) -> Self { PrevNode::Select(node) } } -impl From for PrevNode { - fn from(node: GroupByNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: GroupByNode<'a>) -> Self { PrevNode::GroupBy(node) } } -impl From for PrevNode { - fn from(node: HavingNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HavingNode<'a>) -> Self { PrevNode::Having(node) } } -impl From for PrevNode { - fn from(node: LimitNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: LimitNode<'a>) -> Self { PrevNode::Limit(node) } } -impl From for PrevNode { - fn from(node: LimitOffsetNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: LimitOffsetNode<'a>) -> Self { PrevNode::LimitOffset(node) } } -impl From for PrevNode { - fn from(node: OffsetNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: OffsetNode<'a>) -> Self { PrevNode::Offset(node) } } -impl From for PrevNode { - fn from(node: OffsetLimitNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: OffsetLimitNode<'a>) -> Self { PrevNode::OffsetLimit(node) } } -impl From for PrevNode { - fn from(node: JoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinNode<'a>) -> Self { PrevNode::Join(Box::new(node)) } } -impl From for PrevNode { - fn from(node: JoinConstraintNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: JoinConstraintNode<'a>) -> Self { PrevNode::JoinConstraint(Box::new(node)) } } -impl From for PrevNode { - fn from(node: HashJoinNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: HashJoinNode<'a>) -> Self { PrevNode::HashJoin(node) } } -impl From for PrevNode { - fn from(node: FilterNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: FilterNode<'a>) -> Self { PrevNode::Filter(node) } } -impl From for PrevNode { - fn from(node: OrderByNode) -> Self { +impl<'a> From> for PrevNode<'a> { + fn from(node: OrderByNode<'a>) -> Self { PrevNode::OrderBy(node) } } #[derive(Clone)] -pub struct ProjectNode { - prev_node: PrevNode, - select_items_list: Vec, +pub struct ProjectNode<'a> { + prev_node: PrevNode<'a>, + select_items_list: Vec>, } -impl ProjectNode { - pub fn new, T: Into>(prev_node: N, select_items: T) -> Self { +impl<'a> ProjectNode<'a> { + pub fn new>, T: Into>>( + prev_node: N, + select_items: T, + ) -> Self { Self { prev_node: prev_node.into(), select_items_list: vec![select_items.into()], } } - pub fn project>(mut self, select_items: T) -> Self { + pub fn project>>(mut self, select_items: T) -> Self { self.select_items_list.push(select_items.into()); self } } -impl Prebuild for ProjectNode { +impl<'a> Prebuild for ProjectNode<'a> { fn prebuild(self) -> Result { let mut select_data = self.prev_node.prebuild()?; select_data.projection = self diff --git a/core/src/ast_builder/select/root.rs b/core/src/ast_builder/select/root.rs index 17543eb12..98db86d1b 100644 --- a/core/src/ast_builder/select/root.rs +++ b/core/src/ast_builder/select/root.rs @@ -24,35 +24,35 @@ impl SelectNode { } } - pub fn filter>(self, expr: T) -> FilterNode { + pub fn filter<'a, T: Into>>(self, expr: T) -> FilterNode<'a> { FilterNode::new(self, expr) } - pub fn group_by>(self, expr_list: T) -> GroupByNode { + pub fn group_by<'a, T: Into>>(self, expr_list: T) -> GroupByNode<'a> { GroupByNode::new(self, expr_list) } - pub fn offset>(self, expr: T) -> OffsetNode { + pub fn offset<'a, T: Into>>(self, expr: T) -> OffsetNode<'a> { OffsetNode::new(self, expr) } - pub fn limit>(self, expr: T) -> LimitNode { + pub fn limit<'a, T: Into>>(self, expr: T) -> LimitNode<'a> { LimitNode::new(self, expr) } - pub fn project>(self, select_items: T) -> ProjectNode { + pub fn project<'a, T: Into>>(self, select_items: T) -> ProjectNode<'a> { ProjectNode::new(self, select_items) } - pub fn order_by>(self, order_by_exprs: T) -> OrderByNode { + pub fn order_by<'a, T: Into>>(self, order_by_exprs: T) -> OrderByNode<'a> { OrderByNode::new(self, order_by_exprs) } - pub fn join(self, table_name: &str) -> JoinNode { + pub fn join<'a>(self, table_name: &str) -> JoinNode<'a> { JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Inner) } - pub fn join_as(self, table_name: &str, alias: &str) -> JoinNode { + pub fn join_as<'a>(self, table_name: &str, alias: &str) -> JoinNode<'a> { JoinNode::new( self, table_name.to_owned(), @@ -61,11 +61,11 @@ impl SelectNode { ) } - pub fn left_join(self, table_name: &str) -> JoinNode { + pub fn left_join<'a>(self, table_name: &str) -> JoinNode<'a> { JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Left) } - pub fn left_join_as(self, table_name: &str, alias: &str) -> JoinNode { + pub fn left_join_as<'a>(self, table_name: &str, alias: &str) -> JoinNode<'a> { JoinNode::new( self, table_name.to_owned(), diff --git a/core/src/ast_builder/select_item.rs b/core/src/ast_builder/select_item.rs index b2e8018b6..fa6ac2e7a 100644 --- a/core/src/ast_builder/select_item.rs +++ b/core/src/ast_builder/select_item.rs @@ -9,34 +9,34 @@ use { }; #[derive(Clone)] -pub enum SelectItemNode { +pub enum SelectItemNode<'a> { SelectItem(SelectItem), - Expr(ExprNode), + Expr(ExprNode<'a>), Text(String), } -impl From for SelectItemNode { +impl<'a> From for SelectItemNode<'a> { fn from(select_item: SelectItem) -> Self { Self::SelectItem(select_item) } } -impl From for SelectItemNode { - fn from(expr_node: ExprNode) -> Self { +impl<'a> From> for SelectItemNode<'a> { + fn from(expr_node: ExprNode<'a>) -> Self { Self::Expr(expr_node) } } -impl From<&str> for SelectItemNode { +impl<'a> From<&str> for SelectItemNode<'a> { fn from(select_item: &str) -> Self { Self::Text(select_item.to_owned()) } } -impl TryFrom for SelectItem { +impl<'a> TryFrom> for SelectItem { type Error = Error; - fn try_from(select_item_node: SelectItemNode) -> Result { + fn try_from(select_item_node: SelectItemNode<'a>) -> Result { match select_item_node { SelectItemNode::SelectItem(select_item) => Ok(select_item), SelectItemNode::Text(select_item) => { diff --git a/core/src/ast_builder/select_item_list.rs b/core/src/ast_builder/select_item_list.rs index 1c253901a..51da4c49a 100644 --- a/core/src/ast_builder/select_item_list.rs +++ b/core/src/ast_builder/select_item_list.rs @@ -9,39 +9,39 @@ use { }; #[derive(Clone)] -pub enum SelectItemList { +pub enum SelectItemList<'a> { Text(String), - SelectItems(Vec), + SelectItems(Vec>), } -impl From<&str> for SelectItemList { +impl<'a> From<&str> for SelectItemList<'a> { fn from(exprs: &str) -> Self { SelectItemList::Text(exprs.to_owned()) } } -impl From> for SelectItemList { +impl<'a> From> for SelectItemList<'a> { fn from(select_items: Vec<&str>) -> Self { SelectItemList::SelectItems(select_items.into_iter().map(Into::into).collect()) } } -impl From for SelectItemList { - fn from(expr_node: ExprNode) -> Self { +impl<'a> From> for SelectItemList<'a> { + fn from(expr_node: ExprNode<'a>) -> Self { SelectItemList::SelectItems(vec![expr_node.into()]) } } -impl From> for SelectItemList { - fn from(expr_nodes: Vec) -> Self { +impl<'a> From>> for SelectItemList<'a> { + fn from(expr_nodes: Vec>) -> Self { SelectItemList::SelectItems(expr_nodes.into_iter().map(Into::into).collect()) } } -impl TryFrom for Vec { +impl<'a> TryFrom> for Vec { type Error = Error; - fn try_from(select_items: SelectItemList) -> Result { + fn try_from(select_items: SelectItemList<'a>) -> Result { match select_items { SelectItemList::Text(items) => parse_select_items(items)? .iter() diff --git a/core/src/ast_builder/table.rs b/core/src/ast_builder/table.rs index 9af176b4d..ef3d0c32c 100644 --- a/core/src/ast_builder/table.rs +++ b/core/src/ast_builder/table.rs @@ -25,7 +25,7 @@ impl TableNode { SelectNode::new(self.table_name, None) } - pub fn delete(self) -> DeleteNode { + pub fn delete(self) -> DeleteNode<'static> { DeleteNode::new(self.table_name) } @@ -35,7 +35,11 @@ impl TableNode { } #[cfg(feature = "index")] - pub fn create_index>(self, name: &str, column: T) -> CreateIndexNode { + pub fn create_index<'a, T: Into>>( + self, + name: &str, + column: T, + ) -> CreateIndexNode<'a> { CreateIndexNode::new(self.table_name, name.to_owned(), column.into()) } @@ -64,7 +68,7 @@ impl TableNode { DropTableNode::new(self.table_name, true) } - pub fn update(self) -> UpdateNode { + pub fn update(self) -> UpdateNode<'static> { UpdateNode::new(self.table_name) } diff --git a/core/src/ast_builder/update.rs b/core/src/ast_builder/update.rs index 86c0ee6ed..2e71c7302 100644 --- a/core/src/ast_builder/update.rs +++ b/core/src/ast_builder/update.rs @@ -7,13 +7,13 @@ use { }; #[derive(Clone)] -pub struct UpdateNode { +pub struct UpdateNode<'a> { table_name: String, - assignments: Vec, - selection: Option, + assignments: Vec>, + selection: Option>, } -impl UpdateNode { +impl<'a> UpdateNode<'a> { pub fn new(table_name: String) -> Self { Self { table_name, @@ -22,19 +22,19 @@ impl UpdateNode { } } - pub fn filter>(mut self, expr: T) -> Self { + pub fn filter>>(mut self, expr: T) -> Self { self.selection = Some(expr.into()); self } - pub fn set>(mut self, id: &str, value: T) -> Self { + pub fn set>>(mut self, id: &str, value: T) -> Self { self.assignments .push(AssignmentNode::Expr(id.to_owned(), value.into())); self } } -impl Build for UpdateNode { +impl<'a> Build for UpdateNode<'a> { fn build(self) -> Result { let table_name = self.table_name; let selection = self.selection.map(Expr::try_from).transpose()?; diff --git a/core/src/data/bigdecimal_ext.rs b/core/src/data/bigdecimal_ext.rs index c770569d0..a6ff78d15 100644 --- a/core/src/data/bigdecimal_ext.rs +++ b/core/src/data/bigdecimal_ext.rs @@ -7,6 +7,7 @@ pub trait BigDecimalExt { fn to_i64(&self) -> Option; fn to_i128(&self) -> Option; fn to_u8(&self) -> Option; + fn to_u16(&self) -> Option; fn to_f64(&self) -> Option; } @@ -39,6 +40,10 @@ impl BigDecimalExt for BigDecimal { self.is_integer() .then(|| bigdecimal::ToPrimitive::to_u8(self))? } + fn to_u16(&self) -> Option { + self.is_integer() + .then(|| bigdecimal::ToPrimitive::to_u16(self))? + } fn to_f64(&self) -> Option { bigdecimal::ToPrimitive::to_f64(self) } diff --git a/core/src/data/interval/primitive.rs b/core/src/data/interval/primitive.rs index 1a5c00f92..84cd28ad6 100644 --- a/core/src/data/interval/primitive.rs +++ b/core/src/data/interval/primitive.rs @@ -69,6 +69,16 @@ impl Mul for Interval { } } +impl Mul for Interval { + type Output = Self; + + fn mul(self, rhs: u16) -> Self { + match self { + Interval::Month(v) => Interval::Month(((v as u16) * rhs) as i32), + Interval::Microsecond(v) => Interval::Microsecond(((v as u16) * rhs) as i64), + } + } +} impl Mul for Interval { type Output = Self; @@ -128,6 +138,14 @@ impl Mul for u8 { } } +impl Mul for u16 { + type Output = Interval; + + fn mul(self, rhs: Interval) -> Interval { + rhs * self + } +} + impl Mul for f64 { type Output = Interval; @@ -202,6 +220,17 @@ impl Div for Interval { } } +impl Div for Interval { + type Output = Self; + + fn div(self, rhs: u16) -> Self { + match self { + Interval::Month(v) => Interval::Month(((v as u16) / rhs) as i32), + Interval::Microsecond(v) => Interval::Microsecond(((v as u16) / rhs) as i64), + } + } +} + impl Div for Interval { type Output = Self; @@ -279,6 +308,16 @@ impl Div for u8 { } } +impl Div for u16 { + type Output = Interval; + + fn div(self, rhs: Interval) -> Interval { + match rhs { + Interval::Month(v) => Interval::Month((self / (v as u16)) as i32), + Interval::Microsecond(v) => Interval::Microsecond((self / (v as u16)) as i64), + } + } +} impl Div for f64 { type Output = Interval; @@ -316,6 +355,9 @@ mod tests { assert_eq!(Month(2) * 3_u8, Month(6)); assert_eq!(2_u8 * Month(3), Month(6)); + assert_eq!(Month(2) * 3_u16, Month(6)); + assert_eq!(2_u16 * Month(3), Month(6)); + assert_eq!(Month(2) * 3.0, Month(6)); assert_eq!(2.0 * Month(3), Month(6)); @@ -337,6 +379,9 @@ mod tests { assert_eq!(Month(6) / 3_u8, Month(2)); assert_eq!(6_u8 / Month(2), Month(3)); + assert_eq!(Month(6) / 3_u16, Month(2)); + assert_eq!(6_u16 / Month(2), Month(3)); + assert_eq!(Month(8) / 4.0, Month(2)); assert_eq!(8.0 / Month(4), Month(2)); @@ -358,6 +403,9 @@ mod tests { assert_eq!(Microsecond(2) * 3_u8, Microsecond(6)); assert_eq!(2_u8 * Microsecond(3), Microsecond(6)); + assert_eq!(Microsecond(2) * 3_u16, Microsecond(6)); + assert_eq!(2_u16 * Microsecond(3), Microsecond(6)); + assert_eq!(Microsecond(6) / 3_i8, Microsecond(2)); assert_eq!(6_i8 / Microsecond(2), Microsecond(3)); @@ -375,5 +423,8 @@ mod tests { assert_eq!(Microsecond(6) / 3_u8, Microsecond(2)); assert_eq!(6_u8 / Microsecond(2), Microsecond(3)); + + assert_eq!(Microsecond(6) / 3_u16, Microsecond(2)); + assert_eq!(6_u16 / Microsecond(2), Microsecond(3)); } } diff --git a/core/src/data/key.rs b/core/src/data/key.rs index 102d3ff12..2f4ae45a2 100644 --- a/core/src/data/key.rs +++ b/core/src/data/key.rs @@ -31,6 +31,7 @@ pub enum Key { I128(i128), U8(u8), Decimal(Decimal), + U16(u16), Bool(bool), Str(String), Bytea(Vec), @@ -50,6 +51,7 @@ impl PartialOrd for Key { (Key::I32(l), Key::I32(r)) => Some(l.cmp(r)), (Key::I64(l), Key::I64(r)) => Some(l.cmp(r)), (Key::U8(l), Key::U8(r)) => Some(l.cmp(r)), + (Key::U16(l), Key::U16(r)) => Some(l.cmp(r)), (Key::Decimal(l), Key::Decimal(r)) => Some(l.cmp(r)), (Key::Bool(l), Key::Bool(r)) => Some(l.cmp(r)), (Key::Str(l), Key::Str(r)) => Some(l.cmp(r)), @@ -78,6 +80,7 @@ impl TryFrom for Key { I64(v) => Ok(Key::I64(v)), I128(v) => Ok(Key::I128(v)), U8(v) => Ok(Key::U8(v)), + U16(v) => Ok(Key::U16(v)), Decimal(v) => Ok(Key::Decimal(v)), Str(v) => Ok(Key::Str(v)), Bytea(v) => Ok(Key::Bytea(v)), @@ -166,6 +169,11 @@ impl Key { .chain(v.to_be_bytes().iter()) .copied() .collect::>(), + Key::U16(v) => [VALUE, 1] + .iter() + .chain(v.to_be_bytes().iter()) + .copied() + .collect::>(), Key::Decimal(v) => { let sign = if v.is_sign_positive() { 1 } else { 0 }; let convert = |v: Decimal| { @@ -273,6 +281,7 @@ mod tests { assert_eq!(convert("CAST(11 AS INT32)"), Ok(Key::I32(11))); assert_eq!(convert("2048"), Ok(Key::I64(2048))); assert_eq!(convert("CAST(11 AS UINT8)"), Ok(Key::U8(11))); + assert_eq!(convert("CAST(11 AS UINT16)"), Ok(Key::U16(11))); assert_eq!( convert("CAST(123.45 AS DECIMAL)"), Ok(Key::Decimal(Decimal::from_str("123.45").unwrap())) @@ -436,6 +445,15 @@ mod tests { assert_eq!(cmp(&n1, &n4), Ordering::Less); assert_eq!(cmp(&n3, &n4), Ordering::Equal); + let n1 = U16(0).to_cmp_be_bytes(); + let n2 = U16(3).to_cmp_be_bytes(); + let n3 = U16(20).to_cmp_be_bytes(); + let n4 = U16(20).to_cmp_be_bytes(); + assert_eq!(cmp(&n1, &n2), Ordering::Less); + assert_eq!(cmp(&n3, &n2), Ordering::Greater); + assert_eq!(cmp(&n1, &n4), Ordering::Less); + assert_eq!(cmp(&n3, &n4), Ordering::Equal); + let dec = |n| Decimal(rust_decimal::Decimal::from_str(n).unwrap()); let n1 = dec("-1200.345678").to_cmp_be_bytes(); let n2 = dec("-1.01").to_cmp_be_bytes(); diff --git a/core/src/data/row.rs b/core/src/data/row.rs index f7ef5bb84..3592aadeb 100644 --- a/core/src/data/row.rs +++ b/core/src/data/row.rs @@ -1,7 +1,7 @@ use { crate::{ ast::{ColumnDef, Expr}, - data::{schema::ColumnDefExt, Value}, + data::Value, executor::evaluate_stateless, result::Result, }, diff --git a/core/src/data/schema.rs b/core/src/data/schema.rs index c4d91975a..8da3c2c36 100644 --- a/core/src/data/schema.rs +++ b/core/src/data/schema.rs @@ -1,5 +1,6 @@ use { - crate::ast::{ColumnDef, ColumnOption, ColumnOptionDef, Expr, Statement, ToSql}, + crate::ast::{ColumnDef, ColumnOption, Expr, Statement, ToSql}, + chrono::NaiveDateTime, serde::{Deserialize, Serialize}, std::{fmt::Debug, iter}, strum_macros::Display, @@ -18,6 +19,7 @@ pub struct SchemaIndex { pub name: String, pub expr: Expr, pub order: SchemaIndexOrd, + pub created: NaiveDateTime, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] @@ -25,6 +27,7 @@ pub struct Schema { pub table_name: String, pub column_defs: Vec, pub indexes: Vec, + pub created: NaiveDateTime, } impl Schema { @@ -58,37 +61,27 @@ impl Schema { } } -pub trait ColumnDefExt { - fn is_nullable(&self) -> bool; - - fn get_default(&self) -> Option<&Expr>; -} - -impl ColumnDefExt for ColumnDef { - fn is_nullable(&self) -> bool { +impl ColumnDef { + pub fn is_nullable(&self) -> bool { self.options .iter() - .any(|ColumnOptionDef { option, .. }| option == &ColumnOption::Null) + .any(|option| option == &ColumnOption::Null) } - fn get_default(&self) -> Option<&Expr> { - self.options - .iter() - .find_map(|ColumnOptionDef { option, .. }| match option { - ColumnOption::Default(expr) => Some(expr), - _ => None, - }) + pub fn get_default(&self) -> Option<&Expr> { + self.options.iter().find_map(|option| match option { + ColumnOption::Default(expr) => Some(expr), + _ => None, + }) } } #[cfg(test)] mod tests { + use crate::{ - ast::{ - AstLiteral, ColumnDef, - ColumnOption::{self, Unique}, - ColumnOptionDef, Expr, - }, + ast::{AstLiteral, ColumnDef, ColumnOption, Expr}, + chrono::Utc, data::{Schema, SchemaIndex, SchemaIndexOrd}, prelude::DataType, }; @@ -107,20 +100,15 @@ mod tests { name: "name".to_owned(), data_type: DataType::Text, options: vec![ - ColumnOptionDef { - name: None, - option: ColumnOption::Null, - }, - ColumnOptionDef { - name: None, - option: ColumnOption::Default(Expr::Literal(AstLiteral::QuotedString( - "glue".to_owned(), - ))), - }, + ColumnOption::Null, + ColumnOption::Default(Expr::Literal(AstLiteral::QuotedString( + "glue".to_owned(), + ))), ], }, ], indexes: Vec::new(), + created: Utc::now().naive_utc(), }; assert_eq!( @@ -136,12 +124,10 @@ mod tests { column_defs: vec![ColumnDef { name: "id".to_owned(), data_type: DataType::Int, - options: vec![ColumnOptionDef { - name: None, - option: Unique { is_primary: true }, - }], + options: vec![ColumnOption::Unique { is_primary: true }], }], indexes: Vec::new(), + created: Utc::now().naive_utc(), }; assert_eq!(schema.to_ddl(), "CREATE TABLE User (id INT PRIMARY KEY);"); @@ -168,13 +154,16 @@ mod tests { name: "User_id".to_owned(), expr: Expr::Identifier("id".to_owned()), order: SchemaIndexOrd::Both, + created: Utc::now().naive_utc(), }, SchemaIndex { name: "User_name".to_owned(), expr: Expr::Identifier("name".to_owned()), order: SchemaIndexOrd::Both, + created: Utc::now().naive_utc(), }, ], + created: Utc::now().naive_utc(), }; assert_eq!( diff --git a/core/src/data/value/binary_op/decimal.rs b/core/src/data/value/binary_op/decimal.rs index 4d589c29c..dda2ff22a 100644 --- a/core/src/data/value/binary_op/decimal.rs +++ b/core/src/data/value/binary_op/decimal.rs @@ -18,6 +18,7 @@ impl PartialEq for Decimal { I64(other) => *self == Decimal::from(*other), I128(other) => *self == Decimal::from(*other), U8(other) => *self == Decimal::from(*other), + U16(other) => *self == Decimal::from(*other), F64(other) => Decimal::from_f64_retain(*other) .map(|x| *self == x) .unwrap_or(false), @@ -35,6 +36,7 @@ impl PartialOrd for Decimal { I64(rhs) => self.partial_cmp(&(Decimal::from(rhs))), I128(rhs) => self.partial_cmp(&(Decimal::from(rhs))), U8(rhs) => self.partial_cmp(&(Decimal::from(rhs))), + U16(rhs) => self.partial_cmp(&(Decimal::from(rhs))), F64(rhs) => Decimal::from_f64_retain(rhs) .map(|x| self.partial_cmp(&x)) .unwrap_or(None), @@ -106,6 +108,17 @@ impl TryBinaryOperator for Decimal { .into() }) .map(Decimal), + U16(rhs) => lhs + .checked_add(Decimal::from(rhs)) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: Decimal(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Add, + } + .into() + }) + .map(Decimal), F64(rhs) => Decimal::from_f64_retain(rhs) .map(|x| Ok(Decimal(lhs + x))) .unwrap_or_else(|| Err(ValueError::FloatToDecimalConversionFailure(rhs).into())), @@ -189,6 +202,17 @@ impl TryBinaryOperator for Decimal { .into() }) .map(Decimal), + U16(rhs) => lhs + .checked_sub(Decimal::from(rhs)) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: Decimal(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Subtract, + } + .into() + }) + .map(Decimal), F64(rhs) => Decimal::from_f64_retain(rhs) .map(|x| Ok(Decimal(lhs - x))) .unwrap_or_else(|| Err(ValueError::FloatToDecimalConversionFailure(rhs).into())), @@ -272,6 +296,17 @@ impl TryBinaryOperator for Decimal { .into() }) .map(Decimal), + U16(rhs) => lhs + .checked_mul(Decimal::from(rhs)) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: Decimal(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Multiply, + } + .into() + }) + .map(Decimal), F64(rhs) => Decimal::from_f64_retain(rhs) .map(|x| Ok(Decimal(lhs * x))) .unwrap_or_else(|| Err(ValueError::FloatToDecimalConversionFailure(rhs).into())), @@ -355,6 +390,17 @@ impl TryBinaryOperator for Decimal { .into() }) .map(Decimal), + U16(rhs) => lhs + .checked_div(Decimal::from(rhs)) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: Decimal(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Divide, + } + .into() + }) + .map(Decimal), F64(rhs) => Decimal::from_f64_retain(rhs) .map(|x| Ok(Decimal(lhs / x))) .unwrap_or_else(|| Err(ValueError::FloatToDecimalConversionFailure(rhs).into())), @@ -438,6 +484,17 @@ impl TryBinaryOperator for Decimal { .into() }) .map(Decimal), + U16(rhs) => lhs + .checked_rem(Decimal::from(rhs)) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: Decimal(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Modulo, + } + .into() + }) + .map(Decimal), F64(rhs) => match Decimal::from_f64_retain(rhs) { Some(x) => lhs .checked_rem(x) @@ -543,6 +600,15 @@ mod tests { .into()) ); + assert_eq!( + Decimal::MAX.try_add(&U16(1)), + Err(ValueError::BinaryOperationOverflow { + lhs: Decimal(Decimal::MAX), + rhs: U16(1), + operator: NumericBinaryOperator::Add, + } + .into()) + ); assert_eq!( Decimal::MIN.try_subtract(&I8(1)), Err(ValueError::BinaryOperationOverflow { @@ -588,6 +654,15 @@ mod tests { } .into()) ); + assert_eq!( + Decimal::MIN.try_subtract(&U16(1)), + Err(ValueError::BinaryOperationOverflow { + lhs: Decimal(Decimal::MIN), + rhs: U16(1), + operator: NumericBinaryOperator::Subtract, + } + .into()) + ); assert_eq!( Decimal::MIN.try_subtract(&Decimal(Decimal::ONE)), @@ -644,6 +719,15 @@ mod tests { } .into()) ); + assert_eq!( + Decimal::MAX.try_multiply(&U16(2)), + Err(ValueError::BinaryOperationOverflow { + lhs: Decimal(Decimal::MAX), + rhs: U16(2), + operator: NumericBinaryOperator::Multiply, + } + .into()) + ); assert_eq!( Decimal::MAX.try_multiply(&Decimal(Decimal::TWO)), @@ -703,6 +787,15 @@ mod tests { .into()) ); + assert_eq!( + base.try_divide(&U16(0)), + Err(ValueError::BinaryOperationOverflow { + lhs: Decimal(base), + rhs: U16(0), + operator: NumericBinaryOperator::Divide, + } + .into()) + ); assert_eq!( base.try_divide(&Decimal(Decimal::ZERO)), Err(ValueError::BinaryOperationOverflow { @@ -761,6 +854,16 @@ mod tests { .into()) ); + assert_eq!( + base.try_modulo(&U16(0)), + Err(ValueError::BinaryOperationOverflow { + lhs: Decimal(base), + rhs: U16(0), + operator: NumericBinaryOperator::Modulo, + } + .into()) + ); + assert_eq!( base.try_modulo(&Decimal(Decimal::ZERO)), Err(ValueError::BinaryOperationOverflow { @@ -781,6 +884,7 @@ mod tests { assert_eq!(base, I64(1)); assert_eq!(base, I128(1)); assert_eq!(base, U8(1)); + assert_eq!(base, U16(1)); assert_eq!(base, F64(1.0)); assert_eq!(base, Decimal(Decimal::ONE)); @@ -796,6 +900,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&I128(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&U8(1)), Some(Ordering::Equal)); + assert_eq!(base.partial_cmp(&U16(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&F64(1.0)), Some(Ordering::Equal)); assert_eq!( base.partial_cmp(&Decimal(Decimal::ONE)), @@ -814,6 +919,7 @@ mod tests { assert_eq!(base.try_add(&I64(1)), Ok(Decimal(Decimal::TWO))); assert_eq!(base.try_add(&I128(1)), Ok(Decimal(Decimal::TWO))); assert_eq!(base.try_add(&U8(1)), Ok(Decimal(Decimal::TWO))); + assert_eq!(base.try_add(&U16(1)), Ok(Decimal(Decimal::TWO))); assert_eq!(base.try_add(&F64(1.0)), Ok(Decimal(Decimal::TWO))); assert_eq!( base.try_add(&Decimal(Decimal::ONE)), @@ -840,6 +946,7 @@ mod tests { assert_eq!(base.try_subtract(&I64(1)), Ok(Decimal(Decimal::ZERO))); assert_eq!(base.try_subtract(&I128(1)), Ok(Decimal(Decimal::ZERO))); assert_eq!(base.try_subtract(&U8(1)), Ok(Decimal(Decimal::ZERO))); + assert_eq!(base.try_subtract(&U16(1)), Ok(Decimal(Decimal::ZERO))); assert_eq!(base.try_subtract(&F64(1.0)), Ok(Decimal(Decimal::ZERO))); assert_eq!( base.try_subtract(&Decimal(Decimal::ONE)), @@ -866,6 +973,7 @@ mod tests { assert_eq!(base.try_multiply(&I64(1)), Ok(Decimal(Decimal::ONE))); assert_eq!(base.try_multiply(&I128(1)), Ok(Decimal(Decimal::ONE))); assert_eq!(base.try_multiply(&U8(1)), Ok(Decimal(Decimal::ONE))); + assert_eq!(base.try_multiply(&U16(1)), Ok(Decimal(Decimal::ONE))); assert_eq!(base.try_multiply(&F64(1.0)), Ok(Decimal(Decimal::ONE))); assert_eq!( base.try_multiply(&Decimal(Decimal::ONE)), @@ -892,6 +1000,7 @@ mod tests { assert_eq!(base.try_divide(&I64(1)), Ok(Decimal(Decimal::ONE))); assert_eq!(base.try_divide(&I128(1)), Ok(Decimal(Decimal::ONE))); assert_eq!(base.try_divide(&U8(1)), Ok(Decimal(Decimal::ONE))); + assert_eq!(base.try_divide(&U16(1)), Ok(Decimal(Decimal::ONE))); assert_eq!(base.try_divide(&F64(1.0)), Ok(Decimal(Decimal::ONE))); assert_eq!( base.try_divide(&Decimal(Decimal::ONE)), @@ -918,6 +1027,7 @@ mod tests { assert_eq!(base.try_modulo(&I64(1)), Ok(Decimal(Decimal::ZERO))); assert_eq!(base.try_modulo(&I128(1)), Ok(Decimal(Decimal::ZERO))); assert_eq!(base.try_modulo(&U8(1)), Ok(Decimal(Decimal::ZERO))); + assert_eq!(base.try_modulo(&U16(1)), Ok(Decimal(Decimal::ZERO))); assert_eq!(base.try_modulo(&F64(1.0)), Ok(Decimal(Decimal::ZERO))); assert_eq!( base.try_modulo(&Decimal(Decimal::ONE)), diff --git a/core/src/data/value/binary_op/f64.rs b/core/src/data/value/binary_op/f64.rs index 71483438b..287ac920c 100644 --- a/core/src/data/value/binary_op/f64.rs +++ b/core/src/data/value/binary_op/f64.rs @@ -21,6 +21,7 @@ impl PartialEq for f64 { I64(rhs) => (lhs - (rhs as f64)).abs() < f64::EPSILON, I128(rhs) => (lhs - (rhs as f64)).abs() < f64::EPSILON, U8(rhs) => (lhs - (rhs as f64)).abs() < f64::EPSILON, + U16(rhs) => (lhs - (rhs as f64)).abs() < f64::EPSILON, F64(rhs) => (lhs - rhs).abs() < f64::EPSILON, Decimal(rhs) => Decimal::from_f64_retain(lhs) .map(|x| rhs == x) @@ -39,6 +40,7 @@ impl PartialOrd for f64 { I64(rhs) => self.partial_cmp(&(rhs as f64)), I128(rhs) => self.partial_cmp(&(rhs as f64)), U8(rhs) => self.partial_cmp(&(rhs as f64)), + U16(rhs) => self.partial_cmp(&(rhs as f64)), F64(rhs) => self.partial_cmp(&rhs), Decimal(rhs) => Decimal::from_f64_retain(*self) .map(|x| x.partial_cmp(&rhs)) @@ -61,6 +63,7 @@ impl TryBinaryOperator for f64 { I64(rhs) => Ok(F64(lhs + rhs as f64)), I128(rhs) => Ok(F64(lhs + rhs as f64)), U8(rhs) => Ok(F64(lhs + rhs as f64)), + U16(rhs) => Ok(F64(lhs + rhs as f64)), F64(rhs) => Ok(F64(lhs + rhs)), Decimal(rhs) => Decimal::from_f64_retain(lhs) .map(|x| Ok(Decimal(x + rhs))) @@ -85,6 +88,7 @@ impl TryBinaryOperator for f64 { I64(rhs) => Ok(F64(lhs - rhs as f64)), I128(rhs) => Ok(F64(lhs - rhs as f64)), U8(rhs) => Ok(F64(lhs - rhs as f64)), + U16(rhs) => Ok(F64(lhs - rhs as f64)), F64(rhs) => Ok(F64(lhs - rhs)), Decimal(rhs) => Decimal::from_f64_retain(lhs) .map(|x| Ok(Decimal(x - rhs))) @@ -109,6 +113,7 @@ impl TryBinaryOperator for f64 { I64(rhs) => Ok(F64(lhs * rhs as f64)), I128(rhs) => Ok(F64(lhs * rhs as f64)), U8(rhs) => Ok(F64(lhs * rhs as f64)), + U16(rhs) => Ok(F64(lhs * rhs as f64)), F64(rhs) => Ok(F64(lhs * rhs)), Interval(rhs) => Ok(Interval(lhs * rhs)), Decimal(rhs) => Decimal::from_f64_retain(lhs) @@ -134,6 +139,7 @@ impl TryBinaryOperator for f64 { I64(rhs) => Ok(F64(lhs / rhs as f64)), I128(rhs) => Ok(F64(lhs / rhs as f64)), U8(rhs) => Ok(F64(lhs / rhs as f64)), + U16(rhs) => Ok(F64(lhs / rhs as f64)), F64(rhs) => Ok(F64(lhs / rhs)), Decimal(rhs) => Decimal::from_f64_retain(lhs) .map(|x| Ok(Decimal(x * rhs))) @@ -158,6 +164,7 @@ impl TryBinaryOperator for f64 { I64(rhs) => Ok(F64(lhs % rhs as f64)), I128(rhs) => Ok(F64(lhs % rhs as f64)), U8(rhs) => Ok(F64(lhs % rhs as f64)), + U16(rhs) => Ok(F64(lhs % rhs as f64)), F64(rhs) => Ok(F64(lhs % rhs)), Decimal(rhs) => match Decimal::from_f64_retain(lhs) { Some(x) => x @@ -203,6 +210,7 @@ mod tests { assert_eq!(base, I64(1)); assert_eq!(base, I128(1)); assert_eq!(base, U8(1)); + assert_eq!(base, U16(1)); assert_eq!(base, F64(1.0)); assert_eq!(base, Decimal(Decimal::from(1))); @@ -219,6 +227,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&I128(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&U8(1)), Some(Ordering::Equal)); + assert_eq!(base.partial_cmp(&U16(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&F64(1.0)), Some(Ordering::Equal)); assert_eq!( base.partial_cmp(&Decimal(Decimal::ONE)), @@ -238,6 +247,7 @@ mod tests { assert!(matches!(base.try_add(&I64(1)), Ok(F64(x)) if (x - 2.0).abs() < f64::EPSILON )); assert!(matches!(base.try_add(&I128(1)), Ok(F64(x)) if (x - 2.0).abs() < f64::EPSILON )); assert!(matches!(base.try_add(&U8(1)), Ok(F64(x)) if (x - 2.0).abs() < f64::EPSILON )); + assert!(matches!(base.try_add(&U16(1)), Ok(F64(x)) if (x - 2.0).abs() < f64::EPSILON )); assert!(matches!(base.try_add(&F64(1.0)), Ok(F64(x)) if (x - 2.0).abs() < f64::EPSILON )); assert!( matches!(base.try_add(&Decimal(Decimal::ONE)), Ok(Decimal(x)) if x == Decimal::TWO) @@ -272,6 +282,9 @@ mod tests { matches!(base.try_subtract(&I128(1)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON ) ); assert!(matches!(base.try_subtract(&U8(1)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON )); + assert!( + matches!(base.try_subtract(&U16(1)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON ) + ); assert!( matches!(base.try_subtract(&F64(1.0)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON ) ); @@ -308,6 +321,9 @@ mod tests { matches!(base.try_multiply(&I128(1)), Ok(F64(x)) if (x - 1.0).abs() < f64::EPSILON ) ); assert!(matches!(base.try_multiply(&U8(1)), Ok(F64(x)) if (x - 1.0).abs() < f64::EPSILON )); + assert!( + matches!(base.try_multiply(&U16(1)), Ok(F64(x)) if (x - 1.0).abs() < f64::EPSILON ) + ); assert!( matches!(base.try_multiply(&F64(1.0)), Ok(F64(x)) if (x - 1.0).abs() < f64::EPSILON ) ); @@ -336,6 +352,7 @@ mod tests { assert!(matches!(base.try_divide(&I64(1)), Ok(F64(x)) if (x - 1.0).abs() < f64::EPSILON )); assert!(matches!(base.try_divide(&I128(1)), Ok(F64(x)) if (x - 1.0).abs() < f64::EPSILON )); assert!(matches!(base.try_divide(&U8(1)), Ok(F64(x)) if (x - 1.0).abs() < f64::EPSILON )); + assert!(matches!(base.try_divide(&U16(1)), Ok(F64(x)) if (x - 1.0).abs() < f64::EPSILON )); assert!( matches!(base.try_divide(&F64(1.0)), Ok(F64(x)) if (x - 1.0).abs() < f64::EPSILON ) ); @@ -364,6 +381,7 @@ mod tests { assert!(matches!(base.try_modulo(&I64(1)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON )); assert!(matches!(base.try_modulo(&I128(1)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON )); assert!(matches!(base.try_modulo(&U8(1)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON )); + assert!(matches!(base.try_modulo(&U16(1)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON )); assert!( matches!(base.try_modulo(&F64(1.0)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON ) ); diff --git a/core/src/data/value/binary_op/i128.rs b/core/src/data/value/binary_op/i128.rs index 309eb802c..9d0273924 100644 --- a/core/src/data/value/binary_op/i128.rs +++ b/core/src/data/value/binary_op/i128.rs @@ -19,6 +19,7 @@ impl PartialEq for i128 { I64(other) => self == &(*other as i128), I128(other) => self == other, U8(other) => self == &(*other as i128), + U16(other) => self == &(*other as i128), F64(other) => ((*self as f64) - other).abs() < f64::EPSILON, Decimal(other) => Decimal::from(*self) == *other, _ => false, @@ -35,6 +36,7 @@ impl PartialOrd for i128 { I64(other) => PartialOrd::partial_cmp(self, &(*other as i128)), I128(other) => PartialOrd::partial_cmp(self, other), U8(other) => PartialOrd::partial_cmp(self, &(*other as i128)), + U16(other) => PartialOrd::partial_cmp(self, &(*other as i128)), F64(other) => PartialOrd::partial_cmp(&(*self as f64), other), Decimal(other) => Decimal::from(*self).partial_cmp(other), _ => None, @@ -116,6 +118,17 @@ impl TryBinaryOperator for i128 { .into() }) .map(I128), + U16(rhs) => lhs + .checked_add(rhs as i128) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I128(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Add, + } + .into() + }) + .map(I128), F64(rhs) => Ok(F64(lhs as f64 + rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) + rhs)), Null => Ok(Null), @@ -198,6 +211,17 @@ impl TryBinaryOperator for i128 { .into() }) .map(I128), + U16(rhs) => lhs + .checked_sub(rhs as i128) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I128(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Subtract, + } + .into() + }) + .map(I128), F64(rhs) => Ok(F64(lhs as f64 - rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) - rhs)), @@ -279,6 +303,17 @@ impl TryBinaryOperator for i128 { .into() }) .map(I128), + U16(rhs) => lhs + .checked_mul(rhs as i128) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I128(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Multiply, + } + .into() + }) + .map(I128), F64(rhs) => Ok(F64(lhs as f64 * rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) * rhs)), Interval(rhs) => Ok(Interval(lhs * rhs)), @@ -362,6 +397,17 @@ impl TryBinaryOperator for i128 { .into() }) .map(I128), + U16(rhs) => lhs + .checked_div(rhs as i128) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I128(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Divide, + } + .into() + }) + .map(I128), F64(rhs) => Ok(F64(lhs as f64 / rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) / rhs)), Null => Ok(Null), @@ -443,6 +489,17 @@ impl TryBinaryOperator for i128 { .into() }) .map(I128), + U16(rhs) => lhs + .checked_rem(rhs as i128) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I128(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Modulo, + } + .into() + }) + .map(I128), F64(rhs) => Ok(F64(lhs as f64 % rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) % rhs)), Null => Ok(Null), @@ -474,7 +531,7 @@ mod tests { assert_eq!(I128(1), I128(1)); assert_eq!(I128(1), F64(1.0)); assert_eq!(I128(1), U8(1)); - + assert_eq!(I128(1), U16(1)); assert_eq!(-1_i128, I128(-1)); assert_eq!(0_i128, I128(0)); assert_eq!(1_i128, I128(1)); @@ -538,6 +595,15 @@ mod tests { } .into()) ); + assert_eq!( + i128::MAX.try_add(&U16(1)), + Err(ValueError::BinaryOperationOverflow { + lhs: I128(i128::MAX), + rhs: U16(1), + operator: NumericBinaryOperator::Add + } + .into()) + ); //try_subtract assert_eq!( @@ -597,6 +663,15 @@ mod tests { .into()) ); + assert_eq!( + i128::MIN.try_subtract(&U16(1)), + Err(ValueError::BinaryOperationOverflow { + lhs: I128(i128::MIN), + rhs: U16(1), + operator: NumericBinaryOperator::Subtract + } + .into()) + ); //try multiply assert_eq!(i128::MAX.try_multiply(&I8(1)), Ok(I128(i128::MAX))); assert_eq!(i128::MAX.try_multiply(&I16(1)), Ok(I128(i128::MAX))); @@ -604,6 +679,7 @@ mod tests { assert_eq!(i128::MAX.try_multiply(&I64(1)), Ok(I128(i128::MAX))); assert_eq!(i128::MAX.try_multiply(&I128(1)), Ok(I128(i128::MAX))); assert_eq!(i128::MAX.try_multiply(&U8(1)), Ok(I128(i128::MAX))); + assert_eq!(i128::MAX.try_multiply(&U16(1)), Ok(I128(i128::MAX))); assert_eq!( i128::MAX.try_multiply(&I8(2)), @@ -660,6 +736,15 @@ mod tests { .into()) ); + assert_eq!( + i128::MAX.try_multiply(&U16(2)), + Err(ValueError::BinaryOperationOverflow { + lhs: I128(i128::MAX), + rhs: U16(2), + operator: NumericBinaryOperator::Multiply + } + .into()) + ); //try_divide assert_eq!( i128::MAX.try_divide(&I8(0)), @@ -716,6 +801,15 @@ mod tests { .into()) ); + assert_eq!( + i128::MAX.try_divide(&U16(0)), + Err(ValueError::BinaryOperationOverflow { + lhs: I128(i128::MAX), + rhs: U16(0), + operator: NumericBinaryOperator::Divide + } + .into()) + ); assert_eq!( i128::MAX.try_modulo(&I8(0)), Err(ValueError::BinaryOperationOverflow { @@ -770,6 +864,15 @@ mod tests { } .into()) ); + assert_eq!( + i128::MAX.try_modulo(&U16(0)), + Err(ValueError::BinaryOperationOverflow { + lhs: I128(i128::MAX), + rhs: U16(0), + operator: NumericBinaryOperator::Modulo + } + .into()) + ); } #[test] @@ -782,6 +885,7 @@ mod tests { assert_eq!(base, I64(1)); assert_eq!(base, I128(1)); assert_eq!(base, U8(1)); + assert_eq!(base, U16(1)); assert_eq!(base, F64(1.0)); assert_eq!(base, Decimal(Decimal::ONE)); @@ -798,6 +902,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&I128(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&U8(0)), Some(Ordering::Greater)); + assert_eq!(base.partial_cmp(&U16(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&F64(0.0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&I8(1)), Some(Ordering::Equal)); @@ -806,6 +911,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&I128(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&U8(1)), Some(Ordering::Equal)); + assert_eq!(base.partial_cmp(&U16(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&F64(1.0)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&I8(2)), Some(Ordering::Less)); @@ -814,6 +920,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&I128(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&U8(2)), Some(Ordering::Less)); + assert_eq!(base.partial_cmp(&U16(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&F64(2.0)), Some(Ordering::Less)); assert_eq!( @@ -834,6 +941,7 @@ mod tests { assert_eq!(base.try_add(&I64(1)), Ok(I128(2))); assert_eq!(base.try_add(&I128(1)), Ok(I128(2))); assert_eq!(base.try_add(&U8(1)), Ok(I128(2))); + assert_eq!(base.try_add(&U16(1)), Ok(I128(2))); assert!(matches!(base.try_add(&F64(1.0)), Ok(F64(x)) if (x - 2.0).abs() < f64::EPSILON)); assert_eq!( @@ -862,6 +970,7 @@ mod tests { assert_eq!(base.try_subtract(&I64(1)), Ok(I128(0))); assert_eq!(base.try_subtract(&I128(1)), Ok(I128(0))); assert_eq!(base.try_subtract(&U8(1)), Ok(I128(0))); + assert_eq!(base.try_subtract(&U16(1)), Ok(I128(0))); assert!( matches!(base.try_subtract(&F64(1.0)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON ) @@ -930,6 +1039,7 @@ mod tests { assert_eq!(base.try_divide(&I64(2)), Ok(I128(3))); assert_eq!(base.try_divide(&I128(2)), Ok(I128(3))); assert_eq!(base.try_divide(&U8(2)), Ok(I128(3))); + assert_eq!(base.try_divide(&U16(2)), Ok(I128(3))); assert_eq!(base.try_divide(&I8(-6)), Ok(I128(-1))); assert_eq!(base.try_divide(&I16(-6)), Ok(I128(-1))); @@ -967,6 +1077,7 @@ mod tests { assert_eq!(base.try_modulo(&I64(1)), Ok(I128(0))); assert_eq!(base.try_modulo(&I128(1)), Ok(I128(0))); assert_eq!(base.try_modulo(&U8(1)), Ok(I128(0))); + assert_eq!(base.try_modulo(&U16(1)), Ok(I128(0))); assert_eq!(base.try_modulo(&I8(2)), Ok(I128(1))); assert_eq!(base.try_modulo(&I16(2)), Ok(I128(1))); diff --git a/core/src/data/value/binary_op/i32.rs b/core/src/data/value/binary_op/i32.rs index c30b13ef6..524cf32d3 100644 --- a/core/src/data/value/binary_op/i32.rs +++ b/core/src/data/value/binary_op/i32.rs @@ -19,6 +19,7 @@ impl PartialEq for i32 { I64(other) => (*self as i64) == *other, I128(other) => (*self as i128) == *other, U8(other) => self == &(*other as i32), + U16(other) => self == &(*other as i32), F64(other) => ((*self as f64) - other).abs() < f64::EPSILON, Decimal(other) => Decimal::from(*self) == *other, _ => false, @@ -35,6 +36,7 @@ impl PartialOrd for i32 { I64(other) => PartialOrd::partial_cmp(&(*self as i64), other), I128(other) => PartialOrd::partial_cmp(&(*self as i128), other), U8(other) => PartialOrd::partial_cmp(self, &(*other as i32)), + U16(other) => PartialOrd::partial_cmp(self, &(*other as i32)), F64(other) => PartialOrd::partial_cmp(&(*self as f64), other), Decimal(other) => Decimal::from(*self).partial_cmp(other), _ => None, @@ -115,6 +117,17 @@ impl TryBinaryOperator for i32 { .into() }) .map(I32), + U16(rhs) => lhs + .checked_add(rhs as i32) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I32(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Add, + } + .into() + }) + .map(I32), F64(rhs) => Ok(F64(lhs as f64 + rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) + rhs)), Null => Ok(Null), @@ -197,6 +210,17 @@ impl TryBinaryOperator for i32 { .into() }) .map(I32), + U16(rhs) => lhs + .checked_sub(rhs as i32) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I32(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Subtract, + } + .into() + }) + .map(I32), F64(rhs) => Ok(F64(lhs as f64 - rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) - rhs)), @@ -278,6 +302,17 @@ impl TryBinaryOperator for i32 { .into() }) .map(I32), + U16(rhs) => lhs + .checked_mul(rhs as i32) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I32(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Multiply, + } + .into() + }) + .map(I32), F64(rhs) => Ok(F64(lhs as f64 * rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) * rhs)), Interval(rhs) => Ok(Interval(lhs * rhs)), @@ -361,6 +396,17 @@ impl TryBinaryOperator for i32 { .into() }) .map(I32), + U16(rhs) => lhs + .checked_div(rhs as i32) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I32(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Divide, + } + .into() + }) + .map(I32), F64(rhs) => Ok(F64(lhs as f64 / rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) / rhs)), Null => Ok(Null), @@ -442,6 +488,17 @@ impl TryBinaryOperator for i32 { .into() }) .map(I32), + U16(rhs) => lhs + .checked_rem(rhs as i32) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I32(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Modulo, + } + .into() + }) + .map(I32), F64(rhs) => Ok(F64(lhs as f64 % rhs)), Decimal(rhs) => Ok(Decimal(Decimal::from(lhs) % rhs)), Null => Ok(Null), @@ -489,6 +546,15 @@ mod tests { .into()) ); + assert_eq!( + i32::MAX.try_add(&U16(1)), + Err(ValueError::BinaryOperationOverflow { + lhs: I32(i32::MAX), + rhs: U16(1), + operator: (NumericBinaryOperator::Add) + } + .into()) + ); assert_eq!( i32::MAX.try_add(&I8(1)), Err(ValueError::BinaryOperationOverflow { @@ -519,6 +585,15 @@ mod tests { .into()) ); + assert_eq!( + i32::MAX.try_add(&U16(1)), + Err(ValueError::BinaryOperationOverflow { + lhs: I32(i32::MAX), + rhs: U16(1), + operator: (NumericBinaryOperator::Add) + } + .into()) + ); //try_subtract assert_eq!( i32::MIN.try_subtract(&I8(1)), @@ -545,6 +620,15 @@ mod tests { .into()) ); + assert_eq!( + i32::MIN.try_subtract(&U16(1)), + Err(ValueError::BinaryOperationOverflow { + lhs: I32(i32::MIN), + rhs: U16(1), + operator: (NumericBinaryOperator::Subtract) + } + .into()) + ); //try multiply assert_eq!(i32::MAX.try_multiply(&I8(1)), Ok(I32(i32::MAX))); assert_eq!(i32::MAX.try_multiply(&I64(1)), Ok(I64(i32::MAX as i64))); @@ -563,6 +647,15 @@ mod tests { } .into()) ); + assert_eq!( + i32::MAX.try_multiply(&U16(2)), + Err(ValueError::BinaryOperationOverflow { + lhs: I32(i32::MAX), + rhs: U16(2), + operator: (NumericBinaryOperator::Multiply) + } + .into()) + ); assert_eq!( i32::MAX.try_multiply(&I8(2)), Err(ValueError::BinaryOperationOverflow { @@ -582,6 +675,15 @@ mod tests { .into()) ); + assert_eq!( + i32::MAX.try_multiply(&U16(2)), + Err(ValueError::BinaryOperationOverflow { + lhs: I32(i32::MAX), + rhs: U16(2), + operator: (NumericBinaryOperator::Multiply) + } + .into()) + ); //try_divide assert_eq!( i32::MAX.try_divide(&I8(0)), @@ -620,6 +722,15 @@ mod tests { .into()) ); + assert_eq!( + i32::MAX.try_divide(&U16(0)), + Err(ValueError::BinaryOperationOverflow { + lhs: I32(i32::MAX), + rhs: U16(0), + operator: (NumericBinaryOperator::Divide) + } + .into()) + ); assert_eq!( i32::MAX.try_modulo(&I8(0)), Err(ValueError::BinaryOperationOverflow { @@ -656,6 +767,15 @@ mod tests { } .into()) ); + assert_eq!( + i32::MAX.try_modulo(&U16(0)), + Err(ValueError::BinaryOperationOverflow { + lhs: I32(i32::MAX), + rhs: U16(0), + operator: (NumericBinaryOperator::Modulo) + } + .into()) + ); } #[test] @@ -668,6 +788,7 @@ mod tests { assert_eq!(base, I64(1)); assert_eq!(base, I128(1)); assert_eq!(base, U8(1)); + assert_eq!(base, U16(1)); assert_eq!(base, F64(1.0)); assert_eq!(base, Decimal(Decimal::ONE)); @@ -684,6 +805,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&I128(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&U8(0)), Some(Ordering::Greater)); + assert_eq!(base.partial_cmp(&U16(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&F64(0.0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&I8(1)), Some(Ordering::Equal)); @@ -692,6 +814,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&I128(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&U8(1)), Some(Ordering::Equal)); + assert_eq!(base.partial_cmp(&U16(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&F64(1.0)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&I8(2)), Some(Ordering::Less)); @@ -700,6 +823,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&I128(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&U8(2)), Some(Ordering::Less)); + assert_eq!(base.partial_cmp(&U16(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&F64(2.0)), Some(Ordering::Less)); assert_eq!( @@ -720,6 +844,7 @@ mod tests { assert_eq!(base.try_add(&I64(1)), Ok(I64(2))); assert_eq!(base.try_add(&I128(1)), Ok(I128(2))); assert_eq!(base.try_add(&U8(1)), Ok(U8(2))); + assert_eq!(base.try_add(&U16(1)), Ok(U16(2))); assert!(matches!(base.try_add(&F64(1.0)), Ok(F64(x)) if (x - 2.0).abs() < f64::EPSILON)); assert_eq!( @@ -748,6 +873,7 @@ mod tests { assert_eq!(base.try_subtract(&I64(1)), Ok(I64(0))); assert_eq!(base.try_subtract(&I128(1)), Ok(I128(0))); assert_eq!(base.try_subtract(&U8(1)), Ok(U8(0))); + assert_eq!(base.try_subtract(&U16(1)), Ok(U16(0))); assert!( matches!(base.try_subtract(&F64(1.0)), Ok(F64(x)) if (x - 0.0).abs() < f64::EPSILON ) @@ -779,6 +905,7 @@ mod tests { assert_eq!(base.try_multiply(&I64(2)), Ok(I64(6))); assert_eq!(base.try_multiply(&I128(2)), Ok(I128(6))); assert_eq!(base.try_multiply(&U8(2)), Ok(U8(6))); + assert_eq!(base.try_multiply(&U16(2)), Ok(U16(6))); assert_eq!(base.try_multiply(&I8(-1)), Ok(I32(-3))); assert_eq!(base.try_multiply(&I16(-1)), Ok(I32(-3))); @@ -817,6 +944,7 @@ mod tests { assert_eq!(base.try_divide(&I64(2)), Ok(I64(3))); assert_eq!(base.try_divide(&I128(2)), Ok(I128(3))); assert_eq!(base.try_divide(&U8(2)), Ok(U8(3))); + assert_eq!(base.try_divide(&U16(2)), Ok(U16(3))); assert_eq!(base.try_divide(&I8(-6)), Ok(I32(-1))); assert_eq!(base.try_divide(&I16(-6)), Ok(I32(-1))); @@ -854,6 +982,7 @@ mod tests { assert_eq!(base.try_modulo(&I64(1)), Ok(I64(0))); assert_eq!(base.try_modulo(&I128(1)), Ok(I128(0))); assert_eq!(base.try_modulo(&U8(1)), Ok(U8(0))); + assert_eq!(base.try_modulo(&U16(1)), Ok(U16(0))); assert_eq!(base.try_modulo(&I8(2)), Ok(I32(1))); assert_eq!(base.try_modulo(&I16(2)), Ok(I32(1))); @@ -861,6 +990,7 @@ mod tests { assert_eq!(base.try_modulo(&I64(2)), Ok(I64(1))); assert_eq!(base.try_modulo(&I128(2)), Ok(I128(1))); assert_eq!(base.try_modulo(&U8(2)), Ok(U8(1))); + assert_eq!(base.try_modulo(&U16(2)), Ok(U16(1))); assert!(matches!(base.try_modulo(&F64(1.0)), Ok(F64(x)) if (x).abs() < f64::EPSILON )); assert_eq!( diff --git a/core/src/data/value/binary_op/i64.rs b/core/src/data/value/binary_op/i64.rs index b810c04f9..cbf2d1791 100644 --- a/core/src/data/value/binary_op/i64.rs +++ b/core/src/data/value/binary_op/i64.rs @@ -21,6 +21,7 @@ impl PartialEq for i64 { I64(rhs) => lhs == rhs, I128(rhs) => lhs as i128 == rhs, U8(rhs) => lhs == rhs as i64, + U16(rhs) => lhs == rhs as i64, F64(rhs) => ((lhs as f64) - rhs).abs() < f64::EPSILON, Decimal(rhs) => Decimal::from(lhs) == rhs, _ => false, @@ -37,6 +38,7 @@ impl PartialOrd for i64 { I64(rhs) => PartialOrd::partial_cmp(self, rhs), I128(rhs) => PartialOrd::partial_cmp(&(*self as i128), rhs), U8(rhs) => PartialOrd::partial_cmp(self, &(*rhs as i64)), + U16(rhs) => PartialOrd::partial_cmp(self, &(*rhs as i64)), F64(rhs) => PartialOrd::partial_cmp(&(*self as f64), rhs), Decimal(other) => Decimal::from(*self).partial_cmp(other), _ => None, @@ -117,6 +119,17 @@ impl TryBinaryOperator for i64 { .into() }) .map(I64), + U16(rhs) => lhs + .checked_add(rhs as i64) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I64(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Add, + } + .into() + }) + .map(I64), F64(rhs) => Ok(F64(lhs as f64 + rhs)), Decimal(rhs) => Decimal::from(lhs) .checked_add(rhs) @@ -209,6 +222,17 @@ impl TryBinaryOperator for i64 { .into() }) .map(I64), + U16(rhs) => lhs + .checked_sub(rhs as i64) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I64(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Subtract, + } + .into() + }) + .map(I64), F64(rhs) => Ok(F64(lhs as f64 - rhs)), Decimal(rhs) => Decimal::from(lhs) .checked_sub(rhs) @@ -301,6 +325,17 @@ impl TryBinaryOperator for i64 { .into() }) .map(I64), + U16(rhs) => lhs + .checked_mul(rhs as i64) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I64(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Multiply, + } + .into() + }) + .map(I64), F64(rhs) => Ok(F64(lhs as f64 * rhs)), Decimal(rhs) => Decimal::from(lhs) .checked_mul(rhs) @@ -394,6 +429,17 @@ impl TryBinaryOperator for i64 { .into() }) .map(I64), + U16(rhs) => lhs + .checked_div(rhs as i64) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I64(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Divide, + } + .into() + }) + .map(I64), F64(rhs) => Ok(F64(lhs as f64 / rhs)), Decimal(rhs) => Decimal::from(lhs) .checked_div(rhs) @@ -486,6 +532,17 @@ impl TryBinaryOperator for i64 { .into() }) .map(I64), + U16(rhs) => lhs + .checked_rem(rhs as i64) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: I64(lhs), + rhs: U16(rhs), + operator: NumericBinaryOperator::Modulo, + } + .into() + }) + .map(I64), F64(rhs) => Ok(F64(lhs as f64 % rhs)), Decimal(rhs) => Decimal::from(lhs) .checked_rem(rhs) @@ -535,6 +592,15 @@ mod tests { } .into()) ); + assert_eq!( + i64::MAX.try_add(&U16(1)), + Err(ValueError::BinaryOperationOverflow { + lhs: I64(i64::MAX), + rhs: U16(1), + operator: (NumericBinaryOperator::Add) + } + .into()) + ); assert_eq!( i64::MAX.try_add(&I8(1)), Err(ValueError::BinaryOperationOverflow { @@ -594,6 +660,15 @@ mod tests { .into()) ); + assert_eq!( + i64::MAX.try_add(&U16(u16::MAX)), + Err(ValueError::BinaryOperationOverflow { + lhs: I64(i64::MAX), + rhs: U16(u16::MAX), + operator: (NumericBinaryOperator::Add) + } + .into()) + ); assert_eq!( i64::MIN.try_subtract(&I8(1)), Err(ValueError::BinaryOperationOverflow { @@ -654,12 +729,22 @@ mod tests { .into()) ); + assert_eq!( + i64::MIN.try_subtract(&U16(u16::MAX)), + Err(ValueError::BinaryOperationOverflow { + lhs: I64(i64::MIN), + rhs: U16(u16::MAX), + operator: (NumericBinaryOperator::Subtract) + } + .into()) + ); assert_eq!(i64::MAX.try_multiply(&I8(1)), Ok(I64(i64::MAX))); assert_eq!(i64::MAX.try_multiply(&I16(1)), Ok(I64(i64::MAX))); assert_eq!(i64::MAX.try_multiply(&I32(1)), Ok(I64(i64::MAX))); assert_eq!(i64::MAX.try_multiply(&I64(1)), Ok(I64(i64::MAX))); assert_eq!(i64::MAX.try_multiply(&I128(1)), Ok(I128(i64::MAX as i128))); assert_eq!(i64::MAX.try_multiply(&U8(1)), Ok(I64(i64::MAX))); + assert_eq!(i64::MAX.try_multiply(&U16(1)), Ok(I64(i64::MAX))); assert_eq!( i64::MAX.try_multiply(&I8(2)), @@ -710,6 +795,15 @@ mod tests { } .into()) ); + assert_eq!( + i64::MAX.try_multiply(&U16(2)), + Err(ValueError::BinaryOperationOverflow { + lhs: I64(i64::MAX), + rhs: U16(2), + operator: (NumericBinaryOperator::Multiply) + } + .into()) + ); assert_eq!( i64::MAX.try_divide(&I8(0)), Err(ValueError::BinaryOperationOverflow { @@ -775,6 +869,15 @@ mod tests { .into()) ); + assert_eq!( + i64::MAX.try_divide(&U16(0)), + Err(ValueError::BinaryOperationOverflow { + lhs: I64(i64::MAX), + rhs: U16(0), + operator: (NumericBinaryOperator::Divide) + } + .into()) + ); assert_eq!( i64::MAX.try_modulo(&I8(0)), Err(ValueError::BinaryOperationOverflow { @@ -831,6 +934,15 @@ mod tests { } .into()) ); + assert_eq!( + i64::MAX.try_modulo(&U16(0)), + Err(ValueError::BinaryOperationOverflow { + lhs: I64(i64::MAX), + rhs: U16(0), + operator: (NumericBinaryOperator::Modulo) + } + .into()) + ); } #[test] @@ -843,6 +955,7 @@ mod tests { assert_eq!(base, I64(1)); assert_eq!(base, I128(1)); assert_eq!(base, U8(1)); + assert_eq!(base, U16(1)); assert_eq!(base, F64(1.0)); assert_eq!(base, Decimal(Decimal::ONE)); @@ -859,6 +972,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&I128(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&U8(0)), Some(Ordering::Greater)); + assert_eq!(base.partial_cmp(&U16(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&F64(0.0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&I8(1)), Some(Ordering::Equal)); @@ -867,6 +981,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&I128(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&U8(1)), Some(Ordering::Equal)); + assert_eq!(base.partial_cmp(&U16(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&F64(1.0)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&I8(2)), Some(Ordering::Less)); @@ -875,6 +990,7 @@ mod tests { assert_eq!(base.partial_cmp(&I64(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&I128(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&U8(2)), Some(Ordering::Less)); + assert_eq!(base.partial_cmp(&U16(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&F64(2.0)), Some(Ordering::Less)); assert_eq!( @@ -890,6 +1006,7 @@ mod tests { let base = 1_i64; assert_eq!(base.try_add(&U8(1)), Ok(I64(2))); + assert_eq!(base.try_add(&U16(1)), Ok(I64(2))); assert_eq!(base.try_add(&I8(1)), Ok(I64(2))); assert_eq!(base.try_add(&I16(1)), Ok(I64(2))); assert_eq!(base.try_add(&I32(1)), Ok(I64(2))); @@ -918,6 +1035,7 @@ mod tests { let base = 1_i64; assert_eq!(base.try_subtract(&U8(1)), Ok(I64(0))); + assert_eq!(base.try_subtract(&U16(1)), Ok(I64(0))); assert_eq!(base.try_subtract(&I8(1)), Ok(I64(0))); assert_eq!(base.try_subtract(&I16(1)), Ok(I64(0))); assert_eq!(base.try_subtract(&I32(1)), Ok(I64(0))); @@ -949,6 +1067,7 @@ mod tests { let base = 3_i64; assert_eq!(base.try_multiply(&U8(2)), Ok(I64(6))); + assert_eq!(base.try_multiply(&U16(2)), Ok(I64(6))); assert_eq!(base.try_multiply(&I8(2)), Ok(I64(6))); assert_eq!(base.try_multiply(&I16(2)), Ok(I64(6))); assert_eq!(base.try_multiply(&I32(2)), Ok(I64(6))); @@ -987,6 +1106,7 @@ mod tests { let base = 6_i64; assert_eq!(base.try_divide(&U8(2)), Ok(I64(3))); + assert_eq!(base.try_divide(&U16(2)), Ok(I64(3))); assert_eq!(base.try_divide(&I8(2)), Ok(I64(3))); assert_eq!(base.try_divide(&I16(2)), Ok(I64(3))); assert_eq!(base.try_divide(&I32(2)), Ok(I32(3))); @@ -1030,6 +1150,7 @@ mod tests { assert_eq!(base.try_modulo(&I64(1)), Ok(I64(0))); assert_eq!(base.try_modulo(&I128(1)), Ok(I128(0))); assert_eq!(base.try_modulo(&U8(1)), Ok(I64(0))); + assert_eq!(base.try_modulo(&U16(1)), Ok(I64(0))); assert_eq!(base.try_modulo(&I8(2)), Ok(I64(1))); assert_eq!(base.try_modulo(&I16(2)), Ok(I64(1))); diff --git a/core/src/data/value/binary_op/integer/i16.rs b/core/src/data/value/binary_op/integer/i16.rs index d1eab0f96..e9e902868 100644 --- a/core/src/data/value/binary_op/integer/i16.rs +++ b/core/src/data/value/binary_op/integer/i16.rs @@ -1,12 +1,8 @@ -use { - crate::{impl_try_binary_op, prelude::Value}, - rust_decimal::prelude::Decimal, - std::cmp::Ordering, -}; +use {crate::prelude::Value, rust_decimal::prelude::Decimal, std::cmp::Ordering}; -impl_try_binary_op!(I16, i16); +super::macros::impl_try_binary_op!(I16, i16); #[cfg(test)] -crate::generate_binary_op_tests!(I16, i16); +super::macros::generate_binary_op_tests!(I16, i16); impl PartialEq for i16 { fn eq(&self, other: &Value) -> bool { @@ -17,6 +13,7 @@ impl PartialEq for i16 { I64(other) => (*self as i64) == *other, I128(other) => (*self as i128) == *other, U8(other) => *self == (*other as i16), + U16(other) => (*self as u16) == *other, F64(other) => ((*self as f64) - other).abs() < f64::EPSILON, Decimal(other) => Decimal::from(*self) == *other, _ => false, @@ -33,6 +30,7 @@ impl PartialOrd for i16 { I64(other) => (*self as i64).partial_cmp(other), I128(other) => (*self as i128).partial_cmp(other), U8(other) => self.partial_cmp(&(*other as i16)), + U16(other) => (*self as u16).partial_cmp(other), F64(other) => (*self as f64).partial_cmp(other), Decimal(other) => Decimal::from(*self).partial_cmp(other), _ => None, diff --git a/core/src/data/value/binary_op/integer/i8.rs b/core/src/data/value/binary_op/integer/i8.rs index 778f192e0..d34d168bc 100644 --- a/core/src/data/value/binary_op/integer/i8.rs +++ b/core/src/data/value/binary_op/integer/i8.rs @@ -1,12 +1,8 @@ -use { - crate::{impl_try_binary_op, prelude::Value}, - rust_decimal::prelude::Decimal, - std::cmp::Ordering, -}; +use {crate::prelude::Value, rust_decimal::prelude::Decimal, std::cmp::Ordering}; -impl_try_binary_op!(I8, i8); +super::macros::impl_try_binary_op!(I8, i8); #[cfg(test)] -crate::generate_binary_op_tests!(I8, i8); +super::macros::generate_binary_op_tests!(I8, i8); impl PartialEq for i8 { fn eq(&self, other: &Value) -> bool { @@ -17,6 +13,7 @@ impl PartialEq for i8 { I64(other) => (*self as i64) == *other, I128(other) => (*self as i128) == *other, U8(other) => (*self as i64) == (*other as i64), + U16(other) => (*self as u16) == *other, F64(other) => ((*self as f64) - other).abs() < f64::EPSILON, Decimal(other) => Decimal::from(*self) == *other, _ => false, @@ -33,6 +30,7 @@ impl PartialOrd for i8 { I64(other) => (*self as i64).partial_cmp(other), I128(other) => (*self as i128).partial_cmp(other), U8(other) => (*self as i64).partial_cmp(&(*other as i64)), + U16(other) => (*self as u16).partial_cmp(other), F64(other) => (*self as f64).partial_cmp(other), Decimal(other) => Decimal::from(*self).partial_cmp(other), _ => None, diff --git a/core/src/data/value/binary_op/integer/macros.rs b/core/src/data/value/binary_op/integer/macros.rs index ce2752943..a6a058461 100644 --- a/core/src/data/value/binary_op/integer/macros.rs +++ b/core/src/data/value/binary_op/integer/macros.rs @@ -1,4 +1,3 @@ -#[macro_export] macro_rules! impl_interval_method { (checked_mul, $lhs_variant: ident, $op: ident, $lhs: ident, $rhs: ident) => { return Ok(Value::Interval($lhs * $rhs)) @@ -13,7 +12,6 @@ macro_rules! impl_interval_method { }; } -#[macro_export] macro_rules! impl_method { ($lhs_variant: ident, $lhs_primitive: ident, $lhs: ident, $method: ident, $op: ident, $rhs: ident) => {{ match *$rhs { @@ -77,6 +75,16 @@ macro_rules! impl_method { } .into() }), + U16(rhs) => $lhs + .$method($lhs_primitive::try_from($rhs)?) + .ok_or_else(|| { + ValueError::BinaryOperationOverflow { + lhs: $lhs_variant($lhs), + rhs: U16(rhs), + operator: $op, + } + .into() + }), F64(rhs) => $lhs .$method($lhs_primitive::try_from($rhs)?) .ok_or_else(|| { @@ -99,7 +107,7 @@ macro_rules! impl_method { }), Null => return Ok(Null), Interval(rhs) => { - $crate::impl_interval_method!($method, $lhs_variant, $op, $lhs, rhs); + super::macros::impl_interval_method!($method, $lhs_variant, $op, $lhs, rhs); } _ => Err(ValueError::NonNumericMathOperation { lhs: $lhs_variant($lhs), @@ -112,7 +120,6 @@ macro_rules! impl_method { }}; } -#[macro_export] macro_rules! impl_try_binary_op { ($variant: ident, $primitive: ident) => { use $crate::{ @@ -129,34 +136,33 @@ macro_rules! impl_try_binary_op { fn try_add(&self, rhs: &Self::Rhs) -> Result { let lhs = *self; - $crate::impl_method!($variant, $primitive, lhs, checked_add, Add, rhs) + super::macros::impl_method!($variant, $primitive, lhs, checked_add, Add, rhs) } fn try_subtract(&self, rhs: &Self::Rhs) -> Result { let lhs = *self; - $crate::impl_method!($variant, $primitive, lhs, checked_sub, Subtract, rhs) + super::macros::impl_method!($variant, $primitive, lhs, checked_sub, Subtract, rhs) } fn try_multiply(&self, rhs: &Self::Rhs) -> Result { let lhs = *self; - $crate::impl_method!($variant, $primitive, lhs, checked_mul, Multiply, rhs) + super::macros::impl_method!($variant, $primitive, lhs, checked_mul, Multiply, rhs) } fn try_divide(&self, rhs: &Self::Rhs) -> Result { let lhs = *self; - $crate::impl_method!($variant, $primitive, lhs, checked_div, Divide, rhs) + super::macros::impl_method!($variant, $primitive, lhs, checked_div, Divide, rhs) } fn try_modulo(&self, rhs: &Self::Rhs) -> Result { let lhs = *self; - $crate::impl_method!($variant, $primitive, lhs, checked_rem, Modulo, rhs) + super::macros::impl_method!($variant, $primitive, lhs, checked_rem, Modulo, rhs) } } }; } #[cfg(test)] -#[macro_export] macro_rules! generate_binary_op_tests { ($variant: ident, $primitive: ident) => { mod tests { @@ -198,6 +204,7 @@ macro_rules! generate_binary_op_tests { assert_eq!(base, I64(1)); assert_eq!(base, I128(1)); assert_eq!(base, U8(1)); + assert_eq!(base, U16(1)); assert_ne!(base, Bool(true)); } @@ -217,6 +224,7 @@ macro_rules! generate_binary_op_tests { assert_eq!(base.partial_cmp(&I64(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&I128(0)), Some(Ordering::Greater)); assert_eq!(base.partial_cmp(&U8(0)), Some(Ordering::Greater)); + assert_eq!(base.partial_cmp(&U16(0)), Some(Ordering::Greater)); assert_eq!( base.partial_cmp(&Decimal(Decimal::ONE)), @@ -229,7 +237,7 @@ macro_rules! generate_binary_op_tests { assert_eq!(base.partial_cmp(&I64(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&I128(1)), Some(Ordering::Equal)); assert_eq!(base.partial_cmp(&U8(1)), Some(Ordering::Equal)); - assert_eq!(base.partial_cmp(&F64(1.0)), Some(Ordering::Equal)); + assert_eq!(base.partial_cmp(&U16(1)), Some(Ordering::Equal)); assert_eq!( base.partial_cmp(&Decimal(Decimal::TWO)), @@ -242,6 +250,7 @@ macro_rules! generate_binary_op_tests { assert_eq!(base.partial_cmp(&I64(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&I128(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&U8(2)), Some(Ordering::Less)); + assert_eq!(base.partial_cmp(&U16(2)), Some(Ordering::Less)); assert_eq!(base.partial_cmp(&Bool(true)), None); } @@ -280,6 +289,10 @@ macro_rules! generate_binary_op_tests { $primitive::MAX.try_add(&U8(1)), overflow_err($variant($primitive::MAX), U8(1), Add) ); + assert_eq!( + $primitive::MAX.try_add(&U16(1)), + overflow_err($variant($primitive::MAX), U16(1), Add) + ); } #[test] @@ -320,6 +333,10 @@ macro_rules! generate_binary_op_tests { $primitive::MIN.try_subtract(&U8(1)), overflow_err($variant($primitive::MIN), U8(1), Subtract) ); + assert_eq!( + $primitive::MIN.try_subtract(&U16(1)), + overflow_err($variant($primitive::MIN), U16(1), Subtract) + ); } #[test] @@ -360,6 +377,10 @@ macro_rules! generate_binary_op_tests { $primitive::MAX.try_multiply(&U8(2)), overflow_err($variant($primitive::MAX), U8(2), Multiply) ); + assert_eq!( + $primitive::MAX.try_multiply(&U16(2)), + overflow_err($variant($primitive::MAX), U16(2), Multiply) + ); } #[test] @@ -396,6 +417,10 @@ macro_rules! generate_binary_op_tests { $primitive::MAX.try_divide(&U8(0)), overflow_err($variant($primitive::MAX), U8(0), Divide) ); + assert_eq!( + $primitive::MAX.try_divide(&U16(0)), + overflow_err($variant($primitive::MAX), U16(0), Divide) + ); } #[test] @@ -432,6 +457,10 @@ macro_rules! generate_binary_op_tests { $primitive::MAX.try_modulo(&U8(0)), overflow_err($variant($primitive::MAX), U8(0), Modulo) ); + assert_eq!( + $primitive::MAX.try_modulo(&U16(0)), + overflow_err($variant($primitive::MAX), U16(0), Modulo) + ); } #[test] @@ -446,6 +475,7 @@ macro_rules! generate_binary_op_tests { assert_eq!(base.try_add(&I64(1)), Ok($variant(2))); assert_eq!(base.try_add(&I128(1)), Ok($variant(2))); assert_eq!(base.try_add(&U8(1)), Ok($variant(2))); + assert_eq!(base.try_add(&U16(1)), Ok($variant(2))); assert_eq!( base.try_add(&Bool(true)), @@ -470,6 +500,7 @@ macro_rules! generate_binary_op_tests { assert_eq!(base.try_subtract(&I64(1)), Ok($variant(0))); assert_eq!(base.try_subtract(&I128(1)), Ok($variant(0))); assert_eq!(base.try_subtract(&U8(1)), Ok($variant(0))); + assert_eq!(base.try_subtract(&U16(1)), Ok($variant(0))); assert_eq!( base.try_subtract(&Bool(true)), @@ -494,6 +525,7 @@ macro_rules! generate_binary_op_tests { assert_eq!(base.try_multiply(&I64(2)), Ok($variant(6))); assert_eq!(base.try_multiply(&I128(2)), Ok($variant(6))); assert_eq!(base.try_multiply(&U8(2)), Ok($variant(6))); + assert_eq!(base.try_multiply(&U16(2)), Ok($variant(6))); assert_eq!( base.try_multiply(&Bool(true)), @@ -518,6 +550,7 @@ macro_rules! generate_binary_op_tests { assert_eq!(base.try_divide(&I64(2)), Ok($variant(3))); assert_eq!(base.try_divide(&I128(2)), Ok($variant(3))); assert_eq!(base.try_divide(&U8(2)), Ok($variant(3))); + assert_eq!(base.try_divide(&U16(2)), Ok($variant(3))); assert_eq!( base.try_divide(&Bool(true)), @@ -542,6 +575,7 @@ macro_rules! generate_binary_op_tests { assert_eq!(base.try_modulo(&I64(1)), Ok($variant(0))); assert_eq!(base.try_modulo(&I128(1)), Ok($variant(0))); assert_eq!(base.try_modulo(&U8(1)), Ok($variant(0))); + assert_eq!(base.try_modulo(&U16(1)), Ok($variant(0))); assert_eq!( base.try_modulo(&Bool(true)), @@ -556,3 +590,9 @@ macro_rules! generate_binary_op_tests { } }; } + +#[cfg(test)] +pub(crate) use generate_binary_op_tests; +pub(crate) use impl_interval_method; +pub(crate) use impl_method; +pub(crate) use impl_try_binary_op; diff --git a/core/src/data/value/binary_op/integer/mod.rs b/core/src/data/value/binary_op/integer/mod.rs index 5533d67d1..c1a06b450 100644 --- a/core/src/data/value/binary_op/integer/mod.rs +++ b/core/src/data/value/binary_op/integer/mod.rs @@ -1,5 +1,6 @@ mod i16; mod i8; +mod u16; mod u8; mod macros; diff --git a/core/src/data/value/binary_op/integer/u16.rs b/core/src/data/value/binary_op/integer/u16.rs new file mode 100644 index 000000000..8eff665fc --- /dev/null +++ b/core/src/data/value/binary_op/integer/u16.rs @@ -0,0 +1,38 @@ +use {crate::prelude::Value, rust_decimal::prelude::Decimal, std::cmp::Ordering}; + +super::macros::impl_try_binary_op!(U16, u16); +#[cfg(test)] +super::macros::generate_binary_op_tests!(U16, u16); +impl PartialEq for u16 { + fn eq(&self, other: &Value) -> bool { + match other { + I8(other) => (*self as i16) == (*other as i16), + I16(other) => (*self as i16) == *other, + I32(other) => (*self as i32) == *other, + I64(other) => (*self as i64) == *other, + I128(other) => (*self as i128) == *other, + U8(other) => *self == (*other as u16), + U16(other) => self == other, + F64(other) => ((*self as f64) - other).abs() < f64::EPSILON, + Decimal(other) => Decimal::from(*self) == *other, + _ => false, + } + } +} + +impl PartialOrd for u16 { + fn partial_cmp(&self, other: &Value) -> Option { + match other { + I8(other) => (*self as i16).partial_cmp(&(*other as i16)), + I16(other) => (*self as i16).partial_cmp(other), + I32(other) => (*self as i32).partial_cmp(other), + I64(other) => (*self as i64).partial_cmp(other), + I128(other) => (*self as i128).partial_cmp(other), + U8(other) => self.partial_cmp(&(*other as u16)), + U16(other) => self.partial_cmp(other), + F64(other) => (*self as f64).partial_cmp(other), + Decimal(other) => Decimal::from(*self).partial_cmp(other), + _ => None, + } + } +} diff --git a/core/src/data/value/binary_op/integer/u8.rs b/core/src/data/value/binary_op/integer/u8.rs index 1f17dd0fd..2ae45d7d4 100644 --- a/core/src/data/value/binary_op/integer/u8.rs +++ b/core/src/data/value/binary_op/integer/u8.rs @@ -1,12 +1,8 @@ -use { - crate::{impl_try_binary_op, prelude::Value}, - rust_decimal::prelude::Decimal, - std::cmp::Ordering, -}; +use {crate::prelude::Value, rust_decimal::prelude::Decimal, std::cmp::Ordering}; -impl_try_binary_op!(U8, u8); +super::macros::impl_try_binary_op!(U8, u8); #[cfg(test)] -crate::generate_binary_op_tests!(U8, u8); +super::macros::generate_binary_op_tests!(U8, u8); impl PartialEq for u8 { fn eq(&self, other: &Value) -> bool { @@ -17,6 +13,7 @@ impl PartialEq for u8 { I64(other) => (*self as i64) == *other, I128(other) => (*self as i128) == *other, U8(other) => self == other, + U16(other) => (*self as u16) == *other, F64(other) => ((*self as f64) - other).abs() < f64::EPSILON, Decimal(other) => Decimal::from(*self) == *other, _ => false, @@ -33,6 +30,7 @@ impl PartialOrd for u8 { I64(other) => (*self as i64).partial_cmp(other), I128(other) => (*self as i128).partial_cmp(other), U8(other) => self.partial_cmp(other), + U16(other) => (*self as u16).partial_cmp(other), F64(other) => (*self as f64).partial_cmp(other), Decimal(other) => Decimal::from(*self).partial_cmp(other), _ => None, diff --git a/core/src/data/value/convert.rs b/core/src/data/value/convert.rs index e39b8217c..b47925c1a 100644 --- a/core/src/data/value/convert.rs +++ b/core/src/data/value/convert.rs @@ -24,6 +24,7 @@ impl From<&Value> for String { Value::I64(value) => value.to_string(), Value::I128(value) => value.to_string(), Value::U8(value) => value.to_string(), + Value::U16(value) => value.to_string(), Value::F64(value) => value.to_string(), Value::Date(value) => value.to_string(), Value::Timestamp(value) => value.to_string(), @@ -83,6 +84,11 @@ impl TryFrom<&Value> for bool { 0 => false, _ => return Err(ValueError::ImpossibleCast.into()), }, + Value::U16(value) => match value { + 1 => true, + 0 => false, + _ => return Err(ValueError::ImpossibleCast.into()), + }, Value::F64(value) => { if value.eq(&1.0) { true @@ -137,6 +143,7 @@ impl TryFrom<&Value> for i8 { Value::I64(value) => value.to_i8().ok_or(ValueError::ImpossibleCast)?, Value::I128(value) => value.to_i8().ok_or(ValueError::ImpossibleCast)?, Value::U8(value) => value.to_i8().ok_or(ValueError::ImpossibleCast)?, + Value::U16(value) => value.to_i8().ok_or(ValueError::ImpossibleCast)?, Value::F64(value) => value.to_i8().ok_or(ValueError::ImpossibleCast)?, Value::Str(value) => value .parse::() @@ -173,6 +180,7 @@ impl TryFrom<&Value> for i16 { Value::I64(value) => value.to_i16().ok_or(ValueError::ImpossibleCast)?, Value::I128(value) => value.to_i16().ok_or(ValueError::ImpossibleCast)?, Value::U8(value) => value.to_i16().ok_or(ValueError::ImpossibleCast)?, + Value::U16(value) => value.to_i16().ok_or(ValueError::ImpossibleCast)?, Value::F64(value) => value.to_i16().ok_or(ValueError::ImpossibleCast)?, Value::Str(value) => value .parse::() @@ -209,6 +217,7 @@ impl TryFrom<&Value> for i32 { Value::I64(value) => value.to_i32().ok_or(ValueError::ImpossibleCast)?, Value::I128(value) => value.to_i32().ok_or(ValueError::ImpossibleCast)?, Value::U8(value) => value.to_i32().ok_or(ValueError::ImpossibleCast)?, + Value::U16(value) => value.to_i32().ok_or(ValueError::ImpossibleCast)?, Value::F64(value) => value.to_i32().ok_or(ValueError::ImpossibleCast)?, Value::Str(value) => value .parse::() @@ -245,6 +254,7 @@ impl TryFrom<&Value> for i64 { Value::I64(value) => *value, Value::I128(value) => value.to_i64().ok_or(ValueError::ImpossibleCast)?, Value::U8(value) => value.to_i64().ok_or(ValueError::ImpossibleCast)?, + Value::U16(value) => value.to_i64().ok_or(ValueError::ImpossibleCast)?, Value::F64(value) => value.to_i64().ok_or(ValueError::ImpossibleCast)?, Value::Str(value) => value .parse::() @@ -281,6 +291,7 @@ impl TryFrom<&Value> for i128 { Value::I64(value) => *value as i128, Value::I128(value) => *value, Value::U8(value) => *value as i128, + Value::U16(value) => *value as i128, Value::F64(value) => value.to_i128().ok_or(ValueError::ImpossibleCast)?, Value::Str(value) => value .parse::() @@ -317,6 +328,7 @@ impl TryFrom<&Value> for u8 { Value::I64(value) => value.to_u8().ok_or(ValueError::ImpossibleCast)?, Value::I128(value) => value.to_u8().ok_or(ValueError::ImpossibleCast)?, Value::U8(value) => *value, + Value::U16(value) => value.to_u8().ok_or(ValueError::ImpossibleCast)?, Value::F64(value) => value.to_u8().ok_or(ValueError::ImpossibleCast)?, Value::Str(value) => value .parse::() @@ -334,6 +346,42 @@ impl TryFrom<&Value> for u8 { }) } } +impl TryFrom<&Value> for u16 { + type Error = Error; + + fn try_from(v: &Value) -> Result { + Ok(match v { + Value::Bool(value) => { + if *value { + 1 + } else { + 0 + } + } + Value::I8(value) => value.to_u16().ok_or(ValueError::ImpossibleCast)?, + Value::I16(value) => value.to_u16().ok_or(ValueError::ImpossibleCast)?, + Value::I32(value) => value.to_u16().ok_or(ValueError::ImpossibleCast)?, + Value::I64(value) => value.to_u16().ok_or(ValueError::ImpossibleCast)?, + Value::I128(value) => value.to_u16().ok_or(ValueError::ImpossibleCast)?, + Value::U8(value) => value.to_u16().ok_or(ValueError::ImpossibleCast)?, + Value::U16(value) => *value, + Value::F64(value) => value.to_u16().ok_or(ValueError::ImpossibleCast)?, + Value::Str(value) => value + .parse::() + .map_err(|_| ValueError::ImpossibleCast)?, + Value::Decimal(value) => value.to_u16().ok_or(ValueError::ImpossibleCast)?, + Value::Date(_) + | Value::Timestamp(_) + | Value::Time(_) + | Value::Interval(_) + | Value::Uuid(_) + | Value::Map(_) + | Value::List(_) + | Value::Bytea(_) + | Value::Null => return Err(ValueError::ImpossibleCast.into()), + }) + } +} impl TryFrom<&Value> for f64 { type Error = Error; @@ -353,6 +401,7 @@ impl TryFrom<&Value> for f64 { Value::I64(value) => value.to_f64().ok_or(ValueError::ImpossibleCast)?, Value::I128(value) => *value as f64, Value::U8(value) => value.to_f64().ok_or(ValueError::ImpossibleCast)?, + Value::U16(value) => value.to_f64().ok_or(ValueError::ImpossibleCast)?, Value::F64(value) => *value, Value::Str(value) => value .parse::() @@ -389,6 +438,7 @@ impl TryFrom<&Value> for usize { Value::I64(value) => value.to_usize().ok_or(ValueError::ImpossibleCast)?, Value::I128(value) => value.to_usize().ok_or(ValueError::ImpossibleCast)?, Value::U8(value) => value.to_usize().ok_or(ValueError::ImpossibleCast)?, + Value::U16(value) => value.to_usize().ok_or(ValueError::ImpossibleCast)?, Value::F64(value) => value.to_usize().ok_or(ValueError::ImpossibleCast)?, Value::Str(value) => value .parse::() @@ -425,6 +475,7 @@ impl TryFrom<&Value> for Decimal { Value::I64(value) => Decimal::from_i64(*value).ok_or(ValueError::ImpossibleCast)?, Value::I128(value) => Decimal::from_i128(*value).ok_or(ValueError::ImpossibleCast)?, Value::U8(value) => Decimal::from_u8(*value).ok_or(ValueError::ImpossibleCast)?, + Value::U16(value) => Decimal::from_u16(*value).ok_or(ValueError::ImpossibleCast)?, Value::F64(value) => Decimal::from_f64(*value).ok_or(ValueError::ImpossibleCast)?, Value::Str(value) => { Decimal::from_str(value).map_err(|_| ValueError::ImpossibleCast)? @@ -456,7 +507,7 @@ macro_rules! try_from_owned_value { )*} } -try_from_owned_value!(bool, i8, i16, i32, i64, i128, f64, u8, u128, usize, Decimal); +try_from_owned_value!(bool, i8, i16, i32, i64, i128, f64, u8, u16, u128, usize, Decimal); impl TryFrom<&Value> for NaiveDate { type Error = Error; @@ -549,6 +600,7 @@ mod tests { test!(Value::I64(1234567890), "1234567890"); test!(Value::I128(1234567890), "1234567890"); test!(Value::U8(122), "122"); + test!(Value::U16(122), "122"); test!(Value::F64(1234567890.0987), "1234567890.0987"); test!(Value::Date(date(2021, 11, 20)), "2021-11-20"); test!( @@ -594,6 +646,9 @@ mod tests { test!(Value::U8(1), Ok(true)); test!(Value::U8(0), Ok(false)); test!(Value::U8(2), Err(ValueError::ImpossibleCast.into())); + test!(Value::U16(1), Ok(true)); + test!(Value::U16(0), Ok(false)); + test!(Value::U16(2), Err(ValueError::ImpossibleCast.into())); test!(Value::F64(1.0), Ok(true)); test!(Value::F64(0.0), Ok(false)); test!(Value::Str("true".to_owned()), Ok(true)); @@ -663,6 +718,7 @@ mod tests { test!(Value::I64(122), Ok(122)); test!(Value::I128(122), Ok(122)); test!(Value::U8(122), Ok(122)); + test!(Value::U16(122), Ok(122)); test!(Value::F64(122.0), Ok(122)); test!(Value::F64(122.9), Ok(122)); test!(Value::Str("122".to_owned()), Ok(122)); @@ -703,6 +759,7 @@ mod tests { test!(Value::I64(128), Err(ValueError::ImpossibleCast.into())); test!(Value::I128(128), Err(ValueError::ImpossibleCast.into())); test!(Value::U8(128), Err(ValueError::ImpossibleCast.into())); + test!(Value::U16(128), Err(ValueError::ImpossibleCast.into())); test!(Value::F64(128.0), Err(ValueError::ImpossibleCast.into())); } @@ -727,6 +784,7 @@ mod tests { test!(Value::I64(122), Ok(122)); test!(Value::I128(122), Ok(122)); test!(Value::U8(122), Ok(122)); + test!(Value::U16(122), Ok(122)); test!(Value::F64(122.0), Ok(122)); test!(Value::F64(122.1), Ok(122)); test!(Value::Str("122".to_owned()), Ok(122)); @@ -783,6 +841,7 @@ mod tests { test!(Value::I64(122), Ok(122)); test!(Value::I128(122), Ok(122)); test!(Value::U8(122), Ok(122)); + test!(Value::U16(122), Ok(122)); test!(Value::I64(1234567890), Ok(1234567890)); test!(Value::F64(1234567890.0), Ok(1234567890)); test!(Value::F64(1234567890.1), Ok(1234567890)); @@ -840,6 +899,7 @@ mod tests { test!(Value::I64(122), Ok(122)); test!(Value::I128(122), Ok(122)); test!(Value::U8(122), Ok(122)); + test!(Value::U16(122), Ok(122)); test!(Value::I64(1234567890), Ok(1234567890)); test!(Value::F64(1234567890.0), Ok(1234567890)); test!(Value::F64(1234567890.1), Ok(1234567890)); @@ -897,6 +957,7 @@ mod tests { test!(Value::I64(122), Ok(122)); test!(Value::I128(122), Ok(122)); test!(Value::U8(122), Ok(122)); + test!(Value::U16(122), Ok(122)); test!(Value::I64(1234567890), Ok(1234567890)); test!(Value::F64(1234567890.0), Ok(1234567890)); test!(Value::F64(1234567890.9), Ok(1234567890)); @@ -954,6 +1015,7 @@ mod tests { test!(Value::I64(122), Ok(122)); test!(Value::I128(122), Ok(122)); test!(Value::U8(122), Ok(122)); + test!(Value::U16(122), Ok(122)); test!(Value::F64(122.0), Ok(122)); test!(Value::F64(122.9), Ok(122)); test!(Value::Str("122".to_owned()), Ok(122)); @@ -1001,6 +1063,63 @@ mod tests { test!(Value::F64(256.0), Err(ValueError::ImpossibleCast.into())); } + #[test] + fn try_into_u16() { + macro_rules! test { + ($from: expr, $to: expr) => { + assert_eq!($from.try_into() as Result, $to); + assert_eq!(u16::try_from($from), $to); + }; + } + let timestamp = |y, m, d, hh, mm, ss, ms| { + chrono::NaiveDate::from_ymd(y, m, d).and_hms_milli(hh, mm, ss, ms) + }; + let time = chrono::NaiveTime::from_hms_milli; + let date = chrono::NaiveDate::from_ymd; + test!(Value::Bool(true), Ok(1)); + test!(Value::Bool(false), Ok(0)); + test!(Value::I8(122), Ok(122)); + test!(Value::I16(122), Ok(122)); + test!(Value::I32(122), Ok(122)); + test!(Value::I64(122), Ok(122)); + test!(Value::I128(122), Ok(122)); + test!(Value::U8(122), Ok(122)); + test!(Value::U16(122), Ok(122)); + test!(Value::F64(122.0), Ok(122)); + test!(Value::F64(122.1), Ok(122)); + test!(Value::Str("122".to_owned()), Ok(122)); + test!(Value::Decimal(Decimal::new(122, 0)), Ok(122)); + test!( + Value::Date(date(2021, 11, 20)), + Err(ValueError::ImpossibleCast.into()) + ); + test!( + Value::Timestamp(timestamp(2021, 11, 20, 10, 0, 0, 0)), + Err(ValueError::ImpossibleCast.into()) + ); + test!( + Value::Time(time(10, 0, 0, 0)), + Err(ValueError::ImpossibleCast.into()) + ); + test!( + Value::Interval(I::Month(1)), + Err(ValueError::ImpossibleCast.into()) + ); + test!( + Value::Uuid(195965723427462096757863453463987888808), + Err(ValueError::ImpossibleCast.into()) + ); + test!( + Value::Map(HashMap::new()), + Err(ValueError::ImpossibleCast.into()) + ); + test!( + Value::List(Vec::new()), + Err(ValueError::ImpossibleCast.into()) + ); + test!(Value::Null, Err(ValueError::ImpossibleCast.into())); + } + #[test] fn try_into_f64() { macro_rules! test { @@ -1022,6 +1141,7 @@ mod tests { test!(Value::I64(122), Ok(122.0)); test!(Value::I128(122), Ok(122.0)); test!(Value::U8(122), Ok(122.0)); + test!(Value::U16(122), Ok(122.0)); test!(Value::I64(1234567890), Ok(1234567890.0)); test!(Value::F64(1234567890.1), Ok(1234567890.1)); test!(Value::Str("1234567890.1".to_owned()), Ok(1234567890.1)); @@ -1081,6 +1201,7 @@ mod tests { test!(Value::I64(122), Ok(122)); test!(Value::I128(122), Ok(122)); test!(Value::U8(122), Ok(122)); + test!(Value::U16(122), Ok(122)); test!(Value::I64(1234567890), Ok(1234567890)); test!(Value::F64(1234567890.0), Ok(1234567890)); test!(Value::F64(1234567890.1), Ok(1234567890)); diff --git a/core/src/data/value/error.rs b/core/src/data/value/error.rs index 5c43b9bb8..6c691bf20 100644 --- a/core/src/data/value/error.rs +++ b/core/src/data/value/error.rs @@ -89,6 +89,9 @@ pub enum ValueError { #[error("literal cast failed from text to Unsigned Int(8): {0}")] LiteralCastFromTextToUnsignedInt8Failed(String), + #[error("literal cast failed from text to UINT16: {0}")] + LiteralCastFromTextToUint16Failed(String), + #[error("literal cast failed from text to float: {0}")] LiteralCastFromTextToFloatFailed(String), @@ -110,6 +113,9 @@ pub enum ValueError { #[error("literal cast failed to Unsigned Int(8): {0}")] LiteralCastToUnsignedInt8Failed(String), + #[error("literal cast failed to UINT16: {0}")] + LiteralCastToUint16Failed(String), + #[error("literal cast failed to time: {0}")] LiteralCastToTimeFailed(String), diff --git a/core/src/data/value/expr.rs b/core/src/data/value/expr.rs index 1729004de..94b6980bd 100644 --- a/core/src/data/value/expr.rs +++ b/core/src/data/value/expr.rs @@ -41,6 +41,10 @@ impl TryFrom for Expr { Value::U8(v) => Expr::Literal(AstLiteral::Number( BigDecimal::from_u8(v).ok_or(ValueToExprConversionFailure)?, )), + Value::U16(v) => Expr::Literal(AstLiteral::Number( + BigDecimal::from_u16(v).ok_or(ValueToExprConversionFailure)?, + )), + Value::F64(v) => Expr::Literal(AstLiteral::Number( BigDecimal::from_f64(v).ok_or(ValueToExprConversionFailure)?, )), diff --git a/core/src/data/value/json.rs b/core/src/data/value/json.rs index d73873822..aa661724f 100644 --- a/core/src/data/value/json.rs +++ b/core/src/data/value/json.rs @@ -44,6 +44,7 @@ impl TryFrom for JsonValue { .map(JsonValue::Number) .map_err(|_| ValueError::UnreachableJsonNumberParseFailure(v.to_string()).into()), Value::U8(v) => Ok(v.into()), + Value::U16(v) => Ok(v.into()), Value::F64(v) => Ok(v.into()), Value::Decimal(v) => JsonNumber::from_str(&v.to_string()) .map(JsonValue::Number) @@ -143,6 +144,10 @@ mod tests { Ok(JsonValue::Number(100.into())) ); assert_eq!(Value::U8(100).try_into(), Ok(JsonValue::Number(100.into()))); + assert_eq!( + Value::U16(100).try_into(), + Ok(JsonValue::Number(100.into())) + ); assert!(JsonValue::try_from(Value::I128(i128::MAX)).is_ok()); assert_eq!( diff --git a/core/src/data/value/literal.rs b/core/src/data/value/literal.rs index 4e1a4045b..501441b73 100644 --- a/core/src/data/value/literal.rs +++ b/core/src/data/value/literal.rs @@ -24,6 +24,7 @@ impl PartialEq> for Value { (Value::I64(l), Literal::Number(r)) => r.to_i64().map(|r| *l == r).unwrap_or(false), (Value::I128(l), Literal::Number(r)) => r.to_i128().map(|r| *l == r).unwrap_or(false), (Value::U8(l), Literal::Number(r)) => r.to_u8().map(|r| *l == r).unwrap_or(false), + (Value::U16(l), Literal::Number(r)) => r.to_u16().map(|r| *l == r).unwrap_or(false), (Value::F64(l), Literal::Number(r)) => r.to_f64().map(|r| *l == r).unwrap_or(false), (Value::Str(l), Literal::Text(r)) => l == r.as_ref(), (Value::Bytea(l), Literal::Bytea(r)) => l == r, @@ -66,6 +67,9 @@ impl PartialOrd> for Value { (Value::U8(l), Literal::Number(r)) => { r.to_u8().map(|r| l.partial_cmp(&r)).unwrap_or(None) } + (Value::U16(l), Literal::Number(r)) => { + r.to_u16().map(|r| l.partial_cmp(&r)).unwrap_or(None) + } (Value::F64(l), Literal::Number(r)) => { r.to_f64().map(|r| l.partial_cmp(&r)).unwrap_or(None) } @@ -147,6 +151,10 @@ impl Value { .to_u8() .map(Value::U8) .ok_or_else(|| ValueError::FailedToParseNumber.into()), + (DataType::Uint16, Literal::Number(v)) => v + .to_u16() + .map(Value::U16) + .ok_or_else(|| ValueError::FailedToParseNumber.into()), (DataType::Float, Literal::Number(v)) => v .to_f64() .map(Value::F64) @@ -284,6 +292,19 @@ impl Value { Ok(Value::U8(v)) } + (DataType::Uint16, Literal::Text(v)) => v + .parse::() + .map(Value::U16) + .map_err(|_| ValueError::LiteralCastFromTextToUint16Failed(v.to_string()).into()), + (DataType::Uint16, Literal::Number(v)) => match v.to_u16() { + Some(x) => Ok(Value::U16(x)), + None => Err(ValueError::LiteralCastToUint16Failed(v.to_string()).into()), + }, + (DataType::Uint16, Literal::Boolean(v)) => { + let v = if *v { 1 } else { 0 }; + + Ok(Value::U16(v)) + } (DataType::Float, Literal::Text(v)) => v .parse::() .map(Value::F64) @@ -329,6 +350,7 @@ impl Value { | (DataType::Int, Literal::Null) | (DataType::Int128, Literal::Null) | (DataType::Uint8, Literal::Null) + | (DataType::Uint16, Literal::Null) | (DataType::Float, Literal::Null) | (DataType::Decimal, Literal::Null) | (DataType::Text, Literal::Null) => Ok(Value::Null), @@ -394,6 +416,7 @@ mod tests { assert_eq!(Value::I128(128), num!("128")); assert_eq!(Value::F64(7.123), num!("7.123")); assert_eq!(Value::U8(7), num!("7")); + assert_eq!(Value::U16(64), num!("64")); assert_eq!(Value::Str("Hello".to_owned()), text!("Hello")); assert_eq!(Value::Bytea(bytea()), Literal::Bytea(bytea())); assert_eq!(Value::Date(date(2021, 11, 20)), text!("2021-11-20")); @@ -509,6 +532,7 @@ mod tests { test!(DataType::Int, num!("64"), Value::I64(64)); test!(DataType::Int128, num!("64"), Value::I128(64)); test!(DataType::Uint8, num!("8"), Value::U8(8)); + test!(DataType::Uint16, num!("64"), Value::U16(64)); test!(DataType::Float, num!("123456789"), Value::F64(123456789.0)); test!( @@ -705,6 +729,11 @@ mod tests { test!(DataType::Uint8, Literal::Boolean(true), Value::U8(1)); test!(DataType::Uint8, Literal::Boolean(false), Value::U8(0)); + test!(DataType::Uint16, text!("127"), Value::U16(127)); + test!(DataType::Uint16, num!("125"), Value::U16(125)); + test!(DataType::Uint16, Literal::Boolean(true), Value::U16(1)); + test!(DataType::Uint16, Literal::Boolean(false), Value::U16(0)); + test!(DataType::Float, text!("12345.6789"), Value::F64(12345.6789)); test!(DataType::Float, num!("123456.789"), Value::F64(123456.789)); test!(DataType::Float, Literal::Boolean(true), Value::F64(1.0)); @@ -739,6 +768,7 @@ mod tests { test_null!(DataType::Int, Literal::Null); test_null!(DataType::Int8, Literal::Null); test_null!(DataType::Uint8, Literal::Null); + test_null!(DataType::Uint16, Literal::Null); test_null!(DataType::Float, Literal::Null); test_null!(DataType::Text, Literal::Null); test!( diff --git a/core/src/data/value/mod.rs b/core/src/data/value/mod.rs index 055cc422b..4f0717596 100644 --- a/core/src/data/value/mod.rs +++ b/core/src/data/value/mod.rs @@ -30,6 +30,7 @@ pub enum Value { I64(i64), I128(i128), U8(u8), + U16(u16), F64(f64), Decimal(Decimal), Str(String), @@ -53,6 +54,7 @@ impl PartialEq for Value { (Value::I64(l), _) => l == other, (Value::I128(l), _) => l == other, (Value::U8(l), _) => l == other, + (Value::U16(l), _) => l == other, (Value::F64(l), _) => l == other, (Value::Decimal(l), Value::Decimal(r)) => l == r, (Value::Bool(l), Value::Bool(r)) => l == r, @@ -81,6 +83,7 @@ impl PartialOrd for Value { (Value::I64(l), _) => l.partial_cmp(other), (Value::I128(l), _) => l.partial_cmp(other), (Value::U8(l), _) => l.partial_cmp(other), + (Value::U16(l), _) => l.partial_cmp(other), (Value::F64(l), _) => l.partial_cmp(other), (Value::Decimal(l), Value::Decimal(r)) => Some(l.cmp(r)), (Value::Bool(l), Value::Bool(r)) => Some(l.cmp(r)), @@ -107,6 +110,7 @@ impl Value { Value::I64(v) => *v == 0, Value::I128(v) => *v == 0, Value::U8(v) => *v == 0, + Value::U16(v) => *v == 0, Value::F64(v) => *v == 0.0, Value::Decimal(v) => *v == Decimal::ZERO, _ => false, @@ -121,6 +125,7 @@ impl Value { Value::I64(_) => Some(DataType::Int), Value::I128(_) => Some(DataType::Int128), Value::U8(_) => Some(DataType::Uint8), + Value::U16(_) => Some(DataType::Uint16), Value::F64(_) => Some(DataType::Float), Value::Decimal(_) => Some(DataType::Decimal), Value::Bool(_) => Some(DataType::Boolean), @@ -145,6 +150,7 @@ impl Value { Value::I64(_) => matches!(data_type, DataType::Int), Value::I128(_) => matches!(data_type, DataType::Int128), Value::U8(_) => matches!(data_type, DataType::Uint8), + Value::U16(_) => matches!(data_type, DataType::Uint16), Value::F64(_) => matches!(data_type, DataType::Float), Value::Decimal(_) => matches!(data_type, DataType::Decimal), Value::Bool(_) => matches!(data_type, DataType::Boolean), @@ -187,6 +193,7 @@ impl Value { | (DataType::Int, Value::I64(_)) | (DataType::Int128, Value::I128(_)) | (DataType::Uint8, Value::U8(_)) + | (DataType::Uint16, Value::U16(_)) | (DataType::Float, Value::F64(_)) | (DataType::Decimal, Value::Decimal(_)) | (DataType::Boolean, Value::Bool(_)) @@ -206,6 +213,7 @@ impl Value { (DataType::Int, value) => value.try_into().map(Value::I64), (DataType::Int128, value) => value.try_into().map(Value::I128), (DataType::Uint8, value) => value.try_into().map(Value::U8), + (DataType::Uint16, value) => value.try_into().map(Value::U16), (DataType::Float, value) => value.try_into().map(Value::F64), (DataType::Decimal, value) => value.try_into().map(Value::Decimal), (DataType::Text, value) => Ok(Value::Str(value.into())), @@ -236,6 +244,7 @@ impl Value { (I64(a), b) => a.try_add(b), (I128(a), b) => a.try_add(b), (U8(a), b) => a.try_add(b), + (U16(a), b) => a.try_add(b), (F64(a), b) => a.try_add(b), (Decimal(a), b) => a.try_add(b), (Date(a), Time(b)) => Ok(Timestamp(NaiveDateTime::new(*a, *b))), @@ -249,6 +258,7 @@ impl Value { | (Null, I64(_)) | (Null, I128(_)) | (Null, U8(_)) + | (Null, U16(_)) | (Null, F64(_)) | (Null, Decimal(_)) | (Null, Date(_)) @@ -279,6 +289,7 @@ impl Value { (I64(a), _) => a.try_subtract(other), (I128(a), _) => a.try_subtract(other), (U8(a), _) => a.try_subtract(other), + (U16(a), _) => a.try_subtract(other), (F64(a), _) => a.try_subtract(other), (Decimal(a), _) => a.try_subtract(other), (Date(a), Date(b)) => Ok(Interval(I::days((*a - *b).num_days() as i32))), @@ -306,6 +317,7 @@ impl Value { | (Null, I64(_)) | (Null, I128(_)) | (Null, U8(_)) + | (Null, U16(_)) | (Null, F64(_)) | (Null, Decimal(_)) | (Null, Date(_)) @@ -336,6 +348,7 @@ impl Value { (I64(a), _) => a.try_multiply(other), (I128(a), _) => a.try_multiply(other), (U8(a), _) => a.try_multiply(other), + (U16(a), _) => a.try_multiply(other), (F64(a), _) => a.try_multiply(other), (Decimal(a), _) => a.try_multiply(other), (Interval(a), I8(b)) => Ok(Interval(*a * *b)), @@ -350,6 +363,7 @@ impl Value { | (Null, I64(_)) | (Null, I128(_)) | (Null, U8(_)) + | (Null, U16(_)) | (Null, F64(_)) | (Null, Decimal(_)) | (Null, Interval(_)) @@ -378,6 +392,7 @@ impl Value { (I64(a), _) => a.try_divide(other), (I128(a), _) => a.try_divide(other), (U8(a), _) => a.try_divide(other), + (U16(a), _) => a.try_divide(other), (F64(a), _) => a.try_divide(other), (Decimal(a), _) => a.try_divide(other), (Interval(a), I8(b)) => Ok(Interval(*a / *b)), @@ -386,6 +401,7 @@ impl Value { (Interval(a), I64(b)) => Ok(Interval(*a / *b)), (Interval(a), I128(b)) => Ok(Interval(*a / *b)), (Interval(a), U8(b)) => Ok(Interval(*a / *b)), + (Interval(a), U16(b)) => Ok(Interval(*a / *b)), (Interval(a), F64(b)) => Ok(Interval(*a / *b)), (Null, I8(_)) | (Null, I16(_)) @@ -393,6 +409,7 @@ impl Value { | (Null, I64(_)) | (Null, I128(_)) | (Null, U8(_)) + | (Null, U16(_)) | (Null, F64(_)) | (Null, Decimal(_)) | (Interval(_), Null) @@ -420,6 +437,7 @@ impl Value { (I64(a), _) => a.try_modulo(other), (I128(a), _) => a.try_modulo(other), (U8(a), _) => a.try_modulo(other), + (U16(a), _) => a.try_modulo(other), (F64(a), _) => a.try_modulo(other), (Decimal(a), _) => a.try_modulo(other), (Null, I8(_)) @@ -428,6 +446,7 @@ impl Value { | (Null, I64(_)) | (Null, I128(_)) | (Null, U8(_)) + | (Null, U16(_)) | (Null, F64(_)) | (Null, Decimal(_)) | (Null, Null) => Ok(Null), @@ -448,7 +467,7 @@ impl Value { use Value::*; match self { - I8(_) | I16(_) | I32(_) | I64(_) | I128(_) | U8(_) | F64(_) | Interval(_) + I8(_) | I16(_) | I32(_) | I64(_) | I128(_) | U8(_) | U16(_) | F64(_) | Interval(_) | Decimal(_) => Ok(self.clone()), Null => Ok(Null), _ => Err(ValueError::UnaryPlusOnNonNumeric.into()), @@ -493,6 +512,7 @@ impl Value { I64(a) => factorial_function(*a as i128).map(I128), I128(a) => factorial_function(*a).map(I128), U8(a) => factorial_function(*a as i128).map(I128), + U16(a) => factorial_function(*a as i128).map(I128), F64(_) => Err(ValueError::FactorialOnNonInteger.into()), Null => Ok(Null), _ => Err(ValueError::FactorialOnNonNumeric.into()), @@ -543,7 +563,7 @@ impl Value { pub fn sqrt(&self) -> Result { use Value::*; match self { - I8(_) | I16(_) | I64(_) | I128(_) | U8(_) | F64(_) => { + I8(_) | I16(_) | I64(_) | I128(_) | U8(_) | U16(_) | F64(_) => { let a: f64 = self.try_into()?; Ok(Value::F64(a.sqrt())) } @@ -629,6 +649,7 @@ mod tests { assert_eq!(I64(1), I64(1)); assert_eq!(I128(1), I128(1)); assert_eq!(U8(1), U8(1)); + assert_eq!(U16(1), U16(1)); assert_eq!(I64(1), F64(1.0)); assert_eq!(F64(1.0), I64(1)); assert_eq!(F64(6.11), F64(6.11)); @@ -731,6 +752,10 @@ mod tests { assert_eq!(U8(1).partial_cmp(&U8(0)), Some(Ordering::Greater)); assert_eq!(U8(0).partial_cmp(&U8(0)), Some(Ordering::Equal)); assert_eq!(U8(0).partial_cmp(&U8(1)), Some(Ordering::Less)); + + assert_eq!(U16(1).partial_cmp(&U16(0)), Some(Ordering::Greater)); + assert_eq!(U16(0).partial_cmp(&U16(0)), Some(Ordering::Equal)); + assert_eq!(U16(0).partial_cmp(&U16(1)), Some(Ordering::Less)); } #[test] @@ -831,6 +856,14 @@ mod tests { test!(add U8(1), U8(2) => U8(3)); test!(add U8(1), F64(2.0) => F64(3.0)); + test!(add U16(1), I8(2) => U16(3)); + test!(add U16(1), I16(2) => U16(3)); + test!(add U16(1), I32(2) => U16(3)); + test!(add U16(1), I64(2) => U16(3)); + test!(add U16(1), I128(2) => U16(3)); + test!(add U16(1), U8(2) => U16(3)); + test!(add U16(1), F64(2.0) => F64(3.0)); + test!(add F64(1.0), F64(2.0) => F64(3.0)); test!(add F64(1.0), I8(2) => F64(3.0)); test!(add F64(1.0), I32(2) => F64(3.0)); @@ -907,6 +940,14 @@ mod tests { test!(subtract U8(3), U8(2) => U8(1)); test!(subtract U8(3), F64(2.0) => F64(1.0)); + test!(subtract U16(3), I8(2) => U16(1)); + test!(subtract U16(3), I16(2) => U16(1)); + test!(subtract U16(3), I32(2) => U16(1)); + test!(subtract U16(3), I64(2) => U16(1)); + test!(subtract U16(3), I128(2) => U16(1)); + test!(subtract U16(3), U8(2) => U16(1)); + test!(subtract U16(3), F64(2.0) => F64(1.0)); + test!(subtract I8(3), F64(2.0) => F64(1.0)); test!(subtract I32(3), F64(2.0) => F64(1.0)); test!(subtract I64(3), F64(2.0) => F64(1.0)); @@ -1008,6 +1049,14 @@ mod tests { test!(multiply U8(3), U8(2) => U8(6)); test!(multiply U8(3), F64(2.0) => F64(6.0)); + test!(multiply U16(3), I8(2) => U16(6)); + test!(multiply U16(3), I16(2) => U16(6)); + test!(multiply U16(3), I32(2) => U16(6)); + test!(multiply U16(3), I64(2) => U16(6)); + test!(multiply U16(3), I128(2) => U16(6)); + test!(multiply U16(3), U8(2) => U16(6)); + test!(multiply U16(3), F64(2.0) => F64(6.0)); + test!(multiply F64(3.0), F64(2.0) => F64(6.0)); test!(multiply F64(3.0), I8(2) => F64(6.0)); test!(multiply F64(3.0), I32(2) => F64(6.0)); @@ -1075,6 +1124,14 @@ mod tests { test!(divide U8(6), U8(2) => U8(3)); test!(divide U8(6), F64(2.0) => F64(3.0)); + test!(divide U16(6), I8(2) => U16(3)); + test!(divide U16(6), I16(2) => U16(3)); + test!(divide U16(6), I32(2) => U16(3)); + test!(divide U16(6), I64(2) => U16(3)); + test!(divide U16(6), I128(2) => U16(3)); + test!(divide U16(6), U8(2) => U16(3)); + test!(divide U16(6), F64(2.0) => F64(3.0)); + test!(divide I8(6), F64(2.0) => F64(3.0)); test!(divide I32(6), F64(2.0) => F64(3.0)); test!(divide I64(6), F64(2.0) => F64(3.0)); @@ -1150,6 +1207,7 @@ mod tests { null_test!(add I64(1), Null); null_test!(add I128(1), Null); null_test!(add U8(1), Null); + null_test!(add U16(1), Null); null_test!(add F64(1.0), Null); null_test!(add decimal(1), Null); null_test!(add date(), Null); @@ -1162,6 +1220,7 @@ mod tests { null_test!(subtract I64(1), Null); null_test!(subtract I128(1), Null); null_test!(subtract U8(1), Null); + null_test!(subtract U16(1), Null); null_test!(subtract F64(1.0), Null); null_test!(subtract decimal(1), Null); null_test!(subtract date(), Null); @@ -1174,6 +1233,7 @@ mod tests { null_test!(multiply I64(1), Null); null_test!(multiply I128(1), Null); null_test!(multiply U8(1), Null); + null_test!(multiply U16(1), Null); null_test!(multiply F64(1.0), Null); null_test!(multiply decimal(1), Null); null_test!(multiply mon!(1), Null); @@ -1183,6 +1243,7 @@ mod tests { null_test!(divide I64(1), Null); null_test!(divide I128(1), Null); null_test!(divide U8(1), Null); + null_test!(divide U16(1), Null); null_test!(divide F64(1.0), Null); null_test!(divide decimal(1), Null); null_test!(divide mon!(1), Null); @@ -1192,6 +1253,7 @@ mod tests { null_test!(modulo I64(1), Null); null_test!(modulo I128(1), Null); null_test!(modulo U8(1), Null); + null_test!(modulo U16(1), Null); null_test!(modulo F64(1.0), Null); null_test!(modulo decimal(1), Null); @@ -1201,6 +1263,7 @@ mod tests { null_test!(add Null, I64(1)); null_test!(add Null, I128(1)); null_test!(add Null, U8(1)); + null_test!(add Null, U16(1)); null_test!(add Null, F64(1.0)); null_test!(add Null, decimal(1)); null_test!(add Null, mon!(1)); @@ -1212,6 +1275,7 @@ mod tests { null_test!(subtract Null, I64(1)); null_test!(subtract Null, I128(1)); null_test!(subtract Null, U8(1)); + null_test!(subtract Null, U16(1)); null_test!(subtract Null, F64(1.0)); null_test!(subtract Null, decimal(1)); null_test!(subtract Null, date()); @@ -1224,6 +1288,7 @@ mod tests { null_test!(multiply Null, I64(1)); null_test!(multiply Null, I128(1)); null_test!(multiply Null, U8(1)); + null_test!(multiply Null, U16(1)); null_test!(multiply Null, F64(1.0)); null_test!(multiply Null, decimal(1)); null_test!(divide Null, I8(1)); @@ -1232,6 +1297,7 @@ mod tests { null_test!(divide Null, I64(1)); null_test!(divide Null, I128(1)); null_test!(divide Null, U8(1)); + null_test!(divide Null, U16(1)); null_test!(divide Null, F64(1.0)); null_test!(divide Null, decimal(1)); null_test!(modulo Null, I8(1)); @@ -1239,6 +1305,7 @@ mod tests { null_test!(modulo Null, I64(1)); null_test!(modulo Null, I128(1)); null_test!(modulo Null, U8(1)); + null_test!(modulo Null, U16(1)); null_test!(modulo Null, F64(1.0)); null_test!(modulo Null, decimal(1)); @@ -1281,6 +1348,7 @@ mod tests { cast!(I64(1) => Int , I64(1)); cast!(I128(1) => Int128 , I128(1)); cast!(U8(1) => Uint8 , U8(1)); + cast!(U16(1) => Uint16 , U16(1)); cast!(F64(1.0) => Float , F64(1.0)); cast!(Value::Uuid(123) => Uuid , Value::Uuid(123)); @@ -1298,6 +1366,8 @@ mod tests { cast!(I128(0) => Boolean, Bool(false)); cast!(U8(1) => Boolean, Bool(true)); cast!(U8(0) => Boolean, Bool(false)); + cast!(U16(1) => Boolean, Bool(true)); + cast!(U16(0) => Boolean, Bool(false)); cast!(F64(1.0) => Boolean, Bool(true)); cast!(F64(0.0) => Boolean, Bool(false)); cast!(Null => Boolean, Null); @@ -1342,6 +1412,7 @@ mod tests { cast!(I64(1) => Float, F64(1.0)); cast!(I128(1) => Float, F64(1.0)); cast!(U8(1) => Float, F64(1.0)); + cast!(U16(1) => Float, F64(1.0)); cast!(Str("11".to_owned()) => Float, F64(11.0)); cast!(Null => Float, Null); @@ -1354,6 +1425,7 @@ mod tests { cast!(I64(11) => Text, Str("11".to_owned())); cast!(I128(11) => Text, Str("11".to_owned())); cast!(U8(11) => Text, Str("11".to_owned())); + cast!(U16(11) => Text, Str("11".to_owned())); cast!(F64(1.0) => Text, Str("1".to_owned())); let date = Value::Date(NaiveDate::from_ymd(2021, 5, 1)); @@ -1393,6 +1465,7 @@ mod tests { assert_eq!(a.concat(&I64(1)), Str("A1".to_owned())); assert_eq!(a.concat(&I128(1)), Str("A1".to_owned())); assert_eq!(a.concat(&U8(1)), Str("A1".to_owned())); + assert_eq!(a.concat(&U16(1)), Str("A1".to_owned())); assert_eq!(a.concat(&F64(1.0)), Str("A1".to_owned())); assert_eq!(I64(2).concat(&I64(1)), Str("21".to_owned())); assert!(a.concat(&Null).is_null()); @@ -1428,6 +1501,8 @@ mod tests { assert!(I128(1).validate_type(&D::Text).is_err()); assert!(U8(1).validate_type(&D::Uint8).is_ok()); assert!(U8(1).validate_type(&D::Text).is_err()); + assert!(U16(1).validate_type(&D::Uint16).is_ok()); + assert!(U16(1).validate_type(&D::Text).is_err()); assert!(F64(1.0).validate_type(&D::Float).is_ok()); assert!(F64(1.0).validate_type(&D::Int).is_err()); assert!(Decimal(rust_decimal::Decimal::ONE) @@ -1501,6 +1576,7 @@ mod tests { assert_eq!(I64(5).unary_factorial(), Ok(I128(120))); assert_eq!(I128(5).unary_factorial(), Ok(I128(120))); assert_eq!(U8(5).unary_factorial(), Ok(I128(120))); + assert_eq!(U16(5).unary_factorial(), Ok(I128(120))); assert_eq!( F64(5.0).unary_factorial(), Err(ValueError::FactorialOnNonInteger.into()) @@ -1519,6 +1595,7 @@ mod tests { assert_eq!(I64(9).sqrt(), Ok(F64(3.0))); assert_eq!(I128(9).sqrt(), Ok(F64(3.0))); assert_eq!(U8(9).sqrt(), Ok(F64(3.0))); + assert_eq!(U16(9).sqrt(), Ok(F64(3.0))); assert_eq!(F64(9.0).sqrt(), Ok(F64(3.0))); assert!(Null.sqrt().unwrap().is_null()); assert_eq!( @@ -1572,6 +1649,7 @@ mod tests { assert_eq!(I64(1).get_type(), Some(D::Int)); assert_eq!(I128(1).get_type(), Some(D::Int128)); assert_eq!(U8(1).get_type(), Some(D::Uint8)); + assert_eq!(U16(1).get_type(), Some(D::Uint16)); assert_eq!(F64(1.1).get_type(), Some(D::Float)); assert_eq!(decimal.get_type(), Some(D::Decimal)); assert_eq!(Bool(true).get_type(), Some(D::Boolean)); diff --git a/core/src/executor/alter/table.rs b/core/src/executor/alter/table.rs index 0ae0b3e94..abbd6814d 100644 --- a/core/src/executor/alter/table.rs +++ b/core/src/executor/alter/table.rs @@ -1,13 +1,14 @@ use { super::{validate, AlterError}, crate::{ - ast::{ColumnDef, ColumnOption, ColumnOptionDef, Query, SetExpr, TableFactor, Values}, + ast::{ColumnDef, ColumnOption, Query, SetExpr, TableFactor, Values}, data::{Schema, TableError}, executor::{evaluate_stateless, select::select}, prelude::{DataType, Value}, result::{Error, IntoControlFlow, MutResult, Result, TrySelf}, store::{GStore, GStoreMut}, }, + chrono::Utc, futures::stream::{self, TryStreamExt}, std::{ iter, @@ -41,10 +42,7 @@ pub async fn create_table( let column_def = ColumnDef { name: "N".into(), data_type: DataType::Int, - options: vec![ColumnOptionDef { - name: None, - option: ColumnOption::NotNull, - }], + options: vec![ColumnOption::NotNull], }; vec![column_def] @@ -93,10 +91,7 @@ pub async fn create_table( .map(|(i, data_type)| ColumnDef { name: format!("column{}", i + 1), data_type, - options: vec![ColumnOptionDef { - name: None, - option: ColumnOption::Null, - }], + options: vec![ColumnOption::Null], }) .collect::>(); @@ -110,6 +105,7 @@ pub async fn create_table( table_name: target_table_name.to_owned(), column_defs: target_columns_defs, indexes: vec![], + created: Utc::now().naive_utc(), }; for column_def in &schema.column_defs { diff --git a/core/src/executor/alter/validate.rs b/core/src/executor/alter/validate.rs index dd6a2631e..4d767c6aa 100644 --- a/core/src/executor/alter/validate.rs +++ b/core/src/executor/alter/validate.rs @@ -1,7 +1,7 @@ use { super::AlterError, crate::{ - ast::{ColumnDef, ColumnOption, ColumnOptionDef, DataType}, + ast::{ColumnDef, ColumnOption, DataType}, executor::evaluate_stateless, result::Result, }, @@ -19,7 +19,7 @@ pub fn validate(column_def: &ColumnDef) -> Result<()> { if matches!(data_type, DataType::Float | DataType::Map) && options .iter() - .any(|ColumnOptionDef { option, .. }| matches!(option, ColumnOption::Unique { .. })) + .any(|option| matches!(option, ColumnOption::Unique { .. })) { return Err(AlterError::UnsupportedDataTypeForUniqueColumn( name.to_owned(), @@ -28,12 +28,10 @@ pub fn validate(column_def: &ColumnDef) -> Result<()> { .into()); } - let default = options - .iter() - .find_map(|ColumnOptionDef { option, .. }| match option { - ColumnOption::Default(expr) => Some(expr), - _ => None, - }); + let default = options.iter().find_map(|option| match option { + ColumnOption::Default(expr) => Some(expr), + _ => None, + }); if let Some(expr) = default { evaluate_stateless(None, expr)?; diff --git a/core/src/executor/evaluate/evaluated.rs b/core/src/executor/evaluate/evaluated.rs index 9b6bed689..36f471d40 100644 --- a/core/src/executor/evaluate/evaluated.rs +++ b/core/src/executor/evaluate/evaluated.rs @@ -5,24 +5,18 @@ use { data::{Key, Literal, Value}, result::{Error, Result}, }, - std::{borrow::Cow, cmp::Ordering}, + std::cmp::Ordering, }; #[derive(Clone)] pub enum Evaluated<'a> { Literal(Literal<'a>), - Value(Cow<'a, Value>), + Value(Value), } impl<'a> From for Evaluated<'a> { fn from(value: Value) -> Self { - Evaluated::Value(Cow::Owned(value)) - } -} - -impl<'a> From<&'a Value> for Evaluated<'a> { - fn from(value: &'a Value) -> Self { - Evaluated::Value(Cow::Borrowed(value)) + Evaluated::Value(value) } } @@ -32,7 +26,7 @@ impl TryFrom> for Value { fn try_from(e: Evaluated<'_>) -> Result { match e { Evaluated::Literal(v) => Value::try_from(v), - Evaluated::Value(v) => Ok(v.into_owned()), + Evaluated::Value(v) => Ok(v), } } } @@ -51,7 +45,7 @@ impl TryFrom<&Evaluated<'_>> for Key { fn try_from(evaluated: &Evaluated<'_>) -> Result { match evaluated { Evaluated::Literal(l) => Value::try_from(l)?.try_into(), - Evaluated::Value(v) => v.as_ref().try_into(), + Evaluated::Value(v) => v.try_into(), } } } @@ -65,8 +59,7 @@ impl TryFrom> for bool { Evaluated::Literal(v) => { Err(EvaluateError::BooleanTypeRequired(format!("{:?}", v)).into()) } - Evaluated::Value(Cow::Owned(Value::Bool(v))) => Ok(v), - Evaluated::Value(Cow::Borrowed(Value::Bool(v))) => Ok(*v), + Evaluated::Value(Value::Bool(v)) => Ok(v), Evaluated::Value(v) => { Err(EvaluateError::BooleanTypeRequired(format!("{:?}", v)).into()) } @@ -79,7 +72,7 @@ impl<'a> PartialEq for Evaluated<'a> { match (self, other) { (Evaluated::Literal(a), Evaluated::Literal(b)) => a == b, (Evaluated::Literal(b), Evaluated::Value(a)) - | (Evaluated::Value(a), Evaluated::Literal(b)) => a.as_ref() == b, + | (Evaluated::Value(a), Evaluated::Literal(b)) => a == b, (Evaluated::Value(a), Evaluated::Value(b)) => a == b, } } @@ -89,11 +82,9 @@ impl<'a> PartialOrd for Evaluated<'a> { fn partial_cmp(&self, other: &Evaluated<'a>) -> Option { match (self, other) { (Evaluated::Literal(l), Evaluated::Literal(r)) => l.partial_cmp(r), - (Evaluated::Literal(l), Evaluated::Value(r)) => { - r.as_ref().partial_cmp(l).map(|o| o.reverse()) - } - (Evaluated::Value(l), Evaluated::Literal(r)) => l.as_ref().partial_cmp(r), - (Evaluated::Value(l), Evaluated::Value(r)) => l.as_ref().partial_cmp(r.as_ref()), + (Evaluated::Literal(l), Evaluated::Value(r)) => r.partial_cmp(l).map(|o| o.reverse()), + (Evaluated::Value(l), Evaluated::Literal(r)) => l.partial_cmp(r), + (Evaluated::Value(l), Evaluated::Value(r)) => l.partial_cmp(r), } } } @@ -111,14 +102,12 @@ where match (l, r) { (Evaluated::Literal(l), Evaluated::Literal(r)) => literal_op(l, r).map(Evaluated::Literal), (Evaluated::Literal(l), Evaluated::Value(r)) => { - value_op(&Value::try_from(l)?, r.as_ref()).map(Evaluated::from) + value_op(&Value::try_from(l)?, r).map(Evaluated::from) } (Evaluated::Value(l), Evaluated::Literal(r)) => { - value_op(l.as_ref(), &Value::try_from(r)?).map(Evaluated::from) - } - (Evaluated::Value(l), Evaluated::Value(r)) => { - value_op(l.as_ref(), r.as_ref()).map(Evaluated::from) + value_op(l, &Value::try_from(r)?).map(Evaluated::from) } + (Evaluated::Value(l), Evaluated::Value(r)) => value_op(l, r).map(Evaluated::from), } } @@ -180,14 +169,12 @@ impl<'a> Evaluated<'a> { let evaluated = match (self, other) { (Evaluated::Literal(l), Evaluated::Literal(r)) => Evaluated::Literal(l.concat(r)), (Evaluated::Literal(l), Evaluated::Value(r)) => { - Evaluated::from((&Value::try_from(l)?).concat(r.as_ref())) + Evaluated::from((&Value::try_from(l)?).concat(&r)) } (Evaluated::Value(l), Evaluated::Literal(r)) => { - Evaluated::from(l.as_ref().concat(&Value::try_from(r)?)) - } - (Evaluated::Value(l), Evaluated::Value(r)) => { - Evaluated::from(l.as_ref().concat(r.as_ref())) + Evaluated::from(l.concat(&Value::try_from(r)?)) } + (Evaluated::Value(l), Evaluated::Value(r)) => Evaluated::from(l.concat(&r)), }; Ok(evaluated) @@ -199,13 +186,13 @@ impl<'a> Evaluated<'a> { Evaluated::Literal(l.like(&r, case_sensitive)?) } (Evaluated::Literal(l), Evaluated::Value(r)) => { - Evaluated::from((&Value::try_from(l)?).like(r.as_ref(), case_sensitive)?) + Evaluated::from((&Value::try_from(l)?).like(&r, case_sensitive)?) } (Evaluated::Value(l), Evaluated::Literal(r)) => { - Evaluated::from(l.as_ref().like(&Value::try_from(r)?, case_sensitive)?) + Evaluated::from(l.like(&Value::try_from(r)?, case_sensitive)?) } (Evaluated::Value(l), Evaluated::Value(r)) => { - Evaluated::from(l.as_ref().like(r.as_ref(), case_sensitive)?) + Evaluated::from(l.like(&r, case_sensitive)?) } }; @@ -221,7 +208,7 @@ impl<'a> Evaluated<'a> { pub fn try_into_value(self, data_type: &DataType, nullable: bool) -> Result { let value = match self { - Evaluated::Value(v) => v.into_owned(), + Evaluated::Value(v) => v, Evaluated::Literal(v) => Value::try_from_literal(data_type, &v)?, }; diff --git a/core/src/executor/evaluate/function.rs b/core/src/executor/evaluate/function.rs index 7d24c76e6..17b55f203 100644 --- a/core/src/executor/evaluate/function.rs +++ b/core/src/executor/evaluate/function.rs @@ -5,7 +5,10 @@ use { data::Value, result::Result, }, - std::cmp::{max, min}, + std::{ + cmp::{max, min}, + ops::ControlFlow, + }, uuid::Uuid, }; @@ -14,7 +17,7 @@ macro_rules! eval_to_str { match $evaluated.try_into()? { Value::Str(value) => value, Value::Null => { - return Ok(Value::Null); + return Ok(Evaluated::from(Value::Null)); } _ => { return Err(EvaluateError::FunctionRequiresStringValue($name).into()); @@ -28,7 +31,7 @@ macro_rules! eval_to_int { match $evaluated.try_into()? { Value::I64(num) => num, Value::Null => { - return Ok(Value::Null); + return Ok(Evaluated::from(Value::Null)); } _ => { return Err(EvaluateError::FunctionRequiresIntegerValue($name).into()); @@ -43,7 +46,7 @@ macro_rules! eval_to_float { Value::I64(v) => v as f64, Value::F64(v) => v, Value::Null => { - return Ok(Value::Null); + return Ok(Evaluated::from(Value::Null)); } _ => { return Err(EvaluateError::FunctionRequiresFloatValue($name).into()); @@ -54,31 +57,69 @@ macro_rules! eval_to_float { // --- text --- -pub fn concat(exprs: Vec>) -> Result { - exprs +pub fn concat(exprs: Vec>) -> Result { + enum BreakCase { + Null, + Err(crate::result::Error), + } + + let control_flow = exprs.into_iter().map(|expr| expr.try_into()).try_fold( + Value::Str(String::new()), + |left, right: Result| match right { + Ok(value) if value.is_null() => ControlFlow::Break(BreakCase::Null), + Err(err) => ControlFlow::Break(BreakCase::Err(err)), + Ok(value) => ControlFlow::Continue(left.concat(&value)), + }, + ); + + match control_flow { + ControlFlow::Continue(value) => Ok(Evaluated::from(value)), + ControlFlow::Break(BreakCase::Null) => Ok(Evaluated::from(Value::Null)), + ControlFlow::Break(BreakCase::Err(err)) => Err(err), + } +} + +pub fn concat_ws<'a>( + name: String, + separator: Evaluated<'a>, + exprs: Vec>, +) -> Result> { + let separator = eval_to_str!(name, separator); + + let result = exprs .into_iter() - .map(|expr| expr.try_into()) + .map(Value::try_from) .filter(|value| !matches!(value, Ok(Value::Null))) - .try_fold(Value::Str("".to_owned()), |left, right| { - Ok(left.concat(&right?)) - }) + .map(|value| Ok(String::from(value?))) + .collect::>>()? + .join(&separator); + + Ok(Evaluated::from(Value::Str(result))) } -pub fn lower(name: String, expr: Evaluated<'_>) -> Result { - Ok(Value::Str(eval_to_str!(name, expr).to_lowercase())) +pub fn lower(name: String, expr: Evaluated<'_>) -> Result { + Ok(Evaluated::from(Value::Str( + eval_to_str!(name, expr).to_lowercase(), + ))) } -pub fn upper(name: String, expr: Evaluated<'_>) -> Result { - Ok(Value::Str(eval_to_str!(name, expr).to_uppercase())) +pub fn upper(name: String, expr: Evaluated<'_>) -> Result { + Ok(Evaluated::from(Value::Str( + eval_to_str!(name, expr).to_uppercase(), + ))) } -pub fn left_or_right(name: String, expr: Evaluated<'_>, size: Evaluated<'_>) -> Result { +pub fn left_or_right<'a>( + name: String, + expr: Evaluated<'_>, + size: Evaluated<'_>, +) -> Result> { let string = eval_to_str!(name, expr); let size = match size.try_into()? { Value::I64(number) => usize::try_from(number) .map_err(|_| EvaluateError::FunctionRequiresUSizeValue(name.clone()))?, Value::Null => { - return Ok(Value::Null); + return Ok(Evaluated::Value(Value::Null)); } _ => { return Err(EvaluateError::FunctionRequiresIntegerValue(name).into()); @@ -100,21 +141,21 @@ pub fn left_or_right(name: String, expr: Evaluated<'_>, size: Evaluated<'_>) -> .unwrap_or(string) }; - Ok(Value::Str(converted)) + Ok(Evaluated::from(Value::Str(converted))) } -pub fn lpad_or_rpad( +pub fn lpad_or_rpad<'a>( name: String, expr: Evaluated<'_>, size: Evaluated<'_>, fill: Option>, -) -> Result { +) -> Result> { let string = eval_to_str!(name, expr); let size = match size.try_into()? { Value::I64(number) => usize::try_from(number) .map_err(|_| EvaluateError::FunctionRequiresUSizeValue(name.clone()))?, Value::Null => { - return Ok(Value::Null); + return Ok(Evaluated::Value(Value::Null)); } _ => { return Err(EvaluateError::FunctionRequiresIntegerValue(name).into()); @@ -141,15 +182,15 @@ pub fn lpad_or_rpad( string[0..size].to_owned() }; - Ok(Value::Str(result)) + Ok(Evaluated::from(Value::Str(result))) } -pub fn trim( +pub fn trim<'a>( name: String, expr: Evaluated<'_>, filter_chars: Option>, - trim_where_field: &Option, -) -> Result { + trim_where_field: &'a Option, +) -> Result> { let expr_str = eval_to_str!(name, expr); let expr_str = expr_str.as_str(); let filter_chars = match filter_chars { @@ -164,10 +205,14 @@ pub fn trim( None => expr_str.trim(), }; - Ok(Value::Str(value.to_owned())) + Ok(Evaluated::from(Value::Str(value.to_owned()))) } -pub fn ltrim(name: String, expr: Evaluated<'_>, chars: Option>) -> Result { +pub fn ltrim<'a>( + name: String, + expr: Evaluated<'_>, + chars: Option>, +) -> Result> { let expr = eval_to_str!(name, expr); let chars = match chars { Some(chars) => eval_to_str!(name, chars).chars().collect::>(), @@ -175,10 +220,14 @@ pub fn ltrim(name: String, expr: Evaluated<'_>, chars: Option>) -> }; let value = expr.trim_start_matches(chars.as_slice()).to_owned(); - Ok(Value::Str(value)) + Ok(Evaluated::from(Value::Str(value))) } -pub fn rtrim(name: String, expr: Evaluated<'_>, chars: Option>) -> Result { +pub fn rtrim<'a>( + name: String, + expr: Evaluated<'_>, + chars: Option>, +) -> Result> { let expr = eval_to_str!(name, expr); let chars = match chars { Some(chars) => eval_to_str!(name, chars).chars().collect::>(), @@ -186,29 +235,29 @@ pub fn rtrim(name: String, expr: Evaluated<'_>, chars: Option>) -> }; let value = expr.trim_end_matches(chars.as_slice()).to_owned(); - Ok(Value::Str(value)) + Ok(Evaluated::from(Value::Str(value))) } -pub fn reverse(name: String, expr: Evaluated<'_>) -> Result { +pub fn reverse(name: String, expr: Evaluated<'_>) -> Result { let value = eval_to_str!(name, expr).chars().rev().collect::(); - Ok(Value::Str(value)) + Ok(Evaluated::from(Value::Str(value))) } -pub fn repeat(name: String, expr: Evaluated<'_>, num: Evaluated<'_>) -> Result { +pub fn repeat<'a>(name: String, expr: Evaluated<'_>, num: Evaluated<'_>) -> Result> { let expr = eval_to_str!(name, expr); let num = eval_to_int!(name, num) as usize; let value = expr.repeat(num); - Ok(Value::Str(value)) + Ok(Evaluated::from(Value::Str(value))) } -pub fn substr( +pub fn substr<'a>( name: String, expr: Evaluated<'_>, start: Evaluated<'_>, count: Option>, -) -> Result { +) -> Result> { let string = eval_to_str!(name, expr); let start = eval_to_int!(name, start) - 1; let count = match count { @@ -224,17 +273,17 @@ pub fn substr( let start = min(max(start, 0) as usize, string.len()); let string = String::from(&string[start..end]); - Ok(Value::Str(string)) + Ok(Evaluated::from(Value::Str(string))) } -pub fn ascii(name: String, expr: Evaluated<'_>) -> Result { +pub fn ascii<'a>(name: String, expr: Evaluated<'_>) -> Result> { let string = eval_to_str!(name, expr); let mut iter = string.chars(); match (iter.next(), iter.next()) { (Some(c), None) => { if c.is_ascii() { - Ok(Value::U8(c as u8)) + Ok(Evaluated::from(Value::U8(c as u8))) } else { Err(EvaluateError::NonAsciiCharacterNotAllowed.into()) } @@ -243,13 +292,13 @@ pub fn ascii(name: String, expr: Evaluated<'_>) -> Result { } } -pub fn chr(name: String, expr: Evaluated<'_>) -> Result { +pub fn chr<'a>(name: String, expr: Evaluated<'_>) -> Result> { let expr = eval_to_int!(name, expr); match expr { 0..=255 => { let expr = expr as u8; - Ok(Value::Str((expr as char).to_string())) + Ok(Evaluated::from(Value::Str((expr as char).to_string()))) } _ => Err(EvaluateError::ChrFunctionRequiresIntegerValueInRange0To255.into()), } @@ -257,118 +306,126 @@ pub fn chr(name: String, expr: Evaluated<'_>) -> Result { // --- float --- -pub fn abs(name: String, n: Evaluated<'_>) -> Result { +pub fn abs<'a>(name: String, n: Evaluated<'_>) -> Result> { match n.try_into()? { - Value::I8(v) => Ok(Value::I8(v.abs())), - Value::I64(v) => Ok(Value::I64(v.abs())), - Value::Decimal(v) => Ok(Value::Decimal(v.abs())), - Value::F64(v) => Ok(Value::F64(v.abs())), - Value::Null => Ok(Value::Null), + Value::I8(v) => Ok(Evaluated::from(Value::I8(v.abs()))), + Value::I64(v) => Ok(Evaluated::from(Value::I64(v.abs()))), + Value::Decimal(v) => Ok(Evaluated::from(Value::Decimal(v.abs()))), + Value::F64(v) => Ok(Evaluated::from(Value::F64(v.abs()))), + Value::Null => Ok(Evaluated::from(Value::Null)), _ => Err(EvaluateError::FunctionRequiresFloatValue(name).into()), } } -pub fn ifnull(expr: Evaluated<'_>, then: Evaluated<'_>) -> Result { +pub fn ifnull<'a>(expr: Evaluated<'a>, then: Evaluated<'a>) -> Result> { Ok(match expr.is_null() { - true => then.try_into()?, - false => expr.try_into()?, + true => then, + false => expr, }) } -pub fn sign(name: String, n: Evaluated<'_>) -> Result { +pub fn sign(name: String, n: Evaluated<'_>) -> Result { let x = eval_to_float!(name, n); if x == 0.0 { - return Ok(Value::I8(0)); + return Ok(Evaluated::from(Value::I8(0))); } - Ok(Value::I8(x.signum() as i8)) + Ok(Evaluated::from(Value::I8(x.signum() as i8))) } -pub fn sqrt(n: Evaluated<'_>) -> Result { - Value::sqrt(&n.try_into()?) +pub fn sqrt<'a>(n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::sqrt(&n.try_into()?)?)) } -pub fn power(name: String, expr: Evaluated<'_>, power: Evaluated<'_>) -> Result { +pub fn power<'a>(name: String, expr: Evaluated<'_>, power: Evaluated<'_>) -> Result> { let expr = eval_to_float!(name, expr); let power = eval_to_float!(name, power); - Ok(Value::F64(expr.powf(power))) + Ok(Evaluated::from(Value::F64(expr.powf(power)))) } -pub fn ceil(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).ceil())) +pub fn ceil<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).ceil()))) } -pub fn round(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).round())) +pub fn round<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).round()))) } -pub fn floor(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).floor())) +pub fn floor<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).floor()))) } -pub fn radians(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).to_radians())) +pub fn radians<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64( + eval_to_float!(name, n).to_radians(), + ))) } -pub fn degrees(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).to_degrees())) +pub fn degrees<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64( + eval_to_float!(name, n).to_degrees(), + ))) } -pub fn exp(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).exp())) +pub fn exp<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).exp()))) } -pub fn log(name: String, antilog: Evaluated<'_>, base: Evaluated<'_>) -> Result { +pub fn log<'a>(name: String, antilog: Evaluated<'_>, base: Evaluated<'_>) -> Result> { let antilog = eval_to_float!(name, antilog); let base = eval_to_float!(name, base); - Ok(Value::F64(antilog.log(base))) + Ok(Evaluated::from(Value::F64(antilog.log(base)))) } -pub fn ln(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).ln())) +pub fn ln<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).ln()))) } -pub fn log2(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).log2())) +pub fn log2<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).log2()))) } -pub fn log10(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).log10())) +pub fn log10<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).log10()))) } -pub fn sin(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).sin())) +pub fn sin<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).sin()))) } -pub fn cos(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).cos())) +pub fn cos<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).cos()))) } -pub fn tan(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).tan())) +pub fn tan<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).tan()))) } -pub fn asin(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).asin())) +pub fn asin<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).asin()))) } -pub fn acos(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).acos())) +pub fn acos<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).acos()))) } -pub fn atan(name: String, n: Evaluated<'_>) -> Result { - Ok(Value::F64(eval_to_float!(name, n).atan())) +pub fn atan<'a>(name: String, n: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::F64(eval_to_float!(name, n).atan()))) } // --- integer --- -pub fn div(name: String, dividend: Evaluated<'_>, divisor: Evaluated<'_>) -> Result { +pub fn div<'a>( + name: String, + dividend: Evaluated<'_>, + divisor: Evaluated<'_>, +) -> Result> { let dividend = match dividend.try_into()? { Value::F64(number) => number, Value::I64(number) => number as f64, Value::Null => { - return Ok(Value::Null); + return Ok(Evaluated::from(Value::Null)); } _ => { return Err(EvaluateError::FunctionRequiresFloatOrIntegerValue(name).into()); @@ -385,24 +442,24 @@ pub fn div(name: String, dividend: Evaluated<'_>, divisor: Evaluated<'_>) -> Res _ => number as f64, }, Value::Null => { - return Ok(Value::Null); + return Ok(Evaluated::from(Value::Null)); } _ => { return Err(EvaluateError::FunctionRequiresFloatOrIntegerValue(name).into()); } }; - Ok(Value::I64((dividend / divisor) as i64)) + Ok(Evaluated::from(Value::I64((dividend / divisor) as i64))) } -pub fn gcd(name: String, left: Evaluated<'_>, right: Evaluated<'_>) -> Result { +pub fn gcd<'a>(name: String, left: Evaluated<'_>, right: Evaluated<'_>) -> Result> { let left = eval_to_int!(name, left); let right = eval_to_int!(name, right); - Ok(Value::I64(gcd_i64(left, right))) + Ok(Evaluated::from(Value::I64(gcd_i64(left, right)))) } -pub fn lcm(name: String, left: Evaluated<'_>, right: Evaluated<'_>) -> Result { +pub fn lcm<'a>(name: String, left: Evaluated<'_>, right: Evaluated<'_>) -> Result> { let left = eval_to_int!(name, left); let right = eval_to_int!(name, right); @@ -410,7 +467,7 @@ pub fn lcm(name: String, left: Evaluated<'_>, right: Evaluated<'_>) -> Result i64 { @@ -423,9 +480,13 @@ fn gcd_i64(a: i64, b: i64) -> i64 { // --- etc --- -pub fn unwrap(name: String, expr: Evaluated<'_>, selector: Evaluated<'_>) -> Result { +pub fn unwrap<'a>( + name: String, + expr: Evaluated<'_>, + selector: Evaluated<'_>, +) -> Result> { if expr.is_null() { - return Ok(Value::Null); + return Ok(Evaluated::from(Value::Null)); } let value = match expr { @@ -436,84 +497,107 @@ pub fn unwrap(name: String, expr: Evaluated<'_>, selector: Evaluated<'_>) -> Res }; let selector = eval_to_str!(name, selector); - value.selector(&selector) + Ok(Evaluated::from(value.selector(&selector)?)) } -pub fn generate_uuid() -> Value { - Value::Uuid(Uuid::new_v4().as_u128()) +pub fn generate_uuid<'a>() -> Evaluated<'a> { + Evaluated::from(Value::Uuid(Uuid::new_v4().as_u128())) } -pub fn format(name: String, expr: Evaluated<'_>, format: Evaluated<'_>) -> Result { +pub fn format<'a>( + name: String, + expr: Evaluated<'_>, + format: Evaluated<'_>, +) -> Result> { match expr.try_into()? { Value::Date(expr) => { let format = eval_to_str!(name, format); - Ok(Value::Str( + Ok(Evaluated::from(Value::Str( chrono::NaiveDate::format(&expr, &format).to_string(), - )) + ))) } Value::Timestamp(expr) => { let format = eval_to_str!(name, format); - Ok(Value::Str( + Ok(Evaluated::from(Value::Str( chrono::NaiveDateTime::format(&expr, &format).to_string(), - )) + ))) } Value::Time(expr) => { let format = eval_to_str!(name, format); - Ok(Value::Str( + Ok(Evaluated::from(Value::Str( chrono::NaiveTime::format(&expr, &format).to_string(), - )) + ))) } value => Err(EvaluateError::UnsupportedExprForFormatFunction(value.into()).into()), } } -pub fn to_date(name: String, expr: Evaluated<'_>, format: Evaluated<'_>) -> Result { +pub fn to_date<'a>( + name: String, + expr: Evaluated<'_>, + format: Evaluated<'_>, +) -> Result> { match expr.try_into()? { Value::Str(expr) => { let format = eval_to_str!(name, format); chrono::NaiveDate::parse_from_str(&expr, &format) - .map(Value::Date) .map_err(ChronoFormatError::err_into) + .map(Value::Date) + .map(Evaluated::from) } _ => Err(EvaluateError::FunctionRequiresStringValue(name).into()), } } -pub fn to_timestamp(name: String, expr: Evaluated<'_>, format: Evaluated<'_>) -> Result { +pub fn to_timestamp<'a>( + name: String, + expr: Evaluated<'_>, + format: Evaluated<'_>, +) -> Result> { match expr.try_into()? { Value::Str(expr) => { let format = eval_to_str!(name, format); chrono::NaiveDateTime::parse_from_str(&expr, &format) - .map(Value::Timestamp) .map_err(ChronoFormatError::err_into) + .map(Value::Timestamp) + .map(Evaluated::from) } _ => Err(EvaluateError::FunctionRequiresStringValue(name).into()), } } -pub fn to_time(name: String, expr: Evaluated<'_>, format: Evaluated<'_>) -> Result { +pub fn to_time<'a>( + name: String, + expr: Evaluated<'_>, + format: Evaluated<'_>, +) -> Result> { match expr.try_into()? { Value::Str(expr) => { let format = eval_to_str!(name, format); chrono::NaiveTime::parse_from_str(&expr, &format) .map(Value::Time) .map_err(ChronoFormatError::err_into) + .map(Evaluated::from) } _ => Err(EvaluateError::FunctionRequiresStringValue(name).into()), } } -pub fn position(name: String, from_expr: Evaluated<'_>, sub_expr: Evaluated<'_>) -> Result { +pub fn position<'a>( + name: String, + from_expr: Evaluated<'_>, + sub_expr: Evaluated<'_>, +) -> Result> { let from_expr = eval_to_str!(name, from_expr); let sub_expr = eval_to_str!(name, sub_expr); - Value::position(&Value::Str(from_expr), &Value::Str(sub_expr)) + Value::position(&Value::Str(from_expr), &Value::Str(sub_expr)).map(Evaluated::from) } -pub fn cast(expr: Evaluated<'_>, data_type: &DataType) -> Result { - expr.cast(data_type)?.try_into() +pub fn cast<'a>(expr: Evaluated<'a>, data_type: &DataType) -> Result> { + expr.cast(data_type) } -pub fn extract(field: &DateTimeField, expr: Evaluated<'_>) -> Result { - Value::try_from(expr)?.extract(field) +pub fn extract<'a>(field: &DateTimeField, expr: Evaluated<'_>) -> Result> { + Ok(Evaluated::from(Value::try_from(expr)?.extract(field)?)) } diff --git a/core/src/executor/evaluate/mod.rs b/core/src/executor/evaluate/mod.rs index c004ce6dc..58129e50d 100644 --- a/core/src/executor/evaluate/mod.rs +++ b/core/src/executor/evaluate/mod.rs @@ -271,6 +271,11 @@ async fn evaluate_function<'a>( let exprs = stream::iter(exprs).then(eval).try_collect().await?; f::concat(exprs) } + Function::ConcatWs { separator, exprs } => { + let separator = eval(separator).await?; + let exprs = stream::iter(exprs).then(eval).try_collect().await?; + f::concat_ws(name, separator, exprs) + } Function::IfNull { expr, then } => f::ifnull(eval(expr).await?, eval(then).await?), Function::Lower(expr) => f::lower(name, eval(expr).await?), Function::Upper(expr) => f::upper(name, eval(expr).await?), @@ -360,7 +365,7 @@ async fn evaluate_function<'a>( Function::Floor(expr) => f::floor(name, eval(expr).await?), Function::Radians(expr) => f::radians(name, eval(expr).await?), Function::Degrees(expr) => f::degrees(name, eval(expr).await?), - Function::Pi() => Ok(Value::F64(std::f64::consts::PI)), + Function::Pi() => Ok(Evaluated::from(Value::F64(std::f64::consts::PI))), Function::Exp(expr) => f::exp(name, eval(expr).await?), Function::Log { antilog, base } => { let antilog = eval(antilog).await?; @@ -389,7 +394,7 @@ async fn evaluate_function<'a>( let dividend = eval(dividend).await?; let divisor = eval(divisor).await?; - return dividend.modulo(&divisor); + dividend.modulo(&divisor) } Function::Gcd { left, right } => { let left = eval(left).await?; @@ -412,7 +417,7 @@ async fn evaluate_function<'a>( f::unwrap(name, expr, selector) } Function::GenerateUuid() => Ok(f::generate_uuid()), - Function::Now() => Ok(Value::Timestamp(Utc::now().naive_utc())), + Function::Now() => Ok(Evaluated::from(Value::Timestamp(Utc::now().naive_utc()))), Function::Format { expr, format } => { let expr = eval(expr).await?; let format = eval(format).await?; @@ -451,5 +456,4 @@ async fn evaluate_function<'a>( f::extract(field, expr) } } - .map(Evaluated::from) } diff --git a/core/src/executor/evaluate/stateless.rs b/core/src/executor/evaluate/stateless.rs index 9fd7b6564..fdd9929c4 100644 --- a/core/src/executor/evaluate/stateless.rs +++ b/core/src/executor/evaluate/stateless.rs @@ -245,7 +245,7 @@ fn evaluate_function<'a>( Function::Floor(expr) => f::floor(name, eval(expr)?), Function::Radians(expr) => f::radians(name, eval(expr)?), Function::Degrees(expr) => f::degrees(name, eval(expr)?), - Function::Pi() => Ok(Value::F64(std::f64::consts::PI)), + Function::Pi() => Ok(Evaluated::from(Value::F64(std::f64::consts::PI))), Function::Exp(expr) => f::exp(name, eval(expr)?), Function::Log { antilog, base } => { let antilog = eval(antilog)?; @@ -274,7 +274,7 @@ fn evaluate_function<'a>( let dividend = eval(dividend)?; let divisor = eval(divisor)?; - return dividend.modulo(&divisor); + dividend.modulo(&divisor) } Function::Gcd { left, right } => { let left = eval(left)?; @@ -297,7 +297,7 @@ fn evaluate_function<'a>( f::unwrap(name, expr, selector) } Function::GenerateUuid() => Ok(f::generate_uuid()), - Function::Now() => Ok(Value::Timestamp(Utc::now().naive_utc())), + Function::Now() => Ok(Evaluated::from(Value::Timestamp(Utc::now().naive_utc()))), Function::Format { expr, format } => { let expr = eval(expr)?; let format = eval(format)?; @@ -339,6 +339,11 @@ fn evaluate_function<'a>( let expr = eval(expr)?; f::extract(field, expr) } + Function::ConcatWs { separator, exprs } => { + let separator = eval(separator)?; + let exprs = exprs.iter().map(eval).collect::>>()?; + + f::concat_ws(name, separator, exprs) + } } - .map(Evaluated::from) } diff --git a/core/src/executor/execute.rs b/core/src/executor/execute.rs index dd56706e7..bb43cc927 100644 --- a/core/src/executor/execute.rs +++ b/core/src/executor/execute.rs @@ -8,9 +8,8 @@ use { }, crate::{ ast::{ - ColumnDef, ColumnOption, ColumnOptionDef, DataType, Dictionary, Expr, Query, - SelectItem, SetExpr, Statement, TableAlias, TableFactor, TableWithJoins, Values, - Variable, + ColumnDef, ColumnOption, DataType, Dictionary, Expr, Query, SelectItem, SetExpr, + Statement, TableAlias, TableFactor, TableWithJoins, Values, Variable, }, data::{Key, Row, Schema}, executor::limit::Limit, @@ -27,7 +26,10 @@ use { use super::alter::alter_table; #[cfg(feature = "index")] -use {super::alter::create_index, crate::data::SchemaIndex}; +use { + super::alter::create_index, + crate::ast::{AstLiteral, BinaryOperator}, +}; #[derive(ThisError, Serialize, Debug, PartialEq)] pub enum ExecuteError { @@ -63,9 +65,6 @@ pub enum Payload { Rollback, ShowVariable(PayloadVariable), - - #[cfg(feature = "index")] - ShowIndexes(Vec), } #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -229,9 +228,9 @@ pub async fn execute( .iter() .enumerate() .find(|(_, ColumnDef { options, .. })| { - options.iter().any(|ColumnOptionDef { option, .. }| { - option == &ColumnOption::Unique { is_primary: true } - }) + options + .iter() + .any(|option| option == &ColumnOption::Unique { is_primary: true }) }) .map(|(i, _)| i); @@ -358,18 +357,47 @@ pub async fn execute( } #[cfg(feature = "index")] Statement::ShowIndexes(table_name) => { - let indexes = match storage.fetch_schema(table_name).await { - Ok(Some(Schema { indexes, .. })) => indexes, - Ok(None) => { - return Err(( - storage, - ExecuteError::TableNotFound(table_name.to_owned()).into(), - )); - } - Err(e) => return Err((storage, e)), + let query = Query { + body: SetExpr::Select(Box::new(crate::ast::Select { + projection: vec![SelectItem::Wildcard], + from: TableWithJoins { + relation: TableFactor::Dictionary { + dict: Dictionary::GlueIndexes, + alias: TableAlias { + name: "GLUE_INDEXES".to_owned(), + columns: Vec::new(), + }, + }, + joins: Vec::new(), + }, + selection: Some(Expr::BinaryOp { + left: Box::new(Expr::Identifier("TABLE_NAME".to_owned())), + op: BinaryOperator::Eq, + right: Box::new(Expr::Literal(AstLiteral::QuotedString( + table_name.to_owned(), + ))), + }), + group_by: Vec::new(), + having: None, + })), + order_by: Vec::new(), + limit: None, + offset: None, }; - Ok((storage, Payload::ShowIndexes(indexes))) + let (labels, rows) = try_block!(storage, { + let (labels, rows) = select_with_labels(&storage, &query, None, true).await?; + let rows = rows.try_collect::>().await?; + Ok((labels, rows)) + }); + + match rows.is_empty() { + true => Err(( + storage, + ExecuteError::TableNotFound(table_name.to_owned()).into(), + )), + false => Ok((storage, Payload::Select { labels, rows })), + } } Statement::ShowVariable(variable) => match variable { Variable::Tables => { diff --git a/core/src/executor/fetch.rs b/core/src/executor/fetch.rs index 54384c25e..a72d15ec0 100644 --- a/core/src/executor/fetch.rs +++ b/core/src/executor/fetch.rs @@ -163,12 +163,34 @@ pub async fn fetch_relation_rows<'a>( TableFactor::Dictionary { dict, .. } => { let rows = { #[derive(Iterator)] - enum Rows { + enum Rows { Tables(I1), TableColumns(I2), Indexes(I3), + Objects(I4), } match dict { + Dictionary::GlueObjects => { + let schemas = storage.fetch_all_schemas().await?; + let rows = schemas.into_iter().flat_map(|schema| { + let table_rows = vec![Ok(Row(vec![ + Value::Str(schema.table_name), + Value::Str("TABLE".to_owned()), + Value::Timestamp(schema.created), + ]))]; + let index_rows = schema.indexes.into_iter().map(|index| { + Ok(Row(vec![ + Value::Str(index.name.clone()), + Value::Str("INDEX".to_owned()), + Value::Timestamp(index.created), + ])) + }); + + table_rows.into_iter().chain(index_rows) + }); + + Rows::Objects(rows) + } Dictionary::GlueTables => { let schemas = storage.fetch_all_schemas().await?; let rows = schemas @@ -201,9 +223,8 @@ pub async fn fetch_relation_rows<'a>( |ColumnDef { name, options, .. }| { options .iter() - .any(|column_option_def| { - column_option_def.option - == ColumnOption::Unique { is_primary: true } + .any(|option| { + option == &ColumnOption::Unique { is_primary: true } }) .then_some(name) }, @@ -263,6 +284,11 @@ pub async fn fetch_relation_columns( TableFactor::Table { name, .. } => fetch_columns(storage, name).await, TableFactor::Series { .. } => Ok(vec!["N".to_owned()]), TableFactor::Dictionary { dict, .. } => match dict { + Dictionary::GlueObjects => Ok(vec![ + "OBJECT_NAME".to_owned(), + "OBJECT_TYPE".to_owned(), + "CREATED".to_owned(), + ]), Dictionary::GlueTables => Ok(vec!["TABLE_NAME".to_owned()]), Dictionary::GlueTableColumns => Ok(vec![ "TABLE_NAME".to_owned(), diff --git a/core/src/executor/update.rs b/core/src/executor/update.rs index 18118bc8d..adcfbdb1d 100644 --- a/core/src/executor/update.rs +++ b/core/src/executor/update.rs @@ -4,8 +4,8 @@ use { evaluate::{evaluate, Evaluated}, }, crate::{ - ast::{Assignment, ColumnDef, ColumnOption, ColumnOptionDef}, - data::{schema::ColumnDefExt, Row, Value}, + ast::{Assignment, ColumnDef, ColumnOption}, + data::{Row, Value}, result::Result, store::GStore, }, @@ -51,9 +51,9 @@ impl<'a> Update<'a> { return false; } - options.iter().any(|ColumnOptionDef { option, .. }| { - option == &ColumnOption::Unique { is_primary: true } - }) + options + .iter() + .any(|option| option == &ColumnOption::Unique { is_primary: true }) }) { return Err(UpdateError::UpdateOnPrimaryKeyNotSupported(id.to_owned()).into()); } @@ -87,7 +87,7 @@ impl<'a> Update<'a> { Evaluated::Literal(v) => Value::try_from_literal(data_type, &v)?, Evaluated::Value(v) => { v.validate_type(data_type)?; - v.into_owned() + v } }; diff --git a/core/src/executor/validate.rs b/core/src/executor/validate.rs index 519b754c8..9f99c2f21 100644 --- a/core/src/executor/validate.rs +++ b/core/src/executor/validate.rs @@ -1,6 +1,6 @@ use { crate::{ - ast::{ColumnDef, ColumnOption, ColumnOptionDef}, + ast::{ColumnDef, ColumnOption}, data::{Key, Row, Value}, result::Result, store::Store, @@ -91,41 +91,36 @@ pub async fn validate_unique( All(Vec<(usize, String)>), } - let columns = match &column_validation { - ColumnValidation::All(column_defs) => { - let primary_key_index = column_defs - .iter() - .enumerate() - .find(|(_, column_def)| { - column_def - .options - .iter() - .any(|ColumnOptionDef { option, .. }| { + let columns = + match &column_validation { + ColumnValidation::All(column_defs) => { + let primary_key_index = column_defs + .iter() + .enumerate() + .find(|(_, column_def)| { + column_def.options.iter().any(|option| { matches!(option, ColumnOption::Unique { is_primary: true }) }) - }) - .map(|(i, _)| i); - let other_unique_column_def_count = column_defs - .iter() - .filter(|column_def| { - column_def - .options - .iter() - .any(|ColumnOptionDef { option, .. }| { + }) + .map(|(i, _)| i); + let other_unique_column_def_count = column_defs + .iter() + .filter(|column_def| { + column_def.options.iter().any(|option| { matches!(option, ColumnOption::Unique { is_primary: false }) }) - }) - .count(); + }) + .count(); - match (primary_key_index, other_unique_column_def_count) { - (Some(primary_key_index), 0) => Columns::PrimaryKeyOnly(primary_key_index), - _ => Columns::All(fetch_all_unique_columns(column_defs)), + match (primary_key_index, other_unique_column_def_count) { + (Some(primary_key_index), 0) => Columns::PrimaryKeyOnly(primary_key_index), + _ => Columns::All(fetch_all_unique_columns(column_defs)), + } } - } - ColumnValidation::SpecifiedColumns(column_defs, specified_columns) => Columns::All( - fetch_specified_unique_columns(column_defs, specified_columns), - ), - }; + ColumnValidation::SpecifiedColumns(column_defs, specified_columns) => Columns::All( + fetch_specified_unique_columns(column_defs, specified_columns), + ), + }; match columns { Columns::PrimaryKeyOnly(primary_key_index) => { @@ -197,7 +192,7 @@ fn fetch_all_unique_columns(column_defs: &[ColumnDef]) -> Vec<(usize, String)> { table_col .options .iter() - .any(|opt_def| matches!(opt_def.option, ColumnOption::Unique { .. })) + .any(|option| matches!(option, ColumnOption::Unique { .. })) .then_some((i, table_col.name.to_owned())) }) .collect() @@ -214,7 +209,7 @@ fn fetch_specified_unique_columns( table_col .options .iter() - .any(|ColumnOptionDef { option, .. }| match option { + .any(|option| match option { ColumnOption::Unique { .. } => specified_columns .iter() .any(|specified_col| specified_col == &table_col.name), diff --git a/core/src/plan/expr/function.rs b/core/src/plan/expr/function.rs index fc0fdb5ab..e2c5974ed 100644 --- a/core/src/plan/expr/function.rs +++ b/core/src/plan/expr/function.rs @@ -1,17 +1,18 @@ use { crate::ast::{Expr, Function}, - std::iter::empty, + std::iter::{empty, once}, }; impl Function { - pub fn as_exprs(&self) -> impl ExactSizeIterator { - #[derive(iter_enum::Iterator, iter_enum::ExactSizeIterator)] - enum Exprs { + pub fn as_exprs(&self) -> impl Iterator { + #[derive(iter_enum::Iterator)] + enum Exprs { Empty(I0), Single(I1), Double(I2), Triple(I3), VariableArgs(I4), + VariableArgsWithSingle(I5), } match self { @@ -141,6 +142,9 @@ impl Function { count: Some(expr3), } => Exprs::Triple([expr, expr2, expr3].into_iter()), Self::Concat(exprs) => Exprs::VariableArgs(exprs.iter()), + Self::ConcatWs { separator, exprs } => { + Exprs::VariableArgsWithSingle(once(separator).chain(exprs.iter())) + } } } } @@ -161,10 +165,11 @@ mod tests { _ => unreachable!("only for function tests"), }; let actual = function.as_exprs(); + let actual = actual.collect::>(); assert_eq!(actual.len(), expected.len(), "{sql}"); - for (expected, actual) in expected.iter().zip(actual) { + for (expected, actual) in expected.iter().zip(actual.into_iter()) { assert_eq!(actual, &expr(expected), "{sql}"); } } @@ -264,5 +269,11 @@ mod tests { test(r#"POSITION("men" IN "ramen")"#, &[r#""men""#, r#""ramen""#]); test(r#"POSITION("men" IN ramen)"#, &[r#""men""#, "ramen"]); + + //TypedStringVariableArgs + test( + r#"CONCAT_WS(",", "gluesql", "is", "cool")"#, + &[r#"",""#, r#""gluesql""#, r#""is""#, r#""cool""#], + ); } } diff --git a/core/src/plan/planner.rs b/core/src/plan/planner.rs index 75af33afb..cad601e33 100644 --- a/core/src/plan/planner.rs +++ b/core/src/plan/planner.rs @@ -1,10 +1,7 @@ use { super::context::Context, crate::{ - ast::{ - ColumnDef, ColumnOption, ColumnOptionDef, Expr, Function, Query, TableAlias, - TableFactor, - }, + ast::{ColumnDef, ColumnOption, Expr, Function, Query, TableAlias, TableFactor}, data::Schema, }, std::rc::Rc, @@ -211,9 +208,7 @@ pub trait Planner<'a> { .find_map(|ColumnDef { name, options, .. }| { options .iter() - .any(|ColumnOptionDef { option, .. }| { - matches!(option, ColumnOption::Unique { is_primary: true }) - }) + .any(|option| matches!(option, ColumnOption::Unique { is_primary: true })) .then(|| name.as_str()) }); diff --git a/core/src/translate/data_type.rs b/core/src/translate/data_type.rs index f037da90f..78c07784e 100644 --- a/core/src/translate/data_type.rs +++ b/core/src/translate/data_type.rs @@ -28,6 +28,7 @@ pub fn translate_data_type(sql_data_type: &SqlDataType) -> Result { Some("INT32") => Ok(DataType::Int32), Some("INT128") => Ok(DataType::Int128), Some("UINT8") => Ok(DataType::Uint8), + Some("UINT16") => Ok(DataType::Uint16), _ => Err(TranslateError::UnsupportedDataType(sql_data_type.to_string()).into()), } diff --git a/core/src/translate/ddl.rs b/core/src/translate/ddl.rs index 1ebc6c0a4..f8e5ff553 100644 --- a/core/src/translate/ddl.rs +++ b/core/src/translate/ddl.rs @@ -1,7 +1,7 @@ use { super::{data_type::translate_data_type, expr::translate_expr, TranslateError}, crate::{ - ast::{ColumnDef, ColumnOption, ColumnOptionDef}, + ast::{ColumnDef, ColumnOption}, result::Result, }, sqlparser::ast::{ @@ -72,12 +72,15 @@ pub fn translate_column_def(sql_column_def: &SqlColumnDef) -> Result }) } +/// Translate [`SqlColumnOptionDef`] to [`ColumnOption`]. +/// +/// `sql-parser` parses column option as `{ name, option }` type, +/// but in here we only need `option`. fn translate_column_option_def( sql_column_option_def: &SqlColumnOptionDef, -) -> Result> { - let SqlColumnOptionDef { name, option } = sql_column_option_def; +) -> Result> { + let SqlColumnOptionDef { option, .. } = sql_column_option_def; - let name = name.as_ref().map(|name| name.value.to_owned()); let option = match option { SqlColumnOption::Null => Ok(ColumnOption::Null), SqlColumnOption::NotNull => Ok(ColumnOption::NotNull), @@ -87,18 +90,12 @@ fn translate_column_option_def( } SqlColumnOption::Unique { .. } => { return Ok(vec![ - ColumnOptionDef { - name: name.clone(), - option: ColumnOption::Unique { is_primary: true }, - }, - ColumnOptionDef { - name, - option: ColumnOption::NotNull, - }, + ColumnOption::Unique { is_primary: true }, + ColumnOption::NotNull, ]); } _ => Err(TranslateError::UnsupportedColumnOption(option.to_string()).into()), }?; - Ok(vec![ColumnOptionDef { name, option }]) + Ok(vec![option]) } diff --git a/core/src/translate/error.rs b/core/src/translate/error.rs index 7644b4155..0e9baf082 100644 --- a/core/src/translate/error.rs +++ b/core/src/translate/error.rs @@ -122,4 +122,10 @@ pub enum TranslateError { #[error("unimplemented - compound object is supported: {0}")] CompoundObjectNotSupported(String), + + #[error("cannot create index with reserved name: {0}")] + ReservedIndexName(String), + + #[error("cannot drop primary index")] + CannotDropPrimary, } diff --git a/core/src/translate/function.rs b/core/src/translate/function.rs index f760581e0..0a150e064 100644 --- a/core/src/translate/function.rs +++ b/core/src/translate/function.rs @@ -213,6 +213,19 @@ pub fn translate_function(sql_function: &SqlFunction) -> Result { .collect::>>()?; Ok(Expr::Function(Box::new(Function::Concat(exprs)))) } + "CONCAT_WS" => { + check_len_min(name, args.len(), 2)?; + let separator = translate_expr(args[0])?; + let exprs = args + .into_iter() + .skip(1) + .map(translate_expr) + .collect::>>()?; + Ok(Expr::Function(Box::new(Function::ConcatWs { + separator, + exprs, + }))) + } "LOWER" => translate_function_one_arg(Function::Lower, args, name), "UPPER" => translate_function_one_arg(Function::Upper, args, name), "LEFT" => { diff --git a/core/src/translate/mod.rs b/core/src/translate/mod.rs index fae3e9577..3a0af4342 100644 --- a/core/src/translate/mod.rs +++ b/core/src/translate/mod.rs @@ -114,8 +114,14 @@ pub fn translate(sql_statement: &SqlStatement) -> Result { return Err(TranslateError::CompositeIndexNotSupported.into()); } + let name = translate_object_name(name)?; + + if name.to_uppercase() == "PRIMARY" { + return Err(TranslateError::ReservedIndexName(name).into()); + }; + Ok(Statement::CreateIndex { - name: translate_object_name(name)?, + name, table_name: translate_object_name(table_name)?, column: translate_order_by_expr(&columns[0])?, }) @@ -138,6 +144,10 @@ pub fn translate(sql_statement: &SqlStatement) -> Result { let table_name = object_name[0].value.to_owned(); let name = object_name[1].value.to_owned(); + if name.to_uppercase() == "PRIMARY" { + return Err(TranslateError::CannotDropPrimary.into()); + }; + Ok(Statement::DropIndex { name, table_name }) } #[cfg(feature = "transaction")] diff --git a/core/src/translate/query.rs b/core/src/translate/query.rs index 4e742a36b..1310e366b 100644 --- a/core/src/translate/query.rs +++ b/core/src/translate/query.rs @@ -194,6 +194,10 @@ fn translate_table_factor(sql_table_factor: &SqlTableFactor) -> Result Ok(TableFactor::Dictionary { + dict: Dictionary::GlueObjects, + alias: alias_or_name, + }), "GLUE_TABLES" => Ok(TableFactor::Dictionary { dict: Dictionary::GlueTables, alias: alias_or_name, diff --git a/pkg/javascript/README.md b/pkg/javascript/README.md index dfebe3c37..a24cc89c3 100644 --- a/pkg/javascript/README.md +++ b/pkg/javascript/README.md @@ -4,7 +4,7 @@ [![GitHub](https://img.shields.io/github/stars/gluesql/gluesql)](https://github.com/gluesql/gluesql) [![LICENSE](https://img.shields.io/crates/l/gluesql.svg)](https://github.com/gluesql/gluesql/blob/main/LICENSE) [![Chat](https://img.shields.io/discord/780298017940176946)](https://discord.gg/C6TDEgzDzY) -[![codecov.io](https://codecov.io/github/gluesql/gluesql/coverage.svg?branch=main)](https://codecov.io/github/gluesql/gluesql?branch=main) +[![Coverage Status](https://coveralls.io/repos/github/gluesql/gluesql/badge.svg?branch=main)](https://coveralls.io/github/gluesql/gluesql?branch=main) GlueSQL.js is a SQL database for web browsers and Node.js. It works as an embedded database and entirely runs in the browser. GlueSQL.js supports in-memory storage backend, but it will soon to have localStorage, sessionStorage and indexedDB backend supports. diff --git a/pkg/javascript/gluesql.js b/pkg/javascript/gluesql.js index 888f13630..8d6f37f5f 100644 --- a/pkg/javascript/gluesql.js +++ b/pkg/javascript/gluesql.js @@ -2,15 +2,15 @@ import init, { Glue } from './dist/web/gluesql_js.js'; let loaded = false; -async function load() { - await init(); +async function load(module_or_path) { + await init(module_or_path); loaded = true; } -export async function gluesql() { +export async function gluesql(module_or_path) { if (!loaded) { - await load(); + await load(module_or_path); } return new Glue(); diff --git a/pkg/javascript/web/src/payload.rs b/pkg/javascript/web/src/payload.rs index f9a92b6f7..da0aa87bb 100644 --- a/pkg/javascript/web/src/payload.rs +++ b/pkg/javascript/web/src/payload.rs @@ -60,23 +60,6 @@ fn convert_payload(payload: Payload) -> Json { "columns": Json::Array(columns), }) } - Payload::ShowIndexes(indexes) => { - let indexes = indexes - .into_iter() - .map(|index| { - json!({ - "name": index.name, - "order": index.order.to_owned(), - "description": index.expr.to_sql(), - }) - }) - .collect(); - - json!({ - "type": "SHOW INDEXES", - "indexes": Json::Array(indexes), - }) - } Payload::Insert(num) => json!({ "type": "INSERT", "affected": num diff --git a/pkg/rust/Cargo.toml b/pkg/rust/Cargo.toml index 0c2363140..09967516b 100644 --- a/pkg/rust/Cargo.toml +++ b/pkg/rust/Cargo.toml @@ -19,11 +19,6 @@ keywords = [ [package.metadata.docs.rs] all-features = true -# ref. https://github.com/rustwasm/wasm-pack/issues/1111 -# enable this only for gluesql-js build -# [profile.release] -# opt-level = "s" - [dependencies] gluesql-core = { path = "../../core", version = "0.13.1" } cli = { package = "gluesql-cli", path = "../../cli", version = "0.13.1", optional = true } diff --git a/pkg/rust/examples/memory_storage_usage.rs b/pkg/rust/examples/memory_storage_usage.rs new file mode 100644 index 000000000..eef0be0a5 --- /dev/null +++ b/pkg/rust/examples/memory_storage_usage.rs @@ -0,0 +1,62 @@ +#[cfg(feature = "memory-storage")] +mod api_usage { + use gluesql::{memory_storage::MemoryStorage, prelude::Glue}; + + fn memory_basic() { + let storage = MemoryStorage::default(); + let mut glue = Glue::new(storage); + + glue.execute("DROP TABLE IF EXISTS api_test"); + + glue.execute( + "CREATE TABLE api_test ( + id INTEGER, + name TEXT, + nullable TEXT NULL, + is BOOLEAN + )", + ); + + glue.execute( + "INSERT INTO api_test ( + id, + name, + nullable, + is + ) VALUES + (1, 'test1', 'not null', TRUE), + (2, 'test2', NULL, FALSE)", + ); + } + + fn memory_basic_async() { + use futures::executor::block_on; + + let storage = MemoryStorage::default(); + let mut glue = Glue::new(storage); + + block_on(async { + glue.execute_async("DROP TABLE IF EXISTS api_test").await; + + glue.execute_async( + "CREATE TABLE api_test ( + id INTEGER, + name TEXT, + nullable TEXT NULL, + is BOOLEAN + )", + ) + .await; + }); + } + + pub fn run() { + memory_basic(); + memory_basic_async(); + } +} + +fn main() { + #[cfg(feature = "memory-storage")] + api_usage::run(); +} diff --git a/pkg/rust/src/lib.rs b/pkg/rust/src/lib.rs index 9c9729acb..6bcfc3a01 100644 --- a/pkg/rust/src/lib.rs +++ b/pkg/rust/src/lib.rs @@ -9,11 +9,11 @@ //! ## Examples //! //! ``` -//! # #[cfg(feature = "sled-storage")] +//! # #[cfg(feature = "memory-storage")] //! # fn main() { //! use gluesql::prelude::*; //! -//! let storage = SledStorage::new("data/doc-db").unwrap(); +//! let storage = MemoryStorage::default(); //! let mut glue = Glue::new(storage); //! //! let sqls = vec![ @@ -26,11 +26,11 @@ //! //! for sql in sqls { //! let output = glue.execute(sql).unwrap(); -//! println!("{:?}", output) +//! println!("{:?}", output); //! } //! # } //! -//! # #[cfg(not(feature = "sled-storage"))] +//! # #[cfg(not(feature = "memory-storage"))] //! # fn main() {} //! ``` //! diff --git a/storages/memory-storage/src/alter_table.rs b/storages/memory-storage/src/alter_table.rs index 5ff259b90..8b578e297 100644 --- a/storages/memory-storage/src/alter_table.rs +++ b/storages/memory-storage/src/alter_table.rs @@ -3,7 +3,7 @@ use { async_trait::async_trait, gluesql_core::{ ast::ColumnDef, - data::{schema::ColumnDefExt, Value}, + data::Value, result::{MutResult, Result, TrySelf}, store::AlterTable, store::AlterTableError, diff --git a/storages/sled-storage/src/alter_table.rs b/storages/sled-storage/src/alter_table.rs index cc6eb23cd..3c0d8b8f9 100644 --- a/storages/sled-storage/src/alter_table.rs +++ b/storages/sled-storage/src/alter_table.rs @@ -9,10 +9,7 @@ use { async_trait::async_trait, gluesql_core::{ ast::ColumnDef, - data::{ - schema::{ColumnDefExt, Schema}, - Row, Value, - }, + data::{schema::Schema, Row, Value}, executor::evaluate_stateless, result::{MutResult, Result, TrySelf}, store::{AlterTable, AlterTableError}, @@ -53,6 +50,7 @@ impl AlterTable for SledStorage { let Schema { column_defs, indexes, + created, .. } = old_schema .ok_or_else(|| AlterTableError::TableNotFound(table_name.to_owned()).into()) @@ -62,6 +60,7 @@ impl AlterTable for SledStorage { table_name: new_table_name.to_owned(), column_defs, indexes, + created, }; bincode::serialize(&old_snapshot) @@ -158,6 +157,7 @@ impl AlterTable for SledStorage { let Schema { column_defs, indexes, + created, .. } = snapshot .get(txid, None) @@ -185,6 +185,7 @@ impl AlterTable for SledStorage { table_name: table_name.to_owned(), column_defs, indexes, + created, }; let (snapshot, _) = snapshot.update(txid, schema); let value = bincode::serialize(&snapshot) @@ -235,6 +236,8 @@ impl AlterTable for SledStorage { table_name, column_defs, indexes, + created, + .. } = schema_snapshot .get(txid, None) .ok_or_else(|| AlterTableError::TableNotFound(table_name.to_owned()).into()) @@ -308,6 +311,7 @@ impl AlterTable for SledStorage { table_name, column_defs, indexes, + created, }; let (schema_snapshot, _) = schema_snapshot.update(txid, schema); let schema_value = bincode::serialize(&schema_snapshot) @@ -362,6 +366,8 @@ impl AlterTable for SledStorage { table_name, column_defs, indexes, + created, + .. } = schema_snapshot .get(txid, None) .ok_or_else(|| AlterTableError::TableNotFound(table_name.to_owned()).into()) @@ -428,6 +434,7 @@ impl AlterTable for SledStorage { table_name, column_defs, indexes, + created, }; let (schema_snapshot, _) = schema_snapshot.update(txid, schema); let schema_value = bincode::serialize(&schema_snapshot) diff --git a/storages/sled-storage/src/index_mut.rs b/storages/sled-storage/src/index_mut.rs index c312c9461..a68b3af39 100644 --- a/storages/sled-storage/src/index_mut.rs +++ b/storages/sled-storage/src/index_mut.rs @@ -10,6 +10,7 @@ use { async_trait::async_trait, gluesql_core::{ ast::OrderByExpr, + chrono::Utc, data::{Schema, SchemaIndex, SchemaIndexOrd}, result::{Error, MutResult, Result, TrySelf}, store::{IndexError, IndexMut, Store}, @@ -67,6 +68,7 @@ impl IndexMut for SledStorage { let Schema { column_defs, indexes, + created, .. } = schema .ok_or_else(|| IndexError::ConflictTableNotFound(table_name.to_owned()).into()) @@ -81,6 +83,7 @@ impl IndexMut for SledStorage { name: index_name.to_owned(), expr: index_expr.clone(), order: SchemaIndexOrd::Both, + created: Utc::now().naive_utc(), }; let indexes = indexes @@ -92,6 +95,7 @@ impl IndexMut for SledStorage { table_name: table_name.to_owned(), column_defs, indexes, + created, }; let index_sync = IndexSync::from_schema(tree, txid, &schema); @@ -144,6 +148,7 @@ impl IndexMut for SledStorage { let Schema { column_defs, indexes, + created, .. } = schema .ok_or_else(|| IndexError::ConflictTableNotFound(table_name.to_owned()).into()) @@ -165,6 +170,7 @@ impl IndexMut for SledStorage { table_name: table_name.to_owned(), column_defs, indexes, + created, }; let index_sync = IndexSync::from_schema(tree, txid, &schema); diff --git a/test-suite/src/data_type/mod.rs b/test-suite/src/data_type/mod.rs index 653a01ad2..c431ce58d 100644 --- a/test-suite/src/data_type/mod.rs +++ b/test-suite/src/data_type/mod.rs @@ -12,5 +12,6 @@ pub mod map; pub mod sql_types; pub mod time; pub mod timestamp; +pub mod uint16; pub mod uint8; pub mod uuid; diff --git a/test-suite/src/data_type/uint16.rs b/test-suite/src/data_type/uint16.rs new file mode 100644 index 000000000..bb3db87d5 --- /dev/null +++ b/test-suite/src/data_type/uint16.rs @@ -0,0 +1,47 @@ +use { + crate::*, + gluesql_core::{data::ValueError, prelude::Value::*}, +}; + +test_case!(uint16, async move { + run!( + "CREATE TABLE Item ( + field_one UINT16, + field_two UINT16, + );" + ); + run!(r#"INSERT INTO Item VALUES (1, 1), (2, 2), (3, 3), (4, 4);"#); + + test!( + "INSERT INTO Item VALUES (327689,327689);", + Err(ValueError::FailedToParseNumber.into()) + ); + + test!( + "INSERT INTO Item VALUES (-32769, -32769);", + Err(ValueError::FailedToParseNumber.into()) + ); + test!( + "SELECT field_one, field_two FROM Item", + Ok(select!( + field_one | field_two + U16 | U16; + 1 1; + 2 2; + 3 3; + 4 4 + )) + ); + test!( + "SELECT field_one FROM Item WHERE field_one > 0", + Ok(select!(field_one U16; 1; 2;3;4)) + ); + test!( + "SELECT field_one FROM Item WHERE field_one >= 0", + Ok(select!(field_one U16; 1; 2;3;4)) + ); + test!( + "SELECT field_one FROM Item WHERE field_one = 2", + Ok(select!(field_one U16; 2)) + ); +}); diff --git a/test-suite/src/dictionary_index.rs b/test-suite/src/dictionary_index.rs index af14ad522..7b2d9208a 100644 --- a/test-suite/src/dictionary_index.rs +++ b/test-suite/src/dictionary_index.rs @@ -1,4 +1,7 @@ -use {crate::*, gluesql_core::prelude::Value::*}; +use { + crate::*, + gluesql_core::{prelude::Value::*, translate::TranslateError}, +}; test_case!(ditionary_index, async move { run!("CREATE TABLE Foo (id INT, name TEXT);"); @@ -27,4 +30,30 @@ test_case!(ditionary_index, async move { "Foo".to_owned() "Foo_id_2".to_owned() "BOTH".to_owned() "id + 2".to_owned() false )) ); + + let test_cases = [ + ( + "DROP INDEX Bar.PRIMARY", + Err(TranslateError::CannotDropPrimary.into()), + ), + ( + "CREATE INDEX Primary ON Foo (id)", + Err(TranslateError::ReservedIndexName("Primary".to_owned()).into()), + ), + ( + "SELECT OBJECT_NAME, OBJECT_TYPE FROM GLUE_OBJECTS WHERE CREATED > NOW() - INTERVAL 1 MINUTE", + Ok(select!( + OBJECT_NAME | OBJECT_TYPE ; + Str | Str ; + "Bar".to_owned() "TABLE".to_owned(); + "Bar_name_concat".to_owned() "INDEX".to_owned(); + "Foo".to_owned() "TABLE".to_owned(); + "Foo_id".to_owned() "INDEX".to_owned(); + "Foo_id_2".to_owned() "INDEX".to_owned() + )) + ), + ]; + for (sql, expected) in test_cases { + test!(sql, expected); + } }); diff --git a/test-suite/src/function/cast.rs b/test-suite/src/function/cast.rs index a91e1c49d..ee52d3e0d 100644 --- a/test-suite/src/function/cast.rs +++ b/test-suite/src/function/cast.rs @@ -85,6 +85,14 @@ test_case!(cast_literal, async move { r#"SELECT CAST(-1 AS UINT8) AS cast FROM Item"#, Err(ValueError::LiteralCastToUnsignedInt8Failed("-1".to_owned()).into()), ), + ( + r#"SELECT CAST("foo" AS UINT16) AS cast FROM Item"#, + Err(ValueError::LiteralCastFromTextToUint16Failed("foo".to_owned()).into()), + ), + ( + r#"SELECT CAST(-1 AS UINT16) AS cast FROM Item"#, + Err(ValueError::LiteralCastToUint16Failed("-1".to_owned()).into()), + ), ( r#"SELECT CAST("1.1" AS FLOAT) AS cast FROM Item"#, Ok(select!(cast F64; 1.1)), diff --git a/test-suite/src/function/concat.rs b/test-suite/src/function/concat.rs index 9634ded04..98d258bfa 100644 --- a/test-suite/src/function/concat.rs +++ b/test-suite/src/function/concat.rs @@ -1,6 +1,6 @@ use { crate::*, - gluesql_core::{prelude::Value::*, translate::TranslateError}, + gluesql_core::{data::ValueError, prelude::Value::*, translate::TranslateError}, }; test_case!(concat, async move { @@ -37,12 +37,24 @@ test_case!(concat, async move { test!( r#"select concat("ab", "cd", NULL, "ef") as myconcat from Concat;"#, - Ok(select!( - myconcat - Str; - "abcdef".to_owned() - )) + Ok(select_with_null!(myconcat; Null)) ); + + test!( + r#"select concat() as myconcat from Concat;"#, + Err(TranslateError::FunctionArgsLengthNotMatchingMin { + name: "CONCAT".to_owned(), + expected_minimum: 1, + found: 0 + } + .into()) + ); + + test!( + r#"select concat(DATE "2020-06-11", DATE "2020-16-3") as myconcat from Concat;"#, + Err(ValueError::FailedToParseDate("2020-16-3".to_owned()).into()) + ); + // test with non string arguments test!( r#"select concat(123, 456, 3.14) as myconcat from Concat;"#, diff --git a/test-suite/src/function/concat_ws.rs b/test-suite/src/function/concat_ws.rs new file mode 100644 index 000000000..67dcce4ee --- /dev/null +++ b/test-suite/src/function/concat_ws.rs @@ -0,0 +1,100 @@ +use { + crate::*, + gluesql_core::{prelude::Value::*, translate::TranslateError}, +}; + +test_case!(concat_ws, async move { + test!( + r#"VALUES(CONCAT_WS(",", "AB", "CD", "EF"))"#, + Ok(select!( + column1 + Str; + "AB,CD,EF".to_owned() + )) + ); + + run!( + " + CREATE TABLE Concat ( + id INTEGER, + flag BOOLEAN, + text TEXT, + null_value TEXT NULL, + ); + " + ); + run!(r#"INSERT INTO Concat VALUES (1, TRUE, "Foo", NULL);"#); + + test!( + r#"select concat_ws("/", id, flag, null_value, text) as myc from Concat;"#, + Ok(select!( + myc + Str; + "1/TRUE/Foo".to_owned() + )) + ); + + test!( + r#"select concat_ws("", "ab", "cd") as myc from Concat;"#, + Ok(select!( + myc + Str; + "abcd".to_owned() + )) + ); + + test!( + r#"select concat_ws("", "ab", "cd", "ef") as myconcat from Concat;"#, + Ok(select!( + myconcat + Str; + "abcdef".to_owned() + )) + ); + + test!( + r#"select concat_ws(",", "ab", "cd", "ef") as myconcat from Concat;"#, + Ok(select!( + myconcat + Str; + "ab,cd,ef".to_owned() + )) + ); + + test!( + r#"select concat_ws("/", "ab", "cd", "ef") as myconcat from Concat;"#, + Ok(select!( + myconcat + Str; + "ab/cd/ef".to_owned() + )) + ); + + test!( + r#"select concat_ws("", "ab", "cd", NULL, "ef") as myconcat from Concat;"#, + Ok(select!( + myconcat + Str; + "abcdef".to_owned() + )) + ); + + test!( + r#"select concat_ws("", 123, 456, 3.14) as myconcat from Concat;"#, + Ok(select!( + myconcat + Str; + "1234563.14".to_owned() + )) + ); + + test!( + r#"select concat_ws() as myconcat from Concat;"#, + Err(TranslateError::FunctionArgsLengthNotMatchingMin { + name: "CONCAT_WS".to_owned(), + expected_minimum: 2, + found: 0 + } + .into()) + ); +}); diff --git a/test-suite/src/function/mod.rs b/test-suite/src/function/mod.rs index 9fe4afe83..c3d19dad8 100644 --- a/test-suite/src/function/mod.rs +++ b/test-suite/src/function/mod.rs @@ -4,6 +4,7 @@ pub mod cast; pub mod ceil; pub mod chr; pub mod concat; +pub mod concat_ws; pub mod degrees; pub mod div_mod; pub mod exp_log; diff --git a/test-suite/src/index/showindexes.rs b/test-suite/src/index/showindexes.rs index 3aed1d606..b90116c44 100644 --- a/test-suite/src/index/showindexes.rs +++ b/test-suite/src/index/showindexes.rs @@ -1,11 +1,6 @@ use { crate::*, - gluesql_core::{ - ast::{BinaryOperator, Expr}, - data::{SchemaIndex, SchemaIndexOrd}, - executor::ExecuteError, - prelude::Payload, - }, + gluesql_core::{executor::ExecuteError, prelude::Payload, prelude::Value::*}, }; test_case!(showindexes, async move { @@ -39,32 +34,16 @@ CREATE TABLE Test ( "CREATE INDEX idx_id2 ON Test (id + num)", Ok(Payload::CreateIndex) ); - test!( "show indexes from Test", - Ok(Payload::ShowIndexes(vec![ - SchemaIndex { - name: "idx_id".to_owned(), - order: SchemaIndexOrd::Both, - expr: Expr::Identifier("id".to_owned()) - }, - SchemaIndex { - name: "idx_name".to_owned(), - order: SchemaIndexOrd::Both, - expr: Expr::Identifier("name".to_owned()) - }, - SchemaIndex { - name: "idx_id2".to_owned(), - order: SchemaIndexOrd::Both, - expr: Expr::BinaryOp { - left: Box::new(Expr::Identifier("id".to_owned())), - op: BinaryOperator::Plus, - right: Box::new(Expr::Identifier("num".to_owned())) - } - } - ])) + Ok(select!( + TABLE_NAME | INDEX_NAME | ORDER | EXPRESSION | UNIQUENESS; + Str | Str | Str | Str | Bool; + "Test".to_owned() "idx_id".to_owned() "BOTH".to_owned() "id".to_owned() false; + "Test".to_owned() "idx_name".to_owned() "BOTH".to_owned() "name".to_owned() false; + "Test".to_owned() "idx_id2".to_owned() "BOTH".to_owned() "id + num".to_owned() false + )) ); - test!( "show indexes from NoTable", Err(ExecuteError::TableNotFound("NoTable".to_owned()).into()) diff --git a/test-suite/src/lib.rs b/test-suite/src/lib.rs index 5a4626169..1fb0ff2f2 100644 --- a/test-suite/src/lib.rs +++ b/test-suite/src/lib.rs @@ -100,6 +100,7 @@ macro_rules! generate_store_tests { glue!(function_cast_literal, function::cast::cast_literal); glue!(function_cast_value, function::cast::cast_value); glue!(function_concat, function::concat::concat); + glue!(function_concat_ws, function::concat_ws::concat_ws); glue!(function_ifnull, function::ifnull::ifnull); glue!(function_math_function_asin, function::math_function::asin); glue!(function_math_function_acos, function::math_function::acos); @@ -141,6 +142,7 @@ macro_rules! generate_store_tests { glue!(int32, data_type::int32::int32); glue!(int64, data_type::int64::int64); glue!(int128, data_type::int128::int128); + glue!(uint16, data_type::uint16::uint16); glue!(uint8, data_type::uint8::uint8); glue!(date, data_type::date::date); glue!(timestamp, data_type::timestamp::timestamp);