Skip to main content

sz_orm_core/
quick_query.rs

1//! 快捷查询(Db::name 风格)
2//!
3//! 对应 think-orm 的 `Db::name('user')->where(...)->select()` API。
4//! 无需定义 Model 即可直接基于表名查询/插入/更新/删除。
5//!
6//! # 与 QueryBuilder 的关系
7//!
8//! `QueryBuilder<M>` 要求泛型参数 `M: Model`,适合已知 Model 类型的场景。
9//! `QuickQuery` 则用 `()` 占位 Model,仅依赖表名,避免为临时查询定义 Model。
10//!
11//! # 用法
12//!
13//! ```no_run
14//! use sz_orm_core::quick_query::Db;
15//! use sz_orm_core::{get_dialect, DbType, Value};
16//!
17//! let dialect = get_dialect(DbType::MySQL).unwrap();
18//! // SELECT * FROM users WHERE age > 18 ORDER BY id DESC LIMIT 10
19//! let sql = Db::new(dialect).name("users")
20//!     .where_cond("age > 18")
21//!     .order_desc("id")
22//!     .limit(10)
23//!     .build_select();
24//! ```
25
26use crate::dialect::Dialect;
27use crate::query::QueryBuilder;
28use crate::value::Value;
29use std::collections::HashMap;
30
31/// 内部占位 Model:仅用于满足 `QueryBuilder<M>` 的泛型约束,不携带任何行为
32#[derive(Clone)]
33struct AnonymousModel;
34
35impl crate::model::Model for AnonymousModel {
36    type PrimaryKey = i64;
37    fn table_name() -> &'static str {
38        ""
39    }
40    fn pk(&self) -> Self::PrimaryKey {
41        0
42    }
43    fn set_pk(&mut self, _pk: Self::PrimaryKey) {}
44}
45
46/// 快捷查询入口(think-orm `Db::name()` 风格)
47///
48/// 不要求定义 Model,仅靠表名 + 方言即可生成 SQL。
49pub struct Db {
50    qb: QueryBuilder<AnonymousModel>,
51}
52
53impl Db {
54    /// 创建快捷查询入口
55    pub fn new(dialect: Box<dyn Dialect>) -> Self {
56        Self {
57            qb: QueryBuilder::new(dialect),
58        }
59    }
60
61    /// 指定表名(等价于 think-orm 的 `Db::name('user')`)
62    #[must_use]
63    pub fn name(mut self, table: impl Into<String>) -> Self {
64        self.qb = self.qb.table(table);
65        self
66    }
67
68    /// 选择列
69    #[must_use]
70    pub fn select(mut self, columns: Vec<&str>) -> Self {
71        self.qb = self.qb.select(columns);
72        self
73    }
74
75    /// WHERE 条件(AND)
76    #[must_use]
77    pub fn where_cond(mut self, condition: impl Into<String>) -> Self {
78        self.qb = self.qb.where_cond(condition);
79        self
80    }
81
82    /// WHERE 条件(OR)
83    #[must_use]
84    pub fn or_where(mut self, condition: impl Into<String>) -> Self {
85        self.qb = self.qb.or_where(condition);
86        self
87    }
88
89    /// WHERE IN
90    #[must_use]
91    pub fn where_in(mut self, field: impl Into<String>, values: Vec<Value>) -> Self {
92        self.qb = self.qb.where_in(field, values);
93        self
94    }
95
96    /// WHERE NOT IN
97    #[must_use]
98    pub fn where_not_in(mut self, field: impl Into<String>, values: Vec<Value>) -> Self {
99        self.qb = self.qb.where_not_in(field, values);
100        self
101    }
102
103    /// WHERE BETWEEN
104    #[must_use]
105    pub fn where_between(mut self, field: impl Into<String>, start: Value, end: Value) -> Self {
106        self.qb = self.qb.where_between(field, start, end);
107        self
108    }
109
110    /// WHERE IS NULL
111    #[must_use]
112    pub fn where_null(mut self, field: impl Into<String>) -> Self {
113        self.qb = self.qb.where_null(field);
114        self
115    }
116
117    /// WHERE IS NOT NULL
118    #[must_use]
119    pub fn where_not_null(mut self, field: impl Into<String>) -> Self {
120        self.qb = self.qb.where_not_null(field);
121        self
122    }
123
124    /// ORDER BY field ASC
125    #[must_use]
126    pub fn order_by(mut self, field: impl Into<String>) -> Self {
127        self.qb = self.qb.order_by(field);
128        self
129    }
130
131    /// ORDER BY field DESC
132    #[must_use]
133    pub fn order_desc(mut self, field: impl Into<String>) -> Self {
134        self.qb = self.qb.order_desc(field);
135        self
136    }
137
138    /// GROUP BY
139    #[must_use]
140    pub fn group_by(mut self, field: impl Into<String>) -> Self {
141        self.qb = self.qb.group_by(field);
142        self
143    }
144
145    /// HAVING
146    #[must_use]
147    pub fn having(mut self, condition: impl Into<String>) -> Self {
148        self.qb = self.qb.having(condition);
149        self
150    }
151
152    /// LIMIT
153    #[must_use]
154    pub fn limit(mut self, limit: usize) -> Self {
155        self.qb = self.qb.limit(limit);
156        self
157    }
158
159    /// OFFSET
160    #[must_use]
161    pub fn offset(mut self, offset: usize) -> Self {
162        self.qb = self.qb.offset(offset);
163        self
164    }
165
166    /// 分页(page 从 1 开始)
167    #[must_use]
168    pub fn page(mut self, page: usize, page_size: usize) -> Self {
169        self.qb = self.qb.page(page, page_size);
170        self
171    }
172
173    /// INNER JOIN
174    #[must_use]
175    pub fn join_inner(
176        mut self,
177        table: impl Into<String>,
178        on_left: impl Into<String>,
179        on_right: impl Into<String>,
180    ) -> Self {
181        self.qb = self.qb.join_inner(table, on_left, on_right);
182        self
183    }
184
185    /// LEFT JOIN
186    #[must_use]
187    pub fn join_left(
188        mut self,
189        table: impl Into<String>,
190        on_left: impl Into<String>,
191        on_right: impl Into<String>,
192    ) -> Self {
193        self.qb = self.qb.join_left(table, on_left, on_right);
194        self
195    }
196
197    /// RIGHT JOIN
198    #[must_use]
199    pub fn join_right(
200        mut self,
201        table: impl Into<String>,
202        on_left: impl Into<String>,
203        on_right: impl Into<String>,
204    ) -> Self {
205        self.qb = self.qb.join_right(table, on_left, on_right);
206        self
207    }
208
209    /// 构建 SELECT SQL
210    pub fn build_select(&self) -> String {
211        self.qb.build_select()
212    }
213
214    /// 构建 INSERT SQL
215    pub fn build_insert(&self, data: &HashMap<String, Value>) -> String {
216        self.qb.build_insert(data)
217    }
218
219    /// 构建 UPDATE SQL
220    pub fn build_update(&self, data: &HashMap<String, Value>) -> String {
221        self.qb.build_update(data)
222    }
223
224    /// 构建 DELETE SQL
225    pub fn build_delete(&self) -> String {
226        self.qb.build_delete()
227    }
228
229    /// 构建 COUNT SQL
230    pub fn build_count(&self) -> String {
231        self.qb.build_count()
232    }
233
234    /// 构建 EXISTS SQL
235    pub fn build_exists(&self) -> String {
236        self.qb.build_exists()
237    }
238
239    /// 构建 MAX SQL
240    pub fn build_max(&self, field: &str) -> String {
241        self.qb.build_max(field)
242    }
243
244    /// 构建 MIN SQL
245    pub fn build_min(&self, field: &str) -> String {
246        self.qb.build_min(field)
247    }
248
249    /// 构建 SUM SQL
250    pub fn build_sum(&self, field: &str) -> String {
251        self.qb.build_sum(field)
252    }
253
254    /// 构建 AVG SQL
255    pub fn build_avg(&self, field: &str) -> String {
256        self.qb.build_avg(field)
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263    use crate::db_type::DbType;
264    use crate::dialect::get_dialect;
265
266    fn mysql() -> Box<dyn Dialect> {
267        get_dialect(DbType::MySQL).expect("MySQL dialect")
268    }
269
270    fn pg() -> Box<dyn Dialect> {
271        get_dialect(DbType::PostgreSQL).expect("PG dialect")
272    }
273
274    #[test]
275    fn db_name_basic_select() {
276        let sql = Db::new(mysql()).name("users").build_select();
277        assert_eq!(sql, "SELECT * FROM `users`");
278    }
279
280    #[test]
281    fn db_name_with_where_and_limit() {
282        let sql = Db::new(mysql())
283            .name("users")
284            .where_cond("age > 18")
285            .order_desc("id")
286            .limit(10)
287            .build_select();
288        assert!(sql.contains("SELECT * FROM `users`"));
289        assert!(sql.contains("WHERE age > 18"));
290        assert!(sql.contains("ORDER BY `id` DESC"));
291        assert!(sql.contains("LIMIT 10"));
292    }
293
294    #[test]
295    fn db_name_insert() {
296        let mut data = HashMap::new();
297        data.insert("name".to_string(), Value::String("Alice".to_string()));
298        data.insert("age".to_string(), Value::I64(30));
299        let sql = Db::new(mysql()).name("users").build_insert(&data);
300        assert!(sql.starts_with("INSERT INTO `users`"));
301        assert!(sql.contains("`name`"));
302        assert!(sql.contains("`age`"));
303        assert!(sql.contains("'Alice'"));
304        assert!(sql.contains("30"));
305    }
306
307    #[test]
308    fn db_name_update_with_where() {
309        let mut data = HashMap::new();
310        data.insert("name".to_string(), Value::String("Bob".to_string()));
311        let sql = Db::new(mysql())
312            .name("users")
313            .where_cond("id = 1")
314            .build_update(&data);
315        assert!(sql.starts_with("UPDATE `users` SET"));
316        assert!(sql.contains("`name` = 'Bob'"));
317        assert!(sql.contains("WHERE id = 1"));
318    }
319
320    #[test]
321    fn db_name_delete_with_where() {
322        let sql = Db::new(mysql())
323            .name("users")
324            .where_cond("id = 1")
325            .build_delete();
326        assert_eq!(sql, "DELETE FROM `users` WHERE id = 1");
327    }
328
329    #[test]
330    fn db_name_count() {
331        let sql = Db::new(mysql())
332            .name("users")
333            .where_cond("age > 18")
334            .build_count();
335        assert!(sql.contains("SELECT COUNT(*)"));
336        assert!(sql.contains("FROM `users`"));
337        assert!(sql.contains("WHERE age > 18"));
338    }
339
340    #[test]
341    fn db_name_with_in_clause() {
342        let sql = Db::new(mysql())
343            .name("users")
344            .where_in("id", vec![Value::I64(1), Value::I64(2), Value::I64(3)])
345            .build_select();
346        assert!(sql.contains("WHERE `id` IN (1, 2, 3)"));
347    }
348
349    #[test]
350    fn db_name_with_between() {
351        let sql = Db::new(mysql())
352            .name("orders")
353            .where_between("amount", Value::I64(100), Value::I64(1000))
354            .build_select();
355        assert!(sql.contains("`amount` BETWEEN 100 AND 1000"));
356    }
357
358    #[test]
359    fn db_name_pg_dialect() {
360        let sql = Db::new(pg()).name("users").build_select();
361        assert_eq!(sql, "SELECT * FROM \"users\"");
362    }
363
364    #[test]
365    fn db_name_join_inner() {
366        let sql = Db::new(mysql())
367            .name("orders")
368            .join_inner("users", "orders.user_id", "users.id")
369            .build_select();
370        assert!(sql.contains("INNER JOIN `users` ON `orders.user_id` = `users.id`"));
371    }
372
373    #[test]
374    fn db_name_pagination() {
375        let sql = Db::new(mysql()).name("users").page(3, 20).build_select();
376        // 第 3 页,每页 20 条 → LIMIT 20 OFFSET 40
377        assert!(sql.contains("LIMIT 20"));
378        assert!(sql.contains("OFFSET 40"));
379    }
380
381    #[test]
382    fn db_name_aggregate_functions() {
383        let db = Db::new(mysql())
384            .name("orders")
385            .where_cond("status = 'paid'");
386        assert!(db.build_sum("amount").contains("SUM(`amount`)"));
387        assert!(db.build_max("amount").contains("MAX(`amount`)"));
388        assert!(db.build_min("amount").contains("MIN(`amount`)"));
389        assert!(db.build_avg("amount").contains("AVG(`amount`)"));
390        assert!(db.build_exists().contains("SELECT EXISTS("));
391    }
392
393    #[test]
394    fn db_name_chained_or_where() {
395        let sql = Db::new(mysql())
396            .name("users")
397            .where_cond("age < 18")
398            .or_where("age > 65")
399            .build_select();
400        assert!(sql.contains("WHERE (age < 18 OR age > 65)"));
401    }
402
403    #[test]
404    fn db_name_group_having() {
405        let sql = Db::new(mysql())
406            .name("orders")
407            .select(vec!["user_id", "COUNT(*) as cnt"])
408            .group_by("user_id")
409            .having("COUNT(*) > 5")
410            .build_select();
411        assert!(sql.contains("GROUP BY `user_id`"));
412        assert!(sql.contains("HAVING COUNT(*) > 5"));
413    }
414}