Repository: njord-rs/njord (language: rust)

Issue:
MySql Subquery Support
Reminder for myself to implement subquery support for mysql


Discussion / hints:

Fix patch:
diff --git a/njord/src/column.rs b/njord/src/column.rs
index 2873e609..0147f0ff 100644
--- a/njord/src/column.rs
+++ b/njord/src/column.rs
@@ -1,53 +1,56 @@
-use crate::{sqlite::select::SelectQueryBuilder, table::Table};
+use crate::query::QueryBuilder;
 
 /// Define the enum to represent a column as either a String or SelectQueryBuilder
 #[derive(Clone)]
-pub enum Column<'a, T: Table + Default> {
+pub enum Column<'a> {
     Text(String),
-    SubQuery(SelectQueryBuilder<'a, T>),
+    // Subquery with alias
+    SubQuery(Box<dyn QueryBuilder<'a> + 'a>, String),
 }
 
 // Implement the build method to convert the enum to a string
-impl<'a, T: Table + Default> Column<'a, T> {
+impl<'a> Column<'a> {
     /// Helper function to convert the columns to a string
     pub fn build(&self) -> String {
         match self {
             Column::Text(text) => text.clone(),
-            Column::SubQuery(sub_query) => "(".to_string() + &sub_query.build_query() + ")",
+            Column::SubQuery(sub_query, alias) => {
+                "(".to_string() + &sub_query.to_sql() + ") AS " + alias
+            }
         }
     }
 }
 
 // Implementation of fmt::Display for Column
-impl<'a, T: Table + Default> std::fmt::Display for Column<'a, T> {
+impl<'a> std::fmt::Display for Column<'a> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "{}", self.build())
     }
 }
 
 // Implementation of PartialEq for Column
