Skip to content

Commit 6f30744

Browse files
authored
Added like operator (#42)
* Added 'like' operator. Removed unneeded whitespaces on queries. * Fixes for like operator. Added tests for like operators. * Upgrade to v0.4.1
1 parent cca1c8f commit 6f30744

4 files changed

Lines changed: 135 additions & 36 deletions

File tree

Cargo.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ tokio-postgres = { workspace = true, optional = true }
3333
tiberius = { workspace = true, optional = true }
3434

3535
[workspace.dependencies]
36-
canyon_crud = { version = "0.4.0", path = "canyon_crud" }
37-
canyon_connection = { version = "0.4.0", path = "canyon_connection" }
38-
canyon_entities = { version = "0.4.0", path = "canyon_entities" }
39-
canyon_migrations = { version = "0.4.0", path = "canyon_migrations"}
40-
canyon_macros = { version = "0.4.0", path = "canyon_macros" }
36+
canyon_crud = { version = "0.4.1", path = "canyon_crud" }
37+
canyon_connection = { version = "0.4.1", path = "canyon_connection" }
38+
canyon_entities = { version = "0.4.1", path = "canyon_entities" }
39+
canyon_migrations = { version = "0.4.1", path = "canyon_migrations"}
40+
canyon_macros = { version = "0.4.1", path = "canyon_macros" }
4141

4242
tokio = { version = "1.27.0", features = ["full"] }
4343
tokio-util = { version = "0.7.4", features = ["compat"] }
@@ -61,7 +61,7 @@ quote = "1.0.9"
6161
proc-macro2 = "1.0.27"
6262

6363
[workspace.package]
64-
version = "0.4.0"
64+
version = "0.4.1"
6565
edition = "2021"
6666
authors = ["Alex Vergara<pyzyryab@tutanota.com>, Gonzalo Busto Musi<gonzalo.busto@gmail.com>"]
6767
documentation = "https://zerodaycode.github.io/canyon-book/"

canyon_crud/src/query_elements/operators.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pub trait Operator {
2-
fn as_str(&self) -> &'static str;
2+
fn as_str(&self, placeholder_counter: usize) -> String;
33
}
44

55
/// Enumerated type for represent the comparison operations
@@ -18,15 +18,37 @@ pub enum Comp {
1818
/// Operator "=<" less or equals than value
1919
LtEq,
2020
}
21+
2122
impl Operator for Comp {
22-
fn as_str(&self) -> &'static str {
23+
fn as_str(&self, placeholder_counter: usize) -> String {
24+
match *self {
25+
Self::Eq => format!(" = ${placeholder_counter}"),
26+
Self::Neq => format!(" <> ${placeholder_counter}"),
27+
Self::Gt => format!(" > ${placeholder_counter}"),
28+
Self::GtEq => format!(" >= ${placeholder_counter}"),
29+
Self::Lt => format!(" < ${placeholder_counter}"),
30+
Self::LtEq => format!(" <= ${placeholder_counter}"),
31+
}
32+
}
33+
}
34+
35+
pub enum Like {
36+
/// Operator "LIKE" as '%pattern%'
37+
Full,
38+
/// Operator "LIKE" as '%pattern'
39+
Left,
40+
/// Operator "LIKE" as 'pattern%'
41+
Right,
42+
}
43+
44+
impl Operator for Like {
45+
fn as_str(&self, placeholder_counter: usize) -> String {
2346
match *self {
24-
Self::Eq => " = ",
25-
Self::Neq => " <> ",
26-
Self::Gt => " > ",
27-
Self::GtEq => " >= ",
28-
Self::Lt => " < ",
29-
Self::LtEq => " <= ",
47+
Like::Full => {
48+
format!(" LIKE CONCAT('%', CAST(${placeholder_counter} AS VARCHAR) ,'%')")
49+
}
50+
Like::Left => format!(" LIKE CONCAT('%', CAST(${placeholder_counter} AS VARCHAR))"),
51+
Like::Right => format!(" LIKE CONCAT(CAST(${placeholder_counter} AS VARCHAR) ,'%')"),
3052
}
3153
}
3254
}

canyon_crud/src/query_elements/query_builder.rs

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,8 @@ where
180180
pub fn r#where<Z: FieldValueIdentifier<'a, T>>(&mut self, r#where: Z, op: impl Operator) {
181181
let (column_name, value) = r#where.value();
182182

183-
let where_ = String::from(" WHERE ")
184-
+ column_name
185-
+ op.as_str()
186-
+ "$"
187-
+ &(self.query.params.len() + 1).to_string();
183+
let where_ =
184+
String::from(" WHERE ") + column_name + &op.as_str(self.query.params.len() + 1);
188185

189186
self.query.sql.push_str(&where_);
190187
self.query.params.push(value);
@@ -193,12 +190,7 @@ where
193190
pub fn and<Z: FieldValueIdentifier<'a, T>>(&mut self, r#and: Z, op: impl Operator) {
194191
let (column_name, value) = r#and.value();
195192

196-
let and_ = String::from(" AND ")
197-
+ column_name
198-
+ op.as_str()
199-
+ "$"
200-
+ &(self.query.params.len() + 1).to_string()
201-
+ " ";
193+
let and_ = String::from(" AND ") + column_name + &op.as_str(self.query.params.len() + 1);
202194

203195
self.query.sql.push_str(&and_);
204196
self.query.params.push(value);
@@ -207,12 +199,7 @@ where
207199
pub fn or<Z: FieldValueIdentifier<'a, T>>(&mut self, r#and: Z, op: impl Operator) {
208200
let (column_name, value) = r#and.value();
209201

210-
let and_ = String::from(" OR ")
211-
+ column_name
212-
+ op.as_str()
213-
+ "$"
214-
+ &(self.query.params.len() + 1).to_string()
215-
+ " ";
202+
let and_ = String::from(" OR ") + column_name + &op.as_str(self.query.params.len() + 1);
216203

217204
self.query.sql.push_str(&and_);
218205
self.query.params.push(value);
@@ -246,7 +233,7 @@ where
246233
self.query.params.push(qp)
247234
});
248235

