Repository: gluesql/gluesql
Pull request #237 (2021-06-10 05:19:54)

Validate default expr


Patch:
diff --git a/src/executor/alter/validate.rs b/src/executor/alter/validate.rs
index f6b15747..36db62d6 100644
--- a/src/executor/alter/validate.rs
+++ b/src/executor/alter/validate.rs
@@ -2,6 +2,7 @@ use {
     super::AlterError,
     crate::{
         ast::{ColumnDef, ColumnOption, ColumnOptionDef, DataType},
+        executor::evaluate_stateless,
         result::Result,
     },
 };
@@ -27,5 +28,16 @@ pub fn validate(column_def: &ColumnDef) -> Result<()> {
         .into());
     }
 
+    let default = options
+        .iter()
+        .find_map(|ColumnOptionDef { option, .. }| match option {
+            ColumnOption::Default(expr) => Some(expr),
+            _ => None,
+        });
+
+    if let Some(expr) = default {
+        evaluate_stateless(None, expr)?;
+    }
+
     Ok(())
 }
diff --git a/src/executor/evaluate/error.rs b/src/executor/evaluate/error.rs
index c01fb09b..e4831604 100644
--- a/src/executor/evaluate/error.rs
+++ b/src/executor/evaluate/error.rs
@@ -1,6 +1,4 @@
-use serde::Serialize;
-use std::fmt::Debug;
-use thiserror::Error;
+use {crate::ast::Expr, serde::Serialize, std::fmt::Debug, thiserror::Error};
 
 #[derive(Error, Serialize, Debug, PartialEq)]
 pub enum EvaluateError {
@@ -39,8 +37,8 @@ pub enum EvaluateError {
     #[error("unreachable wildcard expression")]
     UnreachableWildcardExpr,
 
-    #[error("unsupported stateless expression: {0}")]
-    UnsupportedStatelessExpr(String),
+    #[error("unsupported stateless expression: {0:#?}")]
+    UnsupportedStatelessExpr(Expr),
 
     #[error("unreachable empty context")]
     UnreachableEmptyContext,
diff --git a/src/executor/evaluate/stateless.rs b/src/executor/evaluate/stateless.rs
index 050b4ac1..dca5757a 100644
--- a/src/executor/evaluate/stateless.rs
+++ b/src/executor/evaluate/stateless.rs
@@ -105,6 +105,6 @@ pub fn evaluate_stateless<'a>(
         Expr::Wildcard | Expr::QualifiedWildcard(_) => {
             Err(EvaluateError::UnreachableWildcardExpr.into())
         }
-        _ => Err(EvaluateError::UnsupportedStatelessExpr(format!("{:#?}", expr)).into()),
+        _ => Err(EvaluateError::UnsupportedStatelessExpr(expr.clone()).into()),
     }
 }


