diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 08050be0..adb01715 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -65,12 +65,19 @@ jobs: - name: Running Integration Tests for SQLite run: cargo test --test sqlite_tests -# - name: Set up MySQL schema -# env: -# MYSQL_PWD: rootpassword -# run: | -# echo "Injecting schema and data into MySQL..." -# mysql -h 127.0.0.1 -u root njord_db < njord/db/test/mysql.sql + - name: Wait for MySQL to be ready + run: | + until mysqladmin ping -h 127.0.0.1 --silent; do + echo "Waiting for MySQL to be ready..." + sleep 5 + done + + - name: Set up MySQL schema + env: + MYSQL_PWD: rootpassword + run: | + echo "Injecting schema and data into MySQL..." + mysql -h 127.0.0.1 -u njord_user -pnjord_password njord_db < njord/db/test/mysql.sql - name: Running Unit Tests for MySQL run: cargo test mysql @@ -82,4 +89,3 @@ jobs: MYSQL_PASSWORD: njord_password MYSQL_HOST: 127.0.0.1 run: cargo test --test sqlite_tests - diff --git a/docker-compose.yml b/docker-compose.yml index a55753d8..99c46843 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,7 @@ services: volumes: - mysql_data:/var/lib/mysql - ./njord_examples/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql + - ./njord/db/test/mysql.sql:/docker-entrypoint-initdb.d/tests.sql volumes: mysql_data: diff --git a/njord/db/insert.db b/njord/db/insert.db index b8c8894c..cf2d4537 100644 Binary files a/njord/db/insert.db and b/njord/db/insert.db differ diff --git a/njord/db/test/mysql.sql b/njord/db/test/mysql.sql index e69de29b..f2468ce3 100644 --- a/njord/db/test/mysql.sql +++ b/njord/db/test/mysql.sql @@ -0,0 +1,31 @@ +DROP DATABASE IF EXISTS njord_db; + +CREATE DATABASE njord_db; + +USE njord_db; + +-- Table: users +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, -- Auto incrementing primary key for the user ID + username VARCHAR(255) NOT NULL, -- Username field + email VARCHAR(255) NOT NULL, -- Email field + address VARCHAR(255) -- Address field +); + +-- Table: categories +CREATE TABLE categories ( + id INT PRIMARY KEY, -- Primary key for categories + name VARCHAR(255) NOT NULL -- Name of the category +); + +-- Table: products +CREATE TABLE products ( + id INT PRIMARY KEY, -- Primary key for products + name VARCHAR(255) NOT NULL, -- Product name + description TEXT, -- Product description + price DECIMAL(10, 2) NOT NULL, -- Price with up to two decimal places + stock_quantity INT NOT NULL, -- Stock quantity + category_id INT NOT NULL, -- Foreign key to categories (one-to-one relationship) + discount DECIMAL(5, 2) DEFAULT 0.00, -- Discount field with default value + FOREIGN KEY (category_id) REFERENCES categories(id) -- Foreign key constraint to categories table +); diff --git a/njord/src/mysql/select.rs b/njord/src/mysql/select.rs index 9387e9a0..d614571e 100644 --- a/njord/src/mysql/select.rs +++ b/njord/src/mysql/select.rs @@ -42,7 +42,7 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc}; use log::info; use mysql::prelude::*; -use mysql::{prelude::FromRow, Error, PooledConn}; +use mysql::{Error, PooledConn, Value}; use crate::table::Table; use crate::util::{Join, JoinType}; @@ -57,7 +57,7 @@ use crate::util::{Join, JoinType}; /// # Returns /// /// A `SelectQueryBuilder` instance. -pub fn select<'a, T: Table + Default + FromRow>( +pub fn select<'a, T: Table + Default>( conn: &'a mut PooledConn, columns: Vec>, ) -> SelectQueryBuilder<'a, T> { @@ -66,7 +66,7 @@ pub fn select<'a, T: Table + Default + FromRow>( /// A builder for constructing SELECT queries. #[derive(Clone)] -pub struct SelectQueryBuilder<'a, T: Table + Default + FromRow> { +pub struct SelectQueryBuilder<'a, T: Table + Default> { conn: Rc>, table: Option, columns: Vec>, @@ -82,7 +82,7 @@ pub struct SelectQueryBuilder<'a, T: Table + Default + FromRow> { joins: Option>>, } -impl<'a, T: Table + Default + FromRow> SelectQueryBuilder<'a, T> { +impl<'a, T: Table + Default> SelectQueryBuilder<'a, T> { /// Creates a new `SelectQueryBuilder` instance. /// /// # Arguments @@ -366,10 +366,46 @@ impl<'a, T: Table + Default + FromRow> SelectQueryBuilder<'a, T> { info!("{}", final_query); println!("{}", final_query); - // Borrow the connection mutably from the RefCell let mut conn = self.conn.borrow_mut(); + let query_set = conn.query_iter(final_query.as_str()).unwrap(); + + let mut results: Vec = Vec::new(); + + for row_result in query_set { + let row = row_result.unwrap(); // Unwrap the row result + let mut instance = T::default(); + + for column in row.columns_ref() { + // Cells in a row can be indexed by numeric index or by column name + let column_value = &row[column.name_str().as_ref()]; + + let column_value_str = match column_value { + Value::NULL => "NULL".to_string(), + Value::Bytes(bytes) => String::from_utf8_lossy(bytes).to_string(), + Value::Int(i) => i.to_string(), + Value::UInt(u) => u.to_string(), + Value::Float(f) => f.to_string(), + Value::Double(d) => d.to_string(), + Value::Date(year, month, day, hour, min, sec, micro) => format!( + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}", + year, month, day, hour, min, sec, micro + ), + Value::Time(neg, days, hours, minutes, seconds, micros) => format!( + "{}{:02}:{:02}:{:02}.{:06}", + if *neg { "-" } else { "" }, + days * 24 + u32::from(*hours), + minutes, + seconds, + micros + ), + }; + + instance.set_column_value(column.name_str().as_ref(), &column_value_str); + } - let results: Vec = conn.query(final_query.as_str())?; + // Move `instance` to the `results` only after it is fully set up + results.push(instance); + } Ok(results) } @@ -380,7 +416,7 @@ impl<'a, T: Table + Default + FromRow> SelectQueryBuilder<'a, T> { /// The where statement ensures the T is long lived impl<'a, T> QueryBuilder<'a> for SelectQueryBuilder<'a, T> where - T: Table + Default + Clone + FromRow + 'a, // Added 'a bound here + T: Table + Default + Clone + 'a, // Added 'a bound here { fn to_sql(&self) -> String { self.build_query() diff --git a/njord/src/mysql/update.rs b/njord/src/mysql/update.rs index 78eec0d0..5f835fb6 100644 --- a/njord/src/mysql/update.rs +++ b/njord/src/mysql/update.rs @@ -40,10 +40,7 @@ use crate::{ }; use log::info; -use mysql::{ - prelude::{FromRow, Queryable}, - PooledConn, -}; +use mysql::{prelude::Queryable, PooledConn}; use crate::table::Table; @@ -59,7 +56,7 @@ use super::select::SelectQueryBuilder; /// # Returns /// /// An `UpdateQueryBuilder` instance. -pub fn update<'a, T: Table + Default + FromRow>( +pub fn update<'a, T: Table + Default>( conn: &'a mut PooledConn, table: T, ) -> UpdateQueryBuilder<'a, T> { @@ -67,7 +64,7 @@ pub fn update<'a, T: Table + Default + FromRow>( } /// A builder for constructing UPDATE queries. -pub struct UpdateQueryBuilder<'a, T: Table + Default + FromRow> { +pub struct UpdateQueryBuilder<'a, T: Table + Default> { conn: &'a mut PooledConn, table: Option, columns: Vec, @@ -78,7 +75,7 @@ pub struct UpdateQueryBuilder<'a, T: Table + Default + FromRow> { offset: Option, } -impl<'a, T: Table + Default + FromRow> UpdateQueryBuilder<'a, T> { +impl<'a, T: Table + Default> UpdateQueryBuilder<'a, T> { /// Creates a new `UpdateQueryBuilder` instance. /// /// # Arguments diff --git a/njord/tests/mysql/delete_test.rs b/njord/tests/mysql/delete_test.rs new file mode 100644 index 00000000..22073dc2 --- /dev/null +++ b/njord/tests/mysql/delete_test.rs @@ -0,0 +1,52 @@ +use super::User; +use njord::condition::{Condition, Value}; +use njord::keys::AutoIncrementPrimaryKey; +use njord::mysql; +use std::vec; + +#[test] +fn delete_row() { + insert_row(); + + 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::delete(c) + .from(User::default()) + .where_clause(Condition::Eq( + "username".to_string(), + Value::Literal("chasewillden2".to_string()), + )) + .build(); + assert!(result.is_ok()); + } + Err(e) => { + panic!("Failed to DELETE: {:?}", e); + } + } +} + +/// Helper function to insert a row to be deleted +fn insert_row() { + let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let mut conn = mysql::open(url); + + let table_row: User = User { + id: AutoIncrementPrimaryKey::default(), + username: "chasewillden2".to_string(), + email: "chase.willden@example.com".to_string(), + address: "Some Random Address 1".to_string(), + }; + + match conn { + Ok(ref mut c) => { + let result = mysql::insert(c, vec![table_row]); + assert!(result.is_ok()); + } + Err(e) => { + panic!("Failed to INSERT: {:?}", e); + } + } +} diff --git a/njord/tests/mysql/insert_test.rs b/njord/tests/mysql/insert_test.rs new file mode 100644 index 00000000..a4a9ccc4 --- /dev/null +++ b/njord/tests/mysql/insert_test.rs @@ -0,0 +1,27 @@ +use super::User; +use njord::keys::AutoIncrementPrimaryKey; +use njord::mysql; +use std::vec; + +#[test] +fn insert_row() { + let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let mut conn = mysql::open(url); + + let table_row: User = User { + id: AutoIncrementPrimaryKey::default(), + username: "chasewillden".to_string(), + email: "chase.willden@example.com".to_string(), + address: "Some Random Address 1".to_string(), + }; + + match conn { + Ok(ref mut c) => { + let result = mysql::insert(c, vec![table_row]); + assert!(result.is_ok()); + } + Err(e) => { + panic!("Failed to INSERT: {:?}", e); + } + } +} diff --git a/njord/tests/mysql/mod.rs b/njord/tests/mysql/mod.rs index e69de29b..a8a49f93 100644 --- a/njord/tests/mysql/mod.rs +++ b/njord/tests/mysql/mod.rs @@ -0,0 +1,56 @@ +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; +use njord_derive::Table; + +#[derive(Table, Clone)] +#[table_name = "users"] +pub struct User { + pub id: AutoIncrementPrimaryKey, + pub username: String, + pub email: String, + pub address: String, +} + +#[derive(Table)] +#[table_name = "users"] +pub struct UserWithSubQuery { + pub id: AutoIncrementPrimaryKey, + pub username: String, + pub email: String, + pub address: String, + pub additional_address: String, +} + +#[derive(Table)] +#[table_name = "categories"] +pub struct Category { + pub id: PrimaryKey, + pub name: String, +} + +#[derive(Table)] +#[table_name = "products"] +pub struct Product { + pub id: PrimaryKey, + pub name: String, + pub description: String, + pub price: f64, + pub stock_quantity: usize, + pub category: Category, // one-to-one relationship + pub discount: f64, +} + +#[derive(Table)] +#[table_name = "users"] +pub struct UsersWithJoin { + username: String, + price: f64, + name: String, +} diff --git a/njord/tests/mysql/open_test.rs b/njord/tests/mysql/open_test.rs new file mode 100644 index 00000000..82a4ccbe --- /dev/null +++ b/njord/tests/mysql/open_test.rs @@ -0,0 +1,8 @@ +use njord::mysql; + +#[test] +fn open_db() { + let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let result = mysql::open(url); + assert!(result.is_ok()); +} diff --git a/njord/tests/mysql/select_joins_test.rs b/njord/tests/mysql/select_joins_test.rs new file mode 100644 index 00000000..d2b11f9e --- /dev/null +++ b/njord/tests/mysql/select_joins_test.rs @@ -0,0 +1,85 @@ +use njord::condition::Condition; +use njord::mysql; +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), +// } +// } diff --git a/njord/tests/mysql/select_test.rs b/njord/tests/mysql/select_test.rs new file mode 100644 index 00000000..0594695c --- /dev/null +++ b/njord/tests/mysql/select_test.rs @@ -0,0 +1,509 @@ +use njord::condition::Condition; +use njord::keys::AutoIncrementPrimaryKey; +use njord::mysql; +use njord::{column::Column, condition::Value}; +use std::collections::HashMap; + +use crate::User; + +#[test] +fn open_db() { + let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let conn = mysql::open(url); + assert!(conn.is_ok()); +} + +#[test] +fn insert_row() { + let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let mut conn = mysql::open(url); + + 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(ref mut c) => { + let result = mysql::insert(c, vec![table_row]); + assert!(result.is_ok()); + } + Err(e) => { + panic!("Failed to INSERT: {:?}", e); + } + } +} + +#[test] +fn update() { + let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let mut conn = mysql::open(url); + + let columns = vec!["username".to_string()]; + + let condition = Condition::Eq( + "username".to_string(), + Value::Literal("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(ref mut c) => { + let result = mysql::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 url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let mut conn = mysql::open(url); + + let condition = Condition::Eq( + "address".to_string(), + Value::Literal("Some Random Address 1".to_string()), + ); + + let mut order = HashMap::new(); + order.insert(vec!["id".to_string()], "DESC".to_string()); + + match conn { + Ok(ref mut c) => { + let result = mysql::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); + } + } +} + +// FIXME +// #[test] +// fn select() { +// 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::Eq( +// "username".to_string(), +// Value::Literal("mjovanc".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), +// }; +// } + +// FIXME +// #[test] +// fn select_distinct() { +// 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::Eq( +// "username".to_string(), +// Value::Literal("mjovanc".to_string()), +// ); + +// match conn { +// Ok(ref mut c) => { +// let result = mysql::select(c, columns) +// .from(User::default()) +// .where_clause(condition) +// .distinct() +// .build(); + +// match result { +// Ok(r) => { +// // TODO: this test does not work properly since it should return 1 but it seems +// // like it returns all rows because id is different. Need to check up on that. +// assert_eq!(r.len(), 2); +// } +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// }; +// } +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// }; +// } + +// #[test] +// fn select_group_by() { +// let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; +// let mut conn = mysql::open(url); + +// let columns = vec![ +// Column::Text("username".to_string()), +// Column::Text("email".to_string()), +// ]; +// let condition = Condition::Eq( +// "username".to_string(), +// Value::Literal("mjovanc".to_string()), +// ); +// let group_by = vec!["username".to_string(), "email".to_string()]; + +// match conn { +// Ok(ref mut c) => { +// let result = mysql::select(c, columns) +// .from(User::default()) +// .where_clause(condition) +// .group_by(group_by) +// .build(); + +// match result { +// Ok(r) => assert_eq!(r.len(), 1), +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// }; +// } +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// }; +// } + +// #[test] +// fn select_order_by() { +// let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; +// let mut conn = mysql::open(url); + +// let columns = vec![ +// Column::Text("username".to_string()), +// Column::Text("email".to_string()), +// ]; +// let condition = Condition::Eq( +// "username".to_string(), +// Value::Literal("mjovanc".to_string()), +// ); +// let group_by = vec!["username".to_string(), "email".to_string()]; + +// let mut order_by = HashMap::new(); +// order_by.insert(vec!["email".to_string()], "ASC".to_string()); + +// match conn { +// Ok(ref mut c) => { +// let result = mysql::select(c, columns) +// .from(User::default()) +// .where_clause(condition) +// .order_by(order_by) +// .group_by(group_by) +// .build(); + +// match result { +// Ok(r) => assert_eq!(r.len(), 1), +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// }; +// } +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// }; +// } + +// FIXME: +// #[test] +// fn select_limit_offset() { +// let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; +// let mut conn = mysql::open(url); + +// let columns = vec![ +// Column::Text("username".to_string()), +// Column::Text("email".to_string()), +// ]; +// let condition = Condition::Eq( +// "username".to_string(), +// Value::Literal("mjovanc".to_string()), +// ); +// let group_by = vec!["username".to_string(), "email".to_string()]; + +// let mut order_by = HashMap::new(); +// order_by.insert(vec!["id".to_string()], "DESC".to_string()); + +// match conn { +// Ok(ref mut c) => { +// let result = mysql::select(c, columns) +// .from(User::default()) +// .where_clause(condition) +// .order_by(order_by) +// .group_by(group_by) +// .limit(1) +// .offset(0) +// .build(); + +// match result { +// Ok(r) => assert_eq!(r.len(), 1), +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// }; +// } +// Err(error) => panic!("Failed to SELECT: {:?}", error), +// }; +// } + +// FIXME +// #[test] +// fn select_having() { +// 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()), +// ]; +// let condition = Condition::Eq( +// "username".to_string(), +// Value::Literal("mjovanc".to_string()), +// ); +// let group_by = vec!["username".to_string(), "email".to_string()]; + +// let mut order_by = HashMap::new(); +// order_by.insert(vec!["email".to_string()], "DESC".to_string()); + +// let having_condition = Condition::Gt("id".to_string(), Value::Literal("1".to_string())); + +// match conn { +// Ok(ref mut c) => { +// let result = mysql::select(c, columns) +// .from(User::default()) +// .where_clause(condition) +// .order_by(order_by) +// .group_by(group_by) +// .having(having_condition) +// .build(); + +// match result { +// Ok(r) => assert_eq!(r.len(), 1), +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// }; +// } +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// } +// } + +// FIXME: Figure out how to implement these below +// #[test] +// fn select_except() { +// 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 condition1 = Condition::Eq( +// "username".to_string(), +// Value::Literal("mjovanc".to_string()), +// ); +// let condition2 = Condition::Eq( +// "username".to_string(), +// Value::Literal("otheruser".to_string()), +// ); +// let condition3 = Condition::Eq( +// "username".to_string(), +// Value::Literal("anotheruser".to_string()), +// ); + +// match conn { +// Ok(ref mut c) => { +// // Create a new connection for each query builder +// let query1 = mysql::select(c, columns.clone()) +// .from(User::default()) +// .where_clause(condition1); + +// let query2 = mysql::select(c, columns.clone()) +// .from(User::default()) +// .where_clause(condition2); + +// let query3 = mysql::select(c, columns.clone()) +// .from(User::default()) +// .where_clause(condition3); + +// // Test a chain of EXCEPT queries (query1 EXCEPT query2 EXCEPT query3) +// let result = query1.except(query2).except(query3).build(); + +// match result { +// Ok(r) => { +// assert_eq!(r.len(), 2, "Expected 2 results after EXCEPT clauses."); +// } +// Err(e) => panic!("Failed to SELECT with EXCEPT: {:?}", e), +// }; +// } +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// }; +// } + +// #[test] +// fn select_union() { +// 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 condition1 = Condition::Eq("id".to_string(), Value::Literal(42.to_string())); +// let condition2 = Condition::Eq("id".to_string(), Value::Literal(43.to_string())); + +// match conn { +// Ok(ref mut c) => { +// // Create a new connection for each query builder +// let query1 = mysql::select(c, columns.clone()) +// .from(User::default()) +// .where_clause(condition1); + +// let query2 = mysql::select(c, columns.clone()) +// .from(User::default()) +// .where_clause(condition2); + +// // Test a chain of UNION queries (query1 UNION query2) +// let result = query1.union(query2).build(); + +// match result { +// Ok(r) => { +// // We expect two results: mjovanc and otheruser +// assert_eq!(r.len(), 2, "Expected 2 results from the UNION query."); +// assert_eq!( +// r[0].id, +// AutoIncrementPrimaryKey::new(Some(42)), +// "First user should be mjovanc." +// ); +// assert_eq!( +// r[1].id, +// AutoIncrementPrimaryKey::new(Some(43)), +// "Second user should be otheruser." +// ); +// } +// Err(e) => panic!("Failed to SELECT with UNION: {:?}", e), +// }; +// } +// Err(e) => panic!("Failed to SELECT: {:?}", e), +// } +// } + +// #[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(c) => { +// let sub_query = mysql::select(&c, 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(&c, columns) +// .from(UserWithSubQuery::default()) +// .build(); + +// 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), +// }; +// } diff --git a/njord/tests/mysql/update_test.rs b/njord/tests/mysql/update_test.rs new file mode 100644 index 00000000..09d429ac --- /dev/null +++ b/njord/tests/mysql/update_test.rs @@ -0,0 +1,57 @@ +use super::User; +use njord::condition::{Condition, Value}; +use njord::keys::AutoIncrementPrimaryKey; +use njord::mysql; +use std::vec; + +#[test] +fn delete_row() { + insert_row(); + + let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let mut conn = mysql::open(url); + + let columns = vec!["username".to_string()]; + let condition = Condition::Eq( + "username".to_string(), + Value::Literal("chasewillden2".to_string()), + ); + + match conn { + Ok(ref mut c) => { + let result = mysql::update(c, User::default()) + .set(columns) + .where_clause(condition) + .limit(4) + .offset(0) + .build(); + assert!(result.is_ok()); + } + Err(e) => { + panic!("Failed to DELETE: {:?}", e); + } + } +} + +/// Helper function to insert a row to be updated +fn insert_row() { + let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let mut conn = mysql::open(url); + + let table_row: User = User { + id: AutoIncrementPrimaryKey::default(), + username: "chasewillden2".to_string(), + email: "chase.willden@example.com".to_string(), + address: "Some Random Address 1".to_string(), + }; + + match conn { + Ok(ref mut c) => { + let result = mysql::insert(c, vec![table_row]); + assert!(result.is_ok()); + } + Err(e) => { + panic!("Failed to INSERT: {:?}", e); + } + } +} diff --git a/njord_examples/mysql/src/main.rs b/njord_examples/mysql/src/main.rs index fc832b0f..370b6761 100644 --- a/njord_examples/mysql/src/main.rs +++ b/njord_examples/mysql/src/main.rs @@ -1,6 +1,7 @@ use std::fmt::Error; use crate::schema::NearEarthObject; +use njord::column::Column; use njord::keys::AutoIncrementPrimaryKey; use njord::mysql; use reqwest::header::ACCEPT; @@ -26,6 +27,29 @@ pub struct NearEarthObjectResponse { #[tokio::main] async fn main() -> Result<(), Error> { + let _ = insert().await; + let _ = select(); + + Ok(()) +} + +fn select() -> Result<(), Box> { + let url = "mysql://njord_user:njord_password@localhost:3306/njord_db"; + let mut conn = mysql::open(url).unwrap(); + + let results = mysql::select(&mut conn, vec![Column::Text("id".to_string())]) + .from(NearEarthObject::default()) + .build(); + + match results { + Ok(data) => println!("Selected: {:#?}", data.len()), + Err(err) => eprintln!("Error: {}", err), + } + + Ok(()) +} + +async fn insert() -> Result<(), Box> { let neo = get_near_earth_objects(0, 10).await; let mut near_earth_objects: Vec = Vec::new();