Skip to content

Commit 0db0ce2

Browse files
authored
Merge pull request #11 from belchior/change-where-clause-behavior
Changes the where clause behavior
2 parents 44c9b87 + b3c2672 commit 0db0ce2

File tree

12 files changed

+551
-133
lines changed

12 files changed

+551
-133
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ let mut select = sql::Select::new()
1717
let is_admin = true;
1818

1919
if is_admin {
20-
select = select.and("is_admin = true");
20+
select = select.where_clause("is_admin = true");
2121
}
2222

2323
let query = select.as_string();
@@ -141,7 +141,7 @@ fn relations(select: sql::Select) -> sql::Select {
141141
fn conditions(select: sql::Select) -> sql::Select {
142142
select
143143
.where_clause("u.login = $1")
144-
.and("o.id = $2")
144+
.where_clause("o.id = $2")
145145
}
146146

147147
fn as_string(select: sql::Select) -> String {

src/behavior.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use crate::fmt;
21
#[cfg(feature = "sqlite")]
32
use crate::structure::{InsertClause, InsertVars, UpdateClause, UpdateVars};
3+
use crate::{fmt, structure::LogicalOperator};
44
use std::cmp::PartialEq;
55

66
pub trait Concat {
@@ -66,11 +66,17 @@ pub trait ConcatSqlStandard<Clause: PartialEq> {
6666
query: String,
6767
fmts: &fmt::Formatter,
6868
clause: Clause,
69-
items: &Vec<String>,
69+
items: &Vec<(LogicalOperator, String)>,
7070
) -> String {
7171
let fmt::Formatter { lb, space, indent, .. } = fmts;
7272
let sql = if items.is_empty() == false {
73-
let conditions = items.join(&format!("{space}{lb}{indent}AND{space}"));
73+
let ((_, cond), tail) = items.split_first().unwrap();
74+
75+
let first_condition = format!("{lb}{indent}{cond}");
76+
let conditions = tail.iter().fold(first_condition, |acc, (log_op, condition)| {
77+
format!("{acc}{space}{lb}{indent}{log_op}{space}{condition}")
78+
});
79+
7480
format!("WHERE{space}{conditions}{space}{lb}")
7581
} else {
7682
"".to_owned()
@@ -247,6 +253,16 @@ pub trait TransactionQuery: Concat {}
247253
/// Represents all commands that can be used inside the with method
248254
pub trait WithQuery: Concat {}
249255

256+
impl std::fmt::Display for LogicalOperator {
257+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
258+
let v = match self {
259+
LogicalOperator::And => "AND",
260+
LogicalOperator::Or => "OR",
261+
};
262+
write!(f, "{}", v)
263+
}
264+
}
265+
250266
pub fn concat_raw_before_after<Clause: PartialEq>(
251267
items_before: &Vec<(Clause, String)>,
252268
items_after: &Vec<(Clause, String)>,

src/delete/delete.rs

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,14 @@
11
use crate::{
22
behavior::{push_unique, Concat, TransactionQuery, WithQuery},
33
fmt,
4-
structure::{Delete, DeleteClause},
4+
structure::{Delete, DeleteClause, LogicalOperator},
55
};
66

77
impl WithQuery for Delete {}
88

99
impl TransactionQuery for Delete {}
1010

1111
impl Delete {
12-
/// The same as [where_clause](Delete::where_clause) method, useful to write more idiomatic SQL query
13-
///
14-
/// # Example
15-
///
16-
/// ```
17-
/// # use sql_query_builder as sql;
18-
/// let delete = sql::Delete::new()
19-
/// .delete_from("users")
20-
/// .where_clause("created_at < $1")
21-
/// .and("active = false");
22-
///
23-
/// # let expected = "DELETE FROM users WHERE created_at < $1 AND active = false";
24-
/// # assert_eq!(delete.to_string(), expected);
25-
/// ```
26-
pub fn and(mut self, condition: &str) -> Self {
27-
self = self.where_clause(condition);
28-
self
29-
}
30-
3112
/// Gets the current state of the [Delete] and returns it as string
3213
///
3314
/// # Example
@@ -197,22 +178,59 @@ impl Delete {
197178
self
198179
}
199180

200-
/// The `where` clause
181+
/// The `where` clause, this method will concatenate mulltiples calls using the `and` operator.
182+
/// If you intended to use the `or` operator you should use the [where_or](Delete::where_or) method
201183
///
202184
/// # Example
203185
///
204186
/// ```
205187
/// # use sql_query_builder as sql;
206-
/// let delete = sql::Delete::new()
207-
/// .delete_from("users")
208-
/// .where_clause("login = 'foo'")
209-
/// .where_clause("name = 'Foo'");
188+
/// let delete_query = sql::Delete::new()
189+
/// .where_clause("login = $1")
190+
/// .where_clause("status = 'deactivated'")
191+
/// .as_string();
210192
///
211-
/// # let expected = "DELETE FROM users WHERE login = 'foo' AND name = 'Foo'";
212-
/// # assert_eq!(delete.to_string(), expected);
193+
/// # let expected = "WHERE login = $1 AND status = 'deactivated'";
194+
/// # assert_eq!(delete_query, expected);
195+
/// ```
196+
///
197+
/// Outputs
198+
///
199+
/// ```sql
200+
/// WHERE
201+
/// login = $1
202+
/// AND status = 'deactivated'
213203
/// ```
214204
pub fn where_clause(mut self, condition: &str) -> Self {
215-
push_unique(&mut self._where, condition.trim().to_owned());
205+
push_unique(&mut self._where, (LogicalOperator::And, condition.trim().to_owned()));
206+
self
207+
}
208+
209+
/// The `where` clause that concatenate multiples calls using the OR operator.
210+
/// If you intended to use the `and` operator you should use the [where_clause](Delete::where_clause) method
211+
///
212+
/// # Example
213+
///
214+
/// ```
215+
/// # use sql_query_builder as sql;
216+
/// let delete_query = sql::Delete::new()
217+
/// .where_clause("login = 'foo'")
218+
/// .where_or("login = 'bar'")
219+
/// .as_string();
220+
///
221+
/// # let expected = "WHERE login = 'foo' OR login = 'bar'";
222+
/// # assert_eq!(delete_query, expected);
223+
/// ```
224+
///
225+
/// Output
226+
///
227+
/// ```sql
228+
/// WHERE
229+
/// login = 'foo'
230+
/// OR login = 'bar'
231+
/// ```
232+
pub fn where_or(mut self, condition: &str) -> Self {
233+
push_unique(&mut self._where, (LogicalOperator::Or, condition.trim().to_owned()));
216234
self
217235
}
218236
}

src/insert/insert.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,19 +277,19 @@ impl Insert {
277277
/// ```
278278
/// # use sql_query_builder as sql;
279279
/// let query = sql::Insert::new()
280-
/// .insert_into("users (login)")
280+
/// .insert_into("users (login, name)")
281281
/// .values("('foo', 'Foo')")
282282
/// .values("('bar', 'Bar')")
283283
/// .as_string();
284284
///
285-
/// # let expected = "INSERT INTO users (login) VALUES ('foo', 'Foo'), ('bar', 'Bar')";
285+
/// # let expected = "INSERT INTO users (login, name) VALUES ('foo', 'Foo'), ('bar', 'Bar')";
286286
/// # assert_eq!(query, expected);
287287
/// ```
288288
///
289289
/// Output
290290
///
291291
/// ```sql
292-
/// INSERT INTO users (login) VALUES ('foo', 'Foo'), ('bar', 'Bar')
292+
/// INSERT INTO users (login, name) VALUES ('foo', 'Foo'), ('bar', 'Bar')
293293
/// ```
294294
pub fn values(mut self, value: &str) -> Self {
295295
push_unique(&mut self._values, value.trim().to_owned());

src/select/select.rs

Lines changed: 98 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,14 @@
11
use crate::{
22
behavior::{push_unique, Concat, TransactionQuery, WithQuery},
33
fmt,
4-
structure::{Select, SelectClause},
4+
structure::{LogicalOperator, Select, SelectClause},
55
};
66

77
impl TransactionQuery for Select {}
88

99
impl WithQuery for Select {}
1010

1111
impl Select {
12-
/// The same as [where_clause](Select::where_clause) method, useful to write more idiomatic SQL query
13-
///
14-
/// # Example
15-
///
16-
/// ```
17-
/// # use sql_query_builder as sql;
18-
/// let select = sql::Select::new()
19-
/// .where_clause("login = 'foo'")
20-
/// .and("active = true");
21-
///
22-
/// # let expected = "WHERE login = 'foo' AND active = true";
23-
/// # assert_eq!(select.as_string(), expected);
24-
/// ```
25-
pub fn and(mut self, condition: &str) -> Self {
26-
self = self.where_clause(condition);
27-
self
28-
}
29-
3012
/// Gets the current state of the [Select] and returns it as string
3113
///
3214
/// # Example
@@ -64,7 +46,7 @@ impl Select {
6446
/// .select("*")
6547
/// .from("users")
6648
/// .where_clause("login = foo")
67-
/// .and("active = true")
49+
/// .where_clause("active = true")
6850
/// .debug();
6951
/// ```
7052
///
@@ -89,7 +71,7 @@ impl Select {
8971
/// .from("users")
9072
/// .debug()
9173
/// .where_clause("login = foo")
92-
/// .and("active = true")
74+
/// .where_clause("active = true")
9375
/// .as_string();
9476
/// ```
9577
///
@@ -411,20 +393,109 @@ impl Select {
411393
self
412394
}
413395

414-
/// The `where` clause
396+
/// The `where` clause, this method will concatenate mulltiples calls using the `and` operator.
397+
/// If you intended to use the `or` operator you should use the [where_or](Select::where_or) method
415398
///
416399
/// # Example
417400
///
418401
/// ```
419402
/// # use sql_query_builder as sql;
420-
/// let select = sql::Select::new()
421-
/// .where_clause("login = $1");
403+
/// let select_query = sql::Select::new()
404+
/// .where_clause("login = $1")
405+
/// .where_clause("status = 'active'")
406+
/// .as_string();
422407
///
423-
/// # let expected = "WHERE login = $1";
424-
/// # assert_eq!(select.as_string(), expected);
408+
/// # let expected = "WHERE login = $1 AND status = 'active'";
409+
/// # assert_eq!(select_query, expected);
410+
/// ```
411+
///
412+
/// Outputs
413+
///
414+
/// ```sql
415+
/// WHERE
416+
/// login = $1
417+
/// AND status = 'active'
425418
/// ```
426419
pub fn where_clause(mut self, condition: &str) -> Self {
427-
push_unique(&mut self._where, condition.trim().to_owned());
420+
push_unique(&mut self._where, (LogicalOperator::And, condition.trim().to_owned()));
421+
self
422+
}
423+
424+
/// The `where` clause that concatenate multiples calls using the OR operator.
425+
/// If you intended to use the `and` operator you should use the [where_clause](Select::where_clause) method
426+
///
427+
/// # Example
428+
///
429+
/// ```
430+
/// # use sql_query_builder as sql;
431+
/// let select_query = sql::Select::new()
432+
/// .where_clause("login = 'foo'")
433+
/// .where_or("login = 'bar'")
434+
/// .as_string();
435+
///
436+
/// # let expected = "WHERE login = 'foo' OR login = 'bar'";
437+
/// # assert_eq!(select_query, expected);
438+
/// ```
439+
///
440+
/// Output
441+
///
442+
/// ```sql
443+
/// WHERE
444+
/// login = 'foo'
445+
/// OR login = 'bar'
446+
/// ```
447+
///
448+
/// # Example
449+
///
450+
/// ```
451+
/// # use sql_query_builder as sql;
452+
/// let select_query = sql::Select::new()
453+
/// .where_clause("login = 'foo'")
454+
/// .where_or("login = 'bar'")
455+
/// .where_clause("login = 'joe'")
456+
/// .as_string();
457+
///
458+
/// # let expected = "\
459+
/// # WHERE \
460+
/// # login = 'foo' \
461+
/// # OR login = 'bar' \
462+
/// # AND login = 'joe'\
463+
/// # ";
464+
/// # assert_eq!(select_query, expected);
465+
/// ```
466+
/// Output
467+
///
468+
/// ```sql
469+
/// WHERE
470+
/// login = 'foo'
471+
/// OR login = 'bar'
472+
/// AND login = 'joe'
473+
/// ```
474+
///
475+
/// # Example
476+
///
477+
/// If the `where_or` was the first clause then the operator will be ignored
478+
///
479+
/// ```
480+
/// # use sql_query_builder as sql;
481+
/// let select_query = sql::Select::new()
482+
/// .where_or("login = 'joe'")
483+
/// .where_clause("login = 'foo'")
484+
/// .as_string();
485+
///
486+
/// # let expected = "WHERE login = 'joe' AND login = 'foo'";
487+
/// # assert_eq!(select_query, expected);
488+
/// ```
489+
///
490+
/// Output
491+
///
492+
/// ```sql
493+
/// WHERE
494+
/// login = 'joe'
495+
/// AND login = 'foo'
496+
/// ```
497+
pub fn where_or(mut self, condition: &str) -> Self {
498+
push_unique(&mut self._where, (LogicalOperator::Or, condition.trim().to_owned()));
428499
self
429500
}
430501
}

src/structure.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,20 @@ pub enum Combinator {
1111
Union,
1212
}
1313

14+
#[derive(Clone, PartialEq)]
15+
pub enum LogicalOperator {
16+
And,
17+
Or,
18+
}
19+
1420
/// Builder to contruct a [Delete] command
1521
#[derive(Default, Clone)]
1622
pub struct Delete {
1723
pub(crate) _delete_from: String,
1824
pub(crate) _raw_after: Vec<(DeleteClause, String)>,
1925
pub(crate) _raw_before: Vec<(DeleteClause, String)>,
2026
pub(crate) _raw: Vec<String>,
21-
pub(crate) _where: Vec<String>,
27+
pub(crate) _where: Vec<(LogicalOperator, String)>,
2228

2329
#[cfg(any(feature = "postgresql", feature = "sqlite"))]
2430
pub(crate) _returning: Vec<String>,
@@ -127,7 +133,7 @@ pub struct Select {
127133
pub(crate) _raw_before: Vec<(SelectClause, String)>,
128134
pub(crate) _raw: Vec<String>,
129135
pub(crate) _select: Vec<String>,
130-
pub(crate) _where: Vec<String>,
136+
pub(crate) _where: Vec<(LogicalOperator, String)>,
131137

132138
#[cfg(any(feature = "postgresql", feature = "sqlite"))]
133139
pub(crate) _except: Vec<Self>,
@@ -223,7 +229,7 @@ pub struct Update {
223229
pub(crate) _raw_before: Vec<(UpdateClause, String)>,
224230
pub(crate) _raw: Vec<String>,
225231
pub(crate) _set: Vec<String>,
226-
pub(crate) _where: Vec<String>,
232+
pub(crate) _where: Vec<(LogicalOperator, String)>,
227233

228234
#[cfg(any(feature = "postgresql", feature = "sqlite"))]
229235
pub(crate) _from: Vec<String>,

0 commit comments

Comments
 (0)