diff --git a/scripts/coverage_test.sh b/scripts/coverage_test.sh index d093072..f30a3a7 100755 --- a/scripts/coverage_test.sh +++ b/scripts/coverage_test.sh @@ -27,7 +27,6 @@ echo "\n-- --------------------------------------------------------------------- echo "-- Testing SQLite syntax" echo "-- ------------------------------------------------------------------------------\n" RUSTFLAGS="-C instrument-coverage" LLVM_PROFILE_FILE="$COVERAGE_TARGET/$PKG_NAME-%m.profraw" cargo test --target-dir $COVERAGE_TARGET --features sqlite; -RUSTFLAGS="-C instrument-coverage" LLVM_PROFILE_FILE="$COVERAGE_TARGET/$PKG_NAME-%m.profraw" cargo test --target-dir $COVERAGE_TARGET --features mysql; echo "\n-- ------------------------------------------------------------------------------" echo "-- Testing MySQL syntax" diff --git a/src/structure.rs b/src/structure.rs index 6894724..eb453bc 100644 --- a/src/structure.rs +++ b/src/structure.rs @@ -695,7 +695,7 @@ pub enum SelectClause { /// # } /// ``` /// -/// Output (indented for readability) +/// Output /// /// ```sql /// START TRANSACTION isolation level serializable; @@ -711,7 +711,7 @@ pub struct Transaction { pub(crate) _set_transaction: Option, pub(crate) _start_transaction: Option, - #[cfg(any(feature = "postgresql", feature = "sqlite"))] + #[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] pub(crate) _begin: Option, #[cfg(any(feature = "postgresql", feature = "sqlite"))] @@ -726,9 +726,10 @@ pub(crate) enum TrCmd { Rollback, Savepoint, - #[cfg(any(feature = "postgresql", feature = "sqlite"))] + #[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] #[cfg_attr(docsrs, doc(cfg(feature = "postgresql")))] #[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))] + #[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] Begin, #[cfg(any(feature = "postgresql", feature = "sqlite"))] @@ -738,10 +739,12 @@ pub(crate) enum TrCmd { #[cfg(not(feature = "sqlite"))] #[cfg_attr(docsrs, doc(cfg(feature = "postgresql")))] + #[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] SetTransaction, #[cfg(not(feature = "sqlite"))] #[cfg_attr(docsrs, doc(cfg(feature = "postgresql")))] + #[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] StartTransaction, } diff --git a/src/transaction/transaction.rs b/src/transaction/transaction.rs index 968033d..4ea17c9 100644 --- a/src/transaction/transaction.rs +++ b/src/transaction/transaction.rs @@ -7,7 +7,7 @@ use crate::{ utils::push_unique, }; -#[cfg(any(feature = "postgresql", feature = "sqlite"))] +#[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] use crate::structure::{CreateIndex, DropIndex}; impl Transaction { @@ -46,8 +46,6 @@ impl Transaction { /// # Example /// /// ``` - /// # #[cfg(not(feature = "sqlite"))] - /// # { /// # use sql_query_builder as sql; /// let transaction_query = sql::Transaction::new() /// .commit("WORK") @@ -56,7 +54,6 @@ impl Transaction { /// /// # let expected = "COMMIT TRANSACTION;"; /// # assert_eq!(transaction_query, expected); - /// # } /// ``` /// /// Output @@ -596,9 +593,10 @@ impl Transaction { } } -#[cfg(any(feature = "postgresql", feature = "sqlite"))] +#[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] #[cfg_attr(docsrs, doc(cfg(feature = "postgresql")))] #[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))] +#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] impl Transaction { /// The `begin` command, this method will be always added at the beginning of the transation and /// all consecutive call will override the previous value. @@ -710,7 +708,12 @@ impl Transaction { self._ordered_commands.push(cmd); self } +} +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +#[cfg_attr(docsrs, doc(cfg(feature = "postgresql")))] +#[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))] +impl Transaction { /// The `end` command, this method will be always added at the end of the transation and /// all consecutive call will override the previous value. /// diff --git a/src/transaction/transaction_internal.rs b/src/transaction/transaction_internal.rs index cc1113c..9ca3dae 100644 --- a/src/transaction/transaction_internal.rs +++ b/src/transaction/transaction_internal.rs @@ -16,7 +16,7 @@ impl Concat for Transaction { query = self.concat_raw(query, &fmts, &self._raw); - #[cfg(any(feature = "postgresql", feature = "sqlite"))] + #[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] { query = self.concat_begin(query, &fmts); } @@ -52,7 +52,7 @@ impl Concat for TransactionCommand { Rollback => format!("ROLLBACK{arg}"), Savepoint => format!("SAVEPOINT{arg}"), - #[cfg(any(feature = "postgresql", feature = "sqlite"))] + #[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] Begin => format!("BEGIN{arg}"), #[cfg(any(feature = "postgresql", feature = "sqlite"))] End => format!("END{arg}"), @@ -117,7 +117,7 @@ impl TransactionCommand { } } -#[cfg(any(feature = "postgresql", feature = "sqlite"))] +#[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] impl Transaction { fn concat_begin(&self, query: String, fmts: &fmt::Formatter) -> String { let fmt::Formatter { lb, space, .. } = fmts; @@ -128,7 +128,10 @@ impl Transaction { format!("{query}{sql}") } +} +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +impl Transaction { fn concat_end(&self, query: String, fmts: &fmt::Formatter) -> String { let fmt::Formatter { lb, space, .. } = fmts; let sql = match &self._end { diff --git a/tests/command_begin_spec.rs b/tests/command_begin_spec.rs index e6fcba7..7c6ca12 100644 --- a/tests/command_begin_spec.rs +++ b/tests/command_begin_spec.rs @@ -1,4 +1,4 @@ -#[cfg(any(feature = "postgresql", feature = "sqlite"))] +#[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] mod begin_command { use pretty_assertions::assert_eq; use sql_query_builder as sql; diff --git a/tests/command_transaction_spec.rs b/tests/command_transaction_spec.rs index e230a86..17134e1 100644 --- a/tests/command_transaction_spec.rs +++ b/tests/command_transaction_spec.rs @@ -1,3 +1,178 @@ +mod full_api { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + // SQL Standard + #[cfg(not(feature = "sqlite"))] + #[test] + fn sql_standard_with_all_methods() { + let query = sql::Transaction::new() + // required methods + .start_transaction("") + // optional methods + .set_transaction("") + // at least one of methods + .alter_table(sql::AlterTable::new().alter_table("users")) + .create_table(sql::CreateTable::new().create_table("users")) + .delete(sql::Delete::new().delete_from("users")) + .drop_table(sql::DropTable::new().drop_table("users")) + .insert(sql::Insert::new().insert_into("users")) + .select(sql::Select::new().select("login")) + .update(sql::Update::new().update("users")) + // one of methods + .rollback("") + .commit("") + .as_string(); + + let expected_query = "\ + START TRANSACTION; \ + SET TRANSACTION; \ + ALTER TABLE users; \ + CREATE TABLE users; \ + DELETE FROM users; \ + DROP TABLE users; \ + INSERT INTO users; \ + SELECT login; \ + UPDATE users; \ + ROLLBACK; \ + COMMIT;\ + "; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "postgresql")] + #[test] + fn postgres_with_all_methods() { + let query = sql::Transaction::new() + // one of methods + .begin("") + .start_transaction("") + // optional methods + .set_transaction("") + // at least one of methods + .alter_table(sql::AlterTable::new().alter_table("users")) + .create_index(sql::CreateIndex::new().create_index("users_idx")) + .create_table(sql::CreateTable::new().create_table("users")) + .delete(sql::Delete::new().delete_from("users")) + .drop_index(sql::DropIndex::new().drop_index("users_idx")) + .drop_table(sql::DropTable::new().drop_table("users")) + .insert(sql::Insert::new().insert_into("users")) + .select(sql::Select::new().select("login")) + .update(sql::Update::new().update("users")) + // one of methods + .rollback("") + .commit("") + .end("") + .as_string(); + + let expected_query = "\ + BEGIN; \ + START TRANSACTION; \ + SET TRANSACTION; \ + ALTER TABLE users; \ + CREATE INDEX users_idx; \ + CREATE TABLE users; \ + DELETE FROM users; \ + DROP INDEX users_idx; \ + DROP TABLE users; \ + INSERT INTO users; \ + SELECT login; \ + UPDATE users; \ + ROLLBACK; \ + COMMIT; \ + END;\ + "; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "sqlite")] + #[test] + fn sqlite_with_all_methods() { + let query = sql::Transaction::new() + // required method + .begin("") + // at least one of methods + .alter_table(sql::AlterTable::new().alter_table("users")) + .create_index(sql::CreateIndex::new().create_index("users_idx")) + .create_table(sql::CreateTable::new().create_table("users")) + .delete(sql::Delete::new().delete_from("users")) + .drop_index(sql::DropIndex::new().drop_index("users_idx")) + .drop_table(sql::DropTable::new().drop_table("users")) + .insert(sql::Insert::new().insert_into("users")) + .select(sql::Select::new().select("login")) + .update(sql::Update::new().update("users")) + // one of methods + .rollback("") + .commit("") + .end("") + .as_string(); + + let expected_query = "\ + BEGIN; \ + ALTER TABLE users; \ + CREATE INDEX users_idx; \ + CREATE TABLE users; \ + DELETE FROM users; \ + DROP INDEX users_idx; \ + DROP TABLE users; \ + INSERT INTO users; \ + SELECT login; \ + UPDATE users; \ + ROLLBACK; \ + COMMIT; \ + END;\ + "; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "mysql")] + #[test] + fn mysql_with_all_methods() { + let query = sql::Transaction::new() + // one of methods + .begin("") + .start_transaction("") + // optional methods + .set_transaction("") + // at least one of methods + .alter_table(sql::AlterTable::new().alter_table("users")) + .create_index(sql::CreateIndex::new().create_index("users_idx")) + .create_table(sql::CreateTable::new().create_table("users")) + .delete(sql::Delete::new().delete_from("users")) + .drop_index(sql::DropIndex::new().drop_index("users_idx")) + .drop_table(sql::DropTable::new().drop_table("users")) + .insert(sql::Insert::new().insert_into("users")) + .select(sql::Select::new().select("login")) + .update(sql::Update::new().update("users")) + // one of methods + .rollback("") + .commit("") + .as_string(); + + let expected_query = "\ + BEGIN; \ + START TRANSACTION; \ + SET TRANSACTION; \ + ALTER TABLE users; \ + CREATE INDEX users_idx; \ + CREATE TABLE users; \ + DELETE FROM users; \ + DROP INDEX users_idx; \ + DROP TABLE users; \ + INSERT INTO users; \ + SELECT login; \ + UPDATE users; \ + ROLLBACK; \ + COMMIT;\ + "; + + assert_eq!(expected_query, query); + } +} + mod builder_features { use pretty_assertions::assert_eq; use sql_query_builder as sql; @@ -431,90 +606,6 @@ mod delete_method { } } -#[cfg(any(feature = "postgresql", feature = "sqlite"))] -mod create_index_method { - use pretty_assertions::assert_eq; - use sql_query_builder as sql; - - #[test] - fn method_create_index_should_add_a_create_index_command() { - let query = sql::Transaction::new() - .create_index(sql::CreateIndex::new().create_index("users_name_idx")) - .as_string(); - - let expected_query = "CREATE INDEX users_name_idx;"; - - assert_eq!(expected_query, query); - } - - #[test] - fn method_create_index_should_accumulate_values_on_consecutive_calls() { - let query = sql::Transaction::new() - .create_index(sql::CreateIndex::new().create_index("users_name_idx")) - .create_index(sql::CreateIndex::new().create_index("orders_product_name_idx")) - .as_string(); - - let expected_query = "CREATE INDEX users_name_idx; CREATE INDEX orders_product_name_idx;"; - - assert_eq!(expected_query, query); - } - - #[test] - fn method_create_index_should_not_accumulate_values_when_expression_is_empty() { - let query = sql::Transaction::new() - .create_index(sql::CreateIndex::new()) - .create_index(sql::CreateIndex::new().create_index("orders_product_name_idx")) - .create_index(sql::CreateIndex::new()) - .as_string(); - - let expected_query = "CREATE INDEX orders_product_name_idx;"; - - assert_eq!(expected_query, query); - } -} - -#[cfg(any(feature = "postgresql", feature = "sqlite"))] -mod drop_index_method { - use pretty_assertions::assert_eq; - use sql_query_builder as sql; - - #[test] - fn method_drop_index_should_add_a_drop_index_command() { - let query = sql::Transaction::new() - .drop_index(sql::DropIndex::new().drop_index("users_name_idx")) - .as_string(); - - let expected_query = "DROP INDEX users_name_idx;"; - - assert_eq!(expected_query, query); - } - - #[test] - fn method_drop_index_should_accumulate_values_on_consecutive_calls() { - let query = sql::Transaction::new() - .drop_index(sql::DropIndex::new().drop_index("users_name_idx")) - .drop_index(sql::DropIndex::new().drop_index("orders_product_name_idx")) - .as_string(); - - let expected_query = "DROP INDEX users_name_idx; DROP INDEX orders_product_name_idx;"; - - assert_eq!(expected_query, query); - } - - #[test] - fn method_drop_index_should_not_accumulate_values_when_expression_is_empty() { - let query = sql::Transaction::new() - .drop_index(sql::DropIndex::new()) - .drop_index(sql::DropIndex::new().drop_index("orders_product_name_idx")) - .drop_index(sql::DropIndex::new()) - .as_string(); - - let expected_query = "DROP INDEX orders_product_name_idx;"; - - assert_eq!(expected_query, query); - } -} - mod drop_table_method { use pretty_assertions::assert_eq; use sql_query_builder as sql; @@ -684,153 +775,237 @@ mod multi_commands { } } -#[cfg(not(feature = "sqlite"))] -mod order_of_commands { +mod select_method { use pretty_assertions::assert_eq; use sql_query_builder as sql; #[test] - fn command_set_transaction_should_be_add_after_start_transaction() { + fn method_select_should_add_a_select_command() { let query = sql::Transaction::new() - .set_transaction("READ ONLY") - .start_transaction("") + .select(sql::Select::new().select("login, name")) .as_string(); - let expected_query = "\ - START TRANSACTION; \ - SET TRANSACTION READ ONLY;\ - "; + let expected_query = "SELECT login, name;"; assert_eq!(query, expected_query); } #[test] - fn command_commit_should_be_add_after_start_transaction_when_specified() { + fn method_select_should_accumulate_values_on_consecutive_calls() { let query = sql::Transaction::new() - .commit("") - .start_transaction("REPEATABLE READ") + .select(sql::Select::new().select("login, name")) + .select(sql::Select::new().select("login, name")) .as_string(); - let expected_query = "\ - START TRANSACTION REPEATABLE READ; \ - COMMIT;\ - "; + let expected_query = "SELECT login, name; SELECT login, name;"; assert_eq!(query, expected_query); } #[test] - fn command_commit_should_be_add_after_set_transaction_when_specified() { + fn method_select_should_not_accumulate_values_when_expression_is_empty() { let query = sql::Transaction::new() - .commit("") - .set_transaction("READ ONLY") + .select(sql::Select::new()) + .select(sql::Select::new().select("login, name")) + .select(sql::Select::new()) .as_string(); - let expected_query = "\ - SET TRANSACTION READ ONLY; \ - COMMIT;\ - "; + let expected_query = "SELECT login, name;"; assert_eq!(query, expected_query); } } -#[cfg(feature = "sqlite")] -mod order_of_commands { +mod update_method { use pretty_assertions::assert_eq; use sql_query_builder as sql; #[test] - fn command_commit_should_be_add_after_begin_when_specified() { - let query = sql::Transaction::new().commit("").begin("DEFERRED").as_string(); - let expected_query = "\ - BEGIN DEFERRED; \ - COMMIT;\ - "; + fn method_update_should_add_a_update_command() { + let query = sql::Transaction::new() + .update(sql::Update::new().update("users")) + .as_string(); + let expected_query = "UPDATE users;"; assert_eq!(query, expected_query); } #[test] - fn command_end_should_be_add_after_begin_when_specified() { - let query = sql::Transaction::new().end("").begin("IMMEDIATE").as_string(); - let expected_query = "\ - BEGIN IMMEDIATE; \ - END;\ - "; + fn method_update_should_accumulate_values_on_consecutive_calls() { + let query = sql::Transaction::new() + .update(sql::Update::new().update("users")) + .update(sql::Update::new().update("users")) + .as_string(); + let expected_query = "UPDATE users; UPDATE users;"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_update_should_not_accumulate_values_when_expression_is_empty() { + let query = sql::Transaction::new() + .update(sql::Update::new()) + .update(sql::Update::new().update("users")) + .update(sql::Update::new()) + .as_string(); + let expected_query = "UPDATE users;"; assert_eq!(query, expected_query); } } -mod select_method { +#[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] +mod create_index_method { use pretty_assertions::assert_eq; use sql_query_builder as sql; #[test] - fn method_select_should_add_a_select_command() { + fn method_create_index_should_add_a_create_index_command() { let query = sql::Transaction::new() - .select(sql::Select::new().select("login, name")) + .create_index(sql::CreateIndex::new().create_index("users_name_idx")) .as_string(); - let expected_query = "SELECT login, name;"; - assert_eq!(query, expected_query); + let expected_query = "CREATE INDEX users_name_idx;"; + + assert_eq!(expected_query, query); } #[test] - fn method_select_should_accumulate_values_on_consecutive_calls() { + fn method_create_index_should_accumulate_values_on_consecutive_calls() { let query = sql::Transaction::new() - .select(sql::Select::new().select("login, name")) - .select(sql::Select::new().select("login, name")) + .create_index(sql::CreateIndex::new().create_index("users_name_idx")) + .create_index(sql::CreateIndex::new().create_index("orders_product_name_idx")) .as_string(); - let expected_query = "SELECT login, name; SELECT login, name;"; - assert_eq!(query, expected_query); + let expected_query = "CREATE INDEX users_name_idx; CREATE INDEX orders_product_name_idx;"; + + assert_eq!(expected_query, query); } #[test] - fn method_select_should_not_accumulate_values_when_expression_is_empty() { + fn method_create_index_should_not_accumulate_values_when_expression_is_empty() { let query = sql::Transaction::new() - .select(sql::Select::new()) - .select(sql::Select::new().select("login, name")) - .select(sql::Select::new()) + .create_index(sql::CreateIndex::new()) + .create_index(sql::CreateIndex::new().create_index("orders_product_name_idx")) + .create_index(sql::CreateIndex::new()) .as_string(); - let expected_query = "SELECT login, name;"; - assert_eq!(query, expected_query); + let expected_query = "CREATE INDEX orders_product_name_idx;"; + + assert_eq!(expected_query, query); } } -mod update_method { +#[cfg(any(feature = "postgresql", feature = "sqlite", feature = "mysql"))] +mod drop_index_method { use pretty_assertions::assert_eq; use sql_query_builder as sql; #[test] - fn method_update_should_add_a_update_command() { + fn method_drop_index_should_add_a_drop_index_command() { let query = sql::Transaction::new() - .update(sql::Update::new().update("users")) + .drop_index(sql::DropIndex::new().drop_index("users_name_idx")) .as_string(); - let expected_query = "UPDATE users;"; + + let expected_query = "DROP INDEX users_name_idx;"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_drop_index_should_accumulate_values_on_consecutive_calls() { + let query = sql::Transaction::new() + .drop_index(sql::DropIndex::new().drop_index("users_name_idx")) + .drop_index(sql::DropIndex::new().drop_index("orders_product_name_idx")) + .as_string(); + + let expected_query = "DROP INDEX users_name_idx; DROP INDEX orders_product_name_idx;"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_drop_index_should_not_accumulate_values_when_expression_is_empty() { + let query = sql::Transaction::new() + .drop_index(sql::DropIndex::new()) + .drop_index(sql::DropIndex::new().drop_index("orders_product_name_idx")) + .drop_index(sql::DropIndex::new()) + .as_string(); + + let expected_query = "DROP INDEX orders_product_name_idx;"; + + assert_eq!(expected_query, query); + } +} + +#[cfg(not(feature = "sqlite"))] +mod order_of_commands { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn command_set_transaction_should_be_add_after_start_transaction() { + let query = sql::Transaction::new() + .set_transaction("READ ONLY") + .start_transaction("") + .as_string(); + let expected_query = "\ + START TRANSACTION; \ + SET TRANSACTION READ ONLY;\ + "; assert_eq!(query, expected_query); } #[test] - fn method_update_should_accumulate_values_on_consecutive_calls() { + fn command_commit_should_be_add_after_start_transaction_when_specified() { let query = sql::Transaction::new() - .update(sql::Update::new().update("users")) - .update(sql::Update::new().update("users")) + .commit("") + .start_transaction("REPEATABLE READ") .as_string(); - let expected_query = "UPDATE users; UPDATE users;"; + let expected_query = "\ + START TRANSACTION REPEATABLE READ; \ + COMMIT;\ + "; assert_eq!(query, expected_query); } #[test] - fn method_update_should_not_accumulate_values_when_expression_is_empty() { + fn command_commit_should_be_add_after_set_transaction_when_specified() { let query = sql::Transaction::new() - .update(sql::Update::new()) - .update(sql::Update::new().update("users")) - .update(sql::Update::new()) + .commit("") + .set_transaction("READ ONLY") .as_string(); - let expected_query = "UPDATE users;"; + let expected_query = "\ + SET TRANSACTION READ ONLY; \ + COMMIT;\ + "; + + assert_eq!(query, expected_query); + } +} + +#[cfg(feature = "sqlite")] +mod order_of_commands { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn command_commit_should_be_add_after_begin_when_specified() { + let query = sql::Transaction::new().commit("").begin("DEFERRED").as_string(); + let expected_query = "\ + BEGIN DEFERRED; \ + COMMIT;\ + "; + + assert_eq!(query, expected_query); + } + + #[test] + fn command_end_should_be_add_after_begin_when_specified() { + let query = sql::Transaction::new().end("").begin("IMMEDIATE").as_string(); + let expected_query = "\ + BEGIN IMMEDIATE; \ + END;\ + "; assert_eq!(query, expected_query); }