Repository: gluesql/gluesql
Pull request #160 (2021-03-05 06:57:54)

Migrate sqlparser dep version from v0.6.1 to v0.8.0
There were lots of small changes.

Now due to this upgrade, join query without any constraint is now supported.

e.g.
```sql
SELECT * FROM Item JOIN Player
```



Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 9f04b96d..c93701b6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,7 +35,7 @@ iter-enum = "0.2.4"
 itertools = "0.9.0"
 pin-project = "1.0.1"
 serde = { version = "1.0.117", features = ["derive"] }
-sqlparser = { version = "0.6.1", features = ["serde"] }
+sqlparser = { version = "0.8.0", features = ["serde"] }
 thiserror = "1.0.21"
 
 # dependencies for sled-storage
diff --git a/src/data/value/ast_value.rs b/src/data/value/ast_value.rs
index 1e84094c..1562262d 100644
--- a/src/data/value/ast_value.rs
+++ b/src/data/value/ast_value.rs
@@ -10,16 +10,16 @@ impl PartialEq<AstValue> for Value {
         match (self, other) {
             (Value::Bool(l), AstValue::Boolean(r))
             | (Value::OptBool(Some(l)), AstValue::Boolean(r)) => l == r,
-            (Value::I64(l), AstValue::Number(r))
-            | (Value::OptI64(Some(l)), AstValue::Number(r)) => match r.parse::<i64>() {
+            (Value::I64(l), AstValue::Number(r, false))
+            | (Value::OptI64(Some(l)), AstValue::Number(r, false)) => match r.parse::<i64>() {
                 Ok(r) => l == &r,
                 Err(_) => match r.parse::<f64>() {
                     Ok(r) => (*l as f64) == r,
                     Err(_) => false,
                 },
             },
-            (Value::F64(l), AstValue::Number(r))
-            | (Value::OptF64(Some(l)), AstValue::Number(r)) => match r.parse::<f64>() {
+            (Value::F64(l), AstValue::Number(r, false))
+            | (Value::OptF64(Some(l)), AstValue::Number(r, false)) => match r.parse::<f64>() {
                 Ok(r) => l == &r,
                 Err(_) => match r.parse::<i64>() {
                     Ok(r) => *l == (r as f64),
@@ -40,16 +40,16 @@ impl PartialEq<AstValue> for Value {
 impl PartialOrd<AstValue> for Value {
     fn partial_cmp(&self, other: &AstValue) -> Option<Ordering> {
         match (self, other) {
-            (Value::I64(l), AstValue::Number(r))
-            | (Value::OptI64(Some(l)), AstValue::Number(r)) => match r.parse::<i64>() {
+            (Value::I64(l), AstValue::Number(r, false))
+            | (Value::OptI64(Some(l)), AstValue::Number(r, false)) => match r.parse::<i64>() {
                 Ok(r) => Some(l.cmp(&r)),
                 Err(_) => match r.parse::<f64>() {
                     Ok(r) => (*l as f64).partial_cmp(&r),
                     Err(_) => None,
                 },
             },
-            (Value::F64(l), AstValue::Number(r))
-            | (Value::OptF64(Some(l)), AstValue::Number(r)) => match r.parse::<f64>() {
+            (Value::F64(l), AstValue::Number(r, false))
+            | (Value::OptF64(Some(l)), AstValue::Number(r, false)) => match r.parse::<f64>() {
                 Ok(r) => l.partial_cmp(&r),
                 Err(_) => match r.parse::<i64>() {
                     Ok(r) => l.partial_cmp(&(r as f64)),
@@ -68,7 +68,7 @@ impl TryFrom<&AstValue> for Value {
 
     fn try_from(literal: &AstValue) -> Result<Self> {
         match literal {
-            AstValue::Number(v) => v
+            AstValue::Number(v, false) => v
                 .parse::<i64>()
                 .map_or_else(|_| v.parse::<f64>().map(Value::F64), |v| Ok(Value::I64(v)))
                 .map_err(|_| ValueError::FailedToParseNumber.into()),
diff --git a/src/data/value/mod.rs b/src/data/value/mod.rs
index e743c3e6..f26d70a7 100644
--- a/src/data/value/mod.rs
+++ b/src/data/value/mod.rs
@@ -135,11 +135,11 @@ impl Value {
         literal: &AstValue,
     ) -> Result<Self> {
         match (data_type, literal) {
-            (DataType::Int, AstValue::Number(v)) => v
+            (DataType::Int, AstValue::Number(v, false)) => v
                 .parse()
                 .map(|v| nullable.into_value(Value::OptI64(Some(v)), Value::I64(v)))
                 .map_err(|_| ValueError::FailedToParseNumber.into()),
-            (DataType::Float(_), AstValue::Number(v)) => v
+            (DataType::Float(_), AstValue::Number(v, false)) => v
                 .parse()
                 .map(|v| nullable.into_value(Value::OptF64(Some(v)), Value::F64(v)))
                 .map_err(|_| ValueError::FailedToParseNumber.into()),
@@ -226,20 +226,20 @@ impl Value {
 
     pub fn clone_by(&self, literal: &AstValue) -> Result<Self> {
         match (self, literal) {
-            (Value::I64(_), AstValue::Number(v)) => v
+            (Value::I64(_), AstValue::Number(v, false)) => v
                 .parse()
                 .map(Value::I64)
                 .map_err(|_| ValueError::FailedToParseNumber.into()),
-            (Value::OptI64(_), AstValue::Number(v)) => v
+            (Value::OptI64(_), AstValue::Number(v, false)) => v
                 .parse()
                 .map(|v| Value::OptI64(Some(v)))
                 .map_err(|_| ValueError::FailedToParseNumber.into()),
             (Value::OptI64(_), AstValue::Null) => Ok(Value::OptI64(None)),
-            (Value::F64(_), AstValue::Number(v)) => v
+            (Value::F64(_), AstValue::Number(v, false)) => v
                 .parse()
                 .map(Value::F64)
                 .map_err(|_| ValueError::FailedToParseNumber.into()),
-            (Value::OptF64(_), AstValue::Number(v)) => v
+            (Value::OptF64(_), AstValue::Number(v, false)) => v
                 .parse()
                 .map(|v| Value::OptF64(Some(v)))
                 .map_err(|_| ValueError::FailedToParseNumber.into()),
diff --git a/src/executor/aggregate/error.rs b/src/executor/aggregate/error.rs
index 377b596b..e6b6c4f1 100644
--- a/src/executor/aggregate/error.rs
+++ b/src/executor/aggregate/error.rs
@@ -18,4 +18,7 @@ pub enum AggregateError {
 
     #[error("unreachable")]
     Unreachable,
+
+    #[error("unreachable named function arg: {0}")]
+    UnreachableNamedFunctionArg(String),
 }
diff --git a/src/executor/aggregate/mod.rs b/src/executor/aggregate/mod.rs
index 8ebf5564..2faa8640 100644
--- a/src/executor/aggregate/mod.rs
+++ b/src/executor/aggregate/mod.rs
@@ -9,7 +9,7 @@ use std::fmt::Debug;
 use std::pin::Pin;
 use std::rc::Rc;
 
-use sqlparser::ast::{Expr, Function, SelectItem};
+use sqlparser::ast::{Expr, Function, FunctionArg, SelectItem};
 
 use super::context::{AggregateContext, BlendContext, FilterContext};
 use super::evaluate::{evaluate, Evaluated};
@@ -191,8 +191,15 @@ fn aggregate<'a>(
         }
         _ => Err(AggregateError::OnlyIdentifierAllowed),
     };
-    let get_first_value = |args: &[Expr]| {
-        let expr = args.get(0).ok_or(AggregateError::Unreachable)?;
+    let get_first_arg_expr =
+        |args: &'a [FunctionArg]| match args.get(0).ok_or(AggregateError::Unreachable)? {
+            FunctionArg::Unnamed(expr) => Ok(expr),
+            FunctionArg::Named { name, .. } => Err(AggregateError::UnreachableNamedFunctionArg(
+                name.to_string(),
+            )),
+        };
+    let get_first_value = |args: &'a [FunctionArg]| {
+        let expr = get_first_arg_expr(args)?;
 
         get_value(expr)
     };
@@ -213,7 +220,7 @@ fn aggregate<'a>(
 
             match get_name(name)?.to_uppercase().as_str() {
                 "COUNT" => {
-                    let expr = args.get(0).ok_or(AggregateError::Unreachable)?;
+                    let expr = get_first_arg_expr(args)?;
                     let value = Value::I64(match expr {
                         Expr::Wildcard => 1,
                         _ => {
diff --git a/src/executor/evaluate/error.rs b/src/executor/evaluate/error.rs
index 25c3ea55..f85962b0 100644
--- a/src/executor/evaluate/error.rs
+++ b/src/executor/evaluate/error.rs
@@ -53,4 +53,7 @@ pub enum EvaluateError {
 
     #[error("unreachable impossible cast")]
     UnreachableImpossibleCast,
+
+    #[error("unreachable named function argument: {0}")]
+    UnreachableFunctionArg(String),
 }
diff --git a/src/executor/evaluate/evaluated.rs b/src/executor/evaluate/evaluated.rs
index f7f8639c..ad5e2f5b 100644
--- a/src/executor/evaluate/evaluated.rs
+++ b/src/executor/evaluate/evaluated.rs
@@ -112,21 +112,23 @@ impl<'a> PartialOrd for Evaluated<'a> {
 
 fn literal_partial_cmp(l: &AstValue, r: &AstValue) -> Option<Ordering> {
     match (l, r) {
-        (AstValue::Number(l), AstValue::Number(r)) => match (l.parse::<i64>(), r.parse::<i64>()) {
-            (Ok(l), Ok(r)) => Some(l.cmp(&r)),
-            (_, Ok(r)) => match l.parse::<f64>() {
-                Ok(l) => l.partial_cmp(&(r as f64)),
-                _ => None,
-            },
-            (Ok(l), _) => match r.parse::<f64>() {
-                Ok(r) => (l as f64).partial_cmp(&r),
-                _ => None,
-            },
-            _ => match (l.parse::<f64>(), r.parse::<f64>()) {
-                (Ok(l), Ok(r)) => l.partial_cmp(&r),
-                _ => None,
-            },
-        },
+        (AstValue::Number(l, false), AstValue::Number(r, false)) => {
+            match (l.parse::<i64>(), r.parse::<i64>()) {
+                (Ok(l), Ok(r)) => Some(l.cmp(&r)),
+                (_, Ok(r)) => match l.parse::<f64>() {
+                    Ok(l) => l.partial_cmp(&(r as f64)),
+                    _ => None,
+                },
+                (Ok(l), _) => match r.parse::<f64>() {
+                    Ok(r) => (l as f64).partial_cmp(&r),
+                    _ => None,
+                },
+                _ => match (l.parse::<f64>(), r.parse::<f64>()) {
+                    (Ok(l), Ok(r)) => l.partial_cmp(&r),
+                    _ => None,
+                },
+            }
+        }
         (AstValue::SingleQuotedString(l), AstValue::SingleQuotedString(r)) => Some(l.cmp(r)),
         _ => None,
     }
@@ -150,22 +152,22 @@ macro_rules! binary_op {
     ($name:ident, $op:tt) => {
         pub fn $name(&self, other: &Evaluated<'a>) -> Result<Evaluated<'a>> {
             let literal_binary_op = |l: &&AstValue, r: &&AstValue| match (l, r) {
-                (AstValue::Number(l), AstValue::Number(r)) => match (l.parse::<i64>(), r.parse::<i64>()) {
-                    (Ok(l), Ok(r)) => Ok(AstValue::Number((l $op r).to_string())),
+                (AstValue::Number(l, false), AstValue::Number(r, false)) => match (l.parse::<i64>(), r.parse::<i64>()) {
+                    (Ok(l), Ok(r)) => Ok(AstValue::Number((l $op r).to_string(), false)),
                     (Ok(l), _) => match r.parse::<f64>() {
-                        Ok(r) => Ok(AstValue::Number(((l as f64) $op r).to_string())),
+                        Ok(r) => Ok(AstValue::Number(((l as f64) $op r).to_string(), false)),
                         _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
                     },
                     (_, Ok(r)) => match l.parse::<f64>() {
-                        Ok(l) => Ok(AstValue::Number((l $op (r as f64)).to_string())),
+                        Ok(l) => Ok(AstValue::Number((l $op (r as f64)).to_string(), false)),
                         _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
                     },
                     (_, _) => match (l.parse::<f64>(), r.parse::<f64>()) {
-                        (Ok(l), Ok(r)) => Ok(AstValue::Number((l $op r).to_string())),
+                        (Ok(l), Ok(r)) => Ok(AstValue::Number((l $op r).to_string(), false)),
                         _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
                     },
                 },
-                (AstValue::Null, AstValue::Number(_)) | (AstValue::Number(_), AstValue::Null) => {
+                (AstValue::Null, AstValue::Number(_, false)) | (AstValue::Number(_, false), AstValue::Null) => {
                     Ok(AstValue::Null)
                 }
                 _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
@@ -197,11 +199,11 @@ macro_rules! unary_op {
     ($name:ident, $op:tt) => {
         pub fn $name(&self) -> Result<Evaluated<'a>> {
             let literal_unary_op = |v: &&AstValue| match v {
-                AstValue::Number(v) => v
+                AstValue::Number(v, false) => v
                     .parse::<i64>()
                     .map_or_else(
-                        |_| v.parse::<f64>().map(|v| AstValue::Number((0.0 $op v).to_string())),
-                        |v| Ok(AstValue::Number((0 $op v).to_string())),
+                        |_| v.parse::<f64>().map(|v| AstValue::Number((0.0 $op v).to_string(), false)),
+                        |v| Ok(AstValue::Number((0 $op v).to_string(), false)),
                     )
                     .map_err(|_| EvaluateError::LiteralUnaryOperationOnNonNumeric.into()),
                 AstValue::Null => Ok(AstValue::Null),
@@ -255,31 +257,37 @@ impl<'a> Evaluated<'a> {
 fn cast_ast_value(value: &AstValue, data_type: &DataType) -> Result<AstValue> {
     match (data_type, value) {
         (DataType::Boolean, AstValue::SingleQuotedString(value))
-        | (DataType::Boolean, AstValue::Number(value)) => Ok(match value.to_uppercase().as_str() {
-            "TRUE" | "1" => Ok(AstValue::Boolean(true)),
-            "FALSE" | "0" => Ok(AstValue::Boolean(false)),
-            _ => Err(EvaluateError::ImpossibleCast),
-        }?),
-        (DataType::Int, AstValue::Number(value)) => Ok(AstValue::Number(
+        | (DataType::Boolean, AstValue::Number(value, false)) => {
+            Ok(match value.to_uppercase().as_str() {
+                "TRUE" | "1" => Ok(AstValue::Boolean(true)),
+                "FALSE" | "0" => Ok(AstValue::Boolean(false)),
+                _ => Err(EvaluateError::ImpossibleCast),
+            }?)
+        }
+        (DataType::Int, AstValue::Number(value, false)) => Ok(AstValue::Number(
             value
                 .parse::<f64>()
                 .map_err(|_| EvaluateError::UnreachableImpossibleCast)?
                 .trunc()
                 .to_string(),
+            false,
         )),
         (DataType::Int, AstValue::SingleQuotedString(value))
         | (DataType::Float(_), AstValue::SingleQuotedString(value)) => {
-            Ok(AstValue::Number(value.to_string()))
+            Ok(AstValue::Number(value.to_string(), false))
         }
         (DataType::Int, AstValue::Boolean(value))
         | (DataType::Float(_), AstValue::Boolean(value)) => Ok(AstValue::Number(
             (if *value { "1" } else { "0" }).to_string(),
+            false,
         )),
-        (DataType::Float(_), AstValue::Number(value)) => Ok(AstValue::Number(value.to_string())),
+        (DataType::Float(_), AstValue::Number(value, false)) => {
+            Ok(AstValue::Number(value.to_string(), false))
+        }
         (DataType::Text, AstValue::Boolean(value)) => Ok(AstValue::SingleQuotedString(
             (if *value { "TRUE" } else { "FALSE" }).to_string(),
         )),
-        (DataType::Text, AstValue::Number(value)) => {
+        (DataType::Text, AstValue::Number(value, false)) => {
             Ok(AstValue::SingleQuotedString(value.to_string()))
         }
         (_, AstValue::Null) => Ok(AstValue::Null),
diff --git a/src/executor/evaluate/mod.rs b/src/executor/evaluate/mod.rs
index 33c1175c..03381e93 100644
--- a/src/executor/evaluate/mod.rs
+++ b/src/executor/evaluate/mod.rs
@@ -11,7 +11,9 @@ use {
     async_recursion::async_recursion,
     futures::stream::{StreamExt, TryStreamExt},
     im_rc::HashMap,
-    sqlparser::ast::{BinaryOperator, Expr, Function, UnaryOperator, Value as AstValue},
+    sqlparser::ast::{
+        BinaryOperator, Expr, Function, FunctionArg, UnaryOperator, Value as AstValue,
+    },
     std::{convert::TryInto, fmt::Debug, rc::Rc},
 };
 
@@ -34,7 +36,7 @@ pub async fn evaluate<'a, T: 'static + Debug>(
 
     match expr {
         Expr::Value(value) => match value {
-            AstValue::Number(_)
+            AstValue::Number(_, false)
             | AstValue::Boolean(_)
             | AstValue::SingleQuotedString(_)
             | AstValue::Null => Ok(Evaluated::LiteralRef(value)),
@@ -130,6 +132,15 @@ async fn evaluate_function<'a, T: 'static + Debug>(
         evaluate(storage, context, aggregated, expr, use_empty)
     };
 
+    let get_arg_expr = |arg: &'a FunctionArg| -> Result<&'a Expr> {
+        match arg {
+            FunctionArg::Unnamed(expr) => Ok(expr),
+            FunctionArg::Named { name, .. } => {
+                Err(EvaluateError::UnreachableFunctionArg(name.to_string()).into())
+            }
+        }
+    };
+
     let Function { name, args, .. } = func;
 
     match get_name(name)?.to_uppercase().as_str() {
@@ -142,15 +153,18 @@ async fn evaluate_function<'a, T: 'static + Debug>(
                 }
             };
 
-            if args.len() != 1 {
-                return Err(EvaluateError::NumberOfFunctionParamsNotMatching {
-                    expected: 1,
-                    found: args.len(),
+            let arg = match args.len() {
+                1 => &args[0],
+                found => {
+                    return Err(EvaluateError::NumberOfFunctionParamsNotMatching {
+                        expected: 1,
+                        found,
+                    }
+                    .into());
                 }
-                .into());
-            }
+            };
 
-            let expr = &args[0];
+            let expr = get_arg_expr(arg)?;
             let value: Value = match eval(expr).await?.try_into()? {
                 Value::Str(s) => Value::Str(convert(s)),
                 Value::OptStr(s) => Value::OptStr(s.map(convert)),
diff --git a/src/executor/execute.rs b/src/executor/execute.rs
index db7ef183..e951cbfa 100644
--- a/src/executor/execute.rs
+++ b/src/executor/execute.rs
@@ -157,9 +157,9 @@ pub async fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable>
                 AlterTableOperation::RenameTable {
                     table_name: new_table_name,
                 } => {
-                    storage
-                        .rename_schema(table_name, &new_table_name.value)
-                        .await
+                    let new_table_name = try_into!(storage, get_name(new_table_name));
+
+                    storage.rename_schema(table_name, new_table_name).await
                 }
                 AlterTableOperation::RenameColumn {
                     old_column_name,
@@ -246,6 +246,7 @@ async fn prepare<'a, T: 'static + Debug>(
             table_name,
             columns,
             source,
+            ..
         } => {
             let table_name = get_name(table_name)?;
             let Schema { column_defs, .. } = storage
@@ -262,7 +263,7 @@ async fn prepare<'a, T: 'static + Debug>(
                     select(
                         storage,
                         &sqlparser::ast::Query {
-                            ctes: vec![],
+                            with: None,
                             body: SetExpr::Select(select_query.clone()),
                             order_by: vec![],
                             limit: None,
diff --git a/src/executor/join.rs b/src/executor/join.rs
index b706761c..3b9edb95 100644
--- a/src/executor/join.rs
+++ b/src/executor/join.rs
@@ -165,6 +165,7 @@ async fn fetch_joined<'a, T: 'static + Debug>(
         JoinConstraint::Natural => {
             return Err(JoinError::NaturalOnJoinNotSupported.into());
         }
+        JoinConstraint::None => None,
     };
 
     let rows = storage
diff --git a/src/executor/limit.rs b/src/executor/limit.rs
index 461c898e..0cdcfa54 100644
--- a/src/executor/limit.rs
+++ b/src/executor/limit.rs
@@ -21,7 +21,7 @@ impl Limit {
     pub fn new(limit: Option<&Expr>, offset: Option<&Offset>) -> Result<Self> {
         let parse = |expr: &Expr| -> Result<usize> {
             match expr {
-                Expr::Value(AstValue::Number(v)) => {
+                Expr::Value(AstValue::Number(v, false)) => {
                     v.parse().map_err(|_| LimitError::Unreachable.into())
                 }
                 _ => Err(LimitError::Unreachable.into()),


Test patch:
diff --git a/src/tests/join.rs b/src/tests/join.rs
index b22a752c..4cf3fe1f 100644
--- a/src/tests/join.rs
+++ b/src/tests/join.rs
@@ -61,6 +61,7 @@ test_case!(join, async move {
     }
 
     let select_sqls = [
+        (75, "SELECT * FROM Item JOIN Player"),
         (
             15,
             "SELECT * FROM Item LEFT JOIN Player ON Player.id = Item.player_id;",