From 93cd2b159203bf01eb8875d7affe18b3b25b6981 Mon Sep 17 00:00:00 2001 From: Belchior Oliveira Date: Sat, 26 Jul 2025 17:21:22 -0300 Subject: [PATCH] insert builder: fix mutually exclusive methods --- scripts/watch_test.sh | 1 + src/insert/insert.rs | 105 +++++++++++++++------------ src/insert/insert_internal.rs | 133 ++++++++++++++++++++++------------ src/structure.rs | 35 ++++++--- tests/command_insert_spec.rs | 3 - 5 files changed, 172 insertions(+), 105 deletions(-) diff --git a/scripts/watch_test.sh b/scripts/watch_test.sh index 7ea24ff..75fd612 100755 --- a/scripts/watch_test.sh +++ b/scripts/watch_test.sh @@ -8,6 +8,7 @@ # ./scripts/watch_test.sh all # will enable all feature # ./scripts/watch_test.sh postgresql # will enable only the postgresql feature +clear all_features='postgresql sqlite mysql' features='' test_names=$(git status -s | grep 'A[[:space:]]*tests/\|M[[:space:]]*tests/' | sed -e 's/.* //' -e 's/tests\//--test /' -e 's/\.rs//' | tr '\n' ' ') diff --git a/src/insert/insert.rs b/src/insert/insert.rs index 407becc..ca045b2 100644 --- a/src/insert/insert.rs +++ b/src/insert/insert.rs @@ -2,13 +2,10 @@ use crate::{ behavior::TransactionQuery, concat::Concat, fmt, - structure::{Insert, InsertClause, Select}, + structure::{Insert, InsertClause, InsertVariance, Select, ValuesVariance}, utils::push_unique, }; -#[cfg(feature = "mysql")] -use crate::structure::MySqlVariance; - impl TransactionQuery for Insert {} impl Insert { @@ -91,8 +88,7 @@ impl Insert { /// ``` #[cfg(not(feature = "mysql"))] pub fn default_values(mut self) -> Self { - self._default_values = true; - self._values = vec![]; + self._values_variance = ValuesVariance::InsertDefaultValues; self } @@ -115,23 +111,57 @@ impl Insert { /// # let expected = "INSERT INTO users (login, name)"; /// # assert_eq!(expected, insert.to_string()); /// ``` + /// + /// # Mutually exclusive methods + /// + /// Avaliable on `sqlite` only + /// + /// Methods [Insert::insert_into], [Insert::insert_or] and [Insert::replace_into] are mutually exclusive, the last called will overrides the previous ones. + /// + /// ``` + /// # #[cfg(feature = "sqlite")] + /// # { + /// # use sql_query_builder as sql; + /// let insert = sql::Insert::new() + /// .insert_into("users (login, name)") + /// .replace_into("users (login, name)"); + /// # + /// # let expected = "REPLACE INTO users (login, name)"; + /// # assert_eq!(expected, insert.to_string()); + /// # } + /// ``` + /// Output + /// + /// ```sql + /// REPLACE INTO users (login, name) + /// ``` + /// + /// Avaliable on `mysql` only + /// + /// Methods [Insert::insert_into] and the splitted version ([Insert::insert], [Insert::into], [Insert::column]) are mutually exclusive, the last called will overrides the previous ones. + /// + /// ``` + /// # #[cfg(feature = "mysql")] + /// # { + /// # use sql_query_builder as sql; + /// let insert = sql::Insert::new() + /// .insert_into("users (login, name)") + /// .insert("low_priority") + /// .into("users") + /// .column("login"); + /// # + /// # let expected = "INSERT low_priority INTO users (login)"; + /// # assert_eq!(expected, insert.to_string()); + /// # } + /// ``` + /// Output + /// + /// ```sql + /// INSERT low_priority INTO users (login) + /// ``` pub fn insert_into(mut self, table_name: &str) -> Self { self._insert_into = table_name.trim().to_string(); - - #[cfg(feature = "sqlite")] - { - self._insert_or = "".to_string(); - self._replace_into = "".to_string(); - } - - #[cfg(feature = "mysql")] - { - self._insert = "".to_string(); - self._into = "".to_string(); - self._partition = vec![]; - self._column = vec![]; - } - + self._insert_variance = InsertVariance::InsertInto; self } @@ -209,12 +239,7 @@ impl Insert { /// ``` pub fn select(mut self, select: Select) -> Self { self._select = Some(select); - - #[cfg(feature = "mysql")] - { - self._mysql_variance = MySqlVariance::InsertSelect; - } - + self._values_variance = ValuesVariance::InsertSelect; self } @@ -371,16 +396,8 @@ impl Insert { /// ``` pub fn values(mut self, expression: &str) -> Self { push_unique(&mut self._values, expression.trim().to_string()); + self._values_variance = ValuesVariance::InsertValues; - #[cfg(not(feature = "mysql"))] - { - self._default_values = false - } - - #[cfg(feature = "mysql")] - { - self._mysql_variance = MySqlVariance::InsertValues; - } self } } @@ -537,8 +554,7 @@ impl Insert { /// ``` pub fn insert_or(mut self, expression: &str) -> Self { self._insert_or = expression.trim().to_string(); - self._insert_into = "".to_string(); - self._replace_into = "".to_string(); + self._insert_variance = InsertVariance::InsertOr; self } @@ -565,8 +581,7 @@ impl Insert { /// ``` pub fn replace_into(mut self, table_name: &str) -> Self { self._replace_into = table_name.trim().to_string(); - self._insert_into = "".to_string(); - self._insert_or = "".to_string(); + self._insert_variance = InsertVariance::ReplaceInto; self } } @@ -600,7 +615,7 @@ impl Insert { /// ``` pub fn column(mut self, column_name: &str) -> Self { push_unique(&mut self._column, column_name.trim().to_string()); - self._insert_into = "".to_string(); + self._insert_variance = InsertVariance::InsertSplitted; self } @@ -630,7 +645,7 @@ impl Insert { /// ``` pub fn insert(mut self, modifier: &str) -> Self { self._insert = modifier.trim().to_string(); - self._insert_into = "".to_string(); + self._insert_variance = InsertVariance::InsertSplitted; self } @@ -659,7 +674,7 @@ impl Insert { /// ``` pub fn into(mut self, table: &str) -> Self { self._into = table.trim().to_string(); - self._insert_into = "".to_string(); + self._insert_variance = InsertVariance::InsertSplitted; self } @@ -753,7 +768,7 @@ impl Insert { /// ``` pub fn row(mut self, expression: &str) -> Self { push_unique(&mut self._values, expression.trim().to_string()); - self._mysql_variance = MySqlVariance::InsertValuesRow; + self._values_variance = ValuesVariance::InsertValuesRow; self } @@ -781,7 +796,7 @@ impl Insert { /// ``` pub fn set(mut self, assignment: &str) -> Self { push_unique(&mut self._set, assignment.trim().to_string()); - self._mysql_variance = MySqlVariance::InsertSet; + self._values_variance = ValuesVariance::InsertSet; self } } diff --git a/src/insert/insert_internal.rs b/src/insert/insert_internal.rs index cd2fbc4..ff3ef51 100644 --- a/src/insert/insert_internal.rs +++ b/src/insert/insert_internal.rs @@ -1,11 +1,11 @@ use crate::{ concat::{concat_raw_before_after, Concat}, fmt, - structure::{Insert, InsertClause}, + structure::{Insert, InsertClause, ValuesVariance}, }; -#[cfg(feature = "mysql")] -use crate::structure::MySqlVariance; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::structure::InsertVariance; impl Concat for Insert { fn concat(&self, fmts: &fmt::Formatter) -> String { @@ -16,9 +16,17 @@ impl Concat for Insert { query = self.concat_raw(query, &fmts, &self._raw); query = self.concat_insert_into(query, &fmts); query = self.concat_overriding(query, &fmts); - query = self.concat_default_values(query, &fmts); - query = self.concat_values(query, &fmts); - query = self.concat_select(query, &fmts); + match self._values_variance { + ValuesVariance::InsertDefaultValues => { + query = self.concat_default_values(query, &fmts); + } + ValuesVariance::InsertSelect => { + query = self.concat_select(query, &fmts); + } + ValuesVariance::InsertValues => { + query = self.concat_values(query, &fmts); + } + } } #[cfg(feature = "postgresql")] @@ -34,9 +42,17 @@ impl Concat for Insert { ); query = self.concat_insert_into(query, &fmts); query = self.concat_overriding(query, &fmts); - query = self.concat_default_values(query, &fmts); - query = self.concat_values(query, &fmts); - query = self.concat_select(query, &fmts); + match self._values_variance { + ValuesVariance::InsertDefaultValues => { + query = self.concat_default_values(query, &fmts); + } + ValuesVariance::InsertSelect => { + query = self.concat_select(query, &fmts); + } + ValuesVariance::InsertValues => { + query = self.concat_values(query, &fmts); + } + } query = self.concat_on_conflict(query, &fmts); query = self.concat_returning( &self._raw_before, @@ -59,12 +75,28 @@ impl Concat for Insert { InsertClause::With, &self._with, ); - query = self.concat_insert_into(query, &fmts); - query = self.concat_insert_or(query, &fmts); - query = self.concat_replace_into(query, &fmts); - query = self.concat_default_values(query, &fmts); - query = self.concat_values(query, &fmts); - query = self.concat_select(query, &fmts); + match self._insert_variance { + InsertVariance::InsertInto => { + query = self.concat_insert_into(query, &fmts); + } + InsertVariance::InsertOr => { + query = self.concat_insert_or(query, &fmts); + } + InsertVariance::ReplaceInto => { + query = self.concat_replace_into(query, &fmts); + } + } + match self._values_variance { + ValuesVariance::InsertDefaultValues => { + query = self.concat_default_values(query, &fmts); + } + ValuesVariance::InsertSelect => { + query = self.concat_select(query, &fmts); + } + ValuesVariance::InsertValues => { + query = self.concat_values(query, &fmts); + } + } query = self.concat_on_conflict(query, &fmts); query = self.concat_returning( &self._raw_before, @@ -79,9 +111,16 @@ impl Concat for Insert { #[cfg(feature = "mysql")] { query = self.concat_raw(query, &fmts, &self._raw); - query = self.concat_insert_into(query, &fmts); - query = self.concat_insert(query, &fmts); - query = self.concat_into(query, &fmts); + match self._insert_variance { + InsertVariance::InsertInto => { + query = self.concat_insert_into(query, &fmts); + } + InsertVariance::InsertSplitted => { + query = self.concat_insert(query, &fmts); + query = self.concat_into(query, &fmts); + } + } + query = self.concat_partition( &self._raw_before, &self._raw_after, @@ -91,19 +130,21 @@ impl Concat for Insert { &self._partition, ); - match self._mysql_variance { - MySqlVariance::InsertSelect => { - query = self.concat_column( - &self._raw_before, - &self._raw_after, - query, - &fmts, - InsertClause::Column, - &self._column, - ); + match self._values_variance { + ValuesVariance::InsertSelect => { + if self._insert_variance == InsertVariance::InsertSplitted { + query = self.concat_column( + &self._raw_before, + &self._raw_after, + query, + &fmts, + InsertClause::Column, + &self._column, + ); + } query = self.concat_select(query, &fmts); } - MySqlVariance::InsertSet => { + ValuesVariance::InsertSet => { query = self.concat_set( &self._raw_before, &self._raw_after, @@ -113,15 +154,17 @@ impl Concat for Insert { &self._set, ); } - MySqlVariance::InsertValues | MySqlVariance::InsertValuesRow => { - query = self.concat_column( - &self._raw_before, - &self._raw_after, - query, - &fmts, - InsertClause::Column, - &self._column, - ); + ValuesVariance::InsertValues | ValuesVariance::InsertValuesRow => { + if self._insert_variance == InsertVariance::InsertSplitted { + query = self.concat_column( + &self._raw_before, + &self._raw_after, + query, + &fmts, + InsertClause::Column, + &self._column, + ); + } query = self.concat_values(query, &fmts); } } @@ -175,12 +218,10 @@ impl Insert { fn concat_select(&self, query: String, fmts: &fmt::Formatter) -> String { let fmt::Formatter { lb, space, .. } = fmts; - let sql = if let Some(select) = &self._select { + let sql = self._select.as_ref().map_or("".to_string(), |select| { let select_string = select.concat(fmts); format!("{select_string}{space}{lb}") - } else { - "".to_string() - }; + }); concat_raw_before_after( &self._raw_before, @@ -195,11 +236,7 @@ impl Insert { #[cfg(not(feature = "mysql"))] fn concat_default_values(&self, query: String, fmts: &fmt::Formatter) -> String { let fmt::Formatter { lb, space, .. } = fmts; - let sql = if self._default_values { - format!("DEFAULT VALUES{space}{lb}") - } else { - "".to_string() - }; + let sql = format!("DEFAULT VALUES{space}{lb}"); concat_raw_before_after( &self._raw_before, @@ -227,7 +264,7 @@ impl Insert { #[cfg(feature = "mysql")] { - if self._mysql_variance == MySqlVariance::InsertValuesRow { + if self._values_variance == ValuesVariance::InsertValuesRow { format!("ROW{item}") } else { item.clone() diff --git a/src/structure.rs b/src/structure.rs index de42173..256021d 100644 --- a/src/structure.rs +++ b/src/structure.rs @@ -432,11 +432,13 @@ pub enum DropTableParams { #[derive(Default, Clone)] pub struct Insert { pub(crate) _insert_into: String, + pub(crate) _insert_variance: InsertVariance, + pub(crate) _raw: Vec, pub(crate) _raw_after: Vec<(InsertClause, String)>, pub(crate) _raw_before: Vec<(InsertClause, String)>, - pub(crate) _raw: Vec, pub(crate) _select: Option