249-
self.query.sql.push_str(") ");
236+
self.query.sql.push_str(")");
250237
}
251238

252239
fn or_values_in<Z, Q>(&mut self, r#or: Z, values: &'a [Q])
@@ -277,7 +264,7 @@ where
277264
self.query.params.push(qp)
278265
});
279266

280-
self.query.sql.push_str(") ");
267+
self.query.sql.push_str(")");
281268
}
282269

283270
#[inline]

tests/crud/querybuilder_operations.rs

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
///
77
use canyon_sql::{
88
crud::CrudOperations,
9-
query::{operators::Comp, ops::QueryBuilder},
9+
query::{operators::Comp, operators::Like, ops::QueryBuilder},
1010
};
1111

1212
#[cfg(feature = "mssql")]
@@ -34,7 +34,7 @@ fn test_generated_sql_by_the_select_querybuilder() {
3434
// generated SQL by the SelectQueryBuilder<T> is the spected
3535
assert_eq!(
3636
select_with_joins.read_sql(),
37-
"SELECT * FROM league INNER JOIN tournament ON league.id = tournament.league_id LEFT JOIN team ON tournament.id = player.tournament_id WHERE id > $1 AND name = $2 AND name IN ($2, $3) "
37+
"SELECT * FROM league INNER JOIN tournament ON league.id = tournament.league_id LEFT JOIN team ON tournament.id = player.tournament_id WHERE id > $1 AND name = $2 AND name IN ($2, $3)"
3838
)
3939
}
4040

@@ -59,6 +59,96 @@ fn test_crud_find_with_querybuilder() {
5959
assert_eq!(league_idx_0.region, "KOREA");
6060
}
6161