Test patch:
diff --git a/src/tests/alter/alter_table.rs b/src/tests/alter/alter_table.rs
index 9b097786..f2a6e4bf 100644
--- a/src/tests/alter/alter_table.rs
+++ b/src/tests/alter/alter_table.rs
@@ -89,31 +89,10 @@ test_case!(alter_table_add_drop, async move {
         ),
         (
             "ALTER TABLE Foo ADD COLUMN something INTEGER DEFAULT (SELECT id FROM Bar LIMIT 1)",
-            Err(EvaluateError::UnsupportedStatelessExpr(format!(
-                "{:#?}",
-                Expr::Subquery(Box::new(ast::Query {
-                    body: SetExpr::Select(Box::new(Select {
-                        projection: vec![SelectItem::Expr {
-                            expr: Expr::Identifier("id".to_owned()),
-                            label: "id".to_owned(),
-                        }],
-                        from: TableWithJoins {
-                            relation: TableFactor::Table {
-                                name: ObjectName(vec!["Bar".to_owned()]),
-                                alias: None,
-                                index: None,
-                            },
-                            joins: vec![],
-                        },
-                        selection: None,
-                        group_by: vec![],
-                        having: None,
-                    })),
-                    limit: Some(Expr::Literal(AstLiteral::Number("1".to_owned()))),
-                    offset: None,
-                }))
-            ))
-            .into()),
+            Err(
+                EvaluateError::UnsupportedStatelessExpr(expr!("(SELECT id FROM Bar LIMIT 1)"))
+                    .into(),
+            ),
         ),
         (
             "ALTER TABLE Foo ADD COLUMN something SOMEWHAT",
diff --git a/src/tests/alter/create_table.rs b/src/tests/alter/create_table.rs
index 26c7da28..af78ee50 100644
--- a/src/tests/alter/create_table.rs
+++ b/src/tests/alter/create_table.rs
@@ -60,6 +60,10 @@ test_case!(create_table, async move {
             )
             .into()),
         ),
+        (
+            "CREATE TABLE Gluery (id INTEGER DEFAULT (SELECT id FROM Wow))",
+            Err(EvaluateError::UnsupportedStatelessExpr(expr!("(SELECT id FROM Wow)")).into()),
+        ),
     ];
 
     for (sql, expected) in test_cases.into_iter() {
diff --git a/src/tests/migrate.rs b/src/tests/migrate.rs
index afd636ef..d11e55e0 100644
--- a/src/tests/migrate.rs
+++ b/src/tests/migrate.rs
@@ -25,11 +25,7 @@ test_case!(migrate, async move {
             r#"INSERT INTO Test (id, num, name) VALUES (1.1, 1, "good");"#,
         ),
         (
-            EvaluateError::UnsupportedStatelessExpr(format!(
-                "{:#?}",
-                Expr::CompoundIdentifier(vec!["a".to_owned(), "b".to_owned()]),
-            ))
-            .into(),
+            EvaluateError::UnsupportedStatelessExpr(expr!("a.b")).into(),
             "INSERT INTO Test (id, num, name) VALUES (1, 1, a.b);",
         ),
         (
diff --git a/src/tests/tester.rs b/src/tests/tester.rs
index 83b60000..a228746c 100644
--- a/src/tests/tester.rs
+++ b/src/tests/tester.rs
@@ -1,17 +1,96 @@
 use {
     crate::{
-        ast::{IndexItem, SetExpr, Statement, TableFactor},
+        ast::{Expr, IndexItem, SetExpr, Statement, TableFactor},
+        data::{Row, Value},
         executor::{execute, Payload},
-        parse_sql::parse,
+        parse_sql::{parse, parse_expr},
         plan::plan,
         result::Result,
         store::{GStore, GStoreMut},
-        translate::translate,
+        translate::{translate, translate_expr},
     },
     async_trait::async_trait,
     std::{cell::RefCell, fmt::Debug, rc::Rc},
 };
 
+pub fn expr(sql: &str) -> Expr {
+    let parsed = parse_expr(sql).unwrap();
+
+    translate_expr(&parsed).unwrap()
+}
+
+pub fn test(expected: Result<Payload>, found: Result<Payload>) {
+    let (expected, found): (Payload, Payload) = match (expected, found) {
+        (Ok(a), Ok(b)) => (a, b),
+        (a, b) => {
+            assert_eq!(a, b);
+
+            return;
+        }
+    };
+
+    let (expected, found) = match (expected, found) {
+        (
+            Payload::Select {
+                labels: expected_labels,
+                rows: a,
+            },
+            Payload::Select {
+                labels: found_labels,
+                rows: b,
+            },
+        ) => {
+            assert_eq!(expected_labels, found_labels);
+
+            (a, b)
+        }
+        (a, b) => {
+            assert_eq!(a, b);
+
+            return;
+        }
+    };
+
+    assert_eq!(
+        expected.len(),
+        found.len(),
+        "\n[err: number of rows]\nexpected: {:?}\n   found: {:?}",
+        expected,
+        found
+    );
+
+    let rows = expected.into_iter().zip(found.into_iter()).enumerate();
+
+    for (i, (expected, found)) in rows.into_iter() {
+        let Row(expected) = expected;
+        let Row(found) = found;
+
+        assert_eq!(
+            expected.len(),
+            found.len(),
+            "\n[err: size of row] row index: {}\nexpected: {:?}\n   found: {:?}",
+            i,
+            expected,
+            found
+        );
+
+        expected
+            .iter()
+            .zip(found.iter())
+            .for_each(|(expected_val, found_val)| {
+                if matches!((expected_val, found_val), (&Value::Null, &Value::Null)) {
+                    return;
+                }
+
+                assert_eq!(
+                    expected_val, found_val,
+                    "\n[err: value] row index: {}\nexpected: {:?}\n   found: {:?}",
+                    i, expected, found
+                );
+            });
+    }
+}
+
 pub async fn run<T: 'static + Debug, U: GStore<T> + GStoreMut<T>>(
     cell: Rc<RefCell<Option<U>>>,
     sql: &str,
@@ -139,6 +218,13 @@ macro_rules! test_case {
                 };
             }
 
+            #[allow(unused_macros)]
+            macro_rules! expr {
+                ($sql: literal) => {
+                    tests::expr($sql)
+                };
+            }
+
             #[allow(unused_macros)]
             macro_rules! run {
                 ($sql: expr) => {
@@ -163,7 +249,7 @@ macro_rules! test_case {
                 ($expected: expr, $sql: expr) => {
                     let found = tests::run(Rc::clone(&cell), $sql, None).await;
 
-                    test($expected, found);
+                    tests::test($expected, found);
                 };
             }
 
@@ -172,83 +258,10 @@ macro_rules! test_case {
                 ($expected: expr, $indexes: expr, $sql: expr) => {
                     let found = tests::run(Rc::clone(&cell), $sql, Some($indexes)).await;
 
-                    test($expected, found);
+                    tests::test($expected, found);
                 };
             }
 
-            #[allow(unused)]
-            fn test(expected: Result<Payload>, found: Result<Payload>) {
-                let (expected, found): (Payload, Payload) = match (expected, found) {
-                    (Ok(a), Ok(b)) => (a, b),
-                    (a, b) => {
-                        assert_eq!(a, b);
-
-                        return;
-                    }
-                };
-
-                let (expected, found) = match (expected, found) {
-                    (
-                        Payload::Select {
-                            labels: expected_labels,
-                            rows: a,
-                        },
-                        Payload::Select {
-                            labels: found_labels,
-                            rows: b,
-                        },
-                    ) => {
-                        assert_eq!(expected_labels, found_labels);
-
-                        (a, b)
-                    }
-                    (a, b) => {
-                        assert_eq!(a, b);
-
-                        return;
-                    }
-                };
-
-                assert_eq!(
-                    expected.len(),
-                    found.len(),
-                    "\n[err: number of rows]\nexpected: {:?}\n   found: {:?}",
-                    expected,
-                    found
-                );
-
-                let rows = expected.into_iter().zip(found.into_iter()).enumerate();
-
-                for (i, (expected, found)) in rows.into_iter() {
-                    let Row(expected) = expected;
-                    let Row(found) = found;
-
-                    assert_eq!(
-                        expected.len(),
-                        found.len(),
-                        "\n[err: size of row] row index: {}\nexpected: {:?}\n   found: {:?}",
-                        i,
-                        expected,
-                        found
-                    );
-
-                    expected
-                        .iter()
-                        .zip(found.iter())
-                        .for_each(|(expected_val, found_val)| {
-                            if matches!((expected_val, found_val), (&Value::Null, &Value::Null)) {
-                                return;
-                            }
-
-                            assert_eq!(
-                                expected_val, found_val,
-                                "\n[err: value] row index: {}\nexpected: {:?}\n   found: {:?}",
-                                i, expected, found
-                            );
-                        });
-                }
-            }
-
             $content.await
         }
     };