Skip to main content

sql_orm/
query_predicates.rs

1use crate::AliasedEntityColumn;
2use sql_orm_core::{Entity, EntityColumn, SqlTypeMapping};
3use sql_orm_query::{Expr, Predicate};
4
5// The plan maestro fija explĂ­citamente `is_null` e `is_not_null` como API pĂșblica.
6#[allow(clippy::wrong_self_convention)]
7pub trait EntityColumnPredicateExt<E: Entity> {
8    fn eq<V>(self, value: V) -> Predicate
9    where
10        V: SqlTypeMapping;
11
12    fn ne<V>(self, value: V) -> Predicate
13    where
14        V: SqlTypeMapping;
15
16    fn gt<V>(self, value: V) -> Predicate
17    where
18        V: SqlTypeMapping;
19
20    fn gte<V>(self, value: V) -> Predicate
21    where
22        V: SqlTypeMapping;
23
24    fn lt<V>(self, value: V) -> Predicate
25    where
26        V: SqlTypeMapping;
27
28    fn lte<V>(self, value: V) -> Predicate
29    where
30        V: SqlTypeMapping;
31
32    fn is_null(self) -> Predicate;
33
34    fn is_not_null(self) -> Predicate;
35
36    fn contains(self, value: impl Into<String>) -> Predicate;
37
38    fn starts_with(self, value: impl Into<String>) -> Predicate;
39
40    fn ends_with(self, value: impl Into<String>) -> Predicate;
41}
42
43impl<E: Entity> EntityColumnPredicateExt<E> for EntityColumn<E> {
44    fn eq<V>(self, value: V) -> Predicate
45    where
46        V: SqlTypeMapping,
47    {
48        Predicate::eq(Expr::from(self), Expr::value(value.to_sql_value()))
49    }
50
51    fn ne<V>(self, value: V) -> Predicate
52    where
53        V: SqlTypeMapping,
54    {
55        Predicate::ne(Expr::from(self), Expr::value(value.to_sql_value()))
56    }
57
58    fn gt<V>(self, value: V) -> Predicate
59    where
60        V: SqlTypeMapping,
61    {
62        Predicate::gt(Expr::from(self), Expr::value(value.to_sql_value()))
63    }
64
65    fn gte<V>(self, value: V) -> Predicate
66    where
67        V: SqlTypeMapping,
68    {
69        Predicate::gte(Expr::from(self), Expr::value(value.to_sql_value()))
70    }
71
72    fn lt<V>(self, value: V) -> Predicate
73    where
74        V: SqlTypeMapping,
75    {
76        Predicate::lt(Expr::from(self), Expr::value(value.to_sql_value()))
77    }
78
79    fn lte<V>(self, value: V) -> Predicate
80    where
81        V: SqlTypeMapping,
82    {
83        Predicate::lte(Expr::from(self), Expr::value(value.to_sql_value()))
84    }
85
86    fn is_null(self) -> Predicate {
87        Predicate::is_null(Expr::from(self))
88    }
89
90    fn is_not_null(self) -> Predicate {
91        Predicate::is_not_null(Expr::from(self))
92    }
93
94    fn contains(self, value: impl Into<String>) -> Predicate {
95        Predicate::like(
96            Expr::from(self),
97            Expr::value(sql_orm_core::SqlValue::String(format!(
98                "%{}%",
99                value.into()
100            ))),
101        )
102    }
103
104    fn starts_with(self, value: impl Into<String>) -> Predicate {
105        Predicate::like(
106            Expr::from(self),
107            Expr::value(sql_orm_core::SqlValue::String(format!("{}%", value.into()))),
108        )
109    }
110
111    fn ends_with(self, value: impl Into<String>) -> Predicate {
112        Predicate::like(
113            Expr::from(self),
114            Expr::value(sql_orm_core::SqlValue::String(format!("%{}", value.into()))),
115        )
116    }
117}
118
119impl<E: Entity> EntityColumnPredicateExt<E> for AliasedEntityColumn<E> {
120    fn eq<V>(self, value: V) -> Predicate
121    where
122        V: SqlTypeMapping,
123    {
124        Predicate::eq(Expr::from(self), Expr::value(value.to_sql_value()))
125    }
126
127    fn ne<V>(self, value: V) -> Predicate
128    where
129        V: SqlTypeMapping,
130    {
131        Predicate::ne(Expr::from(self), Expr::value(value.to_sql_value()))
132    }
133
134    fn gt<V>(self, value: V) -> Predicate
135    where
136        V: SqlTypeMapping,
137    {
138        Predicate::gt(Expr::from(self), Expr::value(value.to_sql_value()))
139    }
140
141    fn gte<V>(self, value: V) -> Predicate
142    where
143        V: SqlTypeMapping,
144    {
145        Predicate::gte(Expr::from(self), Expr::value(value.to_sql_value()))
146    }
147
148    fn lt<V>(self, value: V) -> Predicate
149    where
150        V: SqlTypeMapping,
151    {
152        Predicate::lt(Expr::from(self), Expr::value(value.to_sql_value()))
153    }
154
155    fn lte<V>(self, value: V) -> Predicate
156    where
157        V: SqlTypeMapping,
158    {
159        Predicate::lte(Expr::from(self), Expr::value(value.to_sql_value()))
160    }
161
162    fn is_null(self) -> Predicate {
163        Predicate::is_null(Expr::from(self))
164    }
165
166    fn is_not_null(self) -> Predicate {
167        Predicate::is_not_null(Expr::from(self))
168    }
169
170    fn contains(self, value: impl Into<String>) -> Predicate {
171        Predicate::like(
172            Expr::from(self),
173            Expr::value(sql_orm_core::SqlValue::String(format!(
174                "%{}%",
175                value.into()
176            ))),
177        )
178    }
179
180    fn starts_with(self, value: impl Into<String>) -> Predicate {
181        Predicate::like(
182            Expr::from(self),
183            Expr::value(sql_orm_core::SqlValue::String(format!("{}%", value.into()))),
184        )
185    }
186
187    fn ends_with(self, value: impl Into<String>) -> Predicate {
188        Predicate::like(
189            Expr::from(self),
190            Expr::value(sql_orm_core::SqlValue::String(format!("%{}", value.into()))),
191        )
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::EntityColumnPredicateExt;
198    use crate::EntityColumnAliasExt;
199    use sql_orm_core::{
200        ColumnMetadata, Entity, EntityColumn, EntityMetadata, PrimaryKeyMetadata, SqlServerType,
201        SqlValue,
202    };
203    use sql_orm_query::{ColumnRef, Expr, Predicate, TableRef};
204
205    struct TestEntity;
206
207    static TEST_ENTITY_COLUMNS: [ColumnMetadata; 2] = [
208        ColumnMetadata {
209            rust_field: "id",
210            column_name: "id",
211            renamed_from: None,
212            sql_type: SqlServerType::BigInt,
213            nullable: false,
214            primary_key: true,
215            identity: None,
216            default_sql: None,
217            computed_sql: None,
218            rowversion: false,
219            insertable: false,
220            updatable: false,
221            max_length: None,
222            precision: None,
223            scale: None,
224        },
225        ColumnMetadata {
226            rust_field: "name",
227            column_name: "name",
228            renamed_from: None,
229            sql_type: SqlServerType::NVarChar,
230            nullable: true,
231            primary_key: false,
232            identity: None,
233            default_sql: None,
234            computed_sql: None,
235            rowversion: false,
236            insertable: true,
237            updatable: true,
238            max_length: Some(120),
239            precision: None,
240            scale: None,
241        },
242    ];
243
244    static TEST_ENTITY_METADATA: EntityMetadata = EntityMetadata {
245        rust_name: "TestEntity",
246        schema: "dbo",
247        table: "test_entities",
248        renamed_from: None,
249        columns: &TEST_ENTITY_COLUMNS,
250        primary_key: PrimaryKeyMetadata {
251            name: None,
252            columns: &["id"],
253        },
254        indexes: &[],
255        foreign_keys: &[],
256        navigations: &[],
257    };
258
259    impl Entity for TestEntity {
260        fn metadata() -> &'static EntityMetadata {
261            &TEST_ENTITY_METADATA
262        }
263    }
264
265    #[allow(non_upper_case_globals)]
266    impl TestEntity {
267        const id: EntityColumn<TestEntity> = EntityColumn::new("id", "id");
268        const name: EntityColumn<TestEntity> = EntityColumn::new("name", "name");
269    }
270
271    #[test]
272    fn comparison_methods_build_expected_predicates() {
273        let expected_column = Expr::Column(ColumnRef::new(
274            TableRef::new("dbo", "test_entities"),
275            "id",
276            "id",
277        ));
278
279        assert_eq!(
280            TestEntity::id.eq(7_i64),
281            Predicate::eq(expected_column.clone(), Expr::Value(SqlValue::I64(7)))
282        );
283        assert_eq!(
284            TestEntity::id.ne(8_i64),
285            Predicate::ne(expected_column.clone(), Expr::Value(SqlValue::I64(8)))
286        );
287        assert_eq!(
288            TestEntity::id.gt(9_i64),
289            Predicate::gt(expected_column.clone(), Expr::Value(SqlValue::I64(9)))
290        );
291        assert_eq!(
292            TestEntity::id.gte(10_i64),
293            Predicate::gte(expected_column.clone(), Expr::Value(SqlValue::I64(10)))
294        );
295        assert_eq!(
296            TestEntity::id.lt(11_i64),
297            Predicate::lt(expected_column.clone(), Expr::Value(SqlValue::I64(11)))
298        );
299        assert_eq!(
300            TestEntity::id.lte(12_i64),
301            Predicate::lte(expected_column, Expr::Value(SqlValue::I64(12)))
302        );
303    }
304
305    #[test]
306    fn null_predicate_methods_build_expected_predicates() {
307        let expected_column = Expr::Column(ColumnRef::new(
308            TableRef::new("dbo", "test_entities"),
309            "name",
310            "name",
311        ));
312
313        assert_eq!(
314            TestEntity::name.is_null(),
315            Predicate::is_null(expected_column.clone())
316        );
317        assert_eq!(
318            TestEntity::name.is_not_null(),
319            Predicate::is_not_null(expected_column)
320        );
321    }
322
323    #[test]
324    fn string_predicate_methods_build_expected_like_patterns() {
325        let expected_column = Expr::Column(ColumnRef::new(
326            TableRef::new("dbo", "test_entities"),
327            "name",
328            "name",
329        ));
330
331        assert_eq!(
332            TestEntity::name.contains("ana"),
333            Predicate::like(
334                expected_column.clone(),
335                Expr::Value(SqlValue::String("%ana%".to_string()))
336            )
337        );
338        assert_eq!(
339            TestEntity::name.starts_with("ana"),
340            Predicate::like(
341                expected_column.clone(),
342                Expr::Value(SqlValue::String("ana%".to_string()))
343            )
344        );
345        assert_eq!(
346            TestEntity::name.ends_with("ana"),
347            Predicate::like(
348                expected_column,
349                Expr::Value(SqlValue::String("%ana".to_string()))
350            )
351        );
352    }
353
354    #[test]
355    fn aliased_columns_build_predicates_against_table_alias() {
356        let expected_column = Expr::Column(ColumnRef::new(
357            TableRef::with_alias("dbo", "test_entities", "t"),
358            "name",
359            "name",
360        ));
361
362        assert_eq!(
363            TestEntity::name.aliased("t").contains("ana"),
364            Predicate::like(
365                expected_column.clone(),
366                Expr::Value(SqlValue::String("%ana%".to_string()))
367            )
368        );
369        assert_eq!(
370            TestEntity::name.aliased("t").is_not_null(),
371            Predicate::is_not_null(expected_column)
372        );
373    }
374}