From 5245156827efc9817c20ae4406ac35caa9172747 Mon Sep 17 00:00:00 2001 From: Belchior Oliveira Date: Wed, 23 Jul 2025 09:01:43 -0300 Subject: [PATCH] Insert builder: fix mutually exclusive methods --- src/insert/insert.rs | 67 ++++++++++- src/insert/insert_internal.rs | 148 +++++++++++++++-------- src/structure.rs | 14 ++- tests/command_insert_spec.rs | 220 ++++++++++++++++++++++++++-------- 4 files changed, 348 insertions(+), 101 deletions(-) diff --git a/src/insert/insert.rs b/src/insert/insert.rs index adb7e35..e0ac44f 100644 --- a/src/insert/insert.rs +++ b/src/insert/insert.rs @@ -6,6 +6,9 @@ use crate::{ utils::push_unique, }; +#[cfg(feature = "mysql")] +use crate::structure::MySqlVariance; + impl TransactionQuery for Insert {} impl Insert { @@ -206,6 +209,12 @@ impl Insert { /// ``` pub fn select(mut self, select: Select) -> Self { self._select = Some(select); + + #[cfg(feature = "mysql")] + { + self._mysql_variance = MySqlVariance::InsertSelect; + } + self } @@ -311,12 +320,67 @@ impl Insert { /// ```sql /// INSERT INTO users (login, name) VALUES ('foo', 'Foo'), ('bar', 'Bar') /// ``` + /// + /// # Composable methods + /// The methods [Insert::values], [Insert::row] are composable, when both was used each line will receive the contructor clause row. + /// + /// # Example + /// ``` + /// # #[cfg(feature = "mysql")] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::Insert::new() + /// .values("('foo', 'Foo')") + /// .row("('bar', 'Bar')") + /// .as_string(); + /// # let expected = "VALUES ROW('foo', 'Foo'), ROW('bar', 'Bar')"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Output + /// + /// ```sql + /// VALUES ROW('foo', 'Foo'), ROW('bar', 'Bar') + /// ``` + /// + /// # Mutually exclusive methods + /// The methods [Insert::values]/[Insert::row], [Insert::select] and [Insert::set] are mutually exclusive, the last called will overrides the previous ones. + /// [Insert::row] and [Insert::set] are available on crate feature `mysql` only. + /// + /// # Example + /// ``` + /// # #[cfg(feature = "mysql")] + /// # { + /// # use sql_query_builder as sql; + /// let query = sql::Insert::new() + /// .values("('foo', 'Foo')") + /// .row("('bar', 'Bar')") + /// .set("login = 'foo'") + /// .set("name = 'Foo'") + /// .as_string(); + /// # let expected = "SET login = 'foo', name = 'Foo'"; + /// # assert_eq!(expected, query); + /// # } + /// ``` + /// + /// Output + /// + /// ```sql + /// SET login = 'foo', name = 'Foo' + /// ``` pub fn values(mut self, expression: &str) -> Self { push_unique(&mut self._values, expression.trim().to_string()); + #[cfg(not(feature = "mysql"))] { self._default_values = false } + + #[cfg(feature = "mysql")] + { + self._mysql_variance = MySqlVariance::InsertValues; + } self } } @@ -689,7 +753,7 @@ impl Insert { /// ``` pub fn row(mut self, expression: &str) -> Self { push_unique(&mut self._values, expression.trim().to_string()); - self._use_row = true; + self._mysql_variance = MySqlVariance::InsertValuesRow; self } @@ -717,6 +781,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 } } diff --git a/src/insert/insert_internal.rs b/src/insert/insert_internal.rs index 20bab9f..cd2fbc4 100644 --- a/src/insert/insert_internal.rs +++ b/src/insert/insert_internal.rs @@ -4,14 +4,26 @@ use crate::{ structure::{Insert, InsertClause}, }; +#[cfg(feature = "mysql")] +use crate::structure::MySqlVariance; + impl Concat for Insert { fn concat(&self, fmts: &fmt::Formatter) -> String { let mut query = "".to_string(); - query = self.concat_raw(query, &fmts, &self._raw); + #[cfg(not(any(feature = "postgresql", feature = "sqlite", feature = "mysql")))] + { + 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); + } - #[cfg(any(feature = "postgresql", feature = "sqlite"))] + #[cfg(feature = "postgresql")] { + query = self.concat_raw(query, &fmts, &self._raw); query = self.concat_with( &self._raw_before, &self._raw_after, @@ -20,77 +32,101 @@ impl Concat for Insert { InsertClause::With, &self._with, ); - } - - query = self.concat_insert_into(query, &fmts); - - #[cfg(feature = "sqlite")] - { - query = self.concat_insert_or(query, &fmts); - query = self.concat_replace_into(query, &fmts); - } - - #[cfg(feature = "mysql")] - { - query = self.concat_insert(query, &fmts); - query = self.concat_into(query, &fmts); - query = self.concat_partition( + 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); + query = self.concat_on_conflict(query, &fmts); + query = self.concat_returning( &self._raw_before, &self._raw_after, query, &fmts, - InsertClause::Partition, - &self._partition, + InsertClause::Returning, + &self._returning, ); - query = self.concat_column( + } + + #[cfg(feature = "sqlite")] + { + query = self.concat_raw(query, &fmts, &self._raw); + query = self.concat_with( &self._raw_before, &self._raw_after, query, &fmts, - InsertClause::Column, - &self._column, + InsertClause::With, + &self._with, ); - query = self.concat_set( + 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); + query = self.concat_on_conflict(query, &fmts); + query = self.concat_returning( &self._raw_before, &self._raw_after, query, &fmts, - InsertClause::Set, - &self._set, + InsertClause::Returning, + &self._returning, ); } - #[cfg(not(any(feature = "sqlite", feature = "mysql")))] - { - query = self.concat_overriding(query, &fmts); - } - - #[cfg(not(any(feature = "mysql")))] - { - query = self.concat_default_values(query, &fmts); - } - - query = self.concat_values(query, &fmts); - - query = self.concat_select(query, &fmts); - #[cfg(feature = "mysql")] { - query = self.concat_on_duplicate_key_update(query, &fmts); - } - - #[cfg(any(feature = "postgresql", feature = "sqlite"))] - { - query = self.concat_on_conflict(query, &fmts); - - query = self.concat_returning( + 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); + query = self.concat_partition( &self._raw_before, &self._raw_after, query, &fmts, - InsertClause::Returning, - &self._returning, + InsertClause::Partition, + &self._partition, ); + + match self._mysql_variance { + MySqlVariance::InsertSelect => { + query = self.concat_column( + &self._raw_before, + &self._raw_after, + query, + &fmts, + InsertClause::Column, + &self._column, + ); + query = self.concat_select(query, &fmts); + } + MySqlVariance::InsertSet => { + query = self.concat_set( + &self._raw_before, + &self._raw_after, + query, + &fmts, + InsertClause::Set, + &self._set, + ); + } + MySqlVariance::InsertValues | MySqlVariance::InsertValuesRow => { + query = self.concat_column( + &self._raw_before, + &self._raw_after, + query, + &fmts, + InsertClause::Column, + &self._column, + ); + query = self.concat_values(query, &fmts); + } + } + + query = self.concat_on_duplicate_key_update(query, &fmts); } query.trim_end().to_string() @@ -184,11 +220,19 @@ impl Insert { .iter() .filter(|item| item.is_empty() == false) .map(|item| { - if self._use_row { - format!("ROW{item}") - } else { + #[cfg(not(feature = "mysql"))] + { item.clone() } + + #[cfg(feature = "mysql")] + { + if self._mysql_variance == MySqlVariance::InsertValuesRow { + format!("ROW{item}") + } else { + item.clone() + } + } }) .collect::>() .join(&sep); diff --git a/src/structure.rs b/src/structure.rs index 9537bd8..5f50c82 100644 --- a/src/structure.rs +++ b/src/structure.rs @@ -437,7 +437,6 @@ pub struct Insert { pub(crate) _raw: Vec, pub(crate) _select: Option