Repository: gluesql/gluesql
Pull request #167 (2021-03-09 13:02:40)

Merge Prepared & Execute in execute.rs

KyGost: Hmm that seems like it might be a mess. I see now why they were separated.
KyGost: Much nicer!
KyGost: I left AlterTable because it would be a bit of a mess to use `try_block` there, `try_into` works nicer.

Patch:
diff --git a/src/data/value/literal.rs b/src/data/value/literal.rs
index def2ab5f..05cd0155 100644
--- a/src/data/value/literal.rs
+++ b/src/data/value/literal.rs
@@ -63,6 +63,7 @@ impl TryFrom<&Literal> for Value {
                 .map_err(|_| ValueError::FailedToParseNumber.into()),
             Literal::Boolean(v) => Ok(Value::Bool(*v)),
             Literal::SingleQuotedString(v) => Ok(Value::Str(v.to_string())),
+            Literal::Null => Ok(Value::Null),
             _ => Err(ValueError::SqlTypeNotSupported.into()),
         }
     }
diff --git a/src/data/value/mod.rs b/src/data/value/mod.rs
index 4317ca9f..3e657b3f 100644
--- a/src/data/value/mod.rs
+++ b/src/data/value/mod.rs
@@ -173,30 +173,6 @@ impl Value {
         }
     }
 
