Skip to content

Commit b0b39e4

Browse files
committed
implemented values command
1 parent cfa9d15 commit b0b39e4

File tree

9 files changed

+561
-21
lines changed

9 files changed

+561
-21
lines changed

src/behavior.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,27 @@ pub trait ConcatMethods<'a, Clause: PartialEq> {
9292
concat_raw_before_after(items_raw_before, items_raw_after, query, fmts, clause, sql)
9393
}
9494

95+
fn concat_values(
96+
&self,
97+
items_raw_before: &Vec<(Clause, String)>,
98+
items_raw_after: &Vec<(Clause, String)>,
99+
query: String,
100+
fmts: &fmt::Formatter,
101+
clause: Clause,
102+
items: &Vec<String>,
103+
) -> String {
104+
let fmt::Formatter { comma, lb, space, .. } = fmts;
105+
let sql = if items.is_empty() == false {
106+
let sep = format!("{comma}{lb}");
107+
let values = items.join(&sep);
108+
format!("VALUES{space}{lb}{values}{space}{lb}")
109+
} else {
110+
"".to_owned()
111+
};
112+
113+
concat_raw_before_after(items_raw_before, items_raw_after, query, fmts, clause, sql)
114+
}
115+
95116
fn concat_where(
96117
&self,
97118
items_raw_before: &Vec<(Clause, String)>,

src/insert.rs

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,14 @@ impl Concat for InsertBuilder<'_> {
236236
}
237237
query = self.concat_insert_into(query, &fmts);
238238
query = self.concat_overriding(query, &fmts);
239-
query = self.concat_values(query, &fmts);
239+
query = self.concat_values(
240+
&self._raw_before,
241+
&self._raw_after,
242+
query,
243+
&fmts,
244+
InsertClause::Values,
245+
&self._values,
246+
);
240247
query = self.concat_select(query, &fmts);
241248

242249
#[cfg(feature = "postgresql")]
@@ -312,25 +319,6 @@ impl InsertBuilder<'_> {
312319
sql,
313320
)
314321
}
315-
316-
fn concat_values(&self, query: String, fmts: &fmt::Formatter) -> String {
317-
let fmt::Formatter { comma, lb, space, .. } = fmts;
318-
let sql = if self._values.is_empty() == false {
319-
let values = self._values.join(comma);
320-
format!("VALUES{space}{values}{space}{lb}")
321-
} else {
322-
"".to_owned()
323-
};
324-
325-
concat_raw_before_after(
326-
&self._raw_before,
327-
&self._raw_after,
328-
query,
329-
fmts,
330-
InsertClause::Values,
331-
sql,
332-
)
333-
}
334322
}
335323

336324
impl std::fmt::Display for InsertBuilder<'_> {

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ mod insert;
77
mod select;
88
mod structure;
99
mod update;
10+
mod values;
1011

1112
pub use crate::structure::{
1213
DeleteBuilder, DeleteClause, InsertBuilder, InsertClause, SelectBuilder, SelectClause, UpdateBuilder, UpdateClause,
14+
ValuesBuilder, ValuesClause,
1315
};

src/select.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,12 @@ impl Concat for SelectBuilder<'_> {
377377