-impl<'a, T: Table + Default> PartialEq for Column<'a, T> {
+impl<'a> PartialEq for Column<'a> {
     fn eq(&self, other: &Self) -> bool {
         self.build() == other.build()
     }
 }
 
 // Implementation of PartialEq<String> for Column
-impl<'a, T: Table + Default> PartialEq<String> for Column<'a, T> {
+impl<'a> PartialEq<String> for Column<'a> {
     fn eq(&self, other: &String) -> bool {
         match self {
             Column::Text(text) => text == other,
-            Column::SubQuery(sub_query) => sub_query.build_query() == *other,
+            Column::SubQuery(sub_query, _) => sub_query.to_sql() == *other,
         }
     }
 }
 
 // Implementation of PartialEq<&str> for Column
-impl<'a, T: Table + Default> PartialEq<&str> for Column<'a, T> {
+impl<'a> PartialEq<&str> for Column<'a> {
     fn eq(&self, other: &&str) -> bool {
         match self {
             Column::Text(text) => text == other,
-            Column::SubQuery(sub_query) => sub_query.build_query() == *other,
+            Column::SubQuery(sub_query, _) => sub_query.to_sql() == *other,
         }
     }
 }
diff --git a/njord/src/keys.rs b/njord/src/keys.rs
index aa5a0c3c..e0543386 100644
--- a/njord/src/keys.rs
+++ b/njord/src/keys.rs
@@ -43,7 +43,7 @@ use serde::{Deserialize, Deserializer};
 /// # Type Parameters
 ///
 /// * `T` - The type of the primary key value, which can be any type.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct PrimaryKey<T>(T);
 
 impl<T> PrimaryKey<T> {
@@ -216,4 +216,4 @@ impl<T: PartialEq> PartialEq for AutoIncrementPrimaryKey<T> {
     fn eq(&self, other: &Self) -> bool {
         self.0 == other.0
     }
-}
\ No newline at end of file
+}
diff --git a/njord/src/mysql/select.rs b/njord/src/mysql/select.rs
index 98e167e8..dcd85f44 100644
--- a/njord/src/mysql/select.rs
+++ b/njord/src/mysql/select.rs
@@ -57,7 +57,7 @@ use crate::util::{Join, JoinType};
 /// # Returns
 ///
 /// A `SelectQueryBuilder` instance.
-pub fn select<T: Table + Default>(columns: Vec<Column<T>>) -> SelectQueryBuilder<T> {
+pub fn select<T: Table + Default>(columns: Vec<Column>) -> SelectQueryBuilder<T> {
     SelectQueryBuilder::new(columns)
 }
 
@@ -65,7 +65,7 @@ pub fn select<T: Table + Default>(columns: Vec<Column<T>>) -> SelectQueryBuilder
 #[derive(Clone)]
 pub struct SelectQueryBuilder<'a, T: Table + Default> {
     table: Option<T>,
-    columns: Vec<Column<'a, T>>,
+    columns: Vec<Column<'a>>,
     where_condition: Option<Condition<'a>>,
     distinct: bool,
     group_by: Option<Vec<String>>,
@@ -85,7 +85,7 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> {
     ///
     /// * `conn` - A `PooledConn` to the MySql database.
     /// * `columns` - A vector of strings representing the columns to be selected.
-    pub fn new(columns: Vec<Column<'a, T>>) -> Self {
+    pub fn new(columns: Vec<Column<'a>>) -> Self {
         SelectQueryBuilder {
             table: None,
             columns,
@@ -107,7 +107,7 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> {
     /// # Arguments
     ///
     /// * `columns` - A vector of strings representing the columns to be selected.
-    pub fn select(mut self, columns: Vec<Column<'a, T>>) -> Self {
+    pub fn select(mut self, columns: Vec<Column<'a>>) -> Self {
         self.columns = columns;
         self
     }
diff --git a/njord/src/sqlite/select.rs b/njord/src/sqlite/select.rs
index 4afc5992..4d8aacc2 100644
--- a/njord/src/sqlite/select.rs
+++ b/njord/src/sqlite/select.rs
@@ -57,7 +57,7 @@ use crate::util::{Join, JoinType};
 /// A `SelectQueryBuilder` instance.
 pub fn select<'a, T: Table + Default>(
     conn: &'a Connection,
-    columns: Vec<Column<'a, T>>,
+    columns: Vec<Column<'a>>,
 ) -> SelectQueryBuilder<'a, T> {
     SelectQueryBuilder::new(conn, columns)
 }
@@ -67,7 +67,7 @@ pub fn select<'a, T: Table + Default>(
 pub struct SelectQueryBuilder<'a, T: Table + Default> {
     conn: &'a Connection,
     table: Option<T>,
-    columns: Vec<Column<'a, T>>,
+    columns: Vec<Column<'a>>,
     where_condition: Option<Condition<'a>>,
     distinct: bool,
     group_by: Option<Vec<String>>,
@@ -87,7 +87,7 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> {
     ///
     /// * `conn` - A `rusqlite::Connection` to the SQLite database.
     /// * `columns` - A vector of strings representing the columns to be selected.
-    pub fn new(conn: &'a Connection, columns: Vec<Column<'a, T>>) -> Self {
+    pub fn new(conn: &'a Connection, columns: Vec<Column<'a>>) -> Self {
         SelectQueryBuilder {
             conn,
             table: None,
@@ -110,7 +110,7 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> {
     /// # Arguments
     ///
     /// * `columns` - A vector of strings representing the columns to be selected.
-    pub fn select(mut self, columns: Vec<Column<'a, T>>) -> Self {
+    pub fn select(mut self, columns: Vec<Column<'a>>) -> Self {
         self.columns = columns;
         self
     }


Test patch:
diff --git a/njord/tests/mysql/mod.rs b/njord/tests/mysql/mod.rs
index a8a49f93..e1e2f5b4 100644
--- a/njord/tests/mysql/mod.rs
+++ b/njord/tests/mysql/mod.rs
@@ -5,7 +5,7 @@ mod select_joins_test;
 mod select_test;
 mod update_test;
 
-use njord::keys::{AutoIncrementPrimaryKey, PrimaryKey};
+use njord::keys::AutoIncrementPrimaryKey;
 use njord::table::Table;
 use njord_derive::Table;
 
@@ -18,7 +18,7 @@ pub struct User {
     pub address: String,
 }
 
-#[derive(Table)]
+#[derive(Table, Clone)]
 #[table_name = "users"]
 pub struct UserWithSubQuery {
     pub id: AutoIncrementPrimaryKey<usize>,
@@ -28,22 +28,23 @@ pub struct UserWithSubQuery {
     pub additional_address: String,
 }
 
-#[derive(Table)]
+#[derive(Table, Clone)]
 #[table_name = "categories"]
 pub struct Category {
-    pub id: PrimaryKey<usize>,
+    pub id: AutoIncrementPrimaryKey<usize>,
     pub name: String,
 }
 
-#[derive(Table)]
+#[derive(Table, Clone)]
 #[table_name = "products"]
 pub struct Product {
-    pub id: PrimaryKey<usize>,
+    pub id: AutoIncrementPrimaryKey<usize>,
     pub name: String,
     pub description: String,
     pub price: f64,
     pub stock_quantity: usize,
-    pub category: Category, // one-to-one relationship
+    // pub category: Category, // one-to-one relationship
+    pub category_id: usize,
     pub discount: f64,
 }
 
@@ -54,3 +55,14 @@ pub struct UsersWithJoin {
     price: f64,
     name: String,
 }
+
+#[derive(Table)]
+#[table_name = "categories"]
+pub struct CategoryWithJoin {
+    name: String,
+    description: String,
+    price: f64,
+    stock_quantity: usize,
+    discount: f64,
+    category_name: String,
+}
diff --git a/njord/tests/mysql/select_joins_test.rs b/njord/tests/mysql/select_joins_test.rs
index d2b11f9e..ec2945a6 100644
--- a/njord/tests/mysql/select_joins_test.rs
+++ b/njord/tests/mysql/select_joins_test.rs
@@ -1,85 +1,174 @@
 use njord::condition::Condition;
+use njord::keys::AutoIncrementPrimaryKey;
 use njord::mysql;
+use njord::table::Table;
 use njord::util::JoinType;
 use njord::{column::Column, condition::Value};
 use std::sync::Arc;
 
-use crate::{Product, UsersWithJoin};
-
-// FIXME
-// #[test]
-// fn select_inner_join() {
-//     let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
-//     let mut conn = mysql::open(url);
-
-//     // Assume we have pre-inserted some data into the users and products tables
-//     let columns = vec![
-//         Column::Text("users.username".to_string()),
-//         Column::Text("products.name".to_string()),
-//         Column::Text("products.price".to_string()),
-//     ];
-
-//     // Assuming a hypothetical join condition: users.id = products.user_id
-//     let join_condition = Condition::Eq(
-//         "users.id".to_string(),
-//         Value::Literal("products.user_id".to_string()),
-//     );
-//     match conn {
-//         Ok(ref mut c) => {
-//             let result = mysql::select(c, columns)
-//                 .from(UsersWithJoin::default())
-//                 .join(
-//                     JoinType::Inner,
-//                     Arc::new(Product::default()),
-//                     join_condition,
-//                 )
-//                 .build();
-//             match result {
-//                 Ok(r) => {
-//                     // Check the number of results and assert against expected values
-//                     assert!(!r.is_empty(), "Expected results, but got none.");
-//                     // Further assertions on expected data can be made here based on inserted data
-//                 }
-//                 Err(e) => panic!("Failed to SELECT with JOIN: {:?}", e),
-//             };
-//         }
-//         Err(e) => panic!("Failed to SELECT: {:?}", e),
-//     }
-// }
-
-// #[test]
-// fn select_left_join() {
-//     let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
-//     let mut conn = mysql::open(url);
-
-//     // Assume we have pre-inserted some data into the users and products tables
-//     let columns = vec![
-//         Column::Text("users.username".to_string()),
-//         Column::Text("products.name".to_string()),
-//         Column::Text("products.price".to_string()),
-//     ];
-
-//     // Assuming a hypothetical join condition: users.id = products.user_id
-//     let join_condition = Condition::Eq(
-//         "users.id".to_string(),
-//         Value::Literal("products.user_id".to_string()),
-//     );
-//     match conn {
-//         Ok(ref mut c) => {
-//             let result = mysql::select(c, columns)
-//                 .from(UsersWithJoin::default())
-//                 .join(JoinType::Left, Arc::new(Product::default()), join_condition)
-//                 .build();
-//             match result {
-//                 Ok(r) => {
-//                     // Check the number of results and assert against expected values
-//                     assert!(!r.is_empty(), "Expected results, but got none.");
-//                     assert_eq!(r.len(), 2, "Expected 2 results from the LEFT JOIN query.");
-//                     // Further assertions on expected data can be made here based on inserted data
-//                 }
-//                 Err(e) => panic!("Failed to SELECT with JOIN: {:?}", e),
-//             };
-//         }
-//         Err(e) => panic!("Failed to SELECT: {:?}", e),
-//     }
-// }
+use crate::{Category, CategoryWithJoin, Product};
+
+fn insert_mock_data<T: Table + Clone + Default>(table_rows: Vec<T>) {
+    let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
+    let mut conn = mysql::open(url);
+
+    match conn {
+        Ok(ref mut c) => {
+            let result = mysql::insert(c, table_rows);
+            assert!(result.is_ok());
+        }
+        Err(e) => {
+            panic!("Failed to INSERT: {:?}", e);
+        }
+    }
+}
+
+fn delete_mock_data<T: Table + Clone + Default>(names: Vec<String>, column: String) {
+    let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
+    let mut conn = mysql::open(url);
+
+    match conn {
+        Ok(ref mut c) => {
+            // Transform Vec<String> into Vec<Value>
+            let value_list: Vec<Value> = names
+                .into_iter()
+                .map(Value::Literal) // Wrap each username as a Value::Literal
+                .collect();
+
+            let result = mysql::delete(c)
+                .from(T::default())
+                .where_clause(Condition::In(column, value_list))
+                .build();
+            assert!(result.is_ok());
+        }
+        Err(e) => {
+            panic!("Failed to DELETE: {:?}", e);
+        }
+    }
+}
+
+#[test]
+fn select_inner_join() {
+    insert_mock_data(vec![Category {
+        id: AutoIncrementPrimaryKey::new(Some(1)),
+        name: "select_inner_join_test".to_string(),
+    }]);
+
+    insert_mock_data(vec![Product {
+        id: AutoIncrementPrimaryKey::new(Some(1)),
+        name: "select_inner_join_test".to_string(),
+        description: "select_inner_join_test".to_string(),
+        price: 10.0,
+        stock_quantity: 10,
+        discount: 0.0,
+        category_id: 1,
+    }]);
+
+    let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
+    let mut conn = mysql::open(url);
+
+    // Assume we have pre-inserted some data into the users and products tables
+    let columns = vec![
+        Column::Text("categories.name".to_string()),
+        Column::Text("products.name".to_string()),
+        Column::Text("products.price".to_string()),
+    ];
+
+    // Assuming a hypothetical join condition: users.id = products.user_id
+    let join_condition = Condition::Eq(
+        "categories.id".to_string(),
+        Value::Literal("products.category_id".to_string()),
+    );
+    match conn {
+        Ok(ref mut c) => {
+            let result = mysql::select(columns)
+                .from(CategoryWithJoin::default())
+                .join(
+                    JoinType::Inner,
+                    Arc::new(Product::default()),
+                    join_condition,
+                )
+                .build(c);
+            match result {
+                Ok(r) => {
+                    // Check the number of results and assert against expected values
+                    assert!(!r.is_empty(), "Expected results, but got none.");
+                    // Further assertions on expected data can be made here based on inserted data
+                }
+                Err(e) => panic!("Failed to SELECT with JOIN: {:?}", e),
+            };
+        }
+        Err(e) => panic!("Failed to SELECT: {:?}", e),
+    }
+
+    delete_mock_data::<Category>(
+        vec!["select_inner_join_test".to_string()],
+        "name".to_string(),
+    );
+
+    delete_mock_data::<Product>(
+        vec!["select_inner_join_test".to_string()],
+        "name".to_string(),
+    );
+}
+
+#[test]
+fn select_left_join() {
+    insert_mock_data(vec![Category {
+        id: AutoIncrementPrimaryKey::new(Some(1)),
+        name: "select_inner_join_test".to_string(),
+    }]);
+
+    insert_mock_data(vec![Product {
+        id: AutoIncrementPrimaryKey::new(Some(1)),
+        name: "select_inner_join_test".to_string(),
+        description: "select_inner_join_test".to_string(),
+        price: 10.0,
+        stock_quantity: 10,
+        discount: 0.0,
+        category_id: 1,
+    }]);
+
+    let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
+    let mut conn = mysql::open(url);
+
+    // Assume we have pre-inserted some data into the users and products tables
+    let columns = vec![
+        Column::Text("categories.name".to_string()),
+        Column::Text("products.name".to_string()),
+        Column::Text("products.price".to_string()),
+    ];
+
+    // Assuming a hypothetical join condition: users.id = products.user_id
+    let join_condition = Condition::Eq(
+        "categories.id".to_string(),
+        Value::Literal("products.category_id".to_string()),
+    );
+    match conn {
+        Ok(ref mut c) => {
+            let result = mysql::select(columns)
+                .from(CategoryWithJoin::default())
+                .join(JoinType::Left, Arc::new(Product::default()), join_condition)
+                .build(c);
+            match result {
+                Ok(r) => {
+                    // Check the number of results and assert against expected values
+                    assert!(!r.is_empty(), "Expected results, but got none.");
+                    // Further assertions on expected data can be made here based on inserted data
+                }
+                Err(e) => panic!("Failed to SELECT with JOIN: {:?}", e),
+            };
+        }
+        Err(e) => panic!("Failed to SELECT: {:?}", e),
+    }
+
+    delete_mock_data::<Category>(
+        vec!["select_inner_join_test".to_string()],
+        "name".to_string(),
+    );
+
+    delete_mock_data::<Product>(
+        vec!["select_inner_join_test".to_string()],
+        "name".to_string(),
+    );
+}
diff --git a/njord/tests/mysql/select_test.rs b/njord/tests/mysql/select_test.rs
index ff6bc087..a3991e95 100644
--- a/njord/tests/mysql/select_test.rs
+++ b/njord/tests/mysql/select_test.rs
@@ -1,8 +1,7 @@
 use njord::condition::Condition;
 use njord::keys::AutoIncrementPrimaryKey;
-use njord::mysql::{self, insert};
+use njord::mysql::{self};
 use njord::{column::Column, condition::Value};
-use serde::de::value;
 use std::collections::HashMap;
 
 use crate::{User, UserWithSubQuery};
@@ -628,78 +627,144 @@ fn select_union() {
     ]);
 }
 
-// #[test]
-// fn select_sub_queries() {
-//     let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
-//     let mut conn = mysql::open(url);
-
-//     match conn {
-//         Ok(ref mut c) => {
-//             let sub_query = mysql::select(vec![Column::Text("username".to_string())])
-//                 .from(UserWithSubQuery::default());
-
-//             let columns = vec![
-//                 Column::Text("id".to_string()),
-//                 Column::Text("username".to_string()),
-//                 Column::Text("email".to_string()),
-//                 Column::Text("address".to_string()),
-//                 Column::SubQuery(sub_query),
-//             ];
-
-//             let result = mysql::select(columns)
-//                 .from(UserWithSubQuery::default())
-//                 .build(c);
-
-//             match result {
-//                 Ok(r) => {
-//                     assert_eq!(r.len(), 2);
-//                     assert_eq!(r[0].additional_address, "mjovanc");
-//                 }
-//                 Err(e) => panic!("Failed to SELECT: {:?}", e),
-//             };
-//         }
-//         Err(e) => panic!("Failed to SELECT: {:?}", e),
-//     };
-// }
-
-// #[test]
-// fn select_in() {
-//     let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
-//     let mut conn = mysql::open(url);
-
-//     let columns = vec![
-//         Column::Text("id".to_string()),
-//         Column::Text("username".to_string()),
-//         Column::Text("email".to_string()),
-//         Column::Text("address".to_string()),
-//     ];
-
-//     let condition = Condition::And(
-//         Box::new(Condition::In(
-//             "username".to_string(),
-//             vec![
-//                 Value::Literal("mjovanc".to_string()),
-//                 Value::Literal("otheruser".to_string()),
-//             ],
-//         )),
-//         Box::new(Condition::NotIn(
-//             "username".to_string(),
-//             vec![Value::Literal("chasewillden".to_string())],
-//         )),
-//     );
-
-//     match conn {
-//         Ok(ref mut c) => {
-//             let result = mysql::select(c, columns)
-//                 .from(User::default())
-//                 .where_clause(condition)
-//                 .build();
-
-//             match result {
-//                 Ok(r) => assert_eq!(r.len(), 2),
-//                 Err(e) => panic!("Failed to SELECT: {:?}", e),
-//             };
-//         }
-//         Err(e) => panic!("Failed to SELECT: {:?}", e),
-//     };
-// }
+#[test]
+fn select_sub_queries() {
+    insert_mock_data(vec![
+        User {
+            id: AutoIncrementPrimaryKey::default(),
+            username: "select_sub_queries_test".to_string(),
+            email: "select_sub_queries_test@example.com".to_string(),
+            address: "Some Random Address 1".to_string(),
+        },
+        User {
+            id: AutoIncrementPrimaryKey::default(),
+            username: "select_sub_queries_test2".to_string(),
+            email: "select_sub_queries_test2@example.com".to_string(),
+            address: "Some Random Address 1".to_string(),
+        },
+        User {
+            id: AutoIncrementPrimaryKey::default(),
+            username: "select_sub_queries_test3".to_string(),
+            email: "select_sub_queries_test3@example.com".to_string(),
+            address: "SubQuery".to_string(),
+        },
+    ]);
+
+    let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
+    let mut conn = mysql::open(url);
+
+    match conn {
+        Ok(ref mut c) => {
+            let sub_query = mysql::select(vec![Column::Text("address".to_string())])
+                .from(UserWithSubQuery::default())
+                .where_clause(Condition::Eq(
+                    "username".to_string(),
+                    Value::Literal("select_sub_queries_test3".to_string()),
+                ))
+                .limit(1);
+
+            let columns = vec![
+                Column::Text("id".to_string()),
+                Column::Text("username".to_string()),
+                Column::Text("email".to_string()),
+                Column::Text("address".to_string()),
+                Column::SubQuery(Box::new(sub_query), "additional_address".to_string()),
+            ];
+
+            let result = mysql::select(columns)
+                .from(UserWithSubQuery::default())
+                .where_clause(Condition::In(
+                    "username".to_string(),
+                    vec![
+                        Value::Literal("select_sub_queries_test".to_string()),
+                        Value::Literal("select_sub_queries_test2".to_string()),
+                    ],
+                ))
+                .build(c);
+
+            match result {
+                Ok(r) => {
+                    assert!(r.len() > 0);
+                    assert_eq!(r[0].additional_address, "SubQuery");
+                }
+                Err(e) => panic!("Failed to SELECT: {:?}", e),
+            };
+        }
+        Err(e) => panic!("Failed to SELECT: {:?}", e),
+    };
+
+    delete_mock_data(vec![
+        "select_sub_queries_test".to_string(),
+        "select_sub_queries_test2".to_string(),
+        "select_sub_queries_test3".to_string(),
+    ]);
+}
+
+#[test]
+fn select_in() {
+    insert_mock_data(vec![
+        User {
+            id: AutoIncrementPrimaryKey::default(),
+            username: "select_in_test".to_string(),
+            email: "select_in_test@example.com".to_string(),
+            address: "Some Random Address 1".to_string(),
+        },
+        User {
+            id: AutoIncrementPrimaryKey::default(),
+            username: "select_in_test2".to_string(),
+            email: "select_in_test2@example.com".to_string(),
+            address: "Some Random Address 1".to_string(),
+        },
+        User {
+            id: AutoIncrementPrimaryKey::default(),
+            username: "select_in_test3".to_string(),
+            email: "select_in_test3@example.com".to_string(),
+            address: "Some Random Address 1".to_string(),
+        },
+    ]);
+
+    let url = "mysql://njord_user:njord_password@localhost:3306/njord_db";
+    let mut conn = mysql::open(url);
+
+    let columns = vec![
+        Column::Text("id".to_string()),
+        Column::Text("username".to_string()),
+        Column::Text("email".to_string()),
+        Column::Text("address".to_string()),
+    ];
+
+    let condition = Condition::And(
+        Box::new(Condition::In(
+            "username".to_string(),
+            vec![
+                Value::Literal("select_in_test".to_string()),
+                Value::Literal("select_in_test2".to_string()),
+            ],
+        )),
+        Box::new(Condition::NotIn(
+            "username".to_string(),
+            vec![Value::Literal("select_in_test3".to_string())],
+        )),
+    );
+
+    match conn {
+        Ok(ref mut c) => {
+            let result = mysql::select(columns)
+                .from(User::default())
+                .where_clause(condition)
+                .build(c);
+
+            match result {
+                Ok(r) => assert_eq!(r.len(), 2),
+                Err(e) => panic!("Failed to SELECT: {:?}", e),
+            };
+        }
+        Err(e) => panic!("Failed to SELECT: {:?}", e),
+    };
+
+    delete_mock_data(vec![
+        "select_in_test".to_string(),
+        "select_in_test2".to_string(),
+        "select_in_test3".to_string(),
+    ]);
+}
diff --git a/njord/tests/sqlite/delete_test.rs b/njord/tests/sqlite/delete_test.rs
index 0d6cf30f..a14650c9 100644
--- a/njord/tests/sqlite/delete_test.rs
+++ b/njord/tests/sqlite/delete_test.rs
@@ -50,7 +50,7 @@ fn delete_with_subquery() {
     match conn {
         Ok(ref c) => {
             let sub_query =
-                SelectQueryBuilder::new(c, vec![Column::<User>::Text("username".to_string())])
+                SelectQueryBuilder::<User>::new(c, vec![Column::Text("username".to_string())])
                     .where_clause(Condition::Eq(
                         "id".to_string(),
                         Value::Literal(1.to_string()),
diff --git a/njord/tests/sqlite/insert_test.rs b/njord/tests/sqlite/insert_test.rs
index 94af17a0..130d5340 100644
--- a/njord/tests/sqlite/insert_test.rs
+++ b/njord/tests/sqlite/insert_test.rs
@@ -41,9 +41,9 @@ fn insert_with_sub_query() {
             let subquery = sqlite::select(
                 c,
                 vec![
-                    Column::<User>::Text("username".to_string()),
-                    Column::<User>::Text("email".to_string()),
-                    Column::<User>::Text("address".to_string()),
+                    Column::Text("username".to_string()),
+                    Column::Text("email".to_string()),
+                    Column::Text("address".to_string()),
                 ],
             )
             .from(User::default())
diff --git a/njord/tests/sqlite/mod.rs b/njord/tests/sqlite/mod.rs
index a8a49f93..011b9ce4 100644
--- a/njord/tests/sqlite/mod.rs
+++ b/njord/tests/sqlite/mod.rs
@@ -18,7 +18,7 @@ pub struct User {
     pub address: String,
 }
 
-#[derive(Table)]
+#[derive(Table, Clone)]
 #[table_name = "users"]
 pub struct UserWithSubQuery {
     pub id: AutoIncrementPrimaryKey<usize>,
diff --git a/njord/tests/sqlite/select_test.rs b/njord/tests/sqlite/select_test.rs
index 8d07289c..d358fc9c 100644
--- a/njord/tests/sqlite/select_test.rs
+++ b/njord/tests/sqlite/select_test.rs
@@ -464,7 +464,7 @@ fn select_sub_queries() {
                 Column::Text("username".to_string()),
                 Column::Text("email".to_string()),
                 Column::Text("address".to_string()),
-                Column::SubQuery(sub_query),
+                Column::SubQuery(Box::new(sub_query), "additional_address".to_string()),
             ];
 
             let result = sqlite::select(&c, columns)