From 5ec2d07aa14c2e78a08c3597cd5bb80637b8be79 Mon Sep 17 00:00:00 2001 From: mjovanc Date: Mon, 7 Oct 2024 13:49:53 +0200 Subject: [PATCH 1/5] Adding JOIN logic --- njord/src/sqlite/select.rs | 52 +++++++++++++++++++++++++++++-- njord/src/util.rs | 28 +++++++++++++++++ njord/tests/sqlite/select_test.rs | 6 ++-- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/njord/src/sqlite/select.rs b/njord/src/sqlite/select.rs index f5ac8e4e..6f988f1d 100644 --- a/njord/src/sqlite/select.rs +++ b/njord/src/sqlite/select.rs @@ -36,13 +36,13 @@ use crate::{ }, }; use std::collections::HashMap; - use rusqlite::{Connection, Result}; use log::info; use rusqlite::types::Value; use crate::table::Table; +use crate::util::{Join, JoinType}; /// Constructs a new SELECT query builder. /// @@ -76,6 +76,7 @@ pub struct SelectQueryBuilder<'a, T: Table + Default> { having_condition: Option, except_clauses: Option>>, union_clauses: Option>>, + joins: Option>>, } impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> { @@ -99,6 +100,7 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> { having_condition: None, except_clauses: None, union_clauses: None, + joins: None, } } @@ -234,6 +236,30 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> { self } + /// Adds a JOIN clause to the query, allowing you to combine rows from two or more tables based on a related column. + /// + /// This method modifies the current query builder to include a join clause with the specified join type, + /// target table, and condition for the join. If there are already existing JOIN clauses, the new clause + /// will be added to the list. If no JOIN clauses exist, a new list will be created with the provided + /// join information. + /// + /// # Arguments + /// + /// * `join_type` - The type of join to perform (e.g., INNER, LEFT, RIGHT, FULL). + /// * `table` - The table to join with the current table. + /// * `on_condition` - The condition that specifies how the tables are related (the ON clause). + /// + /// # Returns + /// + /// Returns the modified `SelectQueryBuilder` instance with the new JOIN clause added. + pub fn join(mut self, join_type: JoinType, table: T, on_condition: Condition) -> Self { + match self.joins { + Some(ref mut joins) => joins.push(Join::new(join_type, table, on_condition)), + None => self.joins = Some(vec![Join::new(join_type, table, on_condition)]), + } + self + } + /// Builds the query string, this function should be used internally. pub fn build_query(&self) -> String { let columns_str = self @@ -249,6 +275,20 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> { .map(|t| t.get_name().to_string()) .unwrap_or("".to_string()); + // Generate JOIN clauses, if any + let join_clauses: Vec = match &self.joins { + Some(joins) => joins.iter().map(|join| { + let join_type_str = match join.join_type { + JoinType::Inner => "INNER JOIN", + JoinType::Left => "LEFT JOIN", + JoinType::Right => "RIGHT JOIN", + JoinType::Full => "FULL OUTER JOIN", + }; + format!("{} {} ON {}", join_type_str, join.table.get_name(), generate_where_condition_str(Some(join.on_condition.clone()))) + }).collect(), + None => Vec::new(), + }; + let distinct_str = if self.distinct { "DISTINCT " } else { "" }; let where_condition_str = generate_where_condition_str(self.where_condition.clone()); let group_by_str = generate_group_by_str(&self.group_by); @@ -258,11 +298,19 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> { let having_str = generate_having_str(self.group_by.is_some(), self.having_condition.as_ref()); + // Create the JOIN clause or an empty string + let join_clause = if !join_clauses.is_empty() { + join_clauses.join(" ") + } else { + String::new() + }; + let mut query = format!( - "SELECT {}{} FROM {} {} {} {} {} {}", + "SELECT {}{} FROM {} {} {} {} {} {} {}", distinct_str, columns_str, table_name, + join_clause, where_condition_str, group_by_str, having_str, diff --git a/njord/src/util.rs b/njord/src/util.rs index bb202ed3..4566c479 100644 --- a/njord/src/util.rs +++ b/njord/src/util.rs @@ -27,6 +27,34 @@ //! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE //! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::condition::Condition; +use crate::table::Table; + +#[derive(Clone)] +pub enum JoinType { + Inner, + Left, + Right, + Full, +} + +#[derive(Clone)] +pub struct Join { + pub join_type: JoinType, + pub table: T, + pub on_condition: Condition, +} + +impl Join { + pub fn new(join_type: JoinType, table: T, on_condition: Condition) -> Self { + Join { + join_type, + table, + on_condition, + } + } +} + /// Converts values for SQL INSERT /// /// # Arguments diff --git a/njord/tests/sqlite/select_test.rs b/njord/tests/sqlite/select_test.rs index f574734c..0d234905 100644 --- a/njord/tests/sqlite/select_test.rs +++ b/njord/tests/sqlite/select_test.rs @@ -1,10 +1,10 @@ +use super::{User, UserWithSubQuery}; use njord::column::Column; use njord::condition::Condition; -use njord::sqlite; use njord::keys::AutoIncrementPrimaryKey; +use njord::sqlite; use std::collections::HashMap; use std::path::Path; -use super::{User, UserWithSubQuery}; #[test] fn select() { @@ -387,4 +387,4 @@ fn select_in() { } Err(e) => panic!("Failed to SELECT: {:?}", e), }; -} \ No newline at end of file +} From 25d638d3be4066bbdea3e693ab4e506b0d63e4f4 Mon Sep 17 00:00:00 2001 From: Christopher Zeuch Date: Mon, 7 Oct 2024 18:01:17 +0200 Subject: [PATCH 2/5] Split tests out into separate files --- njord/tests/sqlite/delete_test.rs | 4 ++-- njord/tests/sqlite/insert_test.rs | 4 ++-- njord/tests/sqlite/open_test.rs | 2 +- njord/tests/sqlite/update_test.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/njord/tests/sqlite/delete_test.rs b/njord/tests/sqlite/delete_test.rs index 513cc65c..90355367 100644 --- a/njord/tests/sqlite/delete_test.rs +++ b/njord/tests/sqlite/delete_test.rs @@ -1,8 +1,8 @@ +use super::User; use njord::condition::Condition; use njord::sqlite; use std::collections::HashMap; use std::path::Path; -use super::User; #[test] fn delete() { @@ -31,4 +31,4 @@ fn delete() { panic!("Failed to DELETE: {:?}", e); } } -} \ No newline at end of file +} diff --git a/njord/tests/sqlite/insert_test.rs b/njord/tests/sqlite/insert_test.rs index 48aa2b3f..1502a8cd 100644 --- a/njord/tests/sqlite/insert_test.rs +++ b/njord/tests/sqlite/insert_test.rs @@ -1,7 +1,7 @@ +use super::User; use njord::keys::AutoIncrementPrimaryKey; use njord::sqlite; use std::path::Path; -use super::User; #[test] fn insert_row() { @@ -25,4 +25,4 @@ fn insert_row() { panic!("Failed to INSERT: {:?}", e); } } -} \ No newline at end of file +} diff --git a/njord/tests/sqlite/open_test.rs b/njord/tests/sqlite/open_test.rs index 3ec057ec..34cb145c 100644 --- a/njord/tests/sqlite/open_test.rs +++ b/njord/tests/sqlite/open_test.rs @@ -8,4 +8,4 @@ fn open_db() { let result = sqlite::open(db_path); assert!(result.is_ok()); -} \ No newline at end of file +} diff --git a/njord/tests/sqlite/update_test.rs b/njord/tests/sqlite/update_test.rs index 42fdf726..6b39c196 100644 --- a/njord/tests/sqlite/update_test.rs +++ b/njord/tests/sqlite/update_test.rs @@ -1,3 +1,4 @@ +use super::User; use njord::column::Column; use njord::condition::Condition; use njord::keys::AutoIncrementPrimaryKey; @@ -5,7 +6,6 @@ use njord::sqlite::select::SelectQueryBuilder; use njord::sqlite::{self}; use std::collections::HashMap; use std::path::Path; -use super::{User}; #[test] fn update() { @@ -83,4 +83,4 @@ fn update_with_sub_queries() { } Err(e) => panic!("Failed to UPDATE: {:?}", e), }; -} \ No newline at end of file +} From 2333d1b66c1f4e4f73d21ca1cc3f6acdfbe4c941 Mon Sep 17 00:00:00 2001 From: mjovanc Date: Mon, 7 Oct 2024 13:49:53 +0200 Subject: [PATCH 3/5] Adding JOIN logic --- njord/tests/sqlite/select_test.rs | 184 +++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 2 deletions(-) diff --git a/njord/tests/sqlite/select_test.rs b/njord/tests/sqlite/select_test.rs index 0d234905..2a537c89 100644 --- a/njord/tests/sqlite/select_test.rs +++ b/njord/tests/sqlite/select_test.rs @@ -1,11 +1,157 @@ -use super::{User, UserWithSubQuery}; use njord::column::Column; use njord::condition::Condition; -use njord::keys::AutoIncrementPrimaryKey; +use njord::keys::{AutoIncrementPrimaryKey, PrimaryKey}; use njord::sqlite; use std::collections::HashMap; use std::path::Path; +#[derive(Table, Clone)] +#[table_name = "users"] +pub struct User { + id: AutoIncrementPrimaryKey, + username: String, + email: String, + address: String, +} + +#[derive(Table)] +#[table_name = "users"] +pub struct UserWithSubQuery { + id: AutoIncrementPrimaryKey, + username: String, + email: String, + address: String, + additional_address: String, +} + +#[derive(Table)] +#[table_name = "categories"] +pub struct Category { + id: PrimaryKey, + name: String, +} + +#[derive(Table)] +#[table_name = "products"] +pub struct Product { + id: PrimaryKey, + name: String, + description: String, + price: f64, + stock_quantity: usize, + category: Category, // one-to-one relationship + discount: f64, +} + +// #[derive(Table)] +// #[table_name = "orders"] +// pub struct Order { +// id: usize, +// user: User, // one-to-one relationship +// products: Vec, // one-to-many relationship - populates from based on junction table (gets from macro attribute "table_name" and combines them for example, orders_products) +// total_cost: f64, +// } + +#[test] +fn open_db() { + let db_relative_path = "./db/open.db"; + let db_path = Path::new(&db_relative_path); + + let result = sqlite::open(db_path); + assert!(result.is_ok()); +} + +#[test] +fn insert_row() { + let db_relative_path = "./db/insert.db"; + let db_path = Path::new(&db_relative_path); + let conn = sqlite::open(db_path); + + let table_row: User = User { + id: AutoIncrementPrimaryKey::default(), + username: "mjovanc".to_string(), + email: "mjovanc@icloud.com".to_string(), + address: "Some Random Address 1".to_string(), + }; + + match conn { + Ok(c) => { + let result = sqlite::insert(c, vec![table_row]); + assert!(result.is_ok()); + } + Err(e) => { + panic!("Failed to INSERT: {:?}", e); + } + } +} + +#[test] +fn update() { + let db_relative_path = "./db/insert.db"; + let db_path = Path::new(&db_relative_path); + let conn = sqlite::open(db_path); + + let columns = vec!["username".to_string()]; + + let condition = Condition::Eq("username".to_string(), "mjovanc".to_string()); + + let table_row: User = User { + id: AutoIncrementPrimaryKey::::new(Some(0)), + username: "mjovanc".to_string(), + email: "mjovanc@icloud.com".to_string(), + address: "Some Random Address 1".to_string(), + }; + + let mut order = HashMap::new(); + order.insert(vec!["id".to_string()], "DESC".to_string()); + + match conn { + Ok(c) => { + let result = sqlite::update(&c, table_row) + .set(columns) + .where_clause(condition) + .order_by(order) + .limit(4) + .offset(0) + .build(); + println!("{:?}", result); + assert!(result.is_ok()); + } + Err(e) => { + panic!("Failed to UPDATE: {:?}", e); + } + } +} + +#[test] +fn delete() { + let db_relative_path = "./db/insert.db"; + let db_path = Path::new(&db_relative_path); + let conn = sqlite::open(db_path); + + let condition = Condition::Eq("address".to_string(), "Some Random Address 1".to_string()); + + let mut order = HashMap::new(); + order.insert(vec!["id".to_string()], "DESC".to_string()); + + match conn { + Ok(c) => { + let result = sqlite::delete(c) + .from(User::default()) + .where_clause(condition) + .order_by(order) + .limit(20) + .offset(0) + .build(); + println!("{:?}", result); + assert!(result.is_ok()); + } + Err(e) => { + panic!("Failed to DELETE: {:?}", e); + } + } +} + #[test] fn select() { let db_relative_path = "./db/select.db"; @@ -388,3 +534,37 @@ fn select_in() { Err(e) => panic!("Failed to SELECT: {:?}", e), }; } + +// #[test] +// fn select_join() { +// let db_relative_path = "./db/select_join.db"; +// let db_path = Path::new(&db_relative_path); +// let conn = sqlite::open(db_path); +// +// // Assume we have pre-inserted some data into the users and products tables +// let columns = vec![ +// "users.username".to_string(), +// "products.name".to_string(), +// "products.price".to_string(), +// ]; +// +// // Assuming a hypothetical join condition: users.id = products.user_id +// let join_condition = Condition::Eq("users.id".to_string(), "products.user_id".to_string()); +// match conn { +// Ok(c) => { +// let result = sqlite::select(&c, columns) +// .from(User::default()) +// .join(JoinType::Inner, Category::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), +// } +// } From 2c3741bd17cf565d2bc74dcc01e0481591502cc3 Mon Sep 17 00:00:00 2001 From: Chase Willden Date: Mon, 7 Oct 2024 15:53:57 -0600 Subject: [PATCH 4/5] Update the Join to be Arc, clean up query, hydrate select_join.db --- njord/db/select_join.db | Bin 0 -> 12288 bytes njord/src/condition.rs | 5 +- njord/src/sqlite/select.rs | 38 +++++++++----- njord/src/util.rs | 10 ++-- njord/tests/sqlite/select_test.rs | 82 ++++++++++++++++++------------ 5 files changed, 85 insertions(+), 50 deletions(-) create mode 100644 njord/db/select_join.db diff --git a/njord/db/select_join.db b/njord/db/select_join.db new file mode 100644 index 0000000000000000000000000000000000000000..aa11e490413a5a82723800bb439e7dfe3aa20a17 GIT binary patch literal 12288 zcmeI&(M!TW90%|_EhVY!AsP|VAp{OIh=^V_J4CQZ+e2@$+!>>7+BP4gC+q*|59mJ$ zeCgllsk`Z5i3L3x-yh!X{N3*6eb~z#UbkCzC|IxWyJpCa$rhn>y~-FNBqK{&mN-^q zLzOsa-1z01A&1`EhP)xQ*(P}|D+B}}009U<00Izz00bZa0SNpLf$?K1s}&0Lxk z95L|w*037}@ikrV@S4GyQ9EmKR*cidTq(z7*p{kgG%pR_;GHO`z7KrcHT@BL5TkOG z^h{SUgWnj;>xax6I?gmNZ|n+o&g;#KT5GOv7zqDXK4LaG^F>ei!t08Fsji@8TY5Bk zrJ{Dk!oK&^16gS{P+vSkAD3xX(=_^g8m&2+E2>i8R~zO2zEm~rr{xOQv>gSsfWova zUj)HiUrtZHXlidx^Y27{5D1Rwwb2tWV=5P$##AOHafEStbCT_pu=FFren&Ix7* z0?DOJ_x}@;pCq$rLjVF0fB*y_009U<00Izz00bbgECM_72w*?Eb~XJA20N-Ae10@u Jx1W-~{|j2%mT~|9 literal 0 HcmV?d00001 diff --git a/njord/src/condition.rs b/njord/src/condition.rs index 0ed8a256..4c21ac7c 100644 --- a/njord/src/condition.rs +++ b/njord/src/condition.rs @@ -74,7 +74,10 @@ impl Condition { pub fn build(&self) -> String { match self { Condition::Eq(column, value) => { - if Condition::is_numeric(value) { + // If contains a dot, assume it's a table.column + if column.contains('.') { + format!("{} = {}", column, value) + } else if Condition::is_numeric(value) { format!("{} = {}", column, value) } else { format!("{} = '{}'", column, value) diff --git a/njord/src/sqlite/select.rs b/njord/src/sqlite/select.rs index 6f988f1d..81d3b216 100644 --- a/njord/src/sqlite/select.rs +++ b/njord/src/sqlite/select.rs @@ -35,8 +35,8 @@ use crate::{ generate_order_by_str, generate_where_condition_str, }, }; -use std::collections::HashMap; use rusqlite::{Connection, Result}; +use std::{collections::HashMap, sync::Arc}; use log::info; use rusqlite::types::Value; @@ -76,7 +76,7 @@ pub struct SelectQueryBuilder<'a, T: Table + Default> { having_condition: Option, except_clauses: Option>>, union_clauses: Option>>, - joins: Option>>, + joins: Option>, } impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> { @@ -252,7 +252,12 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> { /// # Returns /// /// Returns the modified `SelectQueryBuilder` instance with the new JOIN clause added. - pub fn join(mut self, join_type: JoinType, table: T, on_condition: Condition) -> Self { + pub fn join( + mut self, + join_type: JoinType, + table: Arc, + on_condition: Condition, + ) -> Self { match self.joins { Some(ref mut joins) => joins.push(Join::new(join_type, table, on_condition)), None => self.joins = Some(vec![Join::new(join_type, table, on_condition)]), @@ -277,15 +282,24 @@ impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> { // Generate JOIN clauses, if any let join_clauses: Vec = match &self.joins { - Some(joins) => joins.iter().map(|join| { - let join_type_str = match join.join_type { - JoinType::Inner => "INNER JOIN", - JoinType::Left => "LEFT JOIN", - JoinType::Right => "RIGHT JOIN", - JoinType::Full => "FULL OUTER JOIN", - }; - format!("{} {} ON {}", join_type_str, join.table.get_name(), generate_where_condition_str(Some(join.on_condition.clone()))) - }).collect(), + Some(joins) => joins + .iter() + .map(|join| { + let join_type_str = match join.join_type { + JoinType::Inner => "INNER JOIN", + JoinType::Left => "LEFT JOIN", + JoinType::Right => "RIGHT JOIN", + JoinType::Full => "FULL OUTER JOIN", + }; + format!( + "{} {} ON {}", + join_type_str, + join.table.get_name(), + generate_where_condition_str(Some(join.on_condition.clone())) + .replace("WHERE", "") + ) + }) + .collect(), None => Vec::new(), }; diff --git a/njord/src/util.rs b/njord/src/util.rs index 4566c479..cb9bc3b2 100644 --- a/njord/src/util.rs +++ b/njord/src/util.rs @@ -27,6 +27,8 @@ //! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE //! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Arc; + use crate::condition::Condition; use crate::table::Table; @@ -39,14 +41,14 @@ pub enum JoinType { } #[derive(Clone)] -pub struct Join { +pub struct Join { pub join_type: JoinType, - pub table: T, + pub table: Arc, pub on_condition: Condition, } -impl Join { - pub fn new(join_type: JoinType, table: T, on_condition: Condition) -> Self { +impl Join { + pub fn new(join_type: JoinType, table: Arc, on_condition: Condition) -> Self { Join { join_type, table, diff --git a/njord/tests/sqlite/select_test.rs b/njord/tests/sqlite/select_test.rs index 2a537c89..fb0c9020 100644 --- a/njord/tests/sqlite/select_test.rs +++ b/njord/tests/sqlite/select_test.rs @@ -2,8 +2,12 @@ use njord::column::Column; use njord::condition::Condition; use njord::keys::{AutoIncrementPrimaryKey, PrimaryKey}; use njord::sqlite; +use njord::table::Table; +use njord::util::JoinType; +use njord_derive::Table; use std::collections::HashMap; use std::path::Path; +use std::sync::Arc; #[derive(Table, Clone)] #[table_name = "users"] @@ -24,6 +28,14 @@ pub struct UserWithSubQuery { additional_address: String, } +#[derive(Table)] +#[table_name = "users"] +pub struct UsersWithJoin { + username: String, + price: f64, + name: String, +} + #[derive(Table)] #[table_name = "categories"] pub struct Category { @@ -535,36 +547,40 @@ fn select_in() { }; } -// #[test] -// fn select_join() { -// let db_relative_path = "./db/select_join.db"; -// let db_path = Path::new(&db_relative_path); -// let conn = sqlite::open(db_path); -// -// // Assume we have pre-inserted some data into the users and products tables -// let columns = vec![ -// "users.username".to_string(), -// "products.name".to_string(), -// "products.price".to_string(), -// ]; -// -// // Assuming a hypothetical join condition: users.id = products.user_id -// let join_condition = Condition::Eq("users.id".to_string(), "products.user_id".to_string()); -// match conn { -// Ok(c) => { -// let result = sqlite::select(&c, columns) -// .from(User::default()) -// .join(JoinType::Inner, Category::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_join() { + let db_relative_path = "./db/select_join.db"; + let db_path = Path::new(&db_relative_path); + let conn = sqlite::open(db_path); + + // 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(), "products.user_id".to_string()); + match conn { + Ok(c) => { + let result = sqlite::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), + } +} From 1bc91d983515c7f9f26279661cce9de58b4dc0fe Mon Sep 17 00:00:00 2001 From: Chase Willden Date: Mon, 7 Oct 2024 15:59:12 -0600 Subject: [PATCH 5/5] Add left join test --- njord/db/select_join.db | Bin 12288 -> 12288 bytes njord/tests/sqlite/mod.rs | 17 +++- njord/tests/sqlite/select_joins_test.rs | 81 +++++++++++++++++++ njord/tests/sqlite/select_test.rs | 99 +----------------------- 4 files changed, 96 insertions(+), 101 deletions(-) create mode 100644 njord/tests/sqlite/select_joins_test.rs diff --git a/njord/db/select_join.db b/njord/db/select_join.db index aa11e490413a5a82723800bb439e7dfe3aa20a17..63e62dd7bc45124e1f8fd953d07207a5baea80cd 100644 GIT binary patch delta 67 zcmZojXh@hK&B#7c#+i|QW5N=C4krHH4E#6wcW)LHSkEsf!^FxUFDYAEoLZEZn44;p XnwyxJW0aVZQj}U;Y_$2Nyt)7YmxdIi delta 37 tcmZojXh@hK&B!)U#+i|AW5N=CHb(xN4E#4Y3o4xEpBTWh`KG+O007t63yJ^$ diff --git a/njord/tests/sqlite/mod.rs b/njord/tests/sqlite/mod.rs index c4bf4e54..a8a49f93 100644 --- a/njord/tests/sqlite/mod.rs +++ b/njord/tests/sqlite/mod.rs @@ -1,8 +1,9 @@ -mod open_test; -mod insert_test; -mod update_test; mod delete_test; +mod insert_test; +mod open_test; +mod select_joins_test; mod select_test; +mod update_test; use njord::keys::{AutoIncrementPrimaryKey, PrimaryKey}; use njord::table::Table; @@ -44,4 +45,12 @@ pub struct Product { pub stock_quantity: usize, pub category: Category, // one-to-one relationship pub discount: f64, -} \ No newline at end of file +} + +#[derive(Table)] +#[table_name = "users"] +pub struct UsersWithJoin { + username: String, + price: f64, + name: String, +} diff --git a/njord/tests/sqlite/select_joins_test.rs b/njord/tests/sqlite/select_joins_test.rs new file mode 100644 index 00000000..21ff5503 --- /dev/null +++ b/njord/tests/sqlite/select_joins_test.rs @@ -0,0 +1,81 @@ +use njord::column::Column; +use njord::condition::Condition; +use njord::sqlite; +use njord::util::JoinType; +use std::path::Path; +use std::sync::Arc; + +use crate::{Product, UsersWithJoin}; + +#[test] +fn select_inner_join() { + let db_relative_path = "./db/select_join.db"; + let db_path = Path::new(&db_relative_path); + let conn = sqlite::open(db_path); + + // 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(), "products.user_id".to_string()); + match conn { + Ok(c) => { + let result = sqlite::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 db_relative_path = "./db/select_join.db"; + let db_path = Path::new(&db_relative_path); + let conn = sqlite::open(db_path); + + // 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(), "products.user_id".to_string()); + match conn { + Ok(c) => { + let result = sqlite::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), + } +} diff --git a/njord/tests/sqlite/select_test.rs b/njord/tests/sqlite/select_test.rs index fb0c9020..8f750013 100644 --- a/njord/tests/sqlite/select_test.rs +++ b/njord/tests/sqlite/select_test.rs @@ -1,68 +1,11 @@ use njord::column::Column; use njord::condition::Condition; -use njord::keys::{AutoIncrementPrimaryKey, PrimaryKey}; +use njord::keys::AutoIncrementPrimaryKey; use njord::sqlite; -use njord::table::Table; -use njord::util::JoinType; -use njord_derive::Table; use std::collections::HashMap; use std::path::Path; -use std::sync::Arc; - -#[derive(Table, Clone)] -#[table_name = "users"] -pub struct User { - id: AutoIncrementPrimaryKey, - username: String, - email: String, - address: String, -} - -#[derive(Table)] -#[table_name = "users"] -pub struct UserWithSubQuery { - id: AutoIncrementPrimaryKey, - username: String, - email: String, - address: String, - additional_address: String, -} - -#[derive(Table)] -#[table_name = "users"] -pub struct UsersWithJoin { - username: String, - price: f64, - name: String, -} - -#[derive(Table)] -#[table_name = "categories"] -pub struct Category { - id: PrimaryKey, - name: String, -} -#[derive(Table)] -#[table_name = "products"] -pub struct Product { - id: PrimaryKey, - name: String, - description: String, - price: f64, - stock_quantity: usize, - category: Category, // one-to-one relationship - discount: f64, -} - -// #[derive(Table)] -// #[table_name = "orders"] -// pub struct Order { -// id: usize, -// user: User, // one-to-one relationship -// products: Vec, // one-to-many relationship - populates from based on junction table (gets from macro attribute "table_name" and combines them for example, orders_products) -// total_cost: f64, -// } +use crate::{User, UserWithSubQuery}; #[test] fn open_db() { @@ -546,41 +489,3 @@ fn select_in() { Err(e) => panic!("Failed to SELECT: {:?}", e), }; } - -#[test] -fn select_join() { - let db_relative_path = "./db/select_join.db"; - let db_path = Path::new(&db_relative_path); - let conn = sqlite::open(db_path); - - // 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(), "products.user_id".to_string()); - match conn { - Ok(c) => { - let result = sqlite::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), - } -}