378378
impl SelectBuilder<'_> {
379379
#[cfg(feature = "postgresql")]
380-
fn concat_combinator(&self, query: String, fmts: &fmt::Formatter, combinator: crate::structure::Combinator) -> String {
380+
fn concat_combinator(
381+
&self,
382+
query: String,
383+
fmts: &fmt::Formatter,
384+
combinator: crate::structure::Combinator,
385+
) -> String {
381386
use crate::behavior::raw_queries;
382387
use crate::structure::Combinator;
383388

src/structure.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,18 @@ pub enum UpdateClause {
145145
#[cfg(feature = "postgresql")]
146146
With,
147147
}
148+
149+
/// Builder to contruct a values command
150+
#[derive(Default, Clone)]
151+
pub struct ValuesBuilder {
152+
pub(crate) _raw_after: Vec<(ValuesClause, String)>,
153+
pub(crate) _raw_before: Vec<(ValuesClause, String)>,
154+
pub(crate) _raw: Vec<String>,
155+
pub(crate) _values: Vec<String>,
156+
}
157+
158+
/// All available clauses to be used in `raw_before` and `raw_after` methods of the ValuesBuilder
159+
#[derive(PartialEq, Clone)]
160+
pub enum ValuesClause {
161+
Values,
162+
}

src/values.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
use crate::{
2+
behavior::{push_unique, Concat, ConcatMethods, WithQuery},
3+
fmt,
4+
structure::{ValuesBuilder, ValuesClause},
5+
};
6+
7+
impl ValuesBuilder {
8+
/// Gets the current state of the ValuesBuilder and returns it as string
9+
pub fn as_string(&self) -> String {
10+
let fmts = fmt::Formatter::one_line();
11+
self.concat(&fmts)
12+
}
13+
14+
/// Prints the current state of the ValuesBuilder into console output in a more ease to read version.
15+
/// This method is useful to debug complex queries or just to print the generated SQL while you type
16+
/// ```
17+
/// use sql_query_builder::ValuesBuilder;
18+
///
19+
/// let values = ValuesBuilder::new()
20+
/// .values("(1, 'one'), (2, 'two')")
21+
/// .values("(3, 'three')")
22+
/// .debug();
23+
/// ```
24+
///
25+
/// Output
26+
///
27+
/// ```sql
28+
/// VALUES (1, 'one'), (2, 'two'), (3, 'three')
29+
/// ```
30+
pub fn debug(self) -> Self {
31+
let fmts = fmt::Formatter::multi_line();
32+
println!("{}", fmt::colorize(self.concat(&fmts)));
33+
self
34+
}
35+
36+
/// Create ValuesBuilder's instance
37+
pub fn new() -> Self {
38+
Self::default()
39+
}
40+
41+
/// Prints the current state of the ValuesBuilder into console output similar to debug method,
42+
/// the difference is that this method prints in one line.
43+
pub fn print(self) -> Self {
44+
let fmts = fmt::Formatter::one_line();
45+
println!("{}", fmt::colorize(self.concat(&fmts)));
46+
self
47+
}
48+
49+
/// Adds at the beginning a raw SQL query.
50+
///
51+
/// ```
52+
/// use sql_query_builder::ValuesBuilder;
53+
///
54+
/// let raw_query = "insert into my_table(nun, txt)";
55+
/// let values = ValuesBuilder::new()
56+
/// .raw(raw_query)
57+
/// .values("(1, 'one'), (2, 'two')")
58+
/// .debug();
59+
/// ```
60+
///
61+
/// Output
62+
///
63+
/// ```sql
64+
/// insert into my_table(num, txt)
65+
/// VALUES (1, 'one'), (2, 'two')
66+
/// ```
67+
pub fn raw(mut self, raw_sql: &str) -> Self {
68+
push_unique(&mut self._raw, raw_sql.trim().to_owned());
69+
self
70+
}
71+
72+
/// Adds a raw SQL query after a specified clause.
73+
///
74+
/// ```
75+
/// use sql_query_builder::{ValuesBuilder, ValuesClause};
76+
///
77+
/// let raw_query = ", (3, 'three')";
78+
/// let values = ValuesBuilder::new()
79+
/// .values("(1, 'one'), (2, 'two')")
80+
/// .raw_after(ValuesClause::Values, raw_query)
81+
/// .debug();
82+
/// ```
83+
///
84+
/// Output
85+
///
86+
/// ```sql
87+
/// VALUES (1, 'one'), (2, 'two') , (3, 'three')
88+
/// ```
89+
pub fn raw_after(mut self, clause: ValuesClause, raw_sql: &str) -> Self {
90+
self._raw_after.push((clause, raw_sql.trim().to_owned()));
91+
self
92+
}
93+
94+
/// Adds a raw SQL query before a specified clause.
95+
///
96+
/// ```
97+
/// use sql_query_builder::{ValuesBuilder, ValuesClause};
98+
///
99+
/// let raw_query = "/* the values command */";
100+
/// let values = ValuesBuilder::new()
101+
/// .raw_before(ValuesClause::Values, raw_query)
102+
/// .values("(1, 'one'), (2, 'two')")
103+
/// .debug();
104+
/// ```
105+
///
106+
/// Output
107+
///
108+
/// ```sql
109+
/// /* the values command */
110+
/// VALUES (1, 'one'), (2, 'two')
111+
/// ```
112+
pub fn raw_before(mut self, clause: ValuesClause, raw_sql: &str) -> Self {
113+
self._raw_before.push((clause, raw_sql.trim().to_owned()));
114+
self
115+
}
116+
117+
/// The values clause
118+
/// ```
119+
/// use sql_query_builder::ValuesBuilder;
120+
///
121+
/// let values = ValuesBuilder::new()
122+
/// .values("(1, 'one'), (2, 'two')")
123+
/// .values("(3, 'three')");
124+
/// ```
125+
pub fn values(mut self, expression: &str) -> Self {
126+
push_unique(&mut self._values, expression.trim().to_owned());
127+
self
128+
}
129+
}
130+
131+
impl WithQuery for ValuesBuilder {}
132+
133+
impl<'a> ConcatMethods<'a, ValuesClause> for ValuesBuilder {}
134+
135+
impl Concat for ValuesBuilder {
136+
fn concat(&self, fmts: &fmt::Formatter) -> String {
137+
let mut query = "".to_owned();
138+
139+
query = self.concat_raw(query, &fmts, &self._raw);
140+
query = self.concat_values(
141+
&self._raw_before,
142+
&self._raw_after,
143+
query,
144+
&fmts,
145+
ValuesClause::Values,
146+
&self._values,
147+
);
148+
149+
query.trim_end().to_owned()
150+
}
151+
}
152+
153+
impl std::fmt::Display for ValuesBuilder {
154+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
155+
write!(f, "{}", self.as_string())
156+
}
157+
}
158+
159+
impl std::fmt::Debug for ValuesBuilder {
160+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
161+
let fmts = fmt::Formatter::multi_line();
162+
write!(f, "{}", fmt::colorize(self.concat(&fmts)))
163+
}
164+
}