-    pub fn clone_by(&self, literal: &Literal) -> Result<Self> {
-        match (self, literal) {
-            (Value::I64(_), Literal::Number(v, false)) => v
-                .parse()
-                .map(Value::I64)
-                .map_err(|_| ValueError::FailedToParseNumber.into()),
-            (Value::F64(_), Literal::Number(v, false)) => v
-                .parse()
-                .map(Value::F64)
-                .map_err(|_| ValueError::FailedToParseNumber.into()),
-            (Value::Str(_), Literal::SingleQuotedString(v))
-            | (Value::Null, Literal::SingleQuotedString(v)) => Ok(Value::Str(v.clone())),
-            (Value::Bool(_), Literal::Boolean(v)) | (Value::Null, Literal::Boolean(v)) => {
-                Ok(Value::Bool(*v))
-            }
-            (Value::Null, Literal::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()),
-            (_, Literal::Null) => Ok(Value::Null),
-            _ => Err(ValueError::LiteralNotSupported.into()),
-        }
-    }
-
     pub fn add(&self, other: &Value) -> Result<Value> {
         use Value::*;
 
diff --git a/src/executor/evaluate/error.rs b/src/executor/evaluate/error.rs
index 74b4f7ea..cf4cd6ff 100644
--- a/src/executor/evaluate/error.rs
+++ b/src/executor/evaluate/error.rs
@@ -36,11 +36,17 @@ pub enum EvaluateError {
     #[error("unsupported compound identifier {0}")]
     UnsupportedCompoundIdentifier(String),
 
-    #[error("unreachable condition base")]
-    UnreachableConditionBase,
+    #[error("unsupported literal binary arithmetic between {0} and {1}")]
+    UnsupportedLiteralBinaryArithmetic(String, String),
 
-    #[error("unreachable evaluated arithmetic")]
-    UnreachableEvaluatedArithmetic,
+    #[error("unsupported evaluated binary arithmetic between {0} and {1}")]
+    UnsupportedEvaluatedBinaryArithmetic(String, String),
+
+    #[error("unsupported evaluated unary arithmetic of {0}")]
+    UnsupportedEvaluatedUnaryArithmetic(String),
+
+    #[error("unimplemented")]
+    Unimplemented,
 
     #[error("unreachable literal arithmetic")]
     UnreachableLiteralArithmetic,
@@ -48,9 +54,6 @@ pub enum EvaluateError {
     #[error("unreachable empty context")]
     UnreachableEmptyContext,
 
-    #[error("unimplemented")]
-    Unimplemented,
-
     #[error("unreachable named function argument: {0}")]
     UnreachableFunctionArg(String),
 }
diff --git a/src/executor/evaluate/evaluated.rs b/src/executor/evaluate/evaluated.rs
index 313fc322..2f853896 100644
--- a/src/executor/evaluate/evaluated.rs
+++ b/src/executor/evaluate/evaluated.rs
@@ -151,7 +151,7 @@ impl TryInto<Value> for Evaluated<'_> {
 macro_rules! binary_op {
     ($name:ident, $op:tt) => {
         pub fn $name(&self, other: &Evaluated<'a>) -> Result<Evaluated<'a>> {
-            let literal_binary_op = |l: &&Literal, r: &&Literal| match (l, r) {
+            let literal_binary_op = |l: &Literal, r: &Literal| match (l, r) {
                 (Literal::Number(l, false), Literal::Number(r, false)) => match (l.parse::<i64>(), r.parse::<i64>()) {
                     (Ok(l), Ok(r)) => Ok(Literal::Number((l $op r).to_string(), false)),
                     (Ok(l), _) => match r.parse::<f64>() {
@@ -166,35 +166,52 @@ macro_rules! binary_op {
                         (Ok(l), Ok(r)) => Ok(Literal::Number((l $op r).to_string(), false)),
                         _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
                     },
-                },
-                (Literal::Null, Literal::Number(_, false)) | (Literal::Number(_, false), Literal::Null) => {
-                    Ok(Literal::Null)
+                }.map(Evaluated::Literal),
+                (Literal::Null, Literal::Number(_, false))
+                | (Literal::Number(_, false), Literal::Null)
+                | (Literal::Null, Literal::Null) => {
+                    Ok(Evaluated::Literal(Literal::Null))
                 }
-                _ => Err(EvaluateError::UnreachableLiteralArithmetic.into()),
+                _ => Err(
+                    EvaluateError::UnsupportedLiteralBinaryArithmetic(
+                        l.to_string(),
+                        r.to_string()
+                    ).into()
+                ),
+            };
+
+            let value_binary_op = |l: &data::Value, r: &data::Value| {
+                l.$name(r).map(Evaluated::Value)
             };
 
             match (self, other) {
-                (LiteralRef(l), LiteralRef(r)) => literal_binary_op(l, r).map(Evaluated::Literal),
-                (LiteralRef(l), Literal(r)) => literal_binary_op(l, &r).map(Evaluated::Literal),
-                (LiteralRef(l), ValueRef(r)) => (r.clone_by(l)?).$name(r).map(Evaluated::Value),
-                (LiteralRef(l), Value(r)) => (r.clone_by(l)?).$name(r).map(Evaluated::Value),
-                (Literal(l), LiteralRef(r)) => literal_binary_op(&l, r).map(Evaluated::Literal),
-                (Literal(l), Literal(r)) => literal_binary_op(&l, &r).map(Evaluated::Literal),
-                (Literal(l), ValueRef(r)) => (r.clone_by(&l)?).$name(r).map(Evaluated::Value),
-                (Literal(l), Value(r)) => (r.clone_by(&l)?).$name(r).map(Evaluated::Value),
-                (ValueRef(l), LiteralRef(r)) => l.$name(&l.clone_by(r)?).map(Evaluated::Value),
-                (ValueRef(l), Literal(r)) => l.$name(&l.clone_by(&r)?).map(Evaluated::Value),
-                (ValueRef(l), ValueRef(r)) => l.$name(r).map(Evaluated::Value),
-                (ValueRef(l), Value(r)) => l.$name(&r).map(Evaluated::Value),
-                (Value(l), LiteralRef(r)) => (&l).$name(&&l.clone_by(r)?).map(Evaluated::Value),
-                (Value(l), Literal(r)) => (&l).$name(&&l.clone_by(&r)?).map(Evaluated::Value),
-                (Value(l), ValueRef(r)) => (&l).$name(r).map(Evaluated::Value),
-                (Value(l), Value(r)) => (&l).$name(&r).map(Evaluated::Value),
-                _ => Err(EvaluateError::UnreachableEvaluatedArithmetic.into()),
+                (LiteralRef(l), LiteralRef(r)) => literal_binary_op(l, r),
+                (LiteralRef(l), Literal(r))    => literal_binary_op(l, &r),
+                (LiteralRef(l), ValueRef(r))   => value_binary_op(&data::Value::try_from(*l)?, r),
+                (LiteralRef(l), Value(r))      => value_binary_op(&data::Value::try_from(*l)?, r),
+                (Literal(l),    LiteralRef(r)) => literal_binary_op(&l, r),
+                (Literal(l),    Literal(r))    => literal_binary_op(&l, &r),
+                (Literal(l),    ValueRef(r))   => value_binary_op(&data::Value::try_from(l)?, r),
+                (Literal(l),    Value(r))      => value_binary_op(&data::Value::try_from(l)?, r),
+                (ValueRef(l),   LiteralRef(r)) => value_binary_op(l, &data::Value::try_from(*r)?),
+                (ValueRef(l),   Literal(r))    => value_binary_op(l, &data::Value::try_from(r)?),
+                (ValueRef(l),   ValueRef(r))   => value_binary_op(l, r),
+                (ValueRef(l),   Value(r))      => value_binary_op(l, r),
+                (Value(l),      LiteralRef(r)) => value_binary_op(l, &data::Value::try_from(*r)?),
+                (Value(l),      Literal(r))    => value_binary_op(l, &data::Value::try_from(r)?),
+                (Value(l),      ValueRef(r))   => value_binary_op(l, r),
+                (Value(l),      Value(r))      => value_binary_op(l, r),
+                _ => Err(
+                    EvaluateError::UnsupportedEvaluatedBinaryArithmetic(
+                        format!("{:?}", self),
+                        format!("{:?}", other),
+                    ).into()
+                ),
             }
         }
     };
 }
+
 macro_rules! unary_op {
     ($name:ident, $op:tt) => {
         pub fn $name(&self) -> Result<Evaluated<'a>> {
@@ -215,7 +232,9 @@ macro_rules! unary_op {
                 Literal(v) => literal_unary_op(&v).map(Evaluated::Literal),
                 ValueRef(v) => v.$name().map(Evaluated::Value),
                 Value(v) => (&v).$name().map(Evaluated::Value),
-                _ => Err(EvaluateError::UnreachableEvaluatedArithmetic.into()),
+                _ => Err(
+                    EvaluateError::UnsupportedEvaluatedUnaryArithmetic(format!("{:?}", self)).into()
+                ),
             }
         }
     };
diff --git a/src/executor/execute.rs b/src/executor/execute.rs
index e951cbfa..5ae451d1 100644
--- a/src/executor/execute.rs
+++ b/src/executor/execute.rs
@@ -6,11 +6,11 @@ use thiserror::Error as ThisError;
 
 #[cfg(feature = "alter-table")]
 use sqlparser::ast::AlterTableOperation;
-use sqlparser::ast::{ColumnDef, Ident, ObjectType, SetExpr, Statement, Values};
+use sqlparser::ast::{ObjectType, SetExpr, Statement, Values};
 
 use crate::data::{get_name, Row, Schema};
 use crate::parse_sql::Query;
-use crate::result::{Error, MutResult, Result};
+use crate::result::{MutResult, Result};
 use crate::store::{AlterTable, Store, StoreMut};
 
 use super::create_table::validate_table;
@@ -59,6 +59,18 @@ pub async fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable>
     storage: U,
     query: &Query,
 ) -> MutResult<U, Payload> {
+    macro_rules! try_block {
+        ($storage: expr, $block: block) => {{
+            match (|| async { $block })().await {
+                Err(e) => {
+                    return Err(($storage, e));
+                }
+                Ok(v) => v,
+            }
+        }};
+    }
+
+    #[cfg(feature = "alter-table")]
     macro_rules! try_into {
         ($storage: expr, $expr: expr) => {
             match $expr {
@@ -71,88 +83,64 @@ pub async fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable>
     }
 
     let Query(query) = query;
-    let prepared = prepare(&storage, query).await;
-    let prepared = try_into!(storage, prepared);
 
-    match prepared {
-        Prepared::Create {
-            schema,
+    match query {
+        //- Modification
+        //-- Tables
+        Statement::CreateTable {
+            name,
+            columns,
             if_not_exists,
+            ..
         } => {
-            let validated = validate_table(&storage, &schema, if_not_exists).await;
-            try_into!(storage, validated);
+            let schema = try_block!(storage, {
+                let schema = Schema {
+                    table_name: get_name(name)?.to_string(),
+                    column_defs: columns.clone(),
+                };
+
+                validate_table(&storage, &schema, *if_not_exists).await?;
+                Ok(schema)
+            });
 
             storage
                 .insert_schema(&schema)
                 .await
                 .map(|(storage, _)| (storage, Payload::Create))
         }
-        Prepared::Insert(table_name, column_defs, rows) => {
-            let column_defs = Rc::from(column_defs);
-            let validated = validate_rows(
-                &storage,
-                &table_name,
-                ColumnValidation::All(Rc::clone(&column_defs)),
-                rows.iter(),
-            )
-            .await;
-            try_into!(storage, validated);
-
-            let num_rows = rows.len();
-
-            storage
-                .insert_data(table_name, rows)
-                .await
-                .map(|(storage, _)| (storage, Payload::Insert(num_rows)))
-        }
-        Prepared::Delete(keys) => {
-            let num_rows = keys.len();
-
-            storage
-                .delete_data(keys)
-                .await
-                .map(|(storage, _)| (storage, Payload::Delete(num_rows)))
-        }
-        Prepared::Update(table_name, all_column_defs, columns_to_update, rows) => {
-            let all_column_defs = Rc::from(all_column_defs);
-            let validated = validate_rows(
-                &storage,
-                &table_name,
-                ColumnValidation::SpecifiedColumns(Rc::clone(&all_column_defs), columns_to_update),
-                rows.iter().map(|r| &r.1),
-            )
-            .await;
-            try_into!(storage, validated);
-
-            let num_rows = rows.len();
+        Statement::Drop {
+            object_type,
+            names,
+            if_exists,
+            ..
+        } => {
+            if object_type != &ObjectType::Table {
+                return Err((storage, ExecuteError::DropTypeNotSupported.into()));
+            }
 
-            storage
-                .update_data(rows)
+            stream::iter(names.iter().map(Ok))
+                .try_fold(storage, |storage, table_name| async move {
+                    let schema = try_block!(storage, {
+                        let table_name = get_name(table_name)?;
+                        let schema = storage.fetch_schema(table_name).await?;
+
+                        if !if_exists {
+                            schema.ok_or(ExecuteError::TableNotExists)?;
+                        }
+                        Ok(table_name)
+                    });
+                    storage
+                        .delete_schema(schema)
+                        .await
+                        .map(|(storage, _)| storage)
+                })
                 .await
-                .map(|(storage, _)| (storage, Payload::Update(num_rows)))
+                .map(|storage| (storage, Payload::DropTable))
         }
-        Prepared::DropTable {
-            schema_names,
-            if_exists,
-        } => stream::iter(schema_names.into_iter().map(Ok::<&str, (U, Error)>))
-            .try_fold(storage, |storage, table_name| async move {
-                let schema = try_into!(storage, storage.fetch_schema(table_name).await);
-
-                if !if_exists {
-                    try_into!(storage, schema.ok_or(ExecuteError::TableNotExists));
-                }
-
-                storage
-                    .delete_schema(table_name)
-                    .await
-                    .map(|(storage, _)| storage)
-            })
-            .await
-            .map(|storage| (storage, Payload::DropTable)),
-        Prepared::Select { labels, rows } => Ok((storage, Payload::Select { labels, rows })),
-
         #[cfg(feature = "alter-table")]
-        Prepared::AlterTable(table_name, operation) => {
+        Statement::AlterTable { name, operation } => {
+            let table_name = try_into!(storage, get_name(name));
+
             let result = match operation {
                 AlterTableOperation::RenameTable {
                     table_name: new_table_name,
@@ -189,177 +177,151 @@ pub async fn execute<T: 'static + Debug, U: Store<T> + StoreMut<T> + AlterTable>
 
             result.map(|(storage, _)| (storage, Payload::AlterTable))
         }
-    }
-}
 
-enum Prepared<'a, T> {
-    Create {
-        schema: Schema,
-        if_not_exists: bool,
-    },
-    Insert(&'a str, Vec<ColumnDef>, Vec<Row>),
-    Delete(Vec<T>),
-    Update(&'a str, Vec<ColumnDef>, Vec<Ident>, Vec<(T, Row)>),
-    Select {
-        labels: Vec<String>,
-        rows: Vec<Row>,
-    },
-    DropTable {
-        schema_names: Vec<&'a str>,
-        if_exists: bool,
-    },
-
-    #[cfg(feature = "alter-table")]
-    AlterTable(&'a str, &'a AlterTableOperation),
-}
-
-// looks like... false positive
-#[allow(clippy::needless_lifetimes)]
-async fn prepare<'a, T: 'static + Debug>(
-    storage: &impl Store<T>,
-    sql_query: &'a Statement,
-) -> Result<Prepared<'a, T>> {
-    match sql_query {
-        Statement::CreateTable {
-            name,
-            columns,
-            if_not_exists,
-            ..
-        } => {
-            let schema = Schema {
-                table_name: get_name(name)?.clone(),
-                column_defs: columns.clone(),
-            };
-
-            Ok(Prepared::Create {
-                schema,
-                if_not_exists: *if_not_exists,
-            })
-        }
-        Statement::Query(query) => {
-            let (labels, rows) = select_with_labels(storage, &query, None, true).await?;
-            let rows = rows.try_collect::<Vec<_>>().await?;
-
-            Ok(Prepared::Select { labels, rows })
-        }
+        //-- Rows
         Statement::Insert {
             table_name,
             columns,
             source,
             ..
         } => {
-            let table_name = get_name(table_name)?;
-            let Schema { column_defs, .. } = storage
-                .fetch_schema(table_name)
-                .await?
-                .ok_or(ExecuteError::TableNotExists)?;
-
-            let rows = match &source.body {
-                SetExpr::Values(Values(values_list)) => values_list
-                    .iter()
-                    .map(|values| Row::new(&column_defs, columns, values))
-                    .collect::<Result<_>>()?,
-                SetExpr::Select(select_query) => {
-                    select(
-                        storage,
-                        &sqlparser::ast::Query {
+            let (rows, table_name) = try_block!(storage, {
+                let table_name = get_name(table_name)?;
+                let Schema { column_defs, .. } = storage
+                    .fetch_schema(table_name)
+                    .await?
+                    .ok_or(ExecuteError::TableNotExists)?;
+
+                let rows = match &source.body {
+                    SetExpr::Values(Values(values_list)) => values_list
+                        .iter()
+                        .map(|values| Row::new(&column_defs, columns, values))
+                        .collect::<Result<Vec<Row>>>()?,
+                    SetExpr::Select(select_query) => {
+                        let query = || sqlparser::ast::Query {
                             with: None,
                             body: SetExpr::Select(select_query.clone()),
                             order_by: vec![],
                             limit: None,
                             offset: None,
                             fetch: None,
-                        },
-                        None,
-                    )
-                    .await?
-                    .try_collect::<Vec<_>>()
-                    .await?
-                }
-                set_expr => {
-                    return Err(ExecuteError::UnreachableUnsupportedInsertValueType(
-                        set_expr.to_string(),
-                    )
-                    .into());
-                }
-            };
+                        };
+
+                        select(&storage, &query(), None)
+                            .await?
+                            .try_collect::<Vec<_>>()
+                            .await?
+                    }
+                    set_expr => {
+                        return Err(ExecuteError::UnreachableUnsupportedInsertValueType(
+                            set_expr.to_string(),
+                        )
+                        .into());
+                    }
+                };
+
+                let column_defs = Rc::from(column_defs);
+                validate_rows(
+                    &storage,
+                    &table_name,
+                    ColumnValidation::All(Rc::clone(&column_defs)),
+                    rows.iter(),
+                )
+                .await?;
 
-            Ok(Prepared::Insert(table_name, column_defs, rows))
+                Ok((rows, table_name))
+            });
+
+            let num_rows = rows.len();
+
+            storage
+                .insert_data(table_name, rows)
+                .await
+                .map(|(storage, _)| (storage, Payload::Insert(num_rows)))
         }
         Statement::Update {
             table_name,
             selection,
             assignments,
         } => {
-            let table_name = get_name(table_name)?;
-            let Schema { column_defs, .. } = storage
-                .fetch_schema(table_name)
-                .await?
-                .ok_or(ExecuteError::TableNotExists)?;
-            let update = Update::new(storage, table_name, assignments, &column_defs)?;
-            let filter = Filter::new(storage, selection.as_ref(), None, None);
-
-            let all_columns = Rc::from(update.all_columns());
-            let columns_to_update = update.columns_to_update();
-            fetch(storage, table_name, Rc::clone(&all_columns), filter)
-                .await?
-                .and_then(|item| {
-                    let update = &update;
-                    let (_, key, row) = item;
-
-                    async move {
-                        let row = update.apply(row).await?;
-
-                        Ok((key, row))
-                    }
-                })
-                .try_collect::<Vec<_>>()
+            let rows = try_block!(storage, {
+                let table_name = get_name(table_name)?;
+                let Schema { column_defs, .. } = storage
+                    .fetch_schema(table_name)
+                    .await?
+                    .ok_or(ExecuteError::TableNotExists)?;
+                let update = Update::new(&storage, table_name, assignments, &column_defs)?;
+                let filter = Filter::new(&storage, selection.as_ref(), None, None);
+
+                let all_columns = Rc::from(update.all_columns());
+                let columns_to_update = update.columns_to_update();
+                let rows = fetch(&storage, table_name, Rc::clone(&all_columns), filter)
+                    .await?
+                    .and_then(|item| {
+                        let update = &update;
+                        let (_, key, row) = item;
+                        async move {
+                            let row = update.apply(row).await?;
+                            Ok((key, row))
+                        }
+                    })
+                    .try_collect::<Vec<_>>()
+                    .await?;
+
+                let all_column_defs = Rc::from(column_defs);
+                validate_rows(
+                    &storage,
+                    &table_name,
+                    ColumnValidation::SpecifiedColumns(
+                        Rc::clone(&all_column_defs),
+                        columns_to_update,
+                    ),
+                    rows.iter().map(|r| &r.1),
+                )
+                .await?;
+                Ok(rows)
+            });
+            let num_rows = rows.len();
+            storage
+                .update_data(rows)
                 .await
-                .map(|rows| Prepared::Update(table_name, column_defs, columns_to_update, rows))
+                .map(|(storage, _)| (storage, Payload::Update(num_rows)))
         }
         Statement::Delete {
             table_name,
             selection,
         } => {
-            let table_name = get_name(table_name)?;
-            let columns = Rc::from(fetch_columns(storage, table_name).await?);
-            let filter = Filter::new(storage, selection.as_ref(), None, None);
-
-            fetch(storage, table_name, columns, filter)
-                .await?
-                .map_ok(|(_, key, _)| key)
-                .try_collect::<Vec<_>>()
-                .await
-                .map(Prepared::Delete)
-        }
-        Statement::Drop {
-            object_type,
-            names,
-            if_exists,
-            ..
-        } => {
-            if object_type != &ObjectType::Table {
-                return Err(ExecuteError::DropTypeNotSupported.into());
-            }
+            let keys = try_block!(storage, {
+                let table_name = get_name(&table_name)?;
+                let columns = Rc::from(fetch_columns(&storage, table_name).await?);
+                let filter = Filter::new(&storage, selection.as_ref(), None, None);
+
+                fetch(&storage, table_name, columns, filter)
+                    .await?
+                    .map_ok(|(_, key, _)| key)
+                    .try_collect::<Vec<_>>()
+                    .await
+            });
 
-            let names = names
-                .iter()
-                .map(|name| get_name(name).map(|n| n.as_str()))
-                .collect::<Result<_>>()?;
+            let num_keys = keys.len();
 
-            Ok(Prepared::DropTable {
-                schema_names: names,
-                if_exists: *if_exists,
-            })
+            storage
+                .delete_data(keys)
+                .await
+                .map(|(storage, _)| (storage, Payload::Delete(num_keys)))
         }
 
-        #[cfg(feature = "alter-table")]
-        Statement::AlterTable { name, operation } => {
-            let table_name = get_name(name)?;
+        //- Selection
+        Statement::Query(query) => {
+            let (labels, rows) = try_block!(storage, {
+                let (labels, rows) = select_with_labels(&storage, &query, None, true).await?;
+                let rows = rows.try_collect::<Vec<_>>().await?;
 
-            Ok(Prepared::AlterTable(table_name, operation))
-        }
+                Ok((labels, rows))
+            });
 
-        _ => Err(ExecuteError::QueryNotSupported.into()),
+            Ok((storage, Payload::Select { labels, rows }))
+        }
+        _ => Err((storage, ExecuteError::QueryNotSupported.into())),
     }
 }


Test patch:
diff --git a/src/tests/arithmetic.rs b/src/tests/arithmetic.rs
index 924fdb93..e8f097ea 100644
--- a/src/tests/arithmetic.rs
+++ b/src/tests/arithmetic.rs
@@ -80,6 +80,24 @@ test_case!(arithmetic, async move {
             UpdateError::ColumnNotFound("aaa".to_owned()).into(),
             "UPDATE Arith SET aaa = 1",
         ),
+        (
+            EvaluateError::UnsupportedLiteralBinaryArithmetic("true".to_owned(), "1".to_owned())
+                .into(),
+            "SELECT * FROM Arith WHERE TRUE + 1 = 1",
+        ),
+        (
+            EvaluateError::UnsupportedEvaluatedBinaryArithmetic(
+                r#"StringRef("asdf")"#.to_owned(),
+                r#"LiteralRef(Number("1", false))"#.to_owned(),
+            )
+            .into(),
+            r#"SELECT * FROM Arith WHERE "asdf" + 1 > 1"#,
+        ),
+        (
+            EvaluateError::UnsupportedEvaluatedUnaryArithmetic(r#"StringRef("asdf")"#.to_owned())
+                .into(),
+            r#"SELECT * FROM Arith WHERE -"asdf" = TRUE;"#,
+        ),
     ];
 
     for (error, sql) in test_cases.into_iter() {
diff --git a/src/tests/nullable.rs b/src/tests/nullable.rs
index a2cbf26c..4e2dbd90 100644
--- a/src/tests/nullable.rs
+++ b/src/tests/nullable.rs
@@ -106,6 +106,24 @@ CREATE TABLE Test (
             "SELECT id, num FROM Test WHERE NULL IS NOT NULL",
             select!(id | num),
         ),
+        (
+            "SELECT id, num FROM Test WHERE (NULL + id) IS NULL;",
+            select_with_null!(
+                id   | num;
+                Null   I64(2);
+                I64(1)   I64(9);
+                I64(3)   I64(4)
+            ),
+        ),
+        (
+            "SELECT id, num FROM Test WHERE (NULL + NULL) IS NULL;",
+            select_with_null!(
+                id   | num;
+                Null   I64(2);
+                I64(1)   I64(9);
+                I64(3)   I64(4)
+            ),
+        ),
         (
             "SELECT id, num FROM Test WHERE \"NULL\" IS NULL",
             select!(id | num),
@@ -119,6 +137,15 @@ CREATE TABLE Test (
                 I64(3)   I64(4)
             ),
         ),
+        (
+            "SELECT id, num FROM Test WHERE (NULL + id) IS NULL;",
+            select_with_null!(
+                id   | num;
+                Null   I64(2);
+                I64(1)   I64(9);
+                I64(3)   I64(4)
+            ),
+        ),
         (
             "SELECT id, num FROM Test WHERE id + 1 IS NULL;",
             select_with_null!(