sz_orm_core/
quick_query.rs1use crate::dialect::Dialect;
27use crate::query::QueryBuilder;
28use crate::value::Value;
29use std::collections::HashMap;
30
31#[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
46pub struct Db {
50 qb: QueryBuilder<AnonymousModel>,
51}
52
53impl Db {
54 pub fn new(dialect: Box<dyn Dialect>) -> Self {
56 Self {
57 qb: QueryBuilder::new(dialect),
58 }
59 }
60
61 #[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 #[must_use]
70 pub fn select(mut self, columns: Vec<&str>) -> Self {
71 self.qb = self.qb.select(columns);
72 self
73 }
74
75 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[must_use]
154 pub fn limit(mut self, limit: usize) -> Self {
155 self.qb = self.qb.limit(limit);
156 self
157 }
158
159 #[must_use]
161 pub fn offset(mut self, offset: usize) -> Self {
162 self.qb = self.qb.offset(offset);
163 self
164 }
165
166 #[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 #[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 #[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 #[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 pub fn build_select(&self) -> String {
211 self.qb.build_select()
212 }
213
214 pub fn build_insert(&self, data: &HashMap<String, Value>) -> String {
216 self.qb.build_insert(data)
217 }
218
219 pub fn build_update(&self, data: &HashMap<String, Value>) -> String {
221 self.qb.build_update(data)
222 }
223
224 pub fn build_delete(&self) -> String {
226 self.qb.build_delete()
227 }
228
229 pub fn build_count(&self) -> String {
231 self.qb.build_count()
232 }
233
234 pub fn build_exists(&self) -> String {
236 self.qb.build_exists()
237 }
238
239 pub fn build_max(&self, field: &str) -> String {
241 self.qb.build_max(field)
242 }
243
244 pub fn build_min(&self, field: &str) -> String {
246 self.qb.build_min(field)
247 }
248
249 pub fn build_sum(&self, field: &str) -> String {
251 self.qb.build_sum(field)
252 }
253
254 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 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}