62+
/// Builds a new SQL statement for retrieves entities of the `T` type, filtered
63+
/// with the parameters that modifies the base SQL to SELECT * FROM <entity>
64+
#[cfg(feature = "postgres")]
65+
#[canyon_sql::macros::canyon_tokio_test]
66+
fn test_crud_find_with_querybuilder_and_fulllike() {
67+
// Find all the leagues with "LC" in their name
68+
let mut filtered_leagues_result = League::select_query();
69+
filtered_leagues_result.r#where(LeagueFieldValue::name(&"LC"), Like::Full);
70+
71+
assert_eq!(
72+
filtered_leagues_result.read_sql(),
73+
"SELECT * FROM league WHERE name LIKE CONCAT('%', CAST($1 AS VARCHAR) ,'%')"
74+
)
75+
}
76+
77+
/// Builds a new SQL statement for retrieves entities of the `T` type, filtered
78+
/// with the parameters that modifies the base SQL to SELECT * FROM <entity>
79+
#[cfg(feature = "mssql")]
80+
#[canyon_sql::macros::canyon_tokio_test]
81+
fn test_crud_find_with_querybuilder_and_fulllike_datasource() {
82+
// Find all the leagues with "LC" in their name
83+
let mut filtered_leagues_result = League::select_query_datasource(SQL_SERVER_DS);
84+
filtered_leagues_result.r#where(LeagueFieldValue::name(&"LC"), Like::Full);
85+
86+
assert_eq!(
87+
filtered_leagues_result.read_sql(),
88+
"SELECT * FROM league WHERE name LIKE CONCAT('%', CAST($1 AS VARCHAR) ,'%')"
89+
)
90+
}
91+
92+
/// Builds a new SQL statement for retrieves entities of the `T` type, filtered
93+
/// with the parameters that modifies the base SQL to SELECT * FROM <entity>
94+
#[cfg(feature = "postgres")]
95+
#[canyon_sql::macros::canyon_tokio_test]
96+
fn test_crud_find_with_querybuilder_and_leftlike() {
97+
// Find all the leagues whose name ends with "CK"
98+
let mut filtered_leagues_result = League::select_query();
99+
filtered_leagues_result.r#where(LeagueFieldValue::name(&"CK"), Like::Left);
100+
101+
assert_eq!(
102+
filtered_leagues_result.read_sql(),
103+
"SELECT * FROM league WHERE name LIKE CONCAT('%', CAST($1 AS VARCHAR))"
104+
)
105+
}
106+
107+
/// Builds a new SQL statement for retrieves entities of the `T` type, filtered
108+
/// with the parameters that modifies the base SQL to SELECT * FROM <entity>
109+
#[cfg(feature = "mssql")]
110+
#[canyon_sql::macros::canyon_tokio_test]
111+
fn test_crud_find_with_querybuilder_and_leftlike_datasource() {
112+
// Find all the leagues whose name ends with "CK"
113+
let mut filtered_leagues_result = League::select_query();
114+
filtered_leagues_result.r#where(LeagueFieldValue::name(&"CK"), Like::Left);
115+
116+
assert_eq!(
117+
filtered_leagues_result.read_sql(),
118+
"SELECT * FROM league WHERE name LIKE CONCAT('%', CAST($1 AS VARCHAR))"
119+
)
120+
}
121+
122+
/// Builds a new SQL statement for retrieves entities of the `T` type, filtered
123+
/// with the parameters that modifies the base SQL to SELECT * FROM <entity>
124+
#[cfg(feature = "postgres")]
125+
#[canyon_sql::macros::canyon_tokio_test]
126+
fn test_crud_find_with_querybuilder_and_rightlike() {
127+
// Find all the leagues whose name starts with "LC"
128+
let mut filtered_leagues_result = League::select_query();
129+
filtered_leagues_result.r#where(LeagueFieldValue::name(&"LC"), Like::Right);
130+
131+
assert_eq!(
132+
filtered_leagues_result.read_sql(),
133+
"SELECT * FROM league WHERE name LIKE CONCAT(CAST($1 AS VARCHAR) ,'%')"
134+
)
135+
}
136+
137+
/// Builds a new SQL statement for retrieves entities of the `T` type, filtered
138+
/// with the parameters that modifies the base SQL to SELECT * FROM <entity>
139+
#[cfg(feature = "mssql")]
140+
#[canyon_sql::macros::canyon_tokio_test]
141+
fn test_crud_find_with_querybuilder_and_rightlike_datasource() {
142+
// Find all the leagues whose name starts with "LC"
143+
let mut filtered_leagues_result = League::select_query_datasource(SQL_SERVER_DS);
144+
filtered_leagues_result.r#where(LeagueFieldValue::name(&"LC"), Like::Right);
145+
146+
assert_eq!(
147+
filtered_leagues_result.read_sql(),
148+
"SELECT * FROM league WHERE name LIKE CONCAT(CAST($1 AS VARCHAR) ,'%')"
149+
)
150+
}
151+
62152
/// Same than the above but with the specified datasource
63153
#[cfg(feature = "mssql")]
64154
#[canyon_sql::macros::canyon_tokio_test]
@@ -239,7 +329,7 @@ fn test_or_clause_with_in_constraint() {
239329

240330
assert_eq!(
241331
l.read_sql(),
242-
"SELECT * FROM league WHERE name = $1 OR id IN ($1, $2, $3) "
332+
"SELECT * FROM league WHERE name = $1 OR id IN ($1, $2, $3)"
243333
)
244334
}
245335

0 commit comments

Comments
 (0)