1use 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_eq(mut self, field: impl Into<String>, value: Value) -> Self {
78 self.qb = self.qb.where_eq(field, value);
79 self
80 }
81
82 #[must_use]
84 pub fn where_ne(mut self, field: impl Into<String>, value: Value) -> Self {
85 self.qb = self.qb.where_ne(field, value);
86 self
87 }
88
89 #[must_use]
91 pub fn where_gt(mut self, field: impl Into<String>, value: Value) -> Self {
92 self.qb = self.qb.where_gt(field, value);
93 self
94 }
95
96 #[must_use]
98 pub fn where_ge(mut self, field: impl Into<String>, value: Value) -> Self {
99 self.qb = self.qb.where_ge(field, value);
100 self
101 }
102
103 #[must_use]
105 pub fn where_lt(mut self, field: impl Into<String>, value: Value) -> Self {
106 self.qb = self.qb.where_lt(field, value);
107 self
108 }
109
110 #[must_use]
112 pub fn where_le(mut self, field: impl Into<String>, value: Value) -> Self {
113 self.qb = self.qb.where_le(field, value);
114 self
115 }
116
117 #[must_use]
119 pub fn where_like(mut self, field: impl Into<String>, pattern: Value) -> Self {
120 self.qb = self.qb.where_like(field, pattern);
121 self
122 }
123
124 #[must_use]
126 pub fn or_where_eq(mut self, field: impl Into<String>, value: Value) -> Self {
127 self.qb = self.qb.or_where_eq(field, value);
128 self
129 }
130
131 #[must_use]
133 pub fn or_where_ne(mut self, field: impl Into<String>, value: Value) -> Self {
134 self.qb = self.qb.or_where_ne(field, value);
135 self
136 }
137
138 #[must_use]
140 pub fn or_where_gt(mut self, field: impl Into<String>, value: Value) -> Self {
141 self.qb = self.qb.or_where_gt(field, value);
142 self
143 }
144
145 #[must_use]
147 pub fn or_where_ge(mut self, field: impl Into<String>, value: Value) -> Self {
148 self.qb = self.qb.or_where_ge(field, value);
149 self
150 }
151
152 #[must_use]
154 pub fn or_where_lt(mut self, field: impl Into<String>, value: Value) -> Self {
155 self.qb = self.qb.or_where_lt(field, value);
156 self
157 }
158
159 #[must_use]
161 pub fn or_where_le(mut self, field: impl Into<String>, value: Value) -> Self {
162 self.qb = self.qb.or_where_le(field, value);
163 self
164 }
165
166 #[must_use]
168 pub fn or_where_like(mut self, field: impl Into<String>, pattern: Value) -> Self {
169 self.qb = self.qb.or_where_like(field, pattern);
170 self
171 }
172
173 #[must_use]
175 pub fn where_in(mut self, field: impl Into<String>, values: Vec<Value>) -> Self {
176 self.qb = self.qb.where_in(field, values);
177 self
178 }
179
180 #[must_use]
182 pub fn where_not_in(mut self, field: impl Into<String>, values: Vec<Value>) -> Self {
183 self.qb = self.qb.where_not_in(field, values);
184 self
185 }
186
187 #[must_use]
189 pub fn where_between(mut self, field: impl Into<String>, start: Value, end: Value) -> Self {
190 self.qb = self.qb.where_between(field, start, end);
191 self
192 }
193
194 #[must_use]
196 pub fn where_null(mut self, field: impl Into<String>) -> Self {
197 self.qb = self.qb.where_null(field);
198 self
199 }
200
201 #[must_use]
203 pub fn where_not_null(mut self, field: impl Into<String>) -> Self {
204 self.qb = self.qb.where_not_null(field);
205 self
206 }
207
208 #[must_use]
210 pub fn order_by(mut self, field: impl Into<String>) -> Self {
211 self.qb = self.qb.order_by(field);
212 self
213 }
214
215 #[must_use]
217 pub fn order_desc(mut self, field: impl Into<String>) -> Self {
218 self.qb = self.qb.order_desc(field);
219 self
220 }
221
222 #[must_use]
224 pub fn group_by(mut self, field: impl Into<String>) -> Self {
225 self.qb = self.qb.group_by(field);
226 self
227 }
228
229 #[must_use]
231 pub fn having(mut self, condition: impl Into<String>) -> Self {
232 self.qb = self.qb.having(condition);
233 self
234 }
235
236 #[must_use]
238 pub fn limit(mut self, limit: usize) -> Self {
239 self.qb = self.qb.limit(limit);
240 self
241 }
242
243 #[must_use]
245 pub fn offset(mut self, offset: usize) -> Self {
246 self.qb = self.qb.offset(offset);
247 self
248 }
249
250 #[must_use]
252 pub fn page(mut self, page: usize, page_size: usize) -> Self {
253 self.qb = self.qb.page(page, page_size);
254 self
255 }
256
257 #[must_use]
259 pub fn join_inner(
260 mut self,
261 table: impl Into<String>,
262 on_left: impl Into<String>,
263 on_right: impl Into<String>,
264 ) -> Self {
265 self.qb = self.qb.join_inner(table, on_left, on_right);
266 self
267 }
268
269 #[must_use]
271 pub fn join_left(
272 mut self,
273 table: impl Into<String>,
274 on_left: impl Into<String>,
275 on_right: impl Into<String>,
276 ) -> Self {
277 self.qb = self.qb.join_left(table, on_left, on_right);
278 self
279 }
280
281 #[must_use]
283 pub fn join_right(
284 mut self,
285 table: impl Into<String>,
286 on_left: impl Into<String>,
287 on_right: impl Into<String>,
288 ) -> Self {
289 self.qb = self.qb.join_right(table, on_left, on_right);
290 self
291 }
292
293 pub fn build_select(&self) -> String {
295 self.qb.build_select()
296 }
297
298 pub fn build_insert(&self, data: &HashMap<String, Value>) -> String {
300 self.qb.build_insert(data)
301 }
302
303 pub fn build_update(&self, data: &HashMap<String, Value>) -> String {
305 self.qb.build_update(data)
306 }
307
308 pub fn build_delete(&self) -> String {
310 self.qb.build_delete()
311 }
312
313 pub fn build_count(&self) -> String {
315 self.qb.build_count()
316 }
317
318 pub fn build_exists(&self) -> String {
320 self.qb.build_exists()
321 }
322
323 pub fn build_max(&self, field: &str) -> String {
325 self.qb.build_max(field)
326 }
327
328 pub fn build_min(&self, field: &str) -> String {
330 self.qb.build_min(field)
331 }
332
333 pub fn build_sum(&self, field: &str) -> String {
335 self.qb.build_sum(field)
336 }
337
338 pub fn build_avg(&self, field: &str) -> String {
340 self.qb.build_avg(field)
341 }
342}
343
344#[cfg(test)]
345#[allow(deprecated)]
346mod tests {
347 use super::*;
348 use crate::db_type::DbType;
349 use crate::dialect::get_dialect;
350
351 fn mysql() -> Box<dyn Dialect> {
352 get_dialect(DbType::MySQL).expect("MySQL dialect")
353 }
354
355 fn pg() -> Box<dyn Dialect> {
356 get_dialect(DbType::PostgreSQL).expect("PG dialect")
357 }
358
359 #[test]
360 fn db_name_basic_select() {
361 let sql = Db::new(mysql()).name("users").build_select();
362 assert_eq!(sql, "SELECT * FROM `users`");
363 }
364
365 #[test]
366 fn db_name_with_where_and_limit() {
367 let sql = Db::new(mysql())
368 .name("users")
369 .where_gt("age", Value::I64(18))
370 .order_desc("id")
371 .limit(10)
372 .build_select();
373 assert!(sql.contains("SELECT * FROM `users`"));
374 assert!(sql.contains("WHERE `age` > 18"));
375 assert!(sql.contains("ORDER BY `id` DESC"));
376 assert!(sql.contains("LIMIT 10"));
377 }
378
379 #[test]
380 fn db_name_insert() {
381 let mut data = HashMap::new();
382 data.insert("name".to_string(), Value::String("Alice".to_string()));
383 data.insert("age".to_string(), Value::I64(30));
384 let sql = Db::new(mysql()).name("users").build_insert(&data);
385 assert!(sql.starts_with("INSERT INTO `users`"));
386 assert!(sql.contains("`name`"));
387 assert!(sql.contains("`age`"));
388 assert!(sql.contains("'Alice'"));
389 assert!(sql.contains("30"));
390 }
391
392 #[test]
393 fn db_name_update_with_where() {
394 let mut data = HashMap::new();
395 data.insert("name".to_string(), Value::String("Bob".to_string()));
396 let sql = Db::new(mysql())
397 .name("users")
398 .where_eq("id", Value::I64(1))
399 .build_update(&data);
400 assert!(sql.starts_with("UPDATE `users` SET"));
401 assert!(sql.contains("`name` = 'Bob'"));
402 assert!(sql.contains("WHERE `id` = 1"));
403 }
404
405 #[test]
406 fn db_name_delete_with_where() {
407 let sql = Db::new(mysql())
408 .name("users")
409 .where_eq("id", Value::I64(1))
410 .build_delete();
411 assert!(sql.contains("DELETE FROM `users` WHERE `id` = 1"));
412 }
413
414 #[test]
415 fn db_name_count() {
416 let sql = Db::new(mysql())
417 .name("users")
418 .where_gt("age", Value::I64(18))
419 .build_count();
420 assert!(sql.contains("SELECT COUNT(*)"));
421 assert!(sql.contains("FROM `users`"));
422 assert!(sql.contains("WHERE `age` > 18"));
423 }
424
425 #[test]
426 fn db_name_with_in_clause() {
427 let sql = Db::new(mysql())
428 .name("users")
429 .where_in("id", vec![Value::I64(1), Value::I64(2), Value::I64(3)])
430 .build_select();
431 assert!(sql.contains("WHERE `id` IN (1, 2, 3)"));
432 }
433
434 #[test]
435 fn db_name_with_between() {
436 let sql = Db::new(mysql())
437 .name("orders")
438 .where_between("amount", Value::I64(100), Value::I64(1000))
439 .build_select();
440 assert!(sql.contains("`amount` BETWEEN 100 AND 1000"));
441 }
442
443 #[test]
444 fn db_name_pg_dialect() {
445 let sql = Db::new(pg()).name("users").build_select();
446 assert_eq!(sql, "SELECT * FROM \"users\"");
447 }
448
449 #[test]
450 fn db_name_join_inner() {
451 let sql = Db::new(mysql())
452 .name("orders")
453 .join_inner("users", "orders.user_id", "users.id")
454 .build_select();
455 assert!(sql.contains("INNER JOIN `users` ON `orders.user_id` = `users.id`"));
456 }
457
458 #[test]
459 fn db_name_pagination() {
460 let sql = Db::new(mysql()).name("users").page(3, 20).build_select();
461 assert!(sql.contains("LIMIT 20"));
463 assert!(sql.contains("OFFSET 40"));
464 }
465
466 #[test]
467 fn db_name_aggregate_functions() {
468 let db = Db::new(mysql())
469 .name("orders")
470 .where_eq("status", Value::String("paid".into()));
471 assert!(db.build_sum("amount").contains("SUM(`amount`)"));
472 assert!(db.build_max("amount").contains("MAX(`amount`)"));
473 assert!(db.build_min("amount").contains("MIN(`amount`)"));
474 assert!(db.build_avg("amount").contains("AVG(`amount`)"));
475 assert!(db.build_exists().contains("SELECT EXISTS("));
476 }
477
478 #[test]
479 fn db_name_chained_or_where() {
480 let sql = Db::new(mysql())
481 .name("users")
482 .where_lt("age", Value::I64(18))
483 .or_where_gt("age", Value::I64(65))
484 .build_select();
485 assert!(sql.contains("WHERE (`age` < 18 OR `age` > 65)"));
486 }
487
488 #[test]
489 fn db_name_group_having() {
490 let sql = Db::new(mysql())
491 .name("orders")
492 .select(vec!["user_id", "COUNT(*) as cnt"])
493 .group_by("user_id")
494 .having("COUNT(*) > 5")
495 .build_select();
496 assert!(sql.contains("GROUP BY `user_id`"));
497 assert!(sql.contains("HAVING COUNT(*) > 5"));
498 }
499}