From 5db1c7a9728c8c91fe48334ae10da6df8550face Mon Sep 17 00:00:00 2001 From: Belchior Oliveira Date: Sat, 28 Sep 2024 14:58:08 -0300 Subject: [PATCH 1/4] improves documentation --- src/create_table/create_table.rs | 8 ++++---- src/delete/delete.rs | 5 ++--- src/drop_index/drop_index.rs | 18 +++++++++--------- src/drop_table/drop_table.rs | 10 +++++----- src/select/select.rs | 5 ++--- src/structure.rs | 2 +- src/update/update.rs | 5 ++--- tests/command_begin_spec.rs | 2 +- tests/command_commit_spec.rs | 2 +- tests/command_create_table_spec.rs | 2 +- tests/command_end_spec.rs | 2 +- tests/command_set_transaction_spec.rs | 2 +- tests/command_start_transaction_spec.rs | 2 +- 13 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/create_table/create_table.rs b/src/create_table/create_table.rs index 4ede0ae..0ed30a9 100644 --- a/src/create_table/create_table.rs +++ b/src/create_table/create_table.rs @@ -38,7 +38,7 @@ impl CreateTable { self.concat(&fmts) } - /// Define a column to be passed as arguments to the create table command, multiples call will concatenates all column parameters + /// Defines a column to be passed as arguments to the create table command, multiples call will concatenates all column parameters /// /// ### Example /// @@ -100,7 +100,7 @@ impl CreateTable { self } - /// Defines a create table signature. Multiples calls will overrides the previous value + /// Defines a create table parameter. Multiples calls will overrides the previous value /// /// ### Example /// @@ -124,7 +124,7 @@ impl CreateTable { self } - /// Defines a create table signature with the modifer `if not exists`. Multiples calls will overrides the previous value + /// Defines a create table parameter with the `if not exists` modifier. Multiples calls will overrides the previous value /// /// ### Example /// @@ -256,7 +256,7 @@ impl CreateTable { self } - /// Adds at the beginning a raw SQL query. Is useful to create a more complex create table signature like the example below. + /// Adds at the beginning a raw SQL query. Is useful to create a more complex create table command. /// /// ### Example /// diff --git a/src/delete/delete.rs b/src/delete/delete.rs index 26321a3..3cdb6fe 100644 --- a/src/delete/delete.rs +++ b/src/delete/delete.rs @@ -179,8 +179,7 @@ impl Delete { self } - /// This method is un alias of `where_clause`. The `where_and` will concatenate mulltiples calls using the `and` operator. - /// The intention is to enable more idiomatic concatenation of conditions. + /// The method will concatenate multiples calls using the `and` operator. This method is un alias of `where_clause`. /// /// # Example /// @@ -208,7 +207,7 @@ impl Delete { self.where_clause(condition) } - /// The `where` clause, this method will concatenate mulltiples calls using the `and` operator. + /// The `where` clause, this method will concatenate multiples calls using the `and` operator. /// If you intended to use the `or` operator you should use the [where_or](Delete::where_or) method /// /// # Example diff --git a/src/drop_index/drop_index.rs b/src/drop_index/drop_index.rs index 685d5b4..436bb7b 100644 --- a/src/drop_index/drop_index.rs +++ b/src/drop_index/drop_index.rs @@ -31,7 +31,7 @@ impl DropIndex { self.concat(&fmts) } - /// Defines a drop index command, this method overrides the previous value + /// Defines a drop index parameter, this method overrides the previous value /// /// ### Example 1 /// @@ -78,12 +78,12 @@ impl DropIndex { /// ```sql /// DROP INDEX users_name_idx, orders_product_name_idx /// ``` - pub fn drop_index(mut self, table_name: &str) -> Self { - push_unique(&mut self._drop_index, table_name.trim().to_string()); + pub fn drop_index(mut self, index_name: &str) -> Self { + push_unique(&mut self._drop_index, index_name.trim().to_string()); self } - /// Defines a drop index comand with the modifer `if exists`, this method overrides the previous value + /// Defines a drop index parameter with the `if exists` modifier, this method overrides the previous value /// /// ### Example 1 /// @@ -130,8 +130,8 @@ impl DropIndex { /// ```sql /// DROP INDEX IF EXISTS users_name_idx, orders_product_name_idx /// ``` - pub fn drop_index_if_exists(mut self, table_name: &str) -> Self { - push_unique(&mut self._drop_index, table_name.trim().to_string()); + pub fn drop_index_if_exists(mut self, index_name: &str) -> Self { + push_unique(&mut self._drop_index, index_name.trim().to_string()); self._if_exists = true; self } @@ -175,7 +175,7 @@ impl DropIndex { self } - /// Adds at the beginning a raw SQL query. Is useful to create a more complex drop index signature like the example below. + /// Adds at the beginning a raw SQL query. Is useful to create a more complex drop index command. /// /// ### Example /// @@ -202,7 +202,7 @@ impl DropIndex { /// Adds a raw SQL query after a specified parameter. /// - /// The `DropIndexParams::DropIndex` works for both `.drop_index` and `.drop_index_if_exist` methods + /// The `DropIndexParams::DropIndex` works both to `.drop_index` and `.drop_index_if_exist` methods /// /// ### Example /// @@ -229,7 +229,7 @@ impl DropIndex { /// Adds a raw SQL query before a specified parameter. /// - /// The `DropIndexParams::DropIndex` works for both `.drop_index` and `.drop_index_if_exist` methods + /// The `DropIndexParams::DropIndex` works both to `.drop_index` and `.drop_index_if_exist` methods /// /// ### Example /// diff --git a/src/drop_table/drop_table.rs b/src/drop_table/drop_table.rs index 2702eb7..5bdc98c 100644 --- a/src/drop_table/drop_table.rs +++ b/src/drop_table/drop_table.rs @@ -31,7 +31,7 @@ impl DropTable { self.concat(&fmts) } - /// Defines a drop table command, this method overrides the previous value + /// Defines a drop table parameter, this method overrides the previous value /// /// ### Example 1 /// @@ -83,7 +83,7 @@ impl DropTable { self } - /// Defines a drop table comand with the modifer `if exists`, this method overrides the previous value + /// Defines a drop table parameter with the `if exists` modifier, this method overrides the previous value /// /// ### Example 1 /// @@ -175,7 +175,7 @@ impl DropTable { self } - /// Adds at the beginning a raw SQL query. Is useful to create a more complex drop table signature like the example below. + /// Adds at the beginning a raw SQL query. Is useful to create a more complex drop table command. /// /// ### Example /// @@ -202,7 +202,7 @@ impl DropTable { /// Adds a raw SQL query after a specified parameter. /// - /// The `DropTableParams::DropTable` works for both `.drop_table` and `.drop_table_if_exist` methods + /// The `DropTableParams::DropTable` works both to `.drop_table` and `.drop_table_if_exist` methods /// /// ### Example /// @@ -229,7 +229,7 @@ impl DropTable { /// Adds a raw SQL query before a specified parameter. /// - /// The `DropTableParams::DropTable` works for both `.drop_table` and `.drop_table_if_exist` methods + /// The `DropTableParams::DropTable` works both to `.drop_table` and `.drop_table_if_exist` methods /// /// ### Example /// diff --git a/src/select/select.rs b/src/select/select.rs index 74ae4ce..bb2c267 100644 --- a/src/select/select.rs +++ b/src/select/select.rs @@ -394,8 +394,7 @@ impl Select { self } - /// This method is un alias of `where_clause`. The `where_and` will concatenate mulltiples calls using the `and` operator. - /// The intention is to enable more idiomatic concatenation of conditions. + /// The method will concatenate multiples calls using the `and` operator. This method is un alias of `where_clause`. /// /// # Example /// @@ -425,7 +424,7 @@ impl Select { self.where_clause(condition) } - /// The `where` clause, this method will concatenate mulltiples calls using the `and` operator. + /// The `where` clause, this method will concatenate multiples calls using the `and` operator. /// If you intended to use the `or` operator you should use the [where_or](Select::where_or) method /// /// # Example diff --git a/src/structure.rs b/src/structure.rs index 011b615..d316be4 100644 --- a/src/structure.rs +++ b/src/structure.rs @@ -136,7 +136,7 @@ pub enum CreateTableParams { PrimaryKey, } -/// Builder to contruct a [DropIndex] command. This command is available only for the crate features `postgresql` and `sqlite` +/// Builder to contruct a [DropIndex] command. Available only for the crate features `postgresql` and `sqlite` /// /// Basic API /// ``` diff --git a/src/update/update.rs b/src/update/update.rs index 68ada6f..de0644c 100644 --- a/src/update/update.rs +++ b/src/update/update.rs @@ -203,8 +203,7 @@ impl Update { self } - /// This method is un alias of `where_clause`. The `where_and` will concatenate mulltiples calls using the `and` operator. - /// The intention is to enable more idiomatic concatenation of conditions. + /// The method will concatenate multiples calls using the `and` operator. This method is un alias of `where_clause`. /// /// # Example /// @@ -232,7 +231,7 @@ impl Update { self.where_clause(condition) } - /// The `where` clause, this method will concatenate mulltiples calls using the `and` operator. + /// The `where` clause, this method will concatenate multiples calls using the `and` operator. /// If you intended to use the `or` operator you should use the [where_or](Update::where_or) method /// /// # Example diff --git a/tests/command_begin_spec.rs b/tests/command_begin_spec.rs index 373537e..e6fcba7 100644 --- a/tests/command_begin_spec.rs +++ b/tests/command_begin_spec.rs @@ -28,7 +28,7 @@ mod begin_command { } #[test] - fn method_begin_should_override_the_previews_value_on_consecutive_calls() { + fn method_begin_should_overrides_the_current_value_on_consecutive_calls() { let query = sql::Transaction::new() .begin("ISOLATION LEVEL SERIALIZABLE") .begin("ISOLATION LEVEL REPEATABLE READ") diff --git a/tests/command_commit_spec.rs b/tests/command_commit_spec.rs index 0a90203..d69dfe7 100644 --- a/tests/command_commit_spec.rs +++ b/tests/command_commit_spec.rs @@ -27,7 +27,7 @@ mod commit_command { } #[test] - fn method_commit_should_override_the_previews_value_on_consecutive_calls() { + fn method_commit_should_overrides_the_current_value_on_consecutive_calls() { let query = sql::Transaction::new().commit("TRANSACTION").commit("WORK").as_string(); let expected_query = "COMMIT WORK;"; diff --git a/tests/command_create_table_spec.rs b/tests/command_create_table_spec.rs index 4afb658..ef47b4e 100644 --- a/tests/command_create_table_spec.rs +++ b/tests/command_create_table_spec.rs @@ -583,7 +583,7 @@ mod method_primary_key { } #[test] - fn method_primary_key_should_overrides_the_previews_value_on_consecutive_calls() { + fn method_primary_key_should_overrides_the_current_value_on_consecutive_calls() { let query = sql::CreateTable::new() .primary_key("id") .primary_key("login") diff --git a/tests/command_end_spec.rs b/tests/command_end_spec.rs index b1c74d7..81e78d8 100644 --- a/tests/command_end_spec.rs +++ b/tests/command_end_spec.rs @@ -20,7 +20,7 @@ mod end_command { } #[test] - fn method_end_should_override_the_previews_value_on_consecutive_calls() { + fn method_end_should_overrides_the_current_value_on_consecutive_calls() { let query = sql::Transaction::new().end("TRANSACTION").end("").as_string(); let expected_query = "END;"; diff --git a/tests/command_set_transaction_spec.rs b/tests/command_set_transaction_spec.rs index 0ae469a..59961dc 100644 --- a/tests/command_set_transaction_spec.rs +++ b/tests/command_set_transaction_spec.rs @@ -28,7 +28,7 @@ mod set_transaction_command { } #[test] - fn method_set_transaction_should_override_the_previews_value_on_consecutive_calls() { + fn method_set_transaction_should_overrides_the_current_value_on_consecutive_calls() { let query = sql::Transaction::new() .set_transaction("ISOLATION LEVEL SERIALIZABLE") .set_transaction("ISOLATION LEVEL REPEATABLE READ") diff --git a/tests/command_start_transaction_spec.rs b/tests/command_start_transaction_spec.rs index 980d335..9356d89 100644 --- a/tests/command_start_transaction_spec.rs +++ b/tests/command_start_transaction_spec.rs @@ -28,7 +28,7 @@ mod start_transaction_command { } #[test] - fn method_start_transaction_should_override_the_previews_value_on_consecutive_calls() { + fn method_start_transaction_should_overrides_the_current_value_on_consecutive_calls() { let query = sql::Transaction::new() .start_transaction("ISOLATION LEVEL SERIALIZABLE") .start_transaction("ISOLATION LEVEL REPEATABLE READ") From b6f4820c512f1f3022d7eaca175bd94fd73fbbc0 Mon Sep 17 00:00:00 2001 From: Belchior Oliveira Date: Sat, 28 Sep 2024 15:01:48 -0300 Subject: [PATCH 2/4] adds CreateIndex builder --- scripts/test.sh | 11 +- scripts/watch_test.sh | 1 - src/create_index/create_index.rs | 583 ++++++++++++ src/create_index/create_index_internal.rs | 295 ++++++ src/create_index/mod.rs | 2 + src/fmt.rs | 7 +- src/lib.rs | 4 +- src/structure.rs | 72 ++ src/transaction/transaction.rs | 122 ++- tests/clause_where_spec.rs | 180 +++- tests/command_create_index_spec.rs | 1034 +++++++++++++++++++++ tests/command_transaction_spec.rs | 29 + 12 files changed, 2290 insertions(+), 50 deletions(-) create mode 100644 src/create_index/create_index.rs create mode 100644 src/create_index/create_index_internal.rs create mode 100644 src/create_index/mod.rs create mode 100644 tests/command_create_index_spec.rs diff --git a/scripts/test.sh b/scripts/test.sh index 042fca4..578a3bf 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,6 +1,11 @@ #!/bin/sh +test_names=$(git status -s | grep tests/ | sed -e 's/.* //' -e 's/tests\//--test /' -e 's/.rs//' | tr '\n' ' ') + clear -cargo test -cargo test --features postgresql -cargo test --features sqlite +cargo test $test_names +cargo test $test_names --features postgresql +cargo test $test_names --features sqlite + +# run only one test +# cargo test --features sqlite --test name_of_the_test_file name_of_the_test -- --nocapture --color always diff --git a/scripts/watch_test.sh b/scripts/watch_test.sh index cc1ae1a..c1bc033 100755 --- a/scripts/watch_test.sh +++ b/scripts/watch_test.sh @@ -20,5 +20,4 @@ esac [ ! -z "$features" ] && features="--features $features" -# cargo watch -w ./src -w ./tests -x 'test --features postgresql -- --nocapture --color always' cargo watch -w ./src -w ./tests -x "test $features $test_names" diff --git a/src/create_index/create_index.rs b/src/create_index/create_index.rs new file mode 100644 index 0000000..c5fce2d --- /dev/null +++ b/src/create_index/create_index.rs @@ -0,0 +1,583 @@ +use crate::{ + behavior::{push_unique, Concat, TransactionQuery}, + fmt, + structure::{CreateIndex, CreateIndexParams, LogicalOperator}, +}; + +impl TransactionQuery for CreateIndex {} + +impl CreateIndex { + /// Gets the current state of the [CreateIndex] and returns it as string + /// + /// ### Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .as_string(); + /// + /// # let expected = "CREATE INDEX users_name_idx"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Output + /// + /// ```sql + /// CREATE INDEX users_name_idx + /// ``` + pub fn as_string(&self) -> String { + let fmts = fmt::one_line(); + self.concat(&fmts) + } + + /// Defines the column of the table used to create the index + /// + /// ### Example + /// + ///``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .on("users") + /// .column("login") + /// .column("name") + /// .as_string(); + /// + /// # let expected = "ON users (login, name)"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// ON users (login, name) + /// ``` + pub fn column(mut self, column_name: &str) -> Self { + push_unique(&mut self._column, column_name.trim().to_string()); + self + } + + /// Defines a create index parameter, this method overrides the previous value + /// + /// ### Example + /// + ///``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .create_index("orders_product_name_idx") + /// .as_string(); + /// + /// # let expected = "CREATE INDEX orders_product_name_idx"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// CREATE INDEX orders_product_name_idx + /// ``` + pub fn create_index(mut self, index_name: &str) -> Self { + self._index_name = index_name.trim().to_string(); + self._create_index = true; + self + } + + /// Defines a create index parameter with the `if not exists` modifier, this method overrides the previous value + /// + /// ### Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .create_index_if_not_exists("orders_product_name_idx") + /// .to_string(); + /// + /// # let expected = "CREATE INDEX IF NOT EXISTS orders_product_name_idx"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// CREATE INDEX IF NOT EXISTS orders_product_name_idx + /// ``` + pub fn create_index_if_not_exists(mut self, index_name: &str) -> Self { + self._index_name = index_name.trim().to_string(); + self._create_index = true; + self._if_not_exists = true; + self + } + + /// Prints the current state of the [CreateIndex] to the standard output in a more ease to read version. + /// This method is useful to debug complex queries or just print the generated SQL while you type + /// + /// ### Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .on("users") + /// .column("name") + /// .debug() + /// .as_string(); + /// + /// # let expected = "\ + /// # CREATE INDEX users_name_idx \ + /// # ON users (name)\ + /// # "; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Prints to the standard output + /// + /// ```sql + /// -- ------------------------------------------------------------------------------ + /// CREATE INDEX users_name_idx + /// ON users + /// (name) + /// -- ------------------------------------------------------------------------------ + /// ``` + pub fn debug(self) -> Self { + let fmts = fmt::multiline(); + println!("{}", fmt::format(self.concat(&fmts), &fmts)); + self + } + + /// Creates instance of the [CreateIndex] command + pub fn new() -> Self { + Self::default() + } + + /// Defines the `on table_name` clause, this method overrides the previous value + /// + /// ### Example + /// + ///``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .on("users") + /// .on("orders") + /// .as_string(); + /// + /// # let expected = "ON orders"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// ON orders + /// ``` + pub fn on(mut self, table_name: &str) -> Self { + self._on = table_name.trim().to_string(); + self + } + + /// Prints the current state of the [CreateIndex] to the standard output similar to debug method, + /// the difference is that this method prints in one line. + pub fn print(self) -> Self { + let fmts = fmt::one_line(); + println!("{}", fmt::format(self.concat(&fmts), &fmts)); + self + } + + /// Adds at the beginning a raw SQL query. Is useful to create a more complex create index command. + /// + /// ### Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let create_index_query = sql::CreateIndex::new() + /// .raw("/* start index command */") + /// .create_index("users_name_idx") + /// .as_string(); + /// + /// # let expected = "/* start index command */ CREATE INDEX users_name_idx"; + /// # assert_eq!(expected, create_index_query); + /// # } + /// ``` + /// + /// Output + /// + /// ```sql + /// /* create index command */ CREATE INDEX users_name_idx + /// ``` + pub fn raw(mut self, raw_sql: &str) -> Self { + push_unique(&mut self._raw, raw_sql.trim().to_string()); + self + } + + /// Adds a raw SQL query after a specified parameter. + /// + /// The `CreateIndexParams::CreateIndex` works both to `.create_index` and `.create_index_if_not_exists` methods. + /// + /// ### Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let raw = "/* after create index */"; + /// + /// let query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .raw_after(sql::CreateIndexParams::CreateIndex, raw) + /// .on("users") + /// .column("name") + /// .as_string(); + /// + /// # let expected = "\ + /// # CREATE INDEX users_name_idx \ + /// # /* after create index */ \ + /// # ON users (name)\ + /// # "; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Output + /// + /// ```sql + /// CREATE INDEX users_name_idx + /// /* after create index */ + /// ON users (name) + /// ``` + pub fn raw_after(mut self, param: CreateIndexParams, raw_sql: &str) -> Self { + self._raw_after.push((param, raw_sql.trim().to_string())); + self + } + + /// Adds a raw SQL query before a specified parameter. + /// + /// The `CreateIndexParams::CreateIndex` works both to `.create_index` and `.create_index_if_not_exists` methods. + /// + /// ### Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let raw = "/* before create index */"; + /// + /// let query = sql::CreateIndex::new() + /// .raw_before(sql::CreateIndexParams::CreateIndex, raw) + /// .create_index("users_name_idx") + /// .on("users") + /// .column("name") + /// .as_string(); + /// + /// # let expected = "\ + /// # /* before create index */ \ + /// # CREATE INDEX users_name_idx \ + /// # ON users (name)\ + /// # "; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Output + /// + /// ```sql + /// /* before create index */ + /// CREATE INDEX users_name_idx + /// ON users (name) + /// ``` + pub fn raw_before(mut self, param: CreateIndexParams, raw_sql: &str) -> Self { + self._raw_before.push((param, raw_sql.trim().to_string())); + self + } + + /// Defines the `unique` parameter + /// + /// ### Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .unique() + /// .to_string(); + /// + /// # let expected = "CREATE UNIQUE INDEX users_name_idx"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// CREATE UNIQUE INDEX users_name_idx + /// ``` + pub fn unique(mut self) -> Self { + self._unique = true; + self + } + + /// The method will concatenate multiples calls using the `and` operator. This method is un alias of `where_clause`. + /// + /// # Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let select_query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .on("users") + /// .column("name") + /// .where_and("created_at >= $1") + /// .as_string(); + /// + /// # let expected = "\ + /// # CREATE INDEX users_name_idx \ + /// # ON users (name) \ + /// # WHERE created_at >= $1\ + /// # "; + /// # assert_eq!(select_query, expected); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// CREATE INDEX users_name_idx + /// ON users (name) + /// WHERE created_at >= $1 + /// ``` + pub fn where_and(self, condition: &str) -> Self { + self.where_clause(condition) + } + + /// The `where` clause, this method will concatenate multiples calls using the `and` operator. + /// If you intended to use the `or` operator you should use the [where_or](CreateIndex::where_or) method + /// + /// # Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let select_query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .on("users") + /// .column("name") + /// .where_clause("status = 'active'") + /// .as_string(); + /// + /// # let expected = "\ + /// # CREATE INDEX users_name_idx \ + /// # ON users (name) \ + /// # WHERE status = 'active'\ + /// # "; + /// # assert_eq!(select_query, expected); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// CREATE INDEX users_name_idx + /// ON users (name) + /// WHERE status = 'active' + /// ``` + pub fn where_clause(mut self, condition: &str) -> Self { + push_unique(&mut self._where, (LogicalOperator::And, condition.trim().to_string())); + self + } + + /// The `where` clause that concatenate multiples calls using the OR operator. + /// If you intended to use the `and` operator you should use the [where_clause](CreateIndex::where_clause) method + /// + /// # Example + /// + /// ``` + /// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let select_query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .on("users") + /// .column("name") + /// .where_clause("created_at >= $1") + /// .where_or("status = 'active'") + /// .as_string(); + /// + /// # let expected = "\ + /// # CREATE INDEX users_name_idx \ + /// # ON users (name) \ + /// # WHERE \ + /// # created_at >= $1 \ + /// # OR status = 'active'\ + /// # "; + /// # assert_eq!(select_query, expected); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// CREATE INDEX users_name_idx + /// ON users (name) + /// WHERE + /// created_at >= $1 + /// OR status = 'active' + /// ``` + pub fn where_or(mut self, condition: &str) -> Self { + push_unique(&mut self._where, (LogicalOperator::Or, condition.trim().to_string())); + self + } +} + +#[cfg(any(doc, feature = "postgresql"))] +#[cfg_attr(docsrs, doc(cfg(feature = "postgresql")))] +impl CreateIndex { + /// Defines the `concurrently` parameter + /// + /// ### Example + /// + /// ``` + /// # #[cfg(feature = "postgresql")] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .concurrently() + /// .to_string(); + /// + /// # let expected = "CREATE INDEX CONCURRENTLY users_name_idx"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// CREATE INDEX CONCURRENTLY users_name_idx + /// ``` + pub fn concurrently(mut self) -> Self { + self._concurrently = true; + self + } + + /// Defines the include parameter + /// + /// ### Example + /// + ///``` + /// # #[cfg(feature = "postgresql")] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .include("login") + /// .include("name") + /// .as_string(); + /// + /// # let expected = "INCLUDE (login, name)"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// INCLUDE (login, name) + /// ``` + pub fn include(mut self, column_name: &str) -> Self { + push_unique(&mut self._include, column_name.trim().to_string()); + self + } + + /// Defines the `only` parameter + /// + /// ### Example + /// + /// ``` + /// # #[cfg(feature = "postgresql")] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .on("users") + /// .only() + /// .to_string(); + /// + /// # let expected = "ON ONLY users"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// ON ONLY users + /// ``` + pub fn only(mut self) -> Self { + self._only = true; + self + } + + /// Defines the index method to be used to create the index, this method overrides the previous value + /// + /// ### Example + /// + ///``` + /// # #[cfg(feature = "postgresql")] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::CreateIndex::new() + /// .using("btree") + /// .using("gist") + /// .as_string(); + /// + /// # let expected = "USING gist"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Outputs + /// + /// ```sql + /// USING gist + /// ``` + pub fn using(mut self, index_method: &str) -> Self { + self._using = index_method.trim().to_string(); + self + } +} + +impl std::fmt::Display for CreateIndex { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.as_string()) + } +} + +impl std::fmt::Debug for CreateIndex { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let fmts = fmt::multiline(); + write!(f, "{}", fmt::format(self.concat(&fmts), &fmts)) + } +} diff --git a/src/create_index/create_index_internal.rs b/src/create_index/create_index_internal.rs new file mode 100644 index 0000000..4381360 --- /dev/null +++ b/src/create_index/create_index_internal.rs @@ -0,0 +1,295 @@ +use crate::{ + behavior::{concat_raw_before_after, Concat, ConcatSqlStandard}, + fmt, + structure::{CreateIndex, CreateIndexParams}, +}; + +impl ConcatSqlStandard for CreateIndex {} + +impl Concat for CreateIndex { + fn concat(&self, fmts: &fmt::Formatter) -> String { + let mut query = "".to_string(); + + query = self.concat_raw(query, &fmts, &self._raw); + + #[cfg(feature = "postgresql")] + { + query = self.concat_create_index_postgres(query, &fmts); + query = self.concat_on_postgres(query, &fmts); + query = self.concat_using(query, &fmts); + } + + #[cfg(feature = "sqlite")] + { + query = self.concat_create_index_sqlite(query, &fmts); + query = self.concat_on_sqlite(query, &fmts); + } + + query = self.concat_column(query, &fmts); + + #[cfg(feature = "postgresql")] + { + query = self.concat_include(query, &fmts); + } + + query = self.concat_where( + &self._raw_before, + &self._raw_after, + query, + &fmts, + CreateIndexParams::Where, + &self._where, + ); + + query.trim_end().to_string() + } +} + +impl CreateIndex { + fn concat_column(&self, query: String, fmts: &fmt::Formatter) -> String { + let fmt::Formatter { lb, comma, space, .. } = fmts; + + let sql = if self._column.is_empty() == false { + let column_names = self + ._column + .iter() + .filter(|column| column.is_empty() == false) + .map(|column| column.as_str()) + .collect::>() + .join(comma); + + if column_names.is_empty() == false { + format!("({column_names}){space}{lb}") + } else { + "".to_string() + } + } else { + "".to_string() + }; + + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + query, + fmts, + CreateIndexParams::Column, + sql, + ) + } +} + +#[cfg(feature = "postgresql")] +impl CreateIndex { + fn concat_create_index_postgres(&self, query: String, fmts: &fmt::Formatter) -> String { + let fmt::Formatter { lb, space, .. } = fmts; + + let unique = if self._unique { + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + "".to_string(), + fmts, + CreateIndexParams::Unique, + format!("UNIQUE{space}"), + ) + } else { + "".to_string() + }; + + let if_not_exists = if self._if_not_exists { + format!("IF NOT EXISTS{space}") + } else { + "".to_string() + }; + + let index_name = if self._index_name.is_empty() == false { + format!("{}{space}", &self._index_name) + } else { + "".to_string() + }; + + let concurrently = if self._concurrently { + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + "".to_string(), + fmts, + CreateIndexParams::Concurrently, + format!("CONCURRENTLY{space}"), + ) + } else { + "".to_string() + }; + + let modifiers_not_called = self._create_index == false && unique.is_empty() && concurrently.is_empty(); + let if_not_exists_without_index_name = self._if_not_exists && index_name.is_empty(); + + let sql = if modifiers_not_called || if_not_exists_without_index_name { + "".to_string() + } else { + format!("CREATE{space}{unique}INDEX{space}{concurrently}{if_not_exists}{index_name}{lb}") + }; + + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + query, + fmts, + CreateIndexParams::CreateIndex, + sql, + ) + } + + fn concat_include(&self, query: String, fmts: &fmt::Formatter) -> String { + let fmt::Formatter { comma, lb, space, .. } = fmts; + + let sql = if self._include.is_empty() == false { + let column_names = self + ._include + .iter() + .filter(|column| column.is_empty() == false) + .map(|column| column.as_str()) + .collect::>() + .join(comma); + + if column_names.is_empty() == false { + format!("INCLUDE{space}({column_names}){space}{lb}") + } else { + "".to_string() + } + } else { + "".to_string() + }; + + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + query, + fmts, + CreateIndexParams::Include, + sql, + ) + } + + fn concat_on_postgres(&self, query: String, fmts: &fmt::Formatter) -> String { + let fmt::Formatter { space, .. } = fmts; + + let sql = if self._on.is_empty() == false { + let table_name = &self._on; + + let only = if self._only { + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + "".to_string(), + fmts, + CreateIndexParams::Only, + format!("ONLY{space}"), + ) + } else { + "".to_string() + }; + + format!("ON{space}{only}{table_name}{space}") + } else { + "".to_string() + }; + + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + query, + fmts, + CreateIndexParams::On, + sql, + ) + } + + fn concat_using(&self, query: String, fmts: &fmt::Formatter) -> String { + let fmt::Formatter { space, .. } = fmts; + + let sql = if self._using.is_empty() == false { + let index_method = &self._using; + format!("USING{space}{index_method}{space}") + } else { + "".to_string() + }; + + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + query, + fmts, + CreateIndexParams::Using, + sql, + ) + } +} + +#[cfg(feature = "sqlite")] +impl CreateIndex { + fn concat_create_index_sqlite(&self, query: String, fmts: &fmt::Formatter) -> String { + let fmt::Formatter { lb, space, .. } = fmts; + + let unique = if self._unique { + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + "".to_string(), + fmts, + CreateIndexParams::Unique, + format!("UNIQUE{space}"), + ) + } else { + "".to_string() + }; + + let if_not_exists = if self._if_not_exists { + format!("IF NOT EXISTS{space}") + } else { + "".to_string() + }; + + let index_name = if self._index_name.is_empty() == false { + format!("{}{space}", &self._index_name) + } else { + "".to_string() + }; + + let sql = if index_name.is_empty() == false { + format!("CREATE{space}{unique}INDEX{space}{if_not_exists}{index_name}{lb}") + } else { + "".to_string() + }; + + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + query, + fmts, + CreateIndexParams::CreateIndex, + sql, + ) + } + + fn concat_on_sqlite(&self, query: String, fmts: &fmt::Formatter) -> String { + let fmt::Formatter { space, .. } = fmts; + + let sql = if self._on.is_empty() == false { + let table_name = &self._on; + + format!("ON{space}{table_name}{space}") + } else { + "".to_string() + }; + + concat_raw_before_after( + &self._raw_before, + &self._raw_after, + query, + fmts, + CreateIndexParams::On, + sql, + ) + } +} diff --git a/src/create_index/mod.rs b/src/create_index/mod.rs new file mode 100644 index 0000000..056eabf --- /dev/null +++ b/src/create_index/mod.rs @@ -0,0 +1,2 @@ +mod create_index; +mod create_index_internal; diff --git a/src/fmt.rs b/src/fmt.rs index 3720fb7..100364e 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -33,6 +33,7 @@ pub fn colorize(query: String) -> String { (blue, "COLUMN", "column"), (blue, "COMMIT", "commit"), (blue, "COMMITTED", "committed"), + (blue, "CONCURRENTLY", "concurrently"), (blue, "CONFLICT", "conflict"), (blue, "CONSTRAINT", "constraint"), (blue, "CREATE ", "create "), @@ -46,6 +47,7 @@ pub fn colorize(query: String) -> String { (blue, "FROM ", "from "), (blue, "GROUP BY", "group by"), (blue, "HAVING", "having"), + (blue, "INCLUDE", "include"), (blue, "INDEX", "index"), (blue, "INNER", "inner"), (blue, "INSERT", "insert"), @@ -56,6 +58,8 @@ pub fn colorize(query: String) -> String { (blue, "LIMIT ", "limit "), (blue, "NOTHING", "nothing"), (blue, "OFFSET", "offset"), + (blue, "ON ", "on "), + (blue, "ONLY ", "only "), (blue, "ORDER BY", "order by"), (blue, "OVERRIDING", "overriding"), (blue, "PRIMARY", "primary"), @@ -76,7 +80,9 @@ pub fn colorize(query: String) -> String { (blue, "TRANSACTION", "transaction"), (blue, "UNCOMMITTED", "uncommitted"), (blue, "UNION ", "union "), + (blue, "UNIQUE ", "unique "), (blue, "UPDATE ", "update "), + (blue, "USING ", "using "), (blue, "VALUES ", "values "), (blue, "WHERE ", "where "), (blue, "WITH ", "with "), @@ -97,7 +103,6 @@ pub fn colorize(query: String) -> String { (blue, " LAST", " last"), (blue, " LEVEL", " level"), (blue, " NOT", " not"), - (blue, " ON ", " on "), (blue, " OR ", " or "), (blue, " OUTER", " OUTER"), (blue, " UNIQUE", " unique"), diff --git a/src/lib.rs b/src/lib.rs index 70116d5..3457a20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,8 +19,10 @@ pub use crate::structure::{ Insert, InsertClause, Select, SelectClause, Transaction, Update, UpdateClause, Values, ValuesClause, }; +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +mod create_index; #[cfg(any(feature = "postgresql", feature = "sqlite"))] mod drop_index; #[cfg(any(feature = "postgresql", feature = "sqlite"))] -pub use crate::structure::{DropIndex, DropIndexParams}; +pub use crate::structure::{CreateIndex, CreateIndexParams, DropIndex, DropIndexParams}; diff --git a/src/structure.rs b/src/structure.rs index d316be4..b007b21 100644 --- a/src/structure.rs +++ b/src/structure.rs @@ -80,6 +80,78 @@ pub(crate) enum Combinator { Union, } +/// Builder to contruct a [CreateIndex] command. Available only for the crate features `postgresql` and `sqlite` +/// +/// Basic API +/// ``` +/// # #[cfg(any(feature = "postgresql", feature = "sqlite"))] +/// # { +/// use sql_query_builder as sql; +/// +/// let query = sql::CreateIndex::new() +/// .create_index("users_name_idx") +/// .on("users") +/// .column("name") +/// .as_string(); +/// +/// # let expected = "CREATE INDEX users_name_idx ON users (name)"; +/// # assert_eq!(expected, query); +/// # } +/// ``` +/// +/// Output +/// +/// ```sql +/// CREATE INDEX users_name_idx ON users (name) +/// ``` +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +#[derive(Default, Clone)] +pub struct CreateIndex { + pub(crate) _column: Vec, + pub(crate) _index_name: String, + pub(crate) _create_index: bool, + pub(crate) _if_not_exists: bool, + pub(crate) _on: String, + pub(crate) _raw_after: Vec<(CreateIndexParams, String)>, + pub(crate) _raw_before: Vec<(CreateIndexParams, String)>, + pub(crate) _raw: Vec, + pub(crate) _unique: bool, + + #[cfg(any(feature = "postgresql", feature = "sqlite"))] + pub(crate) _where: Vec<(LogicalOperator, String)>, + + #[cfg(feature = "postgresql")] + pub(crate) _concurrently: bool, + #[cfg(feature = "postgresql")] + pub(crate) _include: Vec, + #[cfg(feature = "postgresql")] + pub(crate) _only: bool, + #[cfg(feature = "postgresql")] + pub(crate) _using: String, +} + +/// All available params to be used in [CreateIndex::raw_before] and [CreateIndex::raw_after] methods on [CreateIndex] builder +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +#[derive(PartialEq, Clone)] +pub enum CreateIndexParams { + Column, + CreateIndex, + On, + Unique, + + #[cfg(any(feature = "postgresql", feature = "sqlite"))] + Where, + + #[cfg(feature = "postgresql")] + Concurrently, + #[cfg(feature = "postgresql")] + Only, + #[cfg(feature = "postgresql")] + Using, + #[cfg(feature = "postgresql")] + Include, +} + /// Builder to contruct a [CreateTable] command /// /// Basic API diff --git a/src/transaction/transaction.rs b/src/transaction/transaction.rs index 409da11..7ba5543 100644 --- a/src/transaction/transaction.rs +++ b/src/transaction/transaction.rs @@ -7,7 +7,7 @@ use crate::{ }; #[cfg(any(feature = "postgresql", feature = "sqlite"))] -use crate::structure::DropIndex; +use crate::structure::{CreateIndex, DropIndex}; impl Transaction { /// Gets the current state of the [Transaction] and returns it as string @@ -230,46 +230,6 @@ impl Transaction { self } - /// The `drop index` command, access the [DropIndex] for more info - /// - /// # Example - /// - /// ``` - /// # #[cfg(not(feature = "sqlite"))] - /// # { - /// # use sql_query_builder as sql; - /// let drop_index_name = sql::DropIndex::new() - /// .drop_index("users_name_idx"); - /// - /// let query = sql::Transaction::new() - /// .start_transaction("") - /// .drop_index(drop_index_name) - /// .commit("") - /// .as_string(); - /// - /// # let expected = "\ - /// # START TRANSACTION; \ - /// # DROP INDEX users_name_idx; \ - /// # COMMIT;\ - /// # "; - /// # assert_eq!(expected, query); - /// # } - /// ``` - /// - /// Output (indented for readability) - /// - /// ```sql - /// START TRANSACTION; - /// DROP INDEX users_name_idx; - /// COMMIT; - /// ``` - #[cfg(any(feature = "postgresql", feature = "sqlite"))] - pub fn drop_index(mut self, drop_index: DropIndex) -> Self { - let cmd = Box::new(drop_index); - self._ordered_commands.push(cmd); - self - } - /// The `drop table` command, access the [DropTable] for more info /// /// # Example @@ -670,6 +630,86 @@ impl Transaction { self } + /// The `create index` command, access the [CreateIndex] for more info + /// + /// # Example + /// + /// ``` + /// # #[cfg(not(feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let create_index_name = sql::CreateIndex::new() + /// .create_index("users_name_idx") + /// .on("users") + /// .column("name"); + /// + /// let query = sql::Transaction::new() + /// .start_transaction("") + /// .create_index(create_index_name) + /// .commit("") + /// .as_string(); + /// + /// # let expected = "\ + /// # START TRANSACTION; \ + /// # CREATE INDEX users_name_idx ON users (name); \ + /// # COMMIT;\ + /// # "; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Output (indented for readability) + /// + /// ```sql + /// START TRANSACTION; + /// CREATE INDEX users_name_idx ON users (name); + /// COMMIT; + /// ``` + pub fn create_index(mut self, create_index: CreateIndex) -> Self { + let cmd = Box::new(create_index); + self._ordered_commands.push(cmd); + self + } + + /// The `drop index` command, access the [DropIndex] for more info + /// + /// # Example + /// + /// ``` + /// # #[cfg(not(feature = "sqlite"))] + /// # { + /// # use sql_query_builder as sql; + /// let drop_index_name = sql::DropIndex::new() + /// .drop_index("users_name_idx"); + /// + /// let query = sql::Transaction::new() + /// .start_transaction("") + /// .drop_index(drop_index_name) + /// .commit("") + /// .as_string(); + /// + /// # let expected = "\ + /// # START TRANSACTION; \ + /// # DROP INDEX users_name_idx; \ + /// # COMMIT;\ + /// # "; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Output (indented for readability) + /// + /// ```sql + /// START TRANSACTION; + /// DROP INDEX users_name_idx; + /// COMMIT; + /// ``` + pub fn drop_index(mut self, drop_index: DropIndex) -> Self { + let cmd = Box::new(drop_index); + self._ordered_commands.push(cmd); + self + } + /// 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/tests/clause_where_spec.rs b/tests/clause_where_spec.rs index 032e7fe..8b34fd5 100644 --- a/tests/clause_where_spec.rs +++ b/tests/clause_where_spec.rs @@ -1,4 +1,85 @@ mod where_clause { + #[cfg(any(feature = "postgresql", feature = "sqlite"))] + mod create_index_command { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_where_clause_should_add_the_where_clause() { + let query = sql::CreateIndex::new().where_clause("created_at >= $1").as_string(); + let expected_query = "WHERE created_at >= $1"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_where_clause_should_omit_the_operation_when_was_the_first_clause() { + let query = sql::CreateIndex::new().where_clause("status = 'active'").as_string(); + let expected_query = "WHERE status = 'active'"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_where_clause_should_accumulate_values_on_consecutive_calls_using_the_and_operator() { + let query = sql::CreateIndex::new() + .where_clause("created_at >= $1") + .where_clause("status = 'active'") + .as_string(); + + let expected_query = "\ + WHERE \ + created_at >= $1 \ + AND status = 'active'\ + "; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_where_clause_should_not_accumulate_arguments_with_the_same_content() { + let query = sql::CreateIndex::new() + .where_clause("status = 'active'") + .where_clause("status = 'active'") + .as_string(); + let expected_query = "WHERE status = 'active'"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_where_clause_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new() + .where_clause(" status = 'active' ") + .as_string(); + let expected_query = "WHERE status = 'active'"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_where_clause() { + let query = sql::CreateIndex::new() + .raw_before(sql::CreateIndexParams::Where, "/* uncommon parameter */") + .where_clause("status = 'active'") + .as_string(); + let expected_query = "/* uncommon parameter */ WHERE status = 'active'"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_where_clause() { + let query = sql::CreateIndex::new() + .where_clause("created_at >= $1") + .raw_after(sql::CreateIndexParams::Where, "and created_at < $2") + .as_string(); + let expected_query = "WHERE created_at >= $1 and created_at < $2"; + + assert_eq!(query, expected_query); + } + } + mod delete_command { use pretty_assertions::assert_eq; use sql_query_builder as sql; @@ -36,7 +117,7 @@ mod where_clause { } #[test] - fn method_where_clause_clause_should_not_accumulate_arguments_with_the_same_content() { + fn method_where_clause_should_not_accumulate_arguments_with_the_same_content() { let query = sql::Delete::new() .where_clause("id = $1") .where_clause("id = $1") @@ -271,7 +352,21 @@ mod where_clause { } mod where_and { - mod delete_clause { + #[cfg(any(feature = "postgresql", feature = "sqlite"))] + mod create_index_command { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_where_and_should_be_an_alias_of_where_clause() { + let query = sql::CreateIndex::new().where_and("created_at >= $1").as_string(); + let expected_query = "WHERE created_at >= $1"; + + assert_eq!(query, expected_query); + } + } + + mod delete_command { use pretty_assertions::assert_eq; use sql_query_builder as sql; @@ -312,7 +407,86 @@ mod where_and { } mod where_or { - mod delete_clause { + #[cfg(any(feature = "postgresql", feature = "sqlite"))] + mod create_index_command { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_where_or_should_add_the_where_clause() { + let query = sql::CreateIndex::new().where_or("created_at >= $1").as_string(); + let expected_query = "WHERE created_at >= $1"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_where_or_should_omit_the_operation_when_was_the_first_clause() { + let query = sql::CreateIndex::new().where_or("created_at >= $1").as_string(); + let expected_query = "WHERE created_at >= $1"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_where_or_should_accumulate_values_on_consecutive_calls_using_the_or_operator() { + let query = sql::CreateIndex::new() + .where_or("created_at >= $1") + .where_or("status = 'active'") + .as_string(); + + let expected_query = "\ + WHERE \ + created_at >= $1 \ + OR status = 'active'\ + "; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_where_or_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new().where_or(" status = 'active' ").as_string(); + let expected_query = "WHERE status = 'active'"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_where_or_should_not_accumulate_arguments_with_the_same_content() { + let query = sql::CreateIndex::new() + .where_or("status = 'active'") + .where_or("status = 'active'") + .as_string(); + let expected_query = "WHERE status = 'active'"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_where_clause() { + let query = sql::CreateIndex::new() + .raw_before(sql::CreateIndexParams::Where, "/* uncommon parameter */") + .where_or("status = 'active'") + .as_string(); + let expected_query = "/* uncommon parameter */ WHERE status = 'active'"; + + assert_eq!(query, expected_query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_where_clause() { + let query = sql::CreateIndex::new() + .where_or("status = 'active'") + .raw_after(sql::CreateIndexParams::Where, "/* uncommon parameter */") + .as_string(); + let expected_query = "WHERE status = 'active' /* uncommon parameter */"; + + assert_eq!(query, expected_query); + } + } + + mod delete_command { use pretty_assertions::assert_eq; use sql_query_builder as sql; diff --git a/tests/command_create_index_spec.rs b/tests/command_create_index_spec.rs new file mode 100644 index 0000000..64f328d --- /dev/null +++ b/tests/command_create_index_spec.rs @@ -0,0 +1,1034 @@ +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +mod full_api { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[cfg(feature = "postgresql")] + #[test] + fn postgres_with_all_methods() { + let query = sql::CreateIndex::new() + // at least one of methods + .create_index("users_name_idx") + .create_index_if_not_exists("users_name_idx") + .unique() + .concurrently() + // required methods + .on("users") + .column("name") + // optional methods + .only() + .using("btree") + .include("last_name") + .where_clause("created_at >= $1") + .where_and("created_at < $2") + .where_or("status = 'active'") + .as_string(); + + let expected_query = "\ + CREATE UNIQUE INDEX \ + CONCURRENTLY \ + IF NOT EXISTS users_name_idx \ + ON ONLY users \ + USING btree \ + (name) \ + INCLUDE (last_name) \ + WHERE \ + created_at >= $1 \ + AND created_at < $2 \ + OR status = 'active'\ + "; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "sqlite")] + #[test] + fn sqlite_with_all_methods() { + let query = sql::CreateIndex::new() + // at least one of methods + .create_index("users_name_idx") + .create_index_if_not_exists("users_name_idx") + // required methods + .on("users") + .column("name") + // optional methods + .unique() + .where_clause("created_at >= $1") + .where_and("created_at < $2") + .where_or("status = 'active'") + .as_string(); + + let expected_query = "\ + CREATE UNIQUE INDEX \ + IF NOT EXISTS users_name_idx \ + ON users \ + (name) \ + WHERE \ + created_at >= $1 \ + AND created_at < $2 \ + OR status = 'active'\ + "; + + assert_eq!(expected_query, query); + } +} + +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +mod builder_features { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn create_index_builder_should_be_displayable() { + let create_index = sql::CreateIndex::new().create_index("orders_product_name_idx"); + + println!("{create_index}"); + + let query = create_index.as_string(); + let expected_query = "CREATE INDEX orders_product_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn create_index_builder_should_be_debuggable() { + let create_index = sql::CreateIndex::new().create_index("orders_product_name_idx"); + + println!("{create_index:?}"); + + let expected_query = "CREATE INDEX orders_product_name_idx"; + let query = create_index.as_string(); + + assert_eq!(expected_query, query); + } + + #[test] + fn create_index_builder_should_be_cloneable() { + let login_index = sql::CreateIndex::new().create_index("users_login_idx"); + + let product_name_index = login_index + .clone() + .create_index_if_not_exists("orders_product_name_idx"); + + let expected_login_index = "CREATE INDEX users_login_idx"; + let expected_product_name_index = "CREATE INDEX IF NOT EXISTS orders_product_name_idx"; + + assert_eq!(expected_login_index, login_index.as_string()); + assert_eq!(expected_product_name_index, product_name_index.as_string()); + } + + #[test] + fn create_index_builder_should_be_able_to_conditionally_add_clauses() { + let mut create_index = sql::CreateIndex::new().create_index("orders_product_name_idx"); + + if true { + create_index = create_index.create_index_if_not_exists("users_login_idx"); + } + + let query = create_index.as_string(); + let expected_query = "CREATE INDEX IF NOT EXISTS users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn create_index_builder_should_be_composable() { + fn create_index(select: sql::CreateIndex) -> sql::CreateIndex { + select.create_index("orders_product_name_idx") + } + + fn create_index_if_not_exists(select: sql::CreateIndex) -> sql::CreateIndex { + select.create_index_if_not_exists("users_login_idx") + } + + fn as_string(select: sql::CreateIndex) -> String { + select.as_string() + } + + let query = Some(sql::CreateIndex::new()) + .map(create_index) + .map(create_index_if_not_exists) + .map(as_string) + .unwrap(); + + let expected_query = "CREATE INDEX IF NOT EXISTS users_login_idx"; + + assert_eq!(expected_query, query); + } +} + +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +mod builder_methods { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_new_should_initialize_as_empty_string() { + let query = sql::CreateIndex::new().as_string(); + let expected_query = ""; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_as_string_should_convert_the_current_state_into_string() { + let query = sql::CreateIndex::new().as_string(); + let expected_query = ""; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_debug_should_print_at_console_in_a_human_readable_format() { + let query = sql::CreateIndex::new() + .create_index_if_not_exists("users_login_idx") + .on("users") + .column("login") + .debug() + .as_string(); + + let expected_query = "CREATE INDEX IF NOT EXISTS users_login_idx ON users (login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_print_should_print_in_one_line_the_current_state_of_builder() { + let query = sql::CreateIndex::new() + .create_index_if_not_exists("users_login_idx") + .on("users") + .column("login") + .print() + .as_string(); + + let expected_query = "CREATE INDEX IF NOT EXISTS users_login_idx ON users (login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_should_add_raw_sql() { + let query = sql::CreateIndex::new().raw("create index on users (name)").as_string(); + + let expected_query = "create index on users (name)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_should_accumulate_values_on_consecutive_calls() { + let query = sql::CreateIndex::new() + .raw("create index") + .raw("on users") + .raw("(name)") + .as_string(); + + let expected_query = "create index on users (name)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_should_be_the_first_to_be_concatenated() { + let query = sql::CreateIndex::new() + .raw("/* create index command */") + .create_index("users_login_idx") + .as_string(); + + let expected_query = "/* create index command */ CREATE INDEX users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_should_not_accumulate_arguments_with_the_same_content() { + let query = sql::CreateIndex::new() + .raw("create index") + .raw("create index") + .as_string(); + + let expected_query = "create index"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new() + .raw_after(sql::CreateIndexParams::CreateIndex, " /* command */ ") + .as_string(); + let expected_query = "/* command */"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new() + .raw_before(sql::CreateIndexParams::CreateIndex, " on users (name) ") + .as_string(); + let expected_query = "on users (name)"; + + assert_eq!(expected_query, query); + } +} + +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +mod method_column { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_column_should_define_the_column_of_the_table() { + let query = sql::CreateIndex::new().column("login").as_string(); + let expected_query = "(login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_column_should_not_add_the_column_part_when_has_no_column_name_at_list() { + let query = sql::CreateIndex::new().column("").column("").as_string(); + let expected_query = ""; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_column_should_not_add_column_when_the_column_name_is_empty() { + let query = sql::CreateIndex::new() + .column("") + .column("login") + .column("") + .as_string(); + let expected_query = "(login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_column_should_accumulate_column_names_on_consecutive_calls() { + let query = sql::CreateIndex::new().column("login").column("name").as_string(); + + let expected_query = "(login, name)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_column_should_not_accumulate_parameters_with_the_same_content() { + let query = sql::CreateIndex::new().column("login").column("login").as_string(); + let expected_query = "(login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_column_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new().column(" login ").as_string(); + let expected_query = "(login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_column_parameter() { + let query = sql::CreateIndex::new() + .column("name") + .raw_after(sql::CreateIndexParams::Column, "/* end command */") + .as_string(); + + let expected_query = "(name) /* end command */"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_column_parameter() { + let query = sql::CreateIndex::new() + .raw_before(sql::CreateIndexParams::Column, "on users") + .column("name") + .as_string(); + + let expected_query = "on users (name)"; + + assert_eq!(expected_query, query); + } +} + +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +mod method_create_index { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_create_index_should_define_a_create_index_parameter() { + let query = sql::CreateIndex::new().create_index("users_login_idx").as_string(); + let expected_query = "CREATE INDEX users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_create_index_should_overrides_the_current_value_on_consecutive_calls() { + let query = sql::CreateIndex::new() + .create_index("users_login_idx") + .create_index("orders_product_name_idx") + .as_string(); + + let expected_query = "CREATE INDEX orders_product_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_create_index_should_not_accumulate_parameters_with_the_same_content() { + let query = sql::CreateIndex::new() + .create_index("orders_product_name_idx") + .create_index("orders_product_name_idx") + .as_string(); + let expected_query = "CREATE INDEX orders_product_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_create_index_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new().create_index(" users_login_idx ").as_string(); + let expected_query = "CREATE INDEX users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_create_index_parameter() { + let query = sql::CreateIndex::new() + .create_index("users_login_idx") + .raw_after(sql::CreateIndexParams::CreateIndex, "/* end command */") + .as_string(); + + let expected_query = "CREATE INDEX users_login_idx /* end command */"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_create_index_parameter() { + let query = sql::CreateIndex::new() + .raw_before(sql::CreateIndexParams::CreateIndex, "/* start command */") + .create_index("users_name_idx") + .as_string(); + + let expected_query = "/* start command */ CREATE INDEX users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "postgresql")] + #[test] + fn method_create_index_should_define_the_parameter_without_specify_the_name_of_the_index() { + let query = sql::CreateIndex::new().create_index("").as_string(); + let expected_query = "CREATE INDEX"; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "sqlite")] + #[test] + fn method_create_index_should_define_the_parameter_only_with_name_of_the_index() { + let query = sql::CreateIndex::new().create_index("").as_string(); + let expected_query = ""; + + assert_eq!(expected_query, query); + } +} + +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +mod method_create_index_if_not_exists { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_create_index_if_not_exists_should_define_a_create_index_parameter() { + let query = sql::CreateIndex::new() + .create_index_if_not_exists("users_login_idx") + .as_string(); + let expected_query = "CREATE INDEX IF NOT EXISTS users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_create_index_if_not_exists_should_overrides_the_current_value_on_consecutive_calls() { + let query = sql::CreateIndex::new() + .create_index("users_name_idx") + .create_index_if_not_exists("users_login_idx") + .create_index_if_not_exists("orders_product_name_idx") + .as_string(); + + let expected_query = "CREATE INDEX IF NOT EXISTS orders_product_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_create_index_if_not_exists_should_not_accumulate_parameters_with_the_same_content() { + let query = sql::CreateIndex::new() + .create_index_if_not_exists("orders_product_name_idx") + .create_index_if_not_exists("orders_product_name_idx") + .as_string(); + let expected_query = "CREATE INDEX IF NOT EXISTS orders_product_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_create_index_if_not_exists_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new() + .create_index_if_not_exists(" users_login_idx ") + .as_string(); + let expected_query = "CREATE INDEX IF NOT EXISTS users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_create_index_if_not_exists_parameter() { + let query = sql::CreateIndex::new() + .create_index_if_not_exists("users_login_idx") + .raw_after(sql::CreateIndexParams::CreateIndex, "/* end command */") + .as_string(); + + let expected_query = "CREATE INDEX IF NOT EXISTS users_login_idx /* end command */"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_create_index_if_not_exists_parameter() { + let query = sql::CreateIndex::new() + .raw_before(sql::CreateIndexParams::CreateIndex, "/* start command */") + .create_index_if_not_exists("users_name_idx") + .as_string(); + + let expected_query = "/* start command */ CREATE INDEX IF NOT EXISTS users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "postgresql")] + #[test] + fn method_create_index_if_not_exists_should_define_the_parameter_only_with_the_name_of_the_index_postgres() { + let query = sql::CreateIndex::new().create_index_if_not_exists("").as_string(); + let expected_query = ""; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "sqlite")] + #[test] + fn method_create_index_if_not_exists_should_define_the_parameter_only_with_the_name_of_the_index_sqlite() { + let query = sql::CreateIndex::new().create_index_if_not_exists("").as_string(); + let expected_query = ""; + + assert_eq!(expected_query, query); + } +} + +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +mod method_on { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_on_should_define_a_on_parameter() { + let query = sql::CreateIndex::new().on("users").as_string(); + let expected_query = "ON users"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_on_should_overrides_the_current_value_on_consecutive_calls() { + let query = sql::CreateIndex::new().on("users").on("orders").as_string(); + + let expected_query = "ON orders"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_on_should_not_accumulate_parameters_with_the_same_content() { + let query = sql::CreateIndex::new().on("users").on("users").as_string(); + let expected_query = "ON users"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_on_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new().on(" users ").as_string(); + let expected_query = "ON users"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_on_parameter() { + let query = sql::CreateIndex::new() + .on("users") + .raw_after(sql::CreateIndexParams::On, "(login)") + .as_string(); + + let expected_query = "ON users (login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_on_parameter() { + let query = sql::CreateIndex::new() + .raw_before(sql::CreateIndexParams::On, "create index users_name_idx") + .on("users") + .as_string(); + + let expected_query = "create index users_name_idx ON users"; + + assert_eq!(expected_query, query); + } +} + +#[cfg(any(feature = "postgresql", feature = "sqlite"))] +mod method_unique { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_unique_should_define_a_create_index_parameter_with_the_modifier_unique() { + let query = sql::CreateIndex::new() + .create_index("users_login_idx") + .unique() + .as_string(); + let expected_query = "CREATE UNIQUE INDEX users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_unique_should_define_a_create_index_if_not_exists_parameter_with_the_modifier_unique() { + let query = sql::CreateIndex::new() + .create_index_if_not_exists("users_login_idx") + .unique() + .as_string(); + let expected_query = "CREATE UNIQUE INDEX IF NOT EXISTS users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_unique_should_have_any_effect_in_the_current_state_on_consecutive_calls() { + let query = sql::CreateIndex::new() + .create_index("users_login_idx") + .unique() + .unique() + .as_string(); + + let expected_query = "CREATE UNIQUE INDEX users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_unique_parameter() { + let query = sql::CreateIndex::new() + .create_index("users_name_idx") + .unique() + .raw_after(sql::CreateIndexParams::Unique, "/* uncommon parameter */") + .as_string(); + + let expected_query = "CREATE UNIQUE /* uncommon parameter */ INDEX users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_not_add_raw_sql_when_the_method_unique_was_not_called() { + let query = sql::CreateIndex::new() + .create_index("users_name_idx") + .raw_after(sql::CreateIndexParams::Unique, "/* uncommon parameter */") + .as_string(); + + let expected_query = "CREATE INDEX users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_unique_parameter() { + let query = sql::CreateIndex::new() + .create_index("users_name_idx") + .raw_before(sql::CreateIndexParams::Unique, "/* uncommon parameter */") + .unique() + .as_string(); + + let expected_query = "CREATE /* uncommon parameter */ UNIQUE INDEX users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_not_add_raw_sql_when_the_method_unique_was_not_called() { + let query = sql::CreateIndex::new() + .create_index("users_name_idx") + .raw_before(sql::CreateIndexParams::Unique, "/* uncommon parameter */") + .as_string(); + + let expected_query = "CREATE INDEX users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "postgresql")] + #[test] + fn method_unique_should_define_a_create_index_parameter_even_when_the_method_create_index_was_not_called() { + let query = sql::CreateIndex::new().unique().as_string(); + let expected_query = "CREATE UNIQUE INDEX"; + + assert_eq!(expected_query, query); + } + + #[cfg(feature = "sqlite")] + #[test] + fn method_unique_should_not_define_a_create_index_parameter_when_the_method_create_index_was_not_called() { + let query = sql::CreateIndex::new().unique().as_string(); + let expected_query = ""; + + assert_eq!(expected_query, query); + } +} + +#[cfg(feature = "postgresql")] +mod method_concurrently { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_concurrently_should_define_a_create_index_parameter_with_the_modifier_concurrently() { + let query = sql::CreateIndex::new() + .create_index("users_login_idx") + .concurrently() + .as_string(); + let expected_query = "CREATE INDEX CONCURRENTLY users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_concurrently_should_define_a_create_index_if_not_exists_parameter_with_the_modifier_concurrently() { + let query = sql::CreateIndex::new() + .create_index_if_not_exists("users_login_idx") + .concurrently() + .as_string(); + let expected_query = "CREATE INDEX CONCURRENTLY IF NOT EXISTS users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_concurrently_should_have_any_effect_in_the_current_state_on_consecutive_calls() { + let query = sql::CreateIndex::new() + .create_index("users_login_idx") + .concurrently() + .concurrently() + .as_string(); + + let expected_query = "CREATE INDEX CONCURRENTLY users_login_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_concurrently_parameter() { + let query = sql::CreateIndex::new() + .create_index("users_name_idx") + .concurrently() + .raw_after(sql::CreateIndexParams::Concurrently, "/* uncommon parameter */") + .as_string(); + + let expected_query = "CREATE INDEX CONCURRENTLY /* uncommon parameter */ users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_not_add_raw_sql_when_the_method_concurrently_was_not_called() { + let query = sql::CreateIndex::new() + .create_index("users_name_idx") + .raw_after(sql::CreateIndexParams::Concurrently, "/* uncommon parameter */") + .as_string(); + + let expected_query = "CREATE INDEX users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_concurrently_parameter() { + let query = sql::CreateIndex::new() + .create_index("users_name_idx") + .raw_before(sql::CreateIndexParams::Concurrently, "/* uncommon parameter */") + .concurrently() + .as_string(); + + let expected_query = "CREATE INDEX /* uncommon parameter */ CONCURRENTLY users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_not_add_raw_sql_when_the_method_concurrently_was_not_called() { + let query = sql::CreateIndex::new() + .create_index("users_name_idx") + .raw_before(sql::CreateIndexParams::Concurrently, "/* uncommon parameter */") + .as_string(); + + let expected_query = "CREATE INDEX users_name_idx"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_concurrently_should_define_a_create_index_parameter_even_when_the_method_create_index_was_not_called() { + let query = sql::CreateIndex::new().concurrently().as_string(); + let expected_query = "CREATE INDEX CONCURRENTLY"; + + assert_eq!(expected_query, query); + } +} + +#[cfg(feature = "postgresql")] +mod method_include { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_include_should_define_the_include_parameter() { + let query = sql::CreateIndex::new().include("login").as_string(); + let expected_query = "INCLUDE (login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_include_should_not_add_column_when_the_column_name_is_empty() { + let query = sql::CreateIndex::new() + .include("") + .include("login") + .include("") + .as_string(); + let expected_query = "INCLUDE (login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_include_should_not_defined_the_paramenter_without_column_names() { + let query = sql::CreateIndex::new().include("").as_string(); + let expected_query = ""; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_include_should_accumulate_columns_on_consecutive_calls() { + let query = sql::CreateIndex::new().include("login").include("name").as_string(); + + let expected_query = "INCLUDE (login, name)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_include_should_not_accumulate_columns_with_the_same_content() { + let query = sql::CreateIndex::new().include("login").include("login").as_string(); + let expected_query = "INCLUDE (login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_include_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new().include(" login ").as_string(); + let expected_query = "INCLUDE (login)"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_include_parameter() { + let query = sql::CreateIndex::new() + .include("name") + .raw_after(sql::CreateIndexParams::Include, "/* uncommon parameter */") + .as_string(); + + let expected_query = "INCLUDE (name) /* uncommon parameter */"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_include_parameter() { + let query = sql::CreateIndex::new() + .raw_before(sql::CreateIndexParams::Include, "/* uncommon parameter */") + .include("name") + .as_string(); + + let expected_query = "/* uncommon parameter */ INCLUDE (name)"; + + assert_eq!(expected_query, query); + } +} + +#[cfg(feature = "postgresql")] +mod method_only { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_only_should_define_a_on_parameter_with_the_modifier_only() { + let query = sql::CreateIndex::new().on("users").only().as_string(); + let expected_query = "ON ONLY users"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_only_should_have_any_effect_in_the_current_state_on_consecutive_calls() { + let query = sql::CreateIndex::new().on("users").only().only().as_string(); + + let expected_query = "ON ONLY users"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_only_should_be_defined_in_presence_of_the_method_on() { + let query = sql::CreateIndex::new().only().as_string(); + + let expected_query = ""; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_only_parameter() { + let query = sql::CreateIndex::new() + .on("users") + .only() + .raw_after(sql::CreateIndexParams::Only, "/* uncommon parameter */") + .as_string(); + + let expected_query = "ON ONLY /* uncommon parameter */ users"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_not_add_raw_sql_when_the_method_only_was_not_called() { + let query = sql::CreateIndex::new() + .on("users") + .raw_after(sql::CreateIndexParams::Only, "/* uncommon parameter */") + .as_string(); + + let expected_query = "ON users"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_only_parameter() { + let query = sql::CreateIndex::new() + .on("users") + .raw_before(sql::CreateIndexParams::Only, "/* uncommon parameter */") + .only() + .as_string(); + + let expected_query = "ON /* uncommon parameter */ ONLY users"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_not_add_raw_sql_when_the_method_only_was_not_called() { + let query = sql::CreateIndex::new() + .on("users") + .raw_before(sql::CreateIndexParams::Only, "/* uncommon parameter */") + .as_string(); + + let expected_query = "ON users"; + + assert_eq!(expected_query, query); + } +} + +#[cfg(feature = "postgresql")] +mod method_using { + use pretty_assertions::assert_eq; + use sql_query_builder as sql; + + #[test] + fn method_using_should_define_a_using_parameter() { + let query = sql::CreateIndex::new().using("btree").as_string(); + let expected_query = "USING btree"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_using_should_overrides_the_current_value_on_consecutive_calls() { + let query = sql::CreateIndex::new().using("btree").using("gist").as_string(); + let expected_query = "USING gist"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_using_should_not_accumulate_parameters_with_the_same_content() { + let query = sql::CreateIndex::new().using("gist").using("gist").as_string(); + let expected_query = "USING gist"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_using_should_trim_space_of_the_argument() { + let query = sql::CreateIndex::new().using(" btree ").as_string(); + let expected_query = "USING btree"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_after_should_add_raw_sql_after_using_parameter() { + let query = sql::CreateIndex::new() + .using("btree") + .raw_after(sql::CreateIndexParams::Using, "/* uncommon parameter */") + .as_string(); + + let expected_query = "USING btree /* uncommon parameter */"; + + assert_eq!(expected_query, query); + } + + #[test] + fn method_raw_before_should_add_raw_sql_before_using_parameter() { + let query = sql::CreateIndex::new() + .raw_before(sql::CreateIndexParams::Using, "/* uncommon parameter */") + .using("gist") + .as_string(); + + let expected_query = "/* uncommon parameter */ USING gist"; + + assert_eq!(expected_query, query); + } +} diff --git a/tests/command_transaction_spec.rs b/tests/command_transaction_spec.rs index da8cc81..b2b1377 100644 --- a/tests/command_transaction_spec.rs +++ b/tests/command_transaction_spec.rs @@ -385,6 +385,35 @@ 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); + } +} + #[cfg(any(feature = "postgresql", feature = "sqlite"))] mod drop_index_method { use pretty_assertions::assert_eq; From e2b47721c4af990fdcb673995285a3e00de1db9c Mon Sep 17 00:00:00 2001 From: Belchior Oliveira Date: Fri, 4 Oct 2024 10:28:14 -0300 Subject: [PATCH 3/4] fix doc typos --- src/create_index/create_index.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/create_index/create_index.rs b/src/create_index/create_index.rs index c5fce2d..f7bd572 100644 --- a/src/create_index/create_index.rs +++ b/src/create_index/create_index.rs @@ -151,8 +151,7 @@ impl CreateIndex { /// ```sql /// -- ------------------------------------------------------------------------------ /// CREATE INDEX users_name_idx - /// ON users - /// (name) + /// ON users (name) /// -- ------------------------------------------------------------------------------ /// ``` pub fn debug(self) -> Self { @@ -223,7 +222,7 @@ impl CreateIndex { /// Output /// /// ```sql - /// /* create index command */ CREATE INDEX users_name_idx + /// /* start index command */ CREATE INDEX users_name_idx /// ``` pub fn raw(mut self, raw_sql: &str) -> Self { push_unique(&mut self._raw, raw_sql.trim().to_string()); From d63da0818d27572cfe821a7d4b9f5b0c112f8b89 Mon Sep 17 00:00:00 2001 From: Belchior Oliveira Date: Sun, 6 Oct 2024 15:28:52 -0300 Subject: [PATCH 4/4] improved test --- tests/clause_where_spec.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/clause_where_spec.rs b/tests/clause_where_spec.rs index 8b34fd5..1fa65cf 100644 --- a/tests/clause_where_spec.rs +++ b/tests/clause_where_spec.rs @@ -359,8 +359,11 @@ mod where_and { #[test] fn method_where_and_should_be_an_alias_of_where_clause() { - let query = sql::CreateIndex::new().where_and("created_at >= $1").as_string(); - let expected_query = "WHERE created_at >= $1"; + let query = sql::CreateIndex::new() + .where_and("active = 'true'") + .where_and("created_at >= $1") + .as_string(); + let expected_query = "WHERE active = 'true' AND created_at >= $1"; assert_eq!(query, expected_query); }