Repository: gluesql/gluesql
Pull request #99 (2020-10-18 05:18:30)

More expression support in blend
```sql
SELECT (1 + 2) AS foo FROM Table1;
...
```

Except for a few expressions which is specially handled by `blend.rs` itself, all other expressions are now handled by 'evaluate' module.



Patch:
diff --git a/src/executor/blend.rs b/src/executor/blend.rs
index 6c532327..1fad3b94 100644
--- a/src/executor/blend.rs
+++ b/src/executor/blend.rs
@@ -17,8 +17,8 @@ use crate::store::Store;
 
 #[derive(Error, Serialize, Debug, PartialEq)]
 pub enum BlendError {
-    #[error("this field definition is not supported yet")]
-    FieldDefinitionNotSupported,
+    #[error("Not supported compound identifier: {0}")]
+    NotSupportedCompoundIdentifier(String),
 
     #[error("column not found: {0}")]
     ColumnNotFound(String),
@@ -40,13 +40,6 @@ enum Blended<I1, I2, I3, I4> {
     Err(I4),
 }
 
-struct Context<'a> {
-    table_alias: &'a str,
-    columns: Rc<Vec<Ident>>,
-    values: Option<Vec<Rc<Value>>>,
-    next: Option<Box<Context<'a>>>,
-}
-
 impl<'a, T: 'static + Debug> Blend<'a, T> {
     pub fn new(storage: &'a dyn Store<T>, fields: &'a [SelectItem]) -> Self {
         Self { storage, fields }
@@ -138,7 +131,7 @@ impl<'a, T: 'static + Debug> Blend<'a, T> {
             .iter()
             .flat_map(|item| match item {
                 SelectItem::Wildcard => {
-                    let values = get_all_values(&context).into_iter().map(Ok);
+                    let values = context.get_all_values().into_iter().map(Ok);
 
                     Blended::All(values)
                 }
@@ -150,26 +143,28 @@ impl<'a, T: 'static + Debug> Blend<'a, T> {
                         }
                     };
 
-                    match get_alias_values(&context, table_alias) {
+                    match context.get_alias_values(table_alias) {
                         Some(values) => Blended::AllInTable(values.into_iter().map(Ok)),
                         None => err!(BlendError::TableNotFound(table_alias.to_string())),
                     }
                 }
                 SelectItem::UnnamedExpr(expr) | SelectItem::ExprWithAlias { expr, .. } => {
                     match expr {
-                        Expr::Identifier(ident) => match get_value(&context, &ident.value) {
+                        Expr::Identifier(ident) => match context.get_value(&ident.value) {
                             Some(value) => Blended::Single(once(Ok(value))),
                             None => err!(BlendError::ColumnNotFound(ident.to_string())),
                         },
                         Expr::CompoundIdentifier(idents) => {
                             if idents.len() != 2 {
-                                return err!(BlendError::FieldDefinitionNotSupported);
+                                return err!(BlendError::NotSupportedCompoundIdentifier(
+                                    expr.to_string()
+                                ));
                             }
 
                             let table_alias = &idents[0].value;
                             let column = &idents[1].value;
 
-                            match get_alias_value(&context, table_alias, column) {
+                            match context.get_alias_value(table_alias, column) {
                                 Some(value) => Blended::Single(once(Ok(value))),
                                 None => err!(BlendError::ColumnNotFound(format!(
                                     "{}.{}",
@@ -177,7 +172,7 @@ impl<'a, T: 'static + Debug> Blend<'a, T> {
                                 ))),
                             }
                         }
-                        Expr::BinaryOp { .. } | Expr::Function(_) => {
+                        _ => {
                             let value = evaluate_blended(
                                 self.storage,
                                 None,
@@ -189,12 +184,6 @@ impl<'a, T: 'static + Debug> Blend<'a, T> {
 
                             Blended::Single(once(value))
                         }
-                        Expr::Value(literal) => {
-                            let value = Value::try_from(literal).map(Rc::new);
-
-                            Blended::Single(once(value))
-                        }
-                        _ => err!(BlendError::FieldDefinitionNotSupported),
                     }
                 }
             })
@@ -202,33 +191,22 @@ impl<'a, T: 'static + Debug> Blend<'a, T> {
     }
 }
 
-fn get_value(context: &Context<'_>, target: &str) -> Option<Rc<Value>> {
-    let Context {
-        values,
-        columns,
-        next,
-        ..
-    } = context;
-
-    columns
-        .iter()
-        .position(|column| column.value == target)
-        .map(|index| match values {
-            Some(values) => Rc::clone(&values[index]),
-            None => Rc::new(Value::Empty),
-        })
-        .or_else(|| next.as_ref().and_then(|next| get_value(next, target)))
+struct Context<'a> {
+    table_alias: &'a str,
+    columns: Rc<Vec<Ident>>,
+    values: Option<Vec<Rc<Value>>>,
+    next: Option<Box<Context<'a>>>,
 }
 
-fn get_alias_value(context: &Context<'_>, alias: &str, target: &str) -> Option<Rc<Value>> {
-    let Context {
-        table_alias,
-        values,
-        columns,
-        next,
-    } = context;
+impl Context<'_> {
+    fn get_value(&self, target: &str) -> Option<Rc<Value>> {
+        let Context {
+            values,
+            columns,
+            next,
+            ..
+        } = self;
 
-    if table_alias == &alias {
         columns
             .iter()
             .position(|column| column.value == target)
@@ -236,51 +214,72 @@ fn get_alias_value(context: &Context<'_>, alias: &str, target: &str) -> Option<R
                 Some(values) => Rc::clone(&values[index]),
                 None => Rc::new(Value::Empty),
             })
-    } else {
-        next.as_ref()
-            .and_then(|next| get_alias_value(next, alias, target))
+            .or_else(|| next.as_ref().and_then(|next| next.get_value(target)))
     }
-}
 
-fn get_all_values(context: &Context<'_>) -> Vec<Rc<Value>> {
-    let Context {
-        values,
-        next,
-        columns,
-        ..
-    } = context;
-
-    let values: Vec<Rc<Value>> = match values {
-        Some(values) => values.iter().map(Rc::clone).collect(),
-        None => columns.iter().map(|_| Rc::new(Value::Empty)).collect(),
-    };
-
-    match next.as_ref() {
-        Some(next) => get_all_values(next)
-            .into_iter()
-            .chain(values.into_iter())
-            .collect(),
-        None => values,
+    fn get_alias_value(&self, alias: &str, target: &str) -> Option<Rc<Value>> {
+        let Context {
+            table_alias,
+            values,
+            columns,
+            next,
+        } = self;
+
+        if table_alias == &alias {
+            columns
+                .iter()
+                .position(|column| column.value == target)
+                .map(|index| match values {
+                    Some(values) => Rc::clone(&values[index]),
+                    None => Rc::new(Value::Empty),
+                })
+        } else {
+            next.as_ref()
+                .and_then(|next| next.get_alias_value(alias, target))
+        }
     }
-}
 
-fn get_alias_values(context: &Context<'_>, alias: &str) -> Option<Vec<Rc<Value>>> {
-    let Context {
-        table_alias,
-        values,
-        columns,
-        next,
-    } = context;
+    fn get_all_values(&self) -> Vec<Rc<Value>> {
+        let Context {
+            values,
+            next,
+            columns,
+            ..
+        } = self;
 
-    if table_alias == &alias {
-        let values = match values {
+        let values: Vec<Rc<Value>> = match values {
             Some(values) => values.iter().map(Rc::clone).collect(),
             None => columns.iter().map(|_| Rc::new(Value::Empty)).collect(),
         };
 
-        Some(values)
-    } else {
-        next.as_ref().and_then(|next| get_alias_values(next, alias))
+        match next.as_ref() {
+            Some(next) => next
+                .get_all_values()
+                .into_iter()
+                .chain(values.into_iter())
+                .collect(),
+            None => values,
+        }
+    }
+
+    fn get_alias_values(&self, alias: &str) -> Option<Vec<Rc<Value>>> {
+        let Context {
+            table_alias,
+            values,
+            columns,
+            next,
+        } = self;
+
+        if table_alias == &alias {
+            let values = match values {
+                Some(values) => values.iter().map(Rc::clone).collect(),
+                None => columns.iter().map(|_| Rc::new(Value::Empty)).collect(),
+            };
+
+            Some(values)
+        } else {
+            next.as_ref().and_then(|next| next.get_alias_values(alias))
+        }
     }
 }
 


Test patch:
diff --git a/src/tests/blend.rs b/src/tests/blend.rs
index 5fa8f7dd..62cbb533 100644
--- a/src/tests/blend.rs
+++ b/src/tests/blend.rs
@@ -106,12 +106,12 @@ pub fn blend(mut tester: impl tests::Tester) {
             ),
         ),
         (
-            "SELECT 2+id+2*100-1 as Ident, name FROM BlendUser",
+            "SELECT (1 + 2) as foo, 2+id+2*100-1 as Ident, name FROM BlendUser",
             select!(
-                I64 Str;
-                202   "Taehoon".to_owned();
-                203   "Mike".to_owned();
-                204   "Jorno".to_owned()
+                I64 I64 Str;
+                3   202   "Taehoon".to_owned();
+                3   203   "Mike".to_owned();
+                3   204   "Jorno".to_owned()
             ),
         ),
     ];
@@ -122,8 +122,8 @@ pub fn blend(mut tester: impl tests::Tester) {
 
     let error_cases = vec![
         (
-            BlendError::FieldDefinitionNotSupported,
-            "SELECT (1 * 2) as a FROM BlendItem;",
+            BlendError::NotSupportedCompoundIdentifier("a.b.c.d".to_owned()),
+            "SELECT a.b.c.d as a FROM BlendItem;",
         ),
         (
             BlendError::ColumnNotFound("a".to_owned()),