tests/feature_flag_postgresql.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,20 @@ mod with_clause {
288288
use pretty_assertions::assert_eq;
289289
use sql_query_builder::{DeleteBuilder, DeleteClause};
290290

291+
#[test]
292+
fn method_with_should_accept_delete_builder_as_query_argument() {
293+
let query = DeleteBuilder::new()
294+
.with("deleted_address", DeleteBuilder::new().delete_from("address"))
295+
.delete_from("orders")
296+
.as_string();
297+
let expected_query = "\
298+
WITH deleted_address AS (DELETE FROM address) \
299+
DELETE FROM orders\
300+
";
301+
302+
assert_eq!(query, expected_query);
303+
}
304+
291305
#[test]
292306
fn method_with_should_add_the_with_clause() {
293307
let deleted_users = DeleteBuilder::new()
@@ -403,6 +417,18 @@ mod with_clause {
403417
use pretty_assertions::assert_eq;
404418
use sql_query_builder::{InsertBuilder, InsertClause};
405419

420+
#[test]
421+
fn method_with_should_accept_insert_builder_as_query_argument() {
422+
let query = InsertBuilder::new()
423+
.with("address", InsertBuilder::new().insert_into("address"))
424+
.as_string();
425+
let expected_query = "\
426+
WITH address AS (INSERT INTO address)\
427+
";
428+
429+
assert_eq!(query, expected_query);
430+
}
431+
406432
#[test]
407433
fn method_with_should_add_the_with_clause() {
408434
let inserted_users = InsertBuilder::new()
@@ -518,6 +544,18 @@ mod with_clause {
518544
use pretty_assertions::assert_eq;
519545
use sql_query_builder::{SelectBuilder, SelectClause};
520546

547+
#[test]
548+
fn method_with_should_accept_select_builder_as_query_argument() {
549+
let query = SelectBuilder::new()
550+
.with("address", SelectBuilder::new().select("city"))
551+
.as_string();
552+
let expected_query = "\
553+
WITH address AS (SELECT city)\
554+
";
555+
556+
assert_eq!(query, expected_query);
557+
}
558+
521559
#[test]
522560
fn method_with_should_add_the_with_clause() {
523561
let select_users = SelectBuilder::new().select("login").from("users");
@@ -618,6 +656,18 @@ mod with_clause {
618656
use pretty_assertions::assert_eq;
619657
use sql_query_builder::{UpdateBuilder, UpdateClause};
620658

659+
#[test]
660+
fn method_with_should_accept_update_builder_as_query_argument() {
661+
let query = UpdateBuilder::new()
662+
.with("address", UpdateBuilder::new().set("city = 'foo'"))
663+
.as_string();
664+
let expected_query = "\
665+
WITH address AS (SET city = 'foo')\
666+
";
667+
668+
assert_eq!(query, expected_query);
669+
}
670+
621671
#[test]
622672
fn method_with_should_add_the_with_clause() {
623673
let update_users = UpdateBuilder::new()
@@ -728,6 +778,23 @@ mod with_clause {
728778
assert_eq!(query, expected_query);
729779
}
730780
}
781+
782+
mod values_builder {
783+
use pretty_assertions::assert_eq;
784+
use sql_query_builder::{SelectBuilder, ValuesBuilder};
785+
786+
#[test]
787+
fn method_with_should_accept_values_builder_as_query_argument() {
788+
let query = SelectBuilder::new()
789+
.with("address", ValuesBuilder::new().values("('foo', 'Foo')"))
790+
.as_string();
791+
let expected_query = "\
792+
WITH address AS (VALUES ('foo', 'Foo'))\
793+
";
794+
795+
assert_eq!(query, expected_query);
796+
}
797+
}
731798
}
732799

733800
#[cfg(feature = "postgresql")]

0 commit comments

Comments
 (0)