bottle_orm/query_builder.rs
1//! # Query Builder Module
2//!
3//! This module provides a fluent interface for constructing and executing SQL queries.
4//! It handles SELECT, INSERT, filtering (WHERE), pagination (LIMIT/OFFSET), and ordering operations
5//! with type-safe parameter binding across different database drivers.
6//!
7//! ## Features
8//!
9//! - **Fluent API**: Chainable methods for building complex queries
10//! - **Type-Safe Binding**: Automatic parameter binding with support for multiple types
11//! - **Multi-Driver Support**: Works with PostgreSQL, MySQL, and SQLite
12//! - **UUID Support**: Full support for UUID versions 1-7
13//! - **Pagination**: Built-in LIMIT/OFFSET support with helper methods
14//! - **Custom Filters**: Support for manual SQL construction with closures
15//!
16//! ## Example Usage
17//!
18//! ```rust,ignore
19//! use bottle_orm::{Database, Model};
20//!
21//!
22//! // Simple query
23//! let users: Vec<User> = db.model::<User>()
24//! .filter("age", ">=", 18)
25//! .order("created_at DESC")
26//! .limit(10)
27//! .scan()
28//! .await?;
29//!
30//! // Query with UUID filter
31//! let user_id = Uuid::new_v4();
32//! let user: User = db.model::<User>()
33//! .filter("id", "=", user_id)
34//! .first()
35//! .await?;
36//!
37//! // Insert a new record
38//! let new_user = User {
39//! id: Uuid::new_v7(uuid::Timestamp::now(uuid::NoContext)),
40//! username: "john_doe".to_string(),
41//! age: 25,
42//! };
43//! db.model::<User>().insert(&new_user).await?;
44//! ```
45
46// ============================================================================
47// External Crate Imports
48// ============================================================================
49
50use futures::future::BoxFuture;
51use heck::ToSnakeCase;
52use sqlx::{Any, Arguments, Decode, Encode, Type, any::AnyArguments};
53use std::marker::PhantomData;
54use std::collections::{HashMap, HashSet};
55
56
57// ============================================================================
58// Internal Crate Imports
59// ============================================================================
60
61use crate::{
62 AnyImpl, Error,
63 any_struct::FromAnyRow,
64 database::{Connection, Drivers},
65 model::{ColumnInfo, Model},
66 temporal::{self, is_temporal_type},
67 value_binding::ValueBinder,
68};
69
70// ============================================================================
71// Type Aliases
72// ============================================================================
73
74/// A type alias for filter closures that support manual SQL construction and argument binding.
75///
76/// Filter functions receive the following parameters:
77/// 1. `&mut String` - The SQL query buffer being built
78/// 2. `&mut AnyArguments` - The argument container for binding values
79/// 3. `&Drivers` - The current database driver (determines placeholder syntax)
80/// 4. `&mut usize` - The argument counter (for PostgreSQL `$n` placeholders)
81///
82/// ## Example
83///
84/// ```rust,ignore
85/// let custom_filter: FilterFn = Box::new(|query, args, driver, counter| {
86/// query.push_str(" AND age > ");
87/// match driver {
88/// Drivers::Postgres => {
89/// query.push_str(&format!("${}", counter));
90/// *counter += 1;
91/// }
92/// _ => query.push('?'),
93/// }
94/// args.add(18);
95/// });
96/// });\n/// ```
97pub type FilterFn = Box<dyn Fn(&mut String, &mut AnyArguments<'_>, &Drivers, &mut usize) + Send + Sync>;
98
99// ============================================================================
100// Update Value Traits
101// ============================================================================
102
103/// Trait for types that can be converted to an optional string value for SQL updates.
104///
105/// This trait is used by the `update` method to handle both direct values
106/// (e.g., `update("role", "admin")`) and NULL values (e.g., `update("role", None::<String>)`).
107pub trait ToUpdateValue {
108 /// Converts the value to an `Option<String>` for SQL binding.
109 ///
110 /// # Returns
111 ///
112 /// * `Some(String)` - String representation for a database value
113 /// * `None` - Represents a SQL `NULL`
114 fn to_update_value(self) -> Option<String>;
115}
116
117macro_rules! impl_update_value {
118 ($($t:ty),*) => {
119 $(
120 impl ToUpdateValue for $t {
121 fn to_update_value(self) -> Option<String> {
122 Some(self.to_string())
123 }
124 }
125 impl ToUpdateValue for Option<$t> {
126 fn to_update_value(self) -> Option<String> {
127 self.map(|v| v.to_string())
128 }
129 }
130 )*
131 }
132}
133
134impl_update_value!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64, bool, String, &str, uuid::Uuid);
135impl_update_value!(chrono::DateTime<chrono::Utc>, chrono::DateTime<chrono::FixedOffset>, chrono::NaiveDateTime, chrono::NaiveDate, chrono::NaiveTime);
136
137// ============================================================================
138// Comparison Operators Enum
139// ============================================================================
140
141/// Type-safe comparison operators for filter conditions.
142///
143/// Use these instead of string operators for autocomplete support and type safety.
144///
145/// # Example
146///
147/// ```rust,ignore
148/// use bottle_orm::Op;
149///
150/// db.model::<User>()
151/// .filter(user_fields::AGE, Op::Gte, 18)
152/// .filter(user_fields::NAME, Op::Like, "%John%")
153/// .scan()
154/// .await?;
155/// ```
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum Op {
158 /// Equal: `=`
159 Eq,
160 /// Not Equal: `!=` or `<>`
161 Ne,
162 /// Greater Than: `>`
163 Gt,
164 /// Greater Than or Equal: `>=`
165 Gte,
166 /// Less Than: `<`
167 Lt,
168 /// Less Than or Equal: `<=`
169 Lte,
170 /// SQL LIKE pattern matching
171 Like,
172 /// SQL NOT LIKE pattern matching
173 NotLike,
174 /// SQL IN (for arrays/lists)
175 In,
176 /// SQL NOT IN
177 NotIn,
178 /// SQL BETWEEN
179 Between,
180 /// SQL NOT BETWEEN
181 NotBetween,
182}
183
184impl Op {
185 /// Converts the operator to its SQL string representation.
186 pub fn as_sql(&self) -> &'static str {
187 match self {
188 Op::Eq => "=",
189 Op::Ne => "!=",
190 Op::Gt => ">",
191 Op::Gte => ">=",
192 Op::Lt => "<",
193 Op::Lte => "<=",
194 Op::Like => "LIKE",
195 Op::NotLike => "NOT LIKE",
196 Op::In => "IN",
197 Op::NotIn => "NOT IN",
198 Op::Between => "BETWEEN",
199 Op::NotBetween => "NOT BETWEEN",
200 }
201 }
202}
203
204// ============================================================================
205// QueryBuilder Struct
206// ============================================================================
207
208/// A fluent Query Builder for constructing SQL queries.
209///
210/// `QueryBuilder` provides a type-safe, ergonomic interface for building and executing
211/// SQL queries across different database backends. It supports filtering, ordering,
212/// pagination, and both SELECT and INSERT operations.
213///
214/// ## Type Parameter
215///
216/// * `'a` - Lifetime of the database reference (used for PhantomData)
217/// * `T` - The Model type this query operates on
218/// * `E` - The connection type (Database or Transaction)
219///
220/// ## Fields
221///
222/// * `db` - Reference to the database connection pool or transaction
223/// * `table_name` - Static string containing the table name
224/// * `columns_info` - Metadata about each column in the table
225/// * `columns` - List of column names in snake_case format
226/// * `select_columns` - Specific columns to select (empty = SELECT *)
227/// * `where_clauses` - List of filter functions to apply
228/// * `order_clauses` - List of ORDER BY clauses
229/// * `limit` - Maximum number of rows to return
230/// * `offset` - Number of rows to skip (for pagination)
231/// * `_marker` - PhantomData to bind the generic type T
232pub struct QueryBuilder<T, E> {
233 /// Reference to the database connection pool
234 pub(crate) tx: E,
235
236 /// Database driver type
237 pub(crate) driver: Drivers,
238
239 /// Name of the database table (in original case)
240 pub(crate) table_name: &'static str,
241
242 pub(crate) alias: Option<String>,
243
244 /// Metadata information about each column
245 pub(crate) columns_info: Vec<ColumnInfo>,
246
247 /// List of column names (in snake_case)
248 pub(crate) columns: Vec<String>,
249
250 /// Specific columns to select (empty means SELECT *)
251 pub(crate) select_columns: Vec<String>,
252
253 /// Collection of WHERE clause filter functions
254 pub(crate) where_clauses: Vec<FilterFn>,
255
256 /// Collection of ORDER BY clauses
257 pub(crate) order_clauses: Vec<String>,
258
259 /// Collection of JOIN clause to filter entry tables
260 pub(crate) joins_clauses: Vec<FilterFn>,
261
262 /// Map of table names to their aliases used in JOINS
263 pub(crate) join_aliases: std::collections::HashMap<String, String>,
264
265 /// Maximum number of rows to return (LIMIT)
266 pub(crate) limit: Option<usize>,
267
268 /// Number of rows to skip (OFFSET)
269 pub(crate) offset: Option<usize>,
270
271 /// Activate debug mode in query
272 pub(crate) debug_mode: bool,
273
274 /// Clauses for GROUP BY
275 pub(crate) group_by_clauses: Vec<String>,
276
277 /// Clauses for HAVING
278 pub(crate) having_clauses: Vec<FilterFn>,
279
280 /// Distinct flag
281 pub(crate) is_distinct: bool,
282
283 /// Columns to omit from the query results (inverse of select_columns)
284 pub(crate) omit_columns: Vec<String>,
285
286 /// Whether to include soft-deleted records in query results
287 pub(crate) with_deleted: bool,
288
289 /// UNION and UNION ALL clauses
290 pub(crate) union_clauses: Vec<(String, FilterFn)>,
291
292 /// PhantomData to bind the generic type T
293 pub(crate) _marker: PhantomData<T>,
294}
295
296// ============================================================================
297// QueryBuilder Implementation
298// ============================================================================
299
300impl<T, E> QueryBuilder<T, E>
301where
302 T: Model + Send + Sync + Unpin + AnyImpl,
303 E: Connection,
304{
305 // ========================================================================
306 // Constructor
307 // ========================================================================
308
309 /// Creates a new QueryBuilder instance.
310 ///
311 /// This constructor is typically called internally via `db.model::<T>()`.
312 /// You rarely need to call this directly.
313 ///
314 /// # Arguments
315 ///
316 /// * `db` - Reference to the database connection
317 /// * `table_name` - Name of the table to query
318 /// * `columns_info` - Metadata about table columns
319 /// * `columns` - List of column names
320 ///
321 /// # Returns
322 ///
323 /// A new `QueryBuilder` instance ready for query construction
324 ///
325 /// # Example
326 ///
327 /// ```rust,ignore
328 /// // Usually called via db.model::<User>()
329 /// let query = db.model::<User>();
330 /// ```
331 pub fn new(
332 tx: E,
333 driver: Drivers,
334 table_name: &'static str,
335 columns_info: Vec<ColumnInfo>,
336 columns: Vec<String>,
337 ) -> Self {
338 // Pre-populate omit_columns with globally omitted columns (from #[orm(omit)] attribute)
339 let omit_columns: Vec<String> =
340 columns_info.iter().filter(|c| c.omit).map(|c| c.name.to_snake_case()).collect();
341
342 Self {
343 tx,
344 alias: None,
345 driver,
346 table_name,
347 columns_info,
348 columns,
349 debug_mode: false,
350 select_columns: Vec::new(),
351 where_clauses: Vec::new(),
352 order_clauses: Vec::new(),
353 joins_clauses: Vec::new(),
354 join_aliases: std::collections::HashMap::new(),
355 group_by_clauses: Vec::new(),
356 having_clauses: Vec::new(),
357 is_distinct: false,
358 omit_columns,
359 limit: None,
360 offset: None,
361 with_deleted: false,
362 union_clauses: Vec::new(),
363 _marker: PhantomData,
364 }
365 }
366
367 /// Returns the table name or alias if set.
368 pub(crate) fn get_table_identifier(&self) -> String {
369 self.alias.clone().unwrap_or_else(|| self.table_name.to_snake_case())
370 }
371
372 // ========================================================================
373 // Query Building Methods
374 // ========================================================================
375
376 /// Internal helper to add a WHERE clause with a specific join operator.
377 fn filter_internal<V>(mut self, joiner: &str, col: &'static str, op: Op, value: V) -> Self
378 where
379 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
380 {
381 let op_str = op.as_sql();
382 let table_id = self.get_table_identifier();
383 // Check if the column exists in the main table to avoid ambiguous references in JOINS
384 let is_main_col = self.columns.contains(&col.to_snake_case());
385 let joiner_owned = joiner.to_string();
386 let clause: FilterFn = Box::new(move |query, args, driver, arg_counter| {
387 query.push_str(&joiner_owned);
388 if let Some((table, column)) = col.split_once(".") {
389 // If explicit table prefix is provided, use it
390 query.push_str(&format!("\"{}\".\"{}\"", table, column));
391 } else if is_main_col {
392 // If it's a known column of the main table, apply the table name/alias prefix
393 query.push_str(&format!("\"{}\".\"{}\"", table_id, col));
394 } else {
395 // Otherwise leave it unqualified so the DB can resolve it (or fail if ambiguous)
396 query.push_str(&format!("\"{}\"", col));
397 }
398 query.push(' ');
399 query.push_str(op_str);
400 query.push(' ');
401
402 // Handle different placeholder syntaxes based on database driver
403 match driver {
404 // PostgreSQL uses numbered placeholders: $1, $2, $3, ...
405 Drivers::Postgres => {
406 query.push_str(&format!("${}", arg_counter));
407 *arg_counter += 1;
408 }
409 // MySQL and SQLite use question mark placeholders: ?
410 _ => query.push('?'),
411 }
412
413 // Bind the value to the query
414 let _ = args.add(value.clone());
415 });
416
417 self.where_clauses.push(clause);
418 self
419 }
420
421 /// Adds a WHERE IN (SUBQUERY) clause to the query.
422 ///
423 /// This allows for filtering a column based on the results of another query.
424 ///
425 /// # Example
426 /// ```rust,ignore
427 /// let subquery = db.model::<Post>().select("user_id").filter("views", ">", 1000);
428 /// db.model::<User>().filter_subquery("id", Op::In, subquery).scan().await?;
429 /// ```
430 pub fn filter_subquery<S, SE>(mut self, col: &'static str, op: Op, mut subquery: QueryBuilder<S, SE>) -> Self
431 where
432 S: Model + Send + Sync + Unpin + AnyImpl + 'static,
433 SE: Connection + 'static,
434 {
435 subquery.apply_soft_delete_filter();
436 let table_id = self.get_table_identifier();
437 let is_main_col = self.columns.contains(&col.to_snake_case());
438 let op_str = op.as_sql();
439
440 let clause: FilterFn = Box::new(move |query, args, _driver, arg_counter| {
441 query.push_str(" AND ");
442 if let Some((table, column)) = col.split_once(".") {
443 query.push_str(&format!("\"{}\".\"{}\"", table, column));
444 } else if is_main_col {
445 query.push_str(&format!("\"{}\".\"{}\"", table_id, col));
446 } else {
447 query.push_str(&format!("\"{}\"", col));
448 }
449 query.push_str(&format!(" {} (", op_str));
450
451 subquery.write_select_sql::<S>(query, args, arg_counter);
452 query.push_str(")");
453 });
454
455 self.where_clauses.push(clause);
456 self
457 }
458
459 /// Truncates the table associated with this Model.
460 ///
461 /// This method removes all records from the table. It uses `TRUNCATE TABLE`
462 /// for Postgres and MySQL, and `DELETE FROM` with sequence reset for SQLite.
463 ///
464 /// # Returns
465 ///
466 /// * `Ok(())` - Table truncated successfully
467 /// * `Err(sqlx::Error)` - Database error occurred
468 ///
469 /// # Example
470 ///
471 /// ```rust,ignore
472 /// db.model::<Log>().truncate().await?;
473 /// ```
474 pub async fn truncate(self) -> Result<(), sqlx::Error> {
475 let table_name = self.table_name.to_snake_case();
476 let query = match self.driver {
477 Drivers::Postgres | Drivers::MySQL => format!("TRUNCATE TABLE \"{}\"", table_name),
478 Drivers::SQLite => format!("DELETE FROM \"{}\"", table_name),
479 };
480
481 if self.debug_mode {
482 log::debug!("SQL: {}", query);
483 }
484
485 self.tx.execute(&query, AnyArguments::default()).await?;
486
487 // For SQLite, reset auto-increment if exists
488 if matches!(self.driver, Drivers::SQLite) {
489 let _ = self.tx.execute(&format!("DELETE FROM sqlite_sequence WHERE name='{}'", table_name), AnyArguments::default()).await;
490 }
491
492 Ok(())
493 }
494
495 /// Combines the results of this query with another query using UNION.
496 ///
497 /// This method allows you to combine the result sets of two queries into a single
498 /// result set. Duplicate rows are removed by default.
499 ///
500 /// # Arguments
501 ///
502 /// * `other` - Another QueryBuilder instance to combine with.
503 ///
504 /// # Example
505 ///
506 /// ```rust,ignore
507 /// let q1 = db.model::<User>().filter("age", ">", 18);
508 /// let q2 = db.model::<User>().filter("status", "=", "premium");
509 /// let results = q1.union(q2).scan().await?;
510 /// ```
511 pub fn union(self, other: QueryBuilder<T, E>) -> Self where T: AnyImpl + 'static, E: 'static {
512 self.union_internal("UNION", other)
513 }
514
515 /// Combines the results of this query with another query using UNION ALL.
516 ///
517 /// This method allows you to combine the result sets of two queries into a single
518 /// result set, including all duplicates.
519 ///
520 /// # Arguments
521 ///
522 /// * `other` - Another QueryBuilder instance to combine with.
523 ///
524 /// # Example
525 ///
526 /// ```rust,ignore
527 /// let q1 = db.model::<User>().filter("age", ">", 18);
528 /// let q2 = db.model::<User>().filter("status", "=", "premium");
529 /// let results = q1.union_all(q2).scan().await?;
530 /// ```
531 pub fn union_all(self, other: QueryBuilder<T, E>) -> Self where T: AnyImpl + 'static, E: 'static {
532 self.union_internal("UNION ALL", other)
533 }
534
535 fn union_internal(mut self, op: &str, mut other: QueryBuilder<T, E>) -> Self where T: AnyImpl + 'static, E: 'static {
536 other.apply_soft_delete_filter();
537 let op_owned = op.to_string();
538
539 self.union_clauses.push((op_owned.clone(), Box::new(move |query: &mut String, args: &mut AnyArguments<'_>, _driver: &Drivers, arg_counter: &mut usize| {
540 query.push_str(" ");
541 query.push_str(&op_owned);
542 query.push_str(" ");
543 other.write_select_sql::<T>(query, args, arg_counter);
544 })));
545 self
546 }
547
548 /// Internal helper to write the SELECT SQL to a string buffer.
549 pub(crate) fn write_select_sql<R: AnyImpl>(
550 &self,
551 query: &mut String,
552 args: &mut AnyArguments,
553 arg_counter: &mut usize,
554 ) {
555 query.push_str("SELECT ");
556
557 if self.is_distinct {
558 query.push_str("DISTINCT ");
559 }
560
561 query.push_str(&self.select_args_sql::<R>().join(", "));
562
563 // Build FROM clause
564 query.push_str(" FROM \"");
565 query.push_str(&self.table_name.to_snake_case());
566 query.push_str("\" ");
567 if let Some(alias) = &self.alias {
568 query.push_str(&format!("\"{}\" ", alias));
569 }
570
571 if !self.joins_clauses.is_empty() {
572 for join_clause in &self.joins_clauses {
573 query.push(' ');
574 join_clause(query, args, &self.driver, arg_counter);
575 }
576 }
577
578 query.push_str(" WHERE 1=1");
579
580 // Apply WHERE clauses
581 for clause in &self.where_clauses {
582 clause(query, args, &self.driver, arg_counter);
583 }
584
585 // Apply GROUP BY
586 if !self.group_by_clauses.is_empty() {
587 query.push_str(&format!(" GROUP BY {}", self.group_by_clauses.join(", ")));
588 }
589
590 // Apply HAVING
591 if !self.having_clauses.is_empty() {
592 query.push_str(" HAVING 1=1");
593 for clause in &self.having_clauses {
594 clause(query, args, &self.driver, arg_counter);
595 }
596 }
597
598 // Apply ORDER BY clauses
599 if !self.order_clauses.is_empty() {
600 query.push_str(&format!(" ORDER BY {}", self.order_clauses.join(", ")));
601 }
602
603 // Apply LIMIT clause
604 if let Some(limit) = self.limit {
605 query.push_str(" LIMIT ");
606 match self.driver {
607 Drivers::Postgres => {
608 query.push_str(&format!("${}", arg_counter));
609 *arg_counter += 1;
610 }
611 _ => query.push('?'),
612 }
613 let _ = args.add(limit as i64);
614 }
615
616 // Apply OFFSET clause
617 if let Some(offset) = self.offset {
618 query.push_str(" OFFSET ");
619 match self.driver {
620 Drivers::Postgres => {
621 query.push_str(&format!("${}", arg_counter));
622 *arg_counter += 1;
623 }
624 _ => query.push('?'),
625 }
626 let _ = args.add(offset as i64);
627 }
628
629 // Apply UNION clauses
630 for (_op, clause) in &self.union_clauses {
631 clause(query, args, &self.driver, arg_counter);
632 }
633 }
634
635 /// Adds a WHERE clause to the query.
636 ///
637 /// This method adds a filter condition to the query. Multiple filters can be chained
638 /// and will be combined with AND operators. The value is bound as a parameter to
639 /// prevent SQL injection.
640 ///
641 /// # Type Parameters
642 ///
643 /// * `V` - The type of the value to filter by. Must be encodable for SQL queries.
644 ///
645 /// # Arguments
646 ///
647 /// * `col` - The column name to filter on
648 /// * `op` - The comparison operator (e.g., "=", ">", "LIKE", "IN")
649 /// * `value` - The value to compare against
650 ///
651 /// # Example
652 ///
653 /// ```rust,ignore
654 /// query.filter("age", Op::Gte, 18)
655 /// // SQL: AND "age" >= 18
656 /// ```
657 pub fn filter<V>(self, col: &'static str, op: Op, value: V) -> Self
658 where
659 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
660 {
661 self.filter_internal(" AND ", col, op, value)
662 }
663
664 /// Adds an OR WHERE clause to the query.
665 ///
666 /// # Arguments
667 ///
668 /// * `col` - The column name to filter on
669 /// * `op` - The comparison operator
670 /// * `value` - The value to compare against
671 ///
672 /// # Example
673 ///
674 /// ```rust,ignore
675 /// query.filter("age", Op::Lt, 18).or_filter("active", Op::Eq, false)
676 /// // SQL: AND "age" < 18 OR "active" = false
677 /// ```
678 pub fn or_filter<V>(self, col: &'static str, op: Op, value: V) -> Self
679 where
680 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
681 {
682 self.filter_internal(" OR ", col, op, value)
683 }
684
685 /// Adds an AND NOT WHERE clause to the query.
686 ///
687 /// # Arguments
688 ///
689 /// * `col` - The column name to filter on
690 /// * `op` - The comparison operator
691 /// * `value` - The value to compare against
692 ///
693 /// # Example
694 ///
695 /// ```rust,ignore
696 /// query.not_filter("status", Op::Eq, "banned")
697 /// // SQL: AND NOT "status" = 'banned'
698 /// ```
699 pub fn not_filter<V>(self, col: &'static str, op: Op, value: V) -> Self
700 where
701 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
702 {
703 self.filter_internal(" AND NOT ", col, op, value)
704 }
705
706 /// Adds an OR NOT WHERE clause to the query.
707 ///
708 /// # Arguments
709 ///
710 /// * `col` - The column name to filter on
711 /// * `op` - The comparison operator
712 /// * `value` - The value to compare against
713 ///
714 /// # Example
715 ///
716 /// ```rust,ignore
717 /// query.filter("age", Op::Gt, 18).or_not_filter("status", Op::Eq, "inactive")
718 /// // SQL: AND "age" > 18 OR NOT "status" = 'inactive'
719 /// ```
720 pub fn or_not_filter<V>(self, col: &'static str, op: Op, value: V) -> Self
721 where
722 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
723 {
724 self.filter_internal(" OR NOT ", col, op, value)
725 }
726
727 /// Adds a BETWEEN clause to the query.
728 ///
729 /// # Arguments
730 ///
731 /// * `col` - The column name
732 /// * `start` - The start value of the range
733 /// * `end` - The end value of the range
734 ///
735 /// # Example
736 ///
737 /// ```rust,ignore
738 /// query.between("age", 18, 30)
739 /// // SQL: AND "age" BETWEEN 18 AND 30
740 /// ```
741 pub fn between<V>(mut self, col: &'static str, start: V, end: V) -> Self
742 where
743 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
744 {
745 let table_id = self.get_table_identifier();
746 let is_main_col = self.columns.contains(&col.to_snake_case());
747 let clause: FilterFn = Box::new(move |query, args, driver, arg_counter| {
748 query.push_str(" AND ");
749 if let Some((table, column)) = col.split_once(".") {
750 query.push_str(&format!("\"{}\".\"{}\"", table, column));
751 } else if is_main_col {
752 query.push_str(&format!("\"{}\".\"{}\"", table_id, col));
753 } else {
754 query.push_str(&format!("\"{}\"", col));
755 }
756 query.push_str(" BETWEEN ");
757
758 match driver {
759 Drivers::Postgres => {
760 query.push_str(&format!("${} AND ${}", arg_counter, *arg_counter + 1));
761 *arg_counter += 2;
762 }
763 _ => query.push_str("? AND ?"),
764 }
765
766 let _ = args.add(start.clone());
767 let _ = args.add(end.clone());
768 });
769 self.where_clauses.push(clause);
770 self
771 }
772
773 /// Adds an OR BETWEEN clause to the query.
774 ///
775 /// # Arguments
776 ///
777 /// * `col` - The column name
778 /// * `start` - The start value of the range
779 /// * `end` - The end value of the range
780 ///
781 /// # Example
782 ///
783 /// ```rust,ignore
784 /// query.between("age", 18, 30).or_between("salary", 5000, 10000)
785 /// // SQL: AND "age" BETWEEN 18 AND 30 OR "salary" BETWEEN 5000 AND 10000
786 /// ```
787 pub fn or_between<V>(mut self, col: &'static str, start: V, end: V) -> Self
788 where
789 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
790 {
791 let table_id = self.get_table_identifier();
792 let is_main_col = self.columns.contains(&col.to_snake_case());
793 let clause: FilterFn = Box::new(move |query, args, driver, arg_counter| {
794 query.push_str(" OR ");
795 if let Some((table, column)) = col.split_once(".") {
796 query.push_str(&format!("\"{}\".\"{}\"", table, column));
797 } else if is_main_col {
798 query.push_str(&format!("\"{}\".\"{}\"", table_id, col));
799 } else {
800 query.push_str(&format!("\"{}\"", col));
801 }
802 query.push_str(" BETWEEN ");
803
804 match driver {
805 Drivers::Postgres => {
806 query.push_str(&format!("${} AND ${}", arg_counter, *arg_counter + 1));
807 *arg_counter += 2;
808 }
809 _ => query.push_str("? AND ?"),
810 }
811
812 let _ = args.add(start.clone());
813 let _ = args.add(end.clone());
814 });
815 self.where_clauses.push(clause);
816 self
817 }
818
819 /// Adds an IN list clause to the query.
820 ///
821 /// # Arguments
822 ///
823 /// * `col` - The column name
824 /// * `values` - A vector of values
825 ///
826 /// # Example
827 ///
828 /// ```rust,ignore
829 /// query.in_list("status", vec!["active", "pending"])
830 /// // SQL: AND "status" IN ('active', 'pending')
831 /// ```
832 pub fn in_list<V>(mut self, col: &'static str, values: Vec<V>) -> Self
833 where
834 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
835 {
836 if values.is_empty() {
837 // WHERE 1=0 to ensure empty result
838 let clause: FilterFn = Box::new(|query, _, _, _| {
839 query.push_str(" AND 1=0");
840 });
841 self.where_clauses.push(clause);
842 return self;
843 }
844
845 let table_id = self.get_table_identifier();
846 let is_main_col = self.columns.contains(&col.to_snake_case());
847 let clause: FilterFn = Box::new(move |query, args, driver, arg_counter| {
848 query.push_str(" AND ");
849 if let Some((table, column)) = col.split_once(".") {
850 query.push_str(&format!("\"{}\".\"{}\"", table, column));
851 } else if is_main_col {
852 query.push_str(&format!("\"{}\".\"{}\"", table_id, col));
853 } else {
854 query.push_str(&format!("\"{}\"", col));
855 }
856 query.push_str(" IN (");
857
858 let mut placeholders = Vec::new();
859 for _ in &values {
860 match driver {
861 Drivers::Postgres => {
862 placeholders.push(format!("${}", arg_counter));
863 *arg_counter += 1;
864 }
865 _ => placeholders.push("?".to_string()),
866 }
867 }
868 query.push_str(&placeholders.join(", "));
869 query.push(')');
870
871 for val in &values {
872 let _ = args.add(val.clone());
873 }
874 });
875 self.where_clauses.push(clause);
876 self
877 }
878
879 /// Adds an OR IN list clause to the query.
880 pub fn or_in_list<V>(mut self, col: &'static str, values: Vec<V>) -> Self
881 where
882 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
883 {
884 if values.is_empty() {
885 return self;
886 }
887
888 let table_id = self.get_table_identifier();
889 let is_main_col = self.columns.contains(&col.to_snake_case());
890 let clause: FilterFn = Box::new(move |query, args, driver, arg_counter| {
891 query.push_str(" OR ");
892 if let Some((table, column)) = col.split_once(".") {
893 query.push_str(&format!("\"{}\".\"{}\"", table, column));
894 } else if is_main_col {
895 query.push_str(&format!("\"{}\".\"{}\"", table_id, col));
896 } else {
897 query.push_str(&format!("\"{}\"", col));
898 }
899 query.push_str(" IN (");
900
901 let mut placeholders = Vec::new();
902 for _ in &values {
903 match driver {
904 Drivers::Postgres => {
905 placeholders.push(format!("${}", arg_counter));
906 *arg_counter += 1;
907 }
908 _ => placeholders.push("?".to_string()),
909 }
910 }
911 query.push_str(&placeholders.join(", "));
912 query.push(')');
913
914 for val in &values {
915 let _ = args.add(val.clone());
916 }
917 });
918 self.where_clauses.push(clause);
919 self
920 }
921
922 /// Groups filters inside parentheses with an AND operator.
923 ///
924 /// This allows for constructing complex WHERE clauses with nested logic.
925 ///
926 /// # Arguments
927 ///
928 /// * `f` - A closure that receives a `QueryBuilder` and returns it with more filters
929 ///
930 /// # Example
931 ///
932 /// ```rust,ignore
933 /// db.model::<User>()
934 /// .filter("active", Op::Eq, true)
935 /// .group(|q| q.filter("age", Op::Gt, 18).or_filter("role", Op::Eq, "admin"))
936 /// .scan()
937 /// .await?;
938 /// // SQL: AND "active" = true AND (1=1 AND ("age" > 18 OR "role" = 'admin'))
939 /// ```
940 pub fn group<F>(mut self, f: F) -> Self
941 where
942 F: FnOnce(Self) -> Self,
943 {
944 let old_clauses = std::mem::take(&mut self.where_clauses);
945 self = f(self);
946 let group_clauses = std::mem::take(&mut self.where_clauses);
947 self.where_clauses = old_clauses;
948
949 if !group_clauses.is_empty() {
950 let clause: FilterFn = Box::new(move |query, args, driver, arg_counter| {
951 query.push_str(" AND (1=1");
952 for c in &group_clauses {
953 c(query, args, driver, arg_counter);
954 }
955 query.push_str(")");
956 });
957 self.where_clauses.push(clause);
958 }
959 self
960 }
961
962 /// Groups filters inside parentheses with an OR operator.
963 ///
964 /// # Arguments
965 ///
966 /// * `f` - A closure that receives a `QueryBuilder` and returns it with more filters
967 ///
968 /// # Example
969 ///
970 /// ```rust,ignore
971 /// db.model::<User>()
972 /// .filter("active", Op::Eq, true)
973 /// .or_group(|q| q.filter("role", Op::Eq, "admin").filter("age", Op::Gt, 18))
974 /// .scan()
975 /// .await?;
976 /// // SQL: AND "active" = true OR (1=1 AND ("role" = 'admin' AND "age" > 18))
977 /// ```
978 pub fn or_group<F>(mut self, f: F) -> Self
979 where
980 F: FnOnce(Self) -> Self,
981 {
982 let old_clauses = std::mem::take(&mut self.where_clauses);
983 self = f(self);
984 let group_clauses = std::mem::take(&mut self.where_clauses);
985 self.where_clauses = old_clauses;
986
987 if !group_clauses.is_empty() {
988 let clause: FilterFn = Box::new(move |query, args, driver, arg_counter| {
989 query.push_str(" OR (1=1");
990 for c in &group_clauses {
991 c(query, args, driver, arg_counter);
992 }
993 query.push_str(")");
994 });
995 self.where_clauses.push(clause);
996 }
997 self
998 }
999
1000 /// Adds a raw WHERE clause with a placeholder and a single value.
1001 ///
1002 /// This allows writing raw SQL conditions with a `?` placeholder.
1003 /// To use multiple placeholders with different types, chain multiple `where_raw` calls.
1004 ///
1005 /// # Arguments
1006 ///
1007 /// * `sql` - Raw SQL string with one `?` placeholder (e.g., "age > ?")
1008 /// * `value` - Value to bind
1009 ///
1010 /// # Example
1011 ///
1012 /// ```rust,ignore
1013 /// db.model::<User>()
1014 /// .where_raw("name = ?", "Alice".to_string())
1015 /// .where_raw("age >= ?", 18)
1016 /// .scan()
1017 /// .await?;
1018 /// // SQL: AND name = 'Alice' AND age >= 18
1019 /// ```
1020 pub fn where_raw<V>(mut self, sql: &str, value: V) -> Self
1021 where
1022 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1023 {
1024 self.where_clauses.push(self.create_raw_clause(" AND ", sql, value));
1025 self
1026 }
1027
1028 /// Adds a raw OR WHERE clause with a placeholder.
1029 pub fn or_where_raw<V>(mut self, sql: &str, value: V) -> Self
1030 where
1031 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1032 {
1033 self.where_clauses.push(self.create_raw_clause(" OR ", sql, value));
1034 self
1035 }
1036
1037 /// Internal helper to create a raw SQL clause with a single value.
1038 fn create_raw_clause<V>(&self, joiner: &'static str, sql: &str, value: V) -> FilterFn
1039 where
1040 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1041 {
1042 let sql_owned = sql.to_string();
1043 Box::new(move |query, args, driver, arg_counter| {
1044 query.push_str(joiner);
1045
1046 let mut processed_sql = sql_owned.clone();
1047
1048 // If no placeholder is found, try to be helpful
1049 if !processed_sql.contains('?') {
1050 let trimmed = processed_sql.trim();
1051 if trimmed.ends_with('=') || trimmed.ends_with('>') || trimmed.ends_with('<') || trimmed.to_uppercase().ends_with(" LIKE") {
1052 processed_sql.push_str(" ?");
1053 } else if !trimmed.contains(' ') && !trimmed.contains('(') {
1054 // It looks like just a column name
1055 processed_sql.push_str(" = ?");
1056 }
1057 }
1058
1059 // Replace '?' with driver-specific placeholders only if needed
1060 if matches!(driver, Drivers::Postgres) {
1061 while let Some(pos) = processed_sql.find('?') {
1062 let placeholder = format!("${}", arg_counter);
1063 *arg_counter += 1;
1064 processed_sql.replace_range(pos..pos + 1, &placeholder);
1065 }
1066 }
1067
1068 query.push_str(&processed_sql);
1069 let _ = args.add(value.clone());
1070 })
1071 }
1072
1073 /// Adds an equality filter to the query.
1074 ///
1075 /// This is a convenience wrapper around `filter()` for simple equality checks.
1076 /// It is equivalent to calling `filter(col, "=", value)`.
1077 ///
1078 /// # Type Parameters
1079 ///
1080 /// * `V` - The type of the value to compare against.
1081 ///
1082 /// # Arguments
1083 ///
1084 /// * `col` - The column name to filter on.
1085 /// * `value` - The value to match.
1086 ///
1087 /// # Example
1088 ///
1089 /// ```rust,ignore
1090 /// // Equivalent to filter("age", Op::Eq, 18)
1091 /// query.equals("age", 18)
1092 /// ```
1093 pub fn equals<V>(self, col: &'static str, value: V) -> Self
1094 where
1095 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1096 {
1097 self.filter(col, Op::Eq, value)
1098 }
1099
1100 /// Adds an ORDER BY clause to the query.
1101 ///
1102 /// Specifies the sort order for the query results. Multiple order clauses
1103 /// can be added and will be applied in the order they were added.
1104 ///
1105 /// # Arguments
1106 ///
1107 /// * `order` - The ORDER BY expression (e.g., "created_at DESC", "age ASC, name DESC")
1108 ///
1109 /// # Example
1110 ///
1111 /// ```rust,ignore
1112 /// // Single column ascending (ASC is default)
1113 /// query.order("age")
1114 ///
1115 /// // Single column descending
1116 /// query.order("created_at DESC")
1117 ///
1118 /// // Multiple columns
1119 /// query.order("age DESC, username ASC")
1120 ///
1121 /// // Chain multiple order clauses
1122 /// query
1123 /// .order("priority DESC")
1124 /// .order("created_at ASC")
1125 /// ```
1126 pub fn order(mut self, order: &str) -> Self {
1127 self.order_clauses.push(order.to_string());
1128 self
1129 }
1130
1131 /// Defines a SQL alias for the primary table in the query.
1132 ///
1133 /// This method allows you to set a short alias for the model's underlying table.
1134 /// It is highly recommended when writing complex queries with multiple `JOIN` clauses,
1135 /// preventing the need to repeat the full table name in `.filter()`, `.equals()`, or `.select()`.
1136 ///
1137 /// # Arguments
1138 ///
1139 /// * `alias` - A string slice representing the alias to be used (e.g., "u", "rp").
1140 ///
1141 /// # Example
1142 ///
1143 /// ```rust,ignore
1144 /// // Using 'u' as an alias for the User table
1145 /// let results = db.model::<User>()
1146 /// .alias("u")
1147 /// .join("role_permissions rp", "rp.role_id = u.role")
1148 /// .equals("u.id", user_id)
1149 /// .select("u.username, rp.permission_id")
1150 /// .scan_as::<UserPermissionDTO>()
1151 /// .await?;
1152 /// ```
1153 pub fn alias(mut self, alias: &str) -> Self {
1154 self.alias = Some(alias.to_string());
1155 self
1156 }
1157
1158 /// Placeholder for eager loading relationships (preload).
1159 ///
1160 /// This method is reserved for future implementation of relationship preloading.
1161 /// Currently, it returns `self` unchanged to maintain the fluent interface.
1162 ///
1163 /// # Future Implementation
1164 ///
1165 /// Will support eager loading of related models to avoid N+1 query problems:
1166 ///
1167 /// ```rust,ignore
1168 /// // Future usage example
1169 /// query.preload("posts").preload("comments")
1170 /// ```
1171 // pub fn preload(self) -> Self {
1172 // // TODO: Implement relationship preloading
1173 // self
1174 // }
1175
1176 /// Activates debug mode for this query.
1177 ///
1178 /// When enabled, the generated SQL query will be logged using the `log` crate
1179 /// at the `DEBUG` level before execution.
1180 ///
1181 /// # Note
1182 ///
1183 /// To see the output, you must initialize a logger in your application (e.g., using `env_logger`)
1184 /// and configure it to display `debug` logs for `bottle_orm`.
1185 ///
1186 /// # Example
1187 ///
1188 /// ```rust,ignore
1189 /// db.model::<User>()
1190 /// .filter("active", "=", true)
1191 /// .debug() // Logs SQL: SELECT * FROM "user" WHERE "active" = $1
1192 /// .scan()
1193 /// .await?;
1194 /// ```
1195 pub fn debug(mut self) -> Self {
1196 self.debug_mode = true;
1197 self
1198 }
1199
1200 /// Adds an IS NULL filter for the specified column.
1201 ///
1202 /// # Arguments
1203 ///
1204 /// * `col` - The column name to check for NULL
1205 ///
1206 /// # Example
1207 ///
1208 /// ```rust,ignore
1209 /// db.model::<User>()
1210 /// .is_null("deleted_at")
1211 /// .scan()
1212 /// .await?;
1213 /// // SQL: SELECT * FROM "user" WHERE "deleted_at" IS NULL
1214 /// ```
1215 pub fn is_null(mut self, col: &str) -> Self {
1216 let col_owned = col.to_string();
1217 let table_id = self.get_table_identifier();
1218 let is_main_col = self.columns.contains(&col_owned.to_snake_case());
1219 let clause: FilterFn = Box::new(move |query, _args, _driver, _arg_counter| {
1220 query.push_str(" AND ");
1221 if let Some((table, column)) = col_owned.split_once(".") {
1222 query.push_str(&format!("\"{}\".\"{}\"", table, column));
1223 } else if is_main_col {
1224 query.push_str(&format!("\"{}\".\"{}\"", table_id, col_owned));
1225 } else {
1226 query.push_str(&format!("\"{}\"", col_owned));
1227 }
1228 query.push_str(" IS NULL");
1229 });
1230 self.where_clauses.push(clause);
1231 self
1232 }
1233
1234 /// Adds an IS NOT NULL filter for the specified column.
1235 ///
1236 /// # Arguments
1237 ///
1238 /// * `col` - The column name to check for NOT NULL
1239 ///
1240 /// # Example
1241 ///
1242 /// ```rust,ignore
1243 /// db.model::<User>()
1244 /// .is_not_null("email")
1245 /// .scan()
1246 /// .await?;
1247 /// // SQL: SELECT * FROM "user" WHERE "email" IS NOT NULL
1248 /// ```
1249 pub fn is_not_null(mut self, col: &str) -> Self {
1250 let col_owned = col.to_string();
1251 let table_id = self.get_table_identifier();
1252 let is_main_col = self.columns.contains(&col_owned.to_snake_case());
1253 let clause: FilterFn = Box::new(move |query, _args, _driver, _arg_counter| {
1254 query.push_str(" AND ");
1255 if let Some((table, column)) = col_owned.split_once(".") {
1256 query.push_str(&format!("\"{}\".\"{}\"", table, column));
1257 } else if is_main_col {
1258 query.push_str(&format!("\"{}\".\"{}\"", table_id, col_owned));
1259 } else {
1260 query.push_str(&format!("\"{}\"", col_owned));
1261 }
1262 query.push_str(" IS NOT NULL");
1263 });
1264 self.where_clauses.push(clause);
1265 self
1266 }
1267
1268 /// Includes soft-deleted records in query results.
1269 ///
1270 /// By default, queries on models with a `#[orm(soft_delete)]` column exclude
1271 /// records where that column is not NULL. This method disables that filter.
1272 ///
1273 /// # Example
1274 ///
1275 /// ```rust,ignore
1276 /// // Get all users including deleted ones
1277 /// db.model::<User>()
1278 /// .with_deleted()
1279 /// .scan()
1280 /// .await?;
1281 /// ```
1282 pub fn with_deleted(mut self) -> Self {
1283 self.with_deleted = true;
1284 self
1285 }
1286
1287 /// Adds an INNER JOIN clause to the query.
1288 ///
1289 /// # Arguments
1290 ///
1291 /// * `table` - The name of the table to join (with optional alias)
1292 /// * `on` - The join condition (e.g., "users.id = posts.user_id")
1293 ///
1294 /// # Example
1295 ///
1296 /// ```rust,ignore
1297 /// db.model::<User>()
1298 /// .join("posts p", "u.id = p.user_id")
1299 /// .scan()
1300 /// .await?;
1301 /// // SQL: INNER JOIN "posts" p ON u.id = p.user_id
1302 /// ```
1303 pub fn join(self, table: &str, s_query: &str) -> Self {
1304 self.join_generic("", table, s_query)
1305 }
1306
1307 /// Internal helper for specific join types
1308 fn join_generic(mut self, join_type: &str, table: &str, s_query: &str) -> Self {
1309 let table_owned = table.to_string();
1310 let join_type_owned = join_type.to_string();
1311
1312 let trimmed_value = s_query.replace(" ", "");
1313 let values = trimmed_value.split_once("=");
1314 let mut parsed_query = s_query.to_string();
1315
1316 if let Some((first, second)) = values {
1317 // Try to parse table.column = table.column
1318 if let Some((t1, c1)) = first.split_once('.') {
1319 if let Some((t2, c2)) = second.split_once('.') {
1320 parsed_query = format!("\"{}\".\"{}\" = \"{}\".\"{}\"", t1, c1, t2, c2);
1321 }
1322 }
1323 }
1324
1325 if let Some((table_name, alias)) = table.split_once(" ") {
1326 self.join_aliases.insert(table_name.to_snake_case(), alias.to_string());
1327 } else {
1328 self.join_aliases.insert(table.to_snake_case(), table.to_string());
1329 }
1330
1331 self.joins_clauses.push(Box::new(move |query, _args, _driver, _arg_counter| {
1332 if let Some((table_name, alias)) = table_owned.split_once(" ") {
1333 query.push_str(&format!("{} JOIN \"{}\" \"{}\" ON {}", join_type_owned, table_name, alias, parsed_query));
1334 } else {
1335 query.push_str(&format!("{} JOIN \"{}\" ON {}", join_type_owned, table_owned, parsed_query));
1336 }
1337 }));
1338 self
1339 }
1340
1341 /// Adds a JOIN clause with a placeholder and a bound value.
1342 ///
1343 /// # Arguments
1344 ///
1345 /// * `table` - The name of the table to join
1346 /// * `on` - The join condition with a `?` placeholder
1347 /// * `value` - The value to bind
1348 ///
1349 /// # Example
1350 ///
1351 /// ```rust,ignore
1352 /// db.model::<User>()
1353 /// .join_raw("posts p", "p.user_id = u.id AND p.status = ?", "published")
1354 /// .scan()
1355 /// .await?;
1356 /// // SQL: JOIN "posts" p ON p.user_id = u.id AND p.status = 'published'
1357 /// ```
1358 pub fn join_raw<V>(self, table: &str, on: &str, value: V) -> Self
1359 where
1360 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1361 {
1362 self.join_generic_raw("", table, on, value)
1363 }
1364
1365 /// Adds a raw LEFT JOIN clause with a placeholder and a bound value.
1366 ///
1367 /// # Example
1368 ///
1369 /// ```rust,ignore
1370 /// query.left_join_raw("posts", "posts.user_id = ?", user_id)
1371 /// ```
1372 pub fn left_join_raw<V>(self, table: &str, on: &str, value: V) -> Self
1373 where
1374 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1375 {
1376 self.join_generic_raw("LEFT", table, on, value)
1377 }
1378
1379 /// Adds a raw RIGHT JOIN clause with a placeholder and a bound value.
1380 ///
1381 /// # Example
1382 ///
1383 /// ```rust,ignore
1384 /// query.right_join_raw("users", "users.id = ?", user_id)
1385 /// ```
1386 pub fn right_join_raw<V>(self, table: &str, on: &str, value: V) -> Self
1387 where
1388 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1389 {
1390 self.join_generic_raw("RIGHT", table, on, value)
1391 }
1392
1393 /// Adds a raw INNER JOIN clause with a placeholder and a bound value.
1394 ///
1395 /// # Example
1396 ///
1397 /// ```rust,ignore
1398 /// query.inner_join_raw("accounts", "accounts.user_id = ?", user_id)
1399 /// ```
1400 pub fn inner_join_raw<V>(self, table: &str, on: &str, value: V) -> Self
1401 where
1402 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1403 {
1404 self.join_generic_raw("INNER", table, on, value)
1405 }
1406
1407 /// Adds a raw FULL JOIN clause with a placeholder and a bound value.
1408 ///
1409 /// # Example
1410 ///
1411 /// ```rust,ignore
1412 /// query.full_join_raw("profiles", "profiles.user_id = ?", user_id)
1413 /// ```
1414 pub fn full_join_raw<V>(self, table: &str, on: &str, value: V) -> Self
1415 where
1416 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1417 {
1418 self.join_generic_raw("FULL", table, on, value)
1419 }
1420
1421 /// Internal helper for raw join types
1422 fn join_generic_raw<V>(mut self, join_type: &str, table: &str, on: &str, value: V) -> Self
1423 where
1424 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1425 {
1426 let table_owned = table.to_string();
1427 let on_owned = on.to_string();
1428 let join_type_owned = join_type.to_string();
1429
1430 if let Some((table_name, alias)) = table.split_once(" ") {
1431 self.join_aliases.insert(table_name.to_snake_case(), alias.to_string());
1432 } else {
1433 self.join_aliases.insert(table.to_snake_case(), table.to_string());
1434 }
1435
1436 self.joins_clauses.push(Box::new(move |query, args, driver, arg_counter| {
1437 if let Some((table_name, alias)) = table_owned.split_once(" ") {
1438 query.push_str(&format!("{} JOIN \"{}\" {} ON ", join_type_owned, table_name, alias));
1439 } else {
1440 query.push_str(&format!("{} JOIN \"{}\" ON ", join_type_owned, table_owned));
1441 }
1442
1443 let mut processed_on = on_owned.clone();
1444 if let Some(pos) = processed_on.find('?') {
1445 let placeholder = match driver {
1446 Drivers::Postgres => {
1447 let p = format!("${}", arg_counter);
1448 *arg_counter += 1;
1449 p
1450 }
1451 _ => "?".to_string(),
1452 };
1453 processed_on.replace_range(pos..pos + 1, &placeholder);
1454 }
1455
1456 query.push_str(&processed_on);
1457 let _ = args.add(value.clone());
1458 }));
1459 self
1460 }
1461
1462 /// Adds a LEFT JOIN clause.
1463 ///
1464 /// # Arguments
1465 ///
1466 /// * `table` - The name of the table to join with
1467 /// * `on` - The join condition (e.g., "users.id = posts.user_id")
1468 ///
1469 /// # Example
1470 ///
1471 /// ```rust,ignore
1472 /// // Get all users and their posts (if any)
1473 /// let users_with_posts = db.model::<User>()
1474 /// .left_join("posts p", "u.id = p.user_id")
1475 /// .scan()
1476 /// .await?;
1477 /// // SQL: LEFT JOIN "posts" p ON u.id = p.user_id
1478 /// ```
1479 pub fn left_join(self, table: &str, on: &str) -> Self {
1480 self.join_generic("LEFT", table, on)
1481 }
1482
1483 /// Adds a RIGHT JOIN clause.
1484 ///
1485 /// # Arguments
1486 ///
1487 /// * `table` - The name of the table to join with
1488 /// * `on` - The join condition
1489 ///
1490 /// # Example
1491 ///
1492 /// ```rust,ignore
1493 /// db.model::<Post>()
1494 /// .right_join("users u", "p.user_id = u.id")
1495 /// .scan()
1496 /// .await?;
1497 /// // SQL: RIGHT JOIN "users" u ON p.user_id = u.id
1498 /// ```
1499 pub fn right_join(self, table: &str, on: &str) -> Self {
1500 self.join_generic("RIGHT", table, on)
1501 }
1502
1503 /// Adds an INNER JOIN clause.
1504 ///
1505 /// # Arguments
1506 ///
1507 /// * `table` - The name of the table to join with
1508 /// * `on` - The join condition
1509 ///
1510 /// # Example
1511 ///
1512 /// ```rust,ignore
1513 /// // Get only users who have posts
1514 /// let active_users = db.model::<User>()
1515 /// .inner_join("posts p", "u.id = p.user_id")
1516 /// .scan()
1517 /// .await?;
1518 /// // SQL: INNER JOIN "posts" p ON u.id = p.user_id
1519 /// ```
1520 pub fn inner_join(self, table: &str, on: &str) -> Self {
1521 self.join_generic("INNER", table, on)
1522 }
1523
1524 /// Adds a FULL JOIN clause.
1525 ///
1526 /// # Arguments
1527 ///
1528 /// * `table` - The name of the table to join with
1529 /// * `on` - The join condition
1530 ///
1531 /// # Example
1532 ///
1533 /// ```rust,ignore
1534 /// query.full_join("profiles pr", "u.id = pr.user_id")
1535 /// // SQL: FULL JOIN "profiles" pr ON u.id = pr.user_id
1536 /// ```
1537 pub fn full_join(self, table: &str, on: &str) -> Self {
1538 self.join_generic("FULL", table, on)
1539 }
1540
1541 /// Marks the query to return DISTINCT results.
1542 ///
1543 /// Adds the `DISTINCT` keyword to the SELECT statement, ensuring that unique
1544 /// rows are returned.
1545 ///
1546 /// # Example
1547 ///
1548 /// ```rust,ignore
1549 /// // Get unique ages of users
1550 /// let unique_ages: Vec<i32> = db.model::<User>()
1551 /// .select("age")
1552 /// .distinct()
1553 /// .scan()
1554 /// .await?;
1555 /// ```
1556 pub fn distinct(mut self) -> Self {
1557 self.is_distinct = true;
1558 self
1559 }
1560
1561 /// Adds a GROUP BY clause to the query.
1562 ///
1563 /// Groups rows that have the same values into summary rows. Often used with
1564 /// aggregate functions (COUNT, MAX, MIN, SUM, AVG).
1565 ///
1566 /// # Arguments
1567 ///
1568 /// * `columns` - Comma-separated list of columns to group by
1569 ///
1570 /// # Example
1571 ///
1572 /// ```rust,ignore
1573 /// // Count users by age group
1574 /// let stats: Vec<(i32, i64)> = db.model::<User>()
1575 /// .select("age, COUNT(*)")
1576 /// .group_by("age")
1577 /// .scan()
1578 /// .await?;
1579 /// ```
1580 pub fn group_by(mut self, columns: &str) -> Self {
1581 self.group_by_clauses.push(columns.to_string());
1582 self
1583 }
1584
1585 /// Adds a HAVING clause to the query.
1586 ///
1587 /// Used to filter groups created by `group_by`. Similar to `filter` (WHERE),
1588 /// but operates on grouped records and aggregate functions.
1589 ///
1590 /// # Arguments
1591 ///
1592 /// * `col` - The column or aggregate function to filter on
1593 /// * `op` - Comparison operator
1594 /// * `value` - Value to compare against
1595 ///
1596 /// # Example
1597 ///
1598 /// ```rust,ignore
1599 /// // Get ages with more than 5 users
1600 /// let popular_ages = db.model::<User>()
1601 /// .select("age, COUNT(*)")
1602 /// .group_by("age")
1603 /// .having("COUNT(*)", Op::Gt, 5)
1604 /// .scan()
1605 /// .await?;
1606 /// ```
1607 pub fn having<V>(mut self, col: &'static str, op: Op, value: V) -> Self
1608 where
1609 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
1610 {
1611 let op_str = op.as_sql();
1612 let clause: FilterFn = Box::new(move |query, args, driver, arg_counter| {
1613 query.push_str(" AND ");
1614 query.push_str(col);
1615 query.push(' ');
1616 query.push_str(op_str);
1617 query.push(' ');
1618
1619 match driver {
1620 Drivers::Postgres => {
1621 query.push_str(&format!("${}", arg_counter));
1622 *arg_counter += 1;
1623 }
1624 _ => query.push('?'),
1625 }
1626 let _ = args.add(value.clone());
1627 });
1628
1629 self.having_clauses.push(clause);
1630 self
1631 }
1632
1633 /// Returns the COUNT of rows matching the query.
1634 ///
1635 /// A convenience method that automatically sets `SELECT COUNT(*)` and returns
1636 /// the result as an `i64`.
1637 ///
1638 /// # Returns
1639 ///
1640 /// * `Ok(i64)` - The count of rows
1641 /// * `Err(sqlx::Error)` - Database error
1642 ///
1643 /// # Example
1644 ///
1645 /// ```rust,ignore
1646 /// let user_count = db.model::<User>().count().await?;
1647 /// ```
1648 pub async fn count(mut self) -> Result<i64, sqlx::Error> {
1649 self.select_columns = vec!["COUNT(*)".to_string()];
1650 self.scalar::<i64>().await
1651 }
1652
1653 /// Returns the SUM of the specified column.
1654 ///
1655 /// Calculates the sum of a numeric column.
1656 ///
1657 /// # Arguments
1658 ///
1659 /// * `column` - The column to sum
1660 ///
1661 /// # Example
1662 ///
1663 /// ```rust,ignore
1664 /// let total_age: i64 = db.model::<User>().sum("age").await?;
1665 /// ```
1666 pub async fn sum<N>(mut self, column: &str) -> Result<N, sqlx::Error>
1667 where
1668 N: FromAnyRow + AnyImpl + for<'r> Decode<'r, Any> + Type<Any> + Send + Unpin,
1669 {
1670 let quoted_col = if column.contains('.') {
1671 let parts: Vec<&str> = column.split('.').collect();
1672 format!("\"{}\".\"{}\"", parts[0].trim_matches('"'), parts[1].trim_matches('"'))
1673 } else {
1674 format!("\"{}\"", column.trim_matches('"'))
1675 };
1676 self.select_columns = vec![format!("SUM({})", quoted_col)];
1677 self.scalar::<N>().await
1678 }
1679
1680 /// Returns the AVG of the specified column.
1681 ///
1682 /// Calculates the average value of a numeric column.
1683 ///
1684 /// # Arguments
1685 ///
1686 /// * `column` - The column to average
1687 ///
1688 /// # Example
1689 ///
1690 /// ```rust,ignore
1691 /// let avg_age: f64 = db.model::<User>().avg("age").await?;
1692 /// ```
1693 pub async fn avg<N>(mut self, column: &str) -> Result<N, sqlx::Error>
1694 where
1695 N: FromAnyRow + AnyImpl + for<'r> Decode<'r, Any> + Type<Any> + Send + Unpin,
1696 {
1697 let quoted_col = if column.contains('.') {
1698 let parts: Vec<&str> = column.split('.').collect();
1699 format!("\"{}\".\"{}\"", parts[0].trim_matches('"'), parts[1].trim_matches('"'))
1700 } else {
1701 format!("\"{}\"", column.trim_matches('"'))
1702 };
1703 self.select_columns = vec![format!("AVG({})", quoted_col)];
1704 self.scalar::<N>().await
1705 }
1706
1707 /// Returns the MIN of the specified column.
1708 ///
1709 /// Finds the minimum value in a column.
1710 ///
1711 /// # Arguments
1712 ///
1713 /// * `column` - The column to check
1714 ///
1715 /// # Example
1716 ///
1717 /// ```rust,ignore
1718 /// let min_age: i32 = db.model::<User>().min("age").await?;
1719 /// ```
1720 pub async fn min<N>(mut self, column: &str) -> Result<N, sqlx::Error>
1721 where
1722 N: FromAnyRow + AnyImpl + for<'r> Decode<'r, Any> + Type<Any> + Send + Unpin,
1723 {
1724 let quoted_col = if column.contains('.') {
1725 let parts: Vec<&str> = column.split('.').collect();
1726 format!("\"{}\".\"{}\"", parts[0].trim_matches('"'), parts[1].trim_matches('"'))
1727 } else {
1728 format!("\"{}\"", column.trim_matches('"'))
1729 };
1730 self.select_columns = vec![format!("MIN({})", quoted_col)];
1731 self.scalar::<N>().await
1732 }
1733
1734 /// Returns the MAX of the specified column.
1735 ///
1736 /// Finds the maximum value in a column.
1737 ///
1738 /// # Arguments
1739 ///
1740 /// * `column` - The column to check
1741 ///
1742 /// # Example
1743 ///
1744 /// ```rust,ignore
1745 /// let max_age: i32 = db.model::<User>().max("age").await?;
1746 /// ```
1747 pub async fn max<N>(mut self, column: &str) -> Result<N, sqlx::Error>
1748 where
1749 N: FromAnyRow + AnyImpl + for<'r> Decode<'r, Any> + Type<Any> + Send + Unpin,
1750 {
1751 let quoted_col = if column.contains('.') {
1752 let parts: Vec<&str> = column.split('.').collect();
1753 format!("\"{}\".\"{}\"", parts[0].trim_matches('"'), parts[1].trim_matches('"'))
1754 } else {
1755 format!("\"{}\"", column.trim_matches('"'))
1756 };
1757 self.select_columns = vec![format!("MAX({})", quoted_col)];
1758 self.scalar::<N>().await
1759 }
1760
1761 /// Applies pagination with validation and limits.
1762 ///
1763 /// This is a convenience method that combines `limit()` and `offset()` with
1764 /// built-in validation and maximum value enforcement for safer pagination.
1765 ///
1766 /// # Arguments
1767 ///
1768 /// * `max_value` - Maximum allowed items per page
1769 /// * `default` - Default value if `value` exceeds `max_value`
1770 /// * `page` - Zero-based page number
1771 /// * `value` - Requested items per page
1772 ///
1773 /// # Returns
1774 ///
1775 /// * `Ok(Self)` - The updated QueryBuilder with pagination applied
1776 /// * `Err(Error)` - If `value` is negative
1777 ///
1778 /// # Pagination Logic
1779 ///
1780 /// 1. Validates that `value` is non-negative
1781 /// 2. If `value` > `max_value`, uses `default` instead
1782 /// 3. Calculates offset as: `value * page`
1783 /// 4. Sets limit to `value`
1784 ///
1785 /// # Example
1786 ///
1787 /// ```rust,ignore
1788 /// // Page 0 with 10 items (page 1 in 1-indexed systems)
1789 /// query.pagination(100, 20, 0, 10)? // LIMIT 10 OFFSET 0
1790 ///
1791 /// // Page 2 with 25 items (page 3 in 1-indexed systems)
1792 /// query.pagination(100, 20, 2, 25)? // LIMIT 25 OFFSET 50
1793 ///
1794 /// // Request too many items, falls back to default
1795 /// query.pagination(100, 20, 0, 150)? // LIMIT 20 OFFSET 0 (150 > 100)
1796 ///
1797 /// // Error: negative value
1798 /// query.pagination(100, 20, 0, -10)? // Returns Error
1799 /// ```
1800 pub fn pagination(mut self, max_value: usize, default: usize, page: usize, value: isize) -> Result<Self, Error> {
1801 // Validate that value is non-negative
1802 if value < 0 {
1803 return Err(Error::InvalidArgument("value cannot be negative".into()));
1804 }
1805
1806 let mut f_value = value as usize;
1807
1808 // Enforce maximum value limit
1809 if f_value > max_value {
1810 f_value = default;
1811 }
1812
1813 // Apply offset and limit
1814 self = self.offset(f_value * page);
1815 self = self.limit(f_value);
1816
1817 Ok(self)
1818 }
1819
1820 /// Selects specific columns to return.
1821 ///
1822 /// By default, queries use `SELECT *` to return all columns. This method
1823 /// allows you to specify exactly which columns should be returned.
1824 ///
1825 /// **Note:** Columns are pushed exactly as provided, without automatic
1826 /// snake_case conversion, allowing for aliases and raw SQL fragments.
1827 ///
1828 /// # Arguments
1829 ///
1830 /// * `columns` - Comma-separated list of column names to select
1831 ///
1832 /// # Example
1833 ///
1834 /// ```rust,ignore
1835 /// // Select single column
1836 /// query.select("id")
1837 ///
1838 /// // Select multiple columns
1839 /// query.select("id, username, email")
1840 ///
1841 /// // Select with SQL functions and aliases (now supported)
1842 /// query.select("COUNT(*) as total_count")
1843 /// ```
1844 pub fn select(mut self, columns: &str) -> Self {
1845 self.select_columns.push(columns.to_string());
1846 self
1847 }
1848
1849 /// Excludes specific columns from the query results.
1850 ///
1851 /// This is the inverse of `select()`. Instead of specifying which columns to include,
1852 /// you specify which columns to exclude. All other columns will be returned.
1853 ///
1854 /// # Arguments
1855 ///
1856 /// * `columns` - Comma-separated list of column names to exclude
1857 ///
1858 /// # Priority
1859 ///
1860 /// If both `select()` and `omit()` are used, `select()` takes priority.
1861 ///
1862 /// # Example
1863 ///
1864 /// ```rust,ignore
1865 /// // Exclude password from results
1866 /// let user = db.model::<User>()
1867 /// .omit("password")
1868 /// .first()
1869 /// .await?;
1870 ///
1871 /// // Exclude multiple fields
1872 /// let user = db.model::<User>()
1873 /// .omit("password, secret_token")
1874 /// .first()
1875 /// .await?;
1876 ///
1877 /// // Using with generated field constants (autocomplete support)
1878 /// let user = db.model::<User>()
1879 /// .omit(user_fields::PASSWORD)
1880 /// .first()
1881 /// .await?;
1882 /// ```
1883 pub fn omit(mut self, columns: &str) -> Self {
1884 for col in columns.split(',') {
1885 self.omit_columns.push(col.trim().to_snake_case());
1886 }
1887 self
1888 }
1889
1890 /// Sets the query offset (pagination).
1891 ///
1892 /// Specifies the number of rows to skip before starting to return rows.
1893 /// Commonly used in combination with `limit()` for pagination.
1894 ///
1895 /// # Arguments
1896 ///
1897 /// * `offset` - Number of rows to skip
1898 ///
1899 /// # Example
1900 ///
1901 /// ```rust,ignore
1902 /// // Skip first 20 rows
1903 /// query.offset(20)
1904 ///
1905 /// // Pagination: page 3 with 10 items per page
1906 /// query.limit(10).offset(20) // Skip 2 pages = 20 items
1907 /// ```
1908 pub fn offset(mut self, offset: usize) -> Self {
1909 self.offset = Some(offset);
1910 self
1911 }
1912
1913 /// Sets the maximum number of records to return.
1914 ///
1915 /// Limits the number of rows returned by the query. Essential for pagination
1916 /// and preventing accidentally fetching large result sets.
1917 ///
1918 /// # Arguments
1919 ///
1920 /// * `limit` - Maximum number of rows to return
1921 ///
1922 /// # Example
1923 ///
1924 /// ```rust,ignore
1925 /// // Return at most 10 rows
1926 /// query.limit(10)
1927 ///
1928 /// // Pagination: 50 items per page
1929 /// query.limit(50).offset(page * 50)
1930 /// ```
1931 pub fn limit(mut self, limit: usize) -> Self {
1932 self.limit = Some(limit);
1933 self
1934 }
1935
1936 // ========================================================================
1937 // Insert Operation
1938 // ========================================================================
1939
1940 /// Inserts a new record into the database based on the model instance.
1941 ///
1942 /// This method serializes the model into a SQL INSERT statement with proper
1943 /// type handling for primitives, dates, UUIDs, and other supported types.
1944 ///
1945 /// # Type Binding Strategy
1946 ///
1947 /// The method uses string parsing as a temporary solution for type binding.
1948 /// Values are converted to strings via the model's `to_map()` method, then
1949 /// parsed back to their original types for proper SQL binding.
1950 ///
1951 /// # Supported Types for Insert
1952 ///
1953 /// - **Integers**: `i32`, `i64` (INTEGER, BIGINT)
1954 /// - **Boolean**: `bool` (BOOLEAN)
1955 /// - **Float**: `f64` (DOUBLE PRECISION)
1956 /// - **Text**: `String` (TEXT, VARCHAR)
1957 /// - **UUID**: `Uuid` (UUID) - All versions 1-7 supported
1958 /// - **DateTime**: `DateTime<Utc>` (TIMESTAMPTZ)
1959 /// - **NaiveDateTime**: (TIMESTAMP)
1960 /// - **NaiveDate**: (DATE)
1961 /// - **NaiveTime**: (TIME)
1962 ///
1963 /// # Arguments
1964 ///
1965 /// * `model` - Reference to the model instance to insert
1966 ///
1967 /// # Returns
1968 ///
1969 /// * `Ok(&Self)` - Reference to self for method chaining
1970 /// * `Err(sqlx::Error)` - Database error during insertion
1971 ///
1972 /// # Example
1973 ///
1974 /// ```rust,ignore
1975 ///
1976 /// use chrono::Utc;
1977 ///
1978 /// let new_user = User {
1979 /// id: Uuid::new_v4(),
1980 /// username: "john_doe".to_string(),
1981 /// email: "john@example.com".to_string(),
1982 /// age: 25,
1983 /// active: true,
1984 /// created_at: Utc::now(),
1985 /// };
1986 ///
1987 /// db.model::<User>().insert(&new_user).await?;
1988 /// ```
1989 pub fn insert<'b>(&'b mut self, model: &'b T) -> BoxFuture<'b, Result<(), sqlx::Error>> {
1990 Box::pin(async move {
1991 // Serialize model to a HashMap of column_name -> string_value
1992 let data_map = Model::to_map(model);
1993
1994 // Early return if no data to insert
1995 if data_map.is_empty() {
1996 return Ok(());
1997 }
1998
1999 let table_name = self.table_name.to_snake_case();
2000 let columns_info = <T as Model>::columns();
2001
2002 let mut target_columns = Vec::new();
2003 let mut bindings: Vec<(Option<String>, &str)> = Vec::new();
2004
2005 // Build column list and collect values with their SQL types
2006 for (col_name, value) in data_map {
2007 // Strip the "r#" prefix if present (for Rust keywords used as field names)
2008 let col_name_clean = col_name.strip_prefix("r#").unwrap_or(&col_name).to_snake_case();
2009 target_columns.push(format!("\"{}\"", col_name_clean));
2010
2011 // Find the SQL type for this column
2012 let sql_type = columns_info.iter().find(|c| c.name == col_name).map(|c| c.sql_type).unwrap_or("TEXT");
2013
2014 bindings.push((value, sql_type));
2015 }
2016
2017 // Generate placeholders with proper type casting for PostgreSQL
2018 let placeholders: Vec<String> = bindings
2019 .iter()
2020 .enumerate()
2021 .map(|(i, (_, sql_type))| match self.driver {
2022 Drivers::Postgres => {
2023 let idx = i + 1;
2024 // PostgreSQL requires explicit type casting for some types
2025 if temporal::is_temporal_type(sql_type) {
2026 // Use temporal module for type casting
2027 format!("${}{}", idx, temporal::get_postgres_type_cast(sql_type))
2028 } else {
2029 match *sql_type {
2030 "UUID" => format!("${}::UUID", idx),
2031 "JSONB" | "jsonb" => format!("${}::JSONB", idx),
2032 _ => format!("${}", idx),
2033 }
2034 }
2035 }
2036 // MySQL and SQLite use simple ? placeholders
2037 _ => "?".to_string(),
2038 })
2039 .collect();
2040
2041 // Construct the INSERT query
2042 let query_str = format!(
2043 "INSERT INTO \"{}\" ({}) VALUES ({})",
2044 table_name,
2045 target_columns.join(", "),
2046 placeholders.join(", ")
2047 );
2048
2049 if self.debug_mode {
2050 log::debug!("SQL: {}", query_str);
2051 }
2052
2053 let mut args = AnyArguments::default();
2054
2055 // Bind values using the optimized value_binding module
2056 for (val_opt, sql_type) in bindings {
2057 if let Some(val_str) = val_opt {
2058 if args.bind_value(&val_str, sql_type, &self.driver).is_err() {
2059 let _ = args.add(val_str);
2060 }
2061 } else {
2062 let _ = args.add(None::<String>);
2063 }
2064 }
2065
2066 // Execute the INSERT query
2067 self.tx.execute(&query_str, args).await?;
2068 Ok(())
2069 })
2070 }
2071
2072 /// Inserts multiple records into the database in a single batch operation.
2073 ///
2074 /// This is significantly faster than performing individual inserts in a loop
2075 /// as it generates a single SQL statement with multiple VALUES groups.
2076 ///
2077 /// # Type Binding Strategy
2078 ///
2079 /// Similar to the single record `insert`, this method uses string parsing for
2080 /// type binding. It ensures that all columns defined in the model are included
2081 /// in the insert statement, providing NULL for any missing optional values.
2082 ///
2083 /// # Arguments
2084 ///
2085 /// * `models` - A slice of model instances to insert
2086 ///
2087 /// # Returns
2088 ///
2089 /// * `Ok(())` - Successfully inserted all records
2090 /// * `Err(sqlx::Error)` - Database error during insertion
2091 ///
2092 /// # Example
2093 ///
2094 /// ```rust,ignore
2095 /// let users = vec![
2096 /// User { username: "alice".to_string(), ... },
2097 /// User { username: "bob".to_string(), ... },
2098 /// ];
2099 ///
2100 /// db.model::<User>().batch_insert(&users).await?;
2101 /// ```
2102 pub fn batch_insert<'b>(&'b mut self, models: &'b [T]) -> BoxFuture<'b, Result<(), sqlx::Error>> {
2103 Box::pin(async move {
2104 if models.is_empty() {
2105 return Ok(());
2106 }
2107
2108 let table_name = self.table_name.to_snake_case();
2109 let columns_info = <T as Model>::columns();
2110
2111 // Collect all column names for the INSERT statement
2112 // We use all columns defined in the model to ensure consistency across the batch
2113 let target_columns: Vec<String> = columns_info
2114 .iter()
2115 .map(|c| {
2116 let col_name_clean = c.name.strip_prefix("r#").unwrap_or(c.name).to_snake_case();
2117 format!("\"{}\"", col_name_clean)
2118 })
2119 .collect();
2120
2121 let mut value_groups = Vec::new();
2122 let mut bind_index = 1;
2123
2124 // Generate placeholders for all models
2125 for _ in models {
2126 let mut placeholders = Vec::new();
2127 for col in &columns_info {
2128 match self.driver {
2129 Drivers::Postgres => {
2130 let p = if temporal::is_temporal_type(col.sql_type) {
2131 format!("${}{}", bind_index, temporal::get_postgres_type_cast(col.sql_type))
2132 } else {
2133 match col.sql_type {
2134 "UUID" => format!("${}::UUID", bind_index),
2135 "JSONB" | "jsonb" => format!("${}::JSONB", bind_index),
2136 _ => format!("${}", bind_index),
2137 }
2138 };
2139 placeholders.push(p);
2140 bind_index += 1;
2141 }
2142 _ => {
2143 placeholders.push("?".to_string());
2144 }
2145 }
2146 }
2147 value_groups.push(format!("({})", placeholders.join(", ")));
2148 }
2149
2150 let query_str = format!(
2151 "INSERT INTO \"{}\" ({}) VALUES {}",
2152 table_name,
2153 target_columns.join(", "),
2154 value_groups.join(", ")
2155 );
2156
2157 if self.debug_mode {
2158 log::debug!("SQL Batch: {}", query_str);
2159 }
2160
2161 let mut args = AnyArguments::default();
2162
2163 for model in models {
2164 let data_map = Model::to_map(model);
2165 for col in &columns_info {
2166 let val_opt = data_map.get(col.name);
2167 let sql_type = col.sql_type;
2168
2169 if let Some(Some(val_str)) = val_opt {
2170 if args.bind_value(val_str, sql_type, &self.driver).is_err() {
2171 let _ = args.add(val_str.clone());
2172 }
2173 } else {
2174 // Bind NULL for missing or None values
2175 let _ = args.add(None::<String>);
2176 }
2177 }
2178 }
2179
2180 // Execute the batch INSERT query
2181 self.tx.execute(&query_str, args).await?;
2182 Ok(())
2183 })
2184 }
2185
2186 /// Inserts a record or updates it if a conflict occurs (UPSERT).
2187 ///
2188 /// This method provides a cross-database way to perform "Insert or Update" operations.
2189 /// It uses `ON CONFLICT` for PostgreSQL and SQLite, and `ON DUPLICATE KEY UPDATE` for MySQL.
2190 ///
2191 /// # Arguments
2192 ///
2193 /// * `model` - The model instance to insert or update
2194 /// * `conflict_columns` - Columns that trigger the conflict (e.g., primary key or unique columns)
2195 /// * `update_columns` - Columns to update when a conflict occurs
2196 ///
2197 /// # Returns
2198 ///
2199 /// * `Ok(u64)` - The number of rows affected
2200 /// * `Err(sqlx::Error)` - Database error
2201 ///
2202 /// # Example
2203 ///
2204 /// ```rust,ignore
2205 /// let user = User { id: 1, username: "alice".to_string(), age: 25 };
2206 ///
2207 /// // If id 1 exists, update username and age
2208 /// db.model::<User>().upsert(&user, &["id"], &["username", "age"]).await?;
2209 /// ```
2210 pub fn upsert<'b>(
2211 &'b mut self,
2212 model: &'b T,
2213 conflict_columns: &'b [&'b str],
2214 update_columns: &'b [&'b str],
2215 ) -> BoxFuture<'b, Result<u64, sqlx::Error>> {
2216 Box::pin(async move {
2217 let data_map = Model::to_map(model);
2218 if data_map.is_empty() {
2219 return Ok(0);
2220 }
2221
2222 let table_name = self.table_name.to_snake_case();
2223 let columns_info = <T as Model>::columns();
2224
2225 let mut target_columns = Vec::new();
2226 let mut bindings: Vec<(Option<String>, &str)> = Vec::new();
2227
2228 // Build INSERT part
2229 for (col_name, value) in &data_map {
2230 let col_name_clean = col_name.strip_prefix("r#").unwrap_or(col_name).to_snake_case();
2231 target_columns.push(format!("\"{}\"", col_name_clean));
2232
2233 let sql_type = columns_info.iter().find(|c| {
2234 let c_clean = c.name.strip_prefix("r#").unwrap_or(c.name);
2235 c_clean == *col_name || c_clean.to_snake_case() == col_name_clean
2236 }).map(|c| c.sql_type).unwrap_or("TEXT");
2237 bindings.push((value.clone(), sql_type));
2238 }
2239
2240 let mut arg_counter = 1;
2241 let mut placeholders = Vec::new();
2242 for (_, sql_type) in &bindings {
2243 match self.driver {
2244 Drivers::Postgres => {
2245 let p = if temporal::is_temporal_type(sql_type) {
2246 format!("${}{}", arg_counter, temporal::get_postgres_type_cast(sql_type))
2247 } else {
2248 match *sql_type {
2249 "UUID" => format!("${}::UUID", arg_counter),
2250 "JSONB" | "jsonb" => format!("${}::JSONB", arg_counter),
2251 _ => format!("${}", arg_counter),
2252 }
2253 };
2254 placeholders.push(p);
2255 arg_counter += 1;
2256 }
2257 _ => {
2258 placeholders.push("?".to_string());
2259 }
2260 }
2261 }
2262
2263 let mut query_str = format!(
2264 "INSERT INTO \"{}\" ({}) VALUES ({})",
2265 table_name,
2266 target_columns.join(", "),
2267 placeholders.join(", ")
2268 );
2269
2270 // Build Conflict/Update part
2271 match self.driver {
2272 Drivers::Postgres | Drivers::SQLite => {
2273 let conflict_cols_str = conflict_columns
2274 .iter()
2275 .map(|c| format!("\"{}\"", c.to_snake_case()))
2276 .collect::<Vec<_>>()
2277 .join(", ");
2278
2279 query_str.push_str(&format!(" ON CONFLICT ({}) DO UPDATE SET ", conflict_cols_str));
2280
2281 let mut update_clauses = Vec::new();
2282 let mut update_bindings = Vec::new();
2283
2284 for col in update_columns {
2285 let col_snake = col.to_snake_case();
2286 if let Some((_key, val_opt)) = data_map.iter().find(|(k, _)| {
2287 let k_clean = k.strip_prefix("r#").unwrap_or(*k);
2288 k_clean == *col || k_clean.to_snake_case() == col_snake
2289 }) {
2290 let sql_type_opt = columns_info.iter().find(|c| {
2291 let c_clean = c.name.strip_prefix("r#").unwrap_or(c.name);
2292 c_clean == *col || c_clean.to_snake_case() == col_snake
2293 }).map(|c| c.sql_type);
2294
2295 let sql_type = match sql_type_opt {
2296 Some(t) => t,
2297 None => continue,
2298 };
2299
2300 let placeholder = match self.driver {
2301 Drivers::Postgres => {
2302 let p = if temporal::is_temporal_type(sql_type) {
2303 format!("${}{}", arg_counter, temporal::get_postgres_type_cast(sql_type))
2304 } else {
2305 match sql_type {
2306 "UUID" => format!("${}::UUID", arg_counter),
2307 "JSONB" | "jsonb" => format!("${}::JSONB", arg_counter),
2308 _ => format!("${}", arg_counter),
2309 }
2310 };
2311 arg_counter += 1;
2312 p
2313 }
2314 _ => "?".to_string(),
2315 };
2316 update_clauses.push(format!("\"{}\" = {}", col_snake, placeholder));
2317 update_bindings.push((val_opt.clone(), sql_type));
2318 }
2319 }
2320 if update_clauses.is_empty() {
2321 query_str.push_str(" NOTHING");
2322 } else {
2323 query_str.push_str(&update_clauses.join(", "));
2324 }
2325 bindings.extend(update_bindings);
2326 }
2327 Drivers::MySQL => {
2328 query_str.push_str(" ON DUPLICATE KEY UPDATE ");
2329 let mut update_clauses = Vec::new();
2330 for col in update_columns {
2331 let col_snake = col.to_snake_case();
2332 update_clauses.push(format!("\"{}\" = VALUES(\"{}\")", col_snake, col_snake));
2333 }
2334 query_str.push_str(&update_clauses.join(", "));
2335 }
2336 }
2337
2338 if self.debug_mode {
2339 log::debug!("SQL Upsert: {}", query_str);
2340 }
2341
2342 let mut args = AnyArguments::default();
2343 for (val_opt, sql_type) in bindings {
2344 if let Some(val_str) = val_opt {
2345 if args.bind_value(&val_str, sql_type, &self.driver).is_err() {
2346 let _ = args.add(val_str);
2347 }
2348 } else {
2349 let _ = args.add(None::<String>);
2350 }
2351 }
2352
2353 let result = self.tx.execute(&query_str, args).await?;
2354 Ok(result.rows_affected())
2355 })
2356 }
2357
2358 // ========================================================================
2359 // Query Execution Methods
2360 // ========================================================================
2361
2362 /// Returns the generated SQL string for debugging purposes.
2363 ///
2364 /// This method constructs the SQL query string without executing it.
2365 /// Useful for debugging and logging query construction. Note that this
2366 /// shows placeholders (?, $1, etc.) rather than actual bound values.
2367 ///
2368 /// # Returns
2369 ///
2370 /// A `String` containing the SQL query that would be executed
2371 ///
2372 /// # Example
2373 ///
2374 /// ```rust,ignore
2375 /// let query = db.model::<User>()
2376 /// .filter("age", ">=", 18)
2377 /// .order("created_at DESC")
2378 /// .limit(10);
2379 ///
2380 /// println!("SQL: {}", query.to_sql());
2381 /// // Output: SELECT * FROM "user" WHERE 1=1 AND "age" >= $1 ORDER BY created_at DESC
2382 /// ```
2383 pub fn to_sql(&self) -> String {
2384 let mut query = String::new();
2385 let mut args = AnyArguments::default();
2386 let mut arg_counter = 1;
2387
2388 self.write_select_sql::<T>(&mut query, &mut args, &mut arg_counter);
2389 query
2390 }
2391
2392 /// Generates the list of column selection SQL arguments.
2393 ///
2394 /// This helper function constructs the column list for the SELECT statement.
2395 /// It handles:
2396 /// 1. Mapping specific columns if `select_columns` is set.
2397 /// 2. Defaulting to all columns from the struct `R` if no columns are specified.
2398 /// 3. applying `to_json(...)` casting for temporal types when using `AnyImpl` structs,
2399 /// ensuring compatibility with the `FromAnyRow` deserialization logic.
2400 fn select_args_sql<R: AnyImpl>(&self) -> Vec<String> {
2401 let struct_cols = R::columns();
2402 let table_id = self.get_table_identifier();
2403 let main_table_snake = self.table_name.to_snake_case();
2404
2405 if struct_cols.is_empty() {
2406 if self.select_columns.is_empty() { return vec!["*".to_string()]; }
2407
2408 // If result type is a tuple or primitive (struct_cols is empty),
2409 // we still need to handle temporal types for Postgres.
2410 if matches!(self.driver, Drivers::Postgres) {
2411 let mut args = Vec::new();
2412 for s in &self.select_columns {
2413 for sub in s.split(',') {
2414 let s_trim = sub.trim();
2415 if s_trim.contains(' ') || s_trim.contains('(') {
2416 args.push(s_trim.to_string());
2417 continue;
2418 }
2419
2420 let (t, c) = if let Some((t, c)) = s_trim.split_once('.') {
2421 (t.trim().trim_matches('"'), c.trim().trim_matches('"'))
2422 } else {
2423 (table_id.as_str(), s_trim.trim_matches('"'))
2424 };
2425
2426 let c_snake = c.to_snake_case();
2427 let mut is_temporal = false;
2428
2429 // Check if this column is known to be temporal
2430 if let Some(info) = self.columns_info.iter().find(|info| {
2431 info.name.to_snake_case() == c_snake
2432 }) {
2433 if is_temporal_type(info.sql_type) {
2434 is_temporal = true;
2435 }
2436 }
2437
2438 if is_temporal {
2439 args.push(format!("to_json(\"{}\".\"{}\") #>> '{{}}' AS \"{}\"", t, c, c));
2440 } else {
2441 args.push(format!("\"{}\".\"{}\"", t, c));
2442 }
2443 }
2444 }
2445 return args;
2446 }
2447
2448 return self.select_columns.clone();
2449 }
2450 let mut flat_selects = Vec::new();
2451 for s in &self.select_columns {
2452 for sub in s.split(',') { flat_selects.push(sub.trim().to_string()); }
2453 }
2454 let mut expanded_tables = HashSet::new();
2455 for s in &flat_selects {
2456 if s == "*" { expanded_tables.insert(table_id.clone()); expanded_tables.insert(main_table_snake.clone()); }
2457 else if let Some(t) = s.strip_suffix(".*") { let t_clean = t.trim().trim_matches('"'); expanded_tables.insert(t_clean.to_string()); expanded_tables.insert(t_clean.to_snake_case()); }
2458 }
2459 let mut col_counts = HashMap::new();
2460 for col_info in &struct_cols {
2461 let col_snake = col_info.column.strip_prefix("r#").unwrap_or(col_info.column).to_snake_case();
2462 *col_counts.entry(col_snake).or_insert(0) += 1;
2463 }
2464 let is_tuple = format!("{:?}", std::any::type_name::<R>()).contains('(');
2465 let mut matched_s_indices = HashSet::new();
2466 let mut manual_field_map = HashMap::new();
2467
2468 for (f_idx, s) in flat_selects.iter().enumerate() {
2469 if s == "*" || s.ends_with(".*") { continue; }
2470 let s_lower = s.to_lowercase();
2471 for (s_idx, col_info) in struct_cols.iter().enumerate() {
2472 if matched_s_indices.contains(&s_idx) { continue; }
2473 let col_snake = col_info.column.strip_prefix("r#").unwrap_or(col_info.column).to_snake_case();
2474 let mut m = false;
2475 if let Some((_, alias)) = s_lower.split_once(" as ") {
2476 let ca = alias.trim().trim_matches('"').trim_matches('\'');
2477 if ca == col_info.column || ca == &col_snake { m = true; }
2478 } else if s == col_info.column || s == &col_snake || s.ends_with(&format!(".{}", col_info.column)) || s.ends_with(&format!(".{}", col_snake)) {
2479 m = true;
2480 }
2481 if m { manual_field_map.insert(f_idx, s_idx); matched_s_indices.insert(s_idx); break; }
2482 }
2483 }
2484
2485 let mut args = Vec::new();
2486 if self.select_columns.is_empty() {
2487 for (s_idx, col_info) in struct_cols.iter().enumerate() {
2488 let mut t_use = table_id.clone();
2489 if !col_info.table.is_empty() {
2490 let c_snake = col_info.table.to_snake_case();
2491 if c_snake == main_table_snake { t_use = table_id.clone(); }
2492 else if let Some(alias) = self.join_aliases.get(&c_snake) { t_use = alias.clone(); }
2493 else if self.join_aliases.values().any(|a| a == &col_info.table) { t_use = col_info.table.to_string(); }
2494 }
2495 args.push(self.format_select_field::<R>(s_idx, &t_use, &main_table_snake, &col_counts, is_tuple));
2496 }
2497 } else {
2498 for (f_idx, s) in flat_selects.iter().enumerate() {
2499 let s_trim = s.trim();
2500 if s_trim == "*" || s_trim.ends_with(".*") {
2501 let mut t_exp = if s_trim == "*" { String::new() } else { s_trim.strip_suffix(".*").unwrap().trim().trim_matches('"').to_string() };
2502 if !t_exp.is_empty() && (t_exp.to_snake_case() == main_table_snake || t_exp == table_id) { t_exp = table_id.clone(); }
2503 for (s_idx, col_info) in struct_cols.iter().enumerate() {
2504 if matched_s_indices.contains(&s_idx) { continue; }
2505 let mut t_col = table_id.clone(); let mut known = false;
2506 if !col_info.table.is_empty() {
2507 let c_snake = col_info.table.to_snake_case();
2508 if c_snake == main_table_snake { t_col = table_id.clone(); known = true; }
2509 else if let Some(alias) = self.join_aliases.get(&c_snake) { t_col = alias.clone(); known = true; }
2510 else if self.join_aliases.values().any(|a| a == &col_info.table) { t_col = col_info.table.to_string(); known = true; }
2511 }
2512 if !known && !t_exp.is_empty() && flat_selects.iter().filter(|x| x.ends_with(".*") || *x == "*").count() == 1 { t_col = t_exp.clone(); known = true; }
2513 if (t_exp.is_empty() && known) || (!t_exp.is_empty() && t_col == t_exp) {
2514 args.push(self.format_select_field::<R>(s_idx, &t_col, &main_table_snake, &col_counts, is_tuple));
2515 matched_s_indices.insert(s_idx);
2516 }
2517 }
2518 } else if let Some(s_idx) = manual_field_map.get(&f_idx) {
2519 if s.to_lowercase().contains(" as ") { args.push(s_trim.to_string()); }
2520 else {
2521 let mut t = table_id.clone();
2522 if let Some((prefix, _)) = s_trim.split_once('.') { t = prefix.trim().trim_matches('"').to_string(); }
2523 args.push(self.format_select_field::<R>(*s_idx, &t, &main_table_snake, &col_counts, is_tuple));
2524 }
2525 } else {
2526 if !s_trim.contains(' ') && !s_trim.contains('(') {
2527 if let Some((t, c)) = s_trim.split_once('.') { args.push(format!("\"{}\".\"{}\"", t.trim().trim_matches('"'), c.trim().trim_matches('"'))); }
2528 else { args.push(format!("\"{}\"", s_trim.trim_matches('"'))); }
2529 } else { args.push(s_trim.to_string()); }
2530 }
2531 }
2532 }
2533 if args.is_empty() { vec!["*".to_string()] } else { args }
2534 }
2535
2536 fn format_select_field<R: AnyImpl>(&self, s_idx: usize, table_to_use: &str, main_table_snake: &str, col_counts: &HashMap<String, usize>, is_tuple: bool) -> String {
2537 let col_info = &R::columns()[s_idx];
2538 let col_snake = col_info.column.strip_prefix("r#").unwrap_or(col_info.column).to_snake_case();
2539 let has_collision = *col_counts.get(&col_snake).unwrap_or(&0) > 1;
2540 let alias = if is_tuple || has_collision {
2541 let t_alias = if !col_info.table.is_empty() { col_info.table.to_snake_case() } else { main_table_snake.to_string() };
2542 format!("{}__{}", t_alias.to_lowercase(), col_snake.to_lowercase())
2543 } else { col_snake.to_lowercase() };
2544 if is_temporal_type(col_info.sql_type) && matches!(self.driver, Drivers::Postgres) {
2545 format!("to_json(\"{}\".\"{}\") #>> '{{}}' AS \"{}\"", table_to_use, col_snake, alias)
2546 } else {
2547 format!("\"{}\".\"{}\" AS \"{}\"", table_to_use, col_snake, alias)
2548 }
2549 }
2550
2551 /// Executes the query and returns a list of results.
2552 ///
2553 /// This method builds and executes a SELECT query with all accumulated filters,
2554 /// ordering, and pagination settings. It returns all matching rows as a vector.
2555 ///
2556 /// # Type Parameters
2557 ///
2558 /// * `R` - The result type. Must implement `FromAnyRow` and `AnyImpl`.
2559 ///
2560 /// # Returns
2561 ///
2562 /// * `Ok(Vec<R>)` - Vector of results (empty if no matches)
2563 /// * `Err(sqlx::Error)` - Database error during query execution
2564 ///
2565 /// # Example
2566 ///
2567 /// ```rust,ignore
2568 /// let users: Vec<User> = db.model::<User>()
2569 /// .filter("age", Op::Gte, 18)
2570 /// .scan()
2571 /// .await?;
2572 /// // SQL: SELECT * FROM "user" WHERE "age" >= 18
2573 /// ```
2574 pub async fn scan<R>(mut self) -> Result<Vec<R>, sqlx::Error>
2575 where
2576 R: FromAnyRow + AnyImpl + Send + Unpin,
2577 {
2578 self.apply_soft_delete_filter();
2579 let mut query = String::new();
2580 let mut args = AnyArguments::default();
2581 let mut arg_counter = 1;
2582
2583 self.write_select_sql::<R>(&mut query, &mut args, &mut arg_counter);
2584
2585 if self.debug_mode {
2586 log::debug!("SQL: {}", query);
2587 }
2588
2589 let rows = self.tx.fetch_all(&query, args).await?;
2590 let mut result = Vec::with_capacity(rows.len());
2591 for row in rows {
2592 result.push(R::from_any_row(&row)?);
2593 }
2594 Ok(result)
2595 }
2596
2597 /// Executes the query and maps the result to a custom DTO.
2598 ///
2599 /// Useful for queries that return only a subset of columns or join multiple tables.
2600 ///
2601 /// # Type Parameters
2602 ///
2603 /// * `R` - The DTO type. Must implement `FromAnyRow` and `AnyImpl`.
2604 ///
2605 /// # Returns
2606 ///
2607 /// * `Ok(Vec<R>)` - Vector of results
2608 /// * `Err(sqlx::Error)` - Database error
2609 ///
2610 /// # Example
2611 ///
2612 /// ```rust,ignore
2613 /// let dtos: Vec<UserStats> = db.model::<User>()
2614 /// .select("username, age")
2615 /// .scan_as::<UserStats>()
2616 /// .await?;
2617 /// // SQL: SELECT "username", "age" FROM "user"
2618 /// ```
2619 pub async fn scan_as<R>(mut self) -> Result<Vec<R>, sqlx::Error>
2620 where
2621 R: FromAnyRow + AnyImpl + Send + Unpin,
2622 {
2623 self.apply_soft_delete_filter();
2624 let mut query = String::new();
2625 let mut args = AnyArguments::default();
2626 let mut arg_counter = 1;
2627
2628 self.write_select_sql::<R>(&mut query, &mut args, &mut arg_counter);
2629
2630 if self.debug_mode {
2631 log::debug!("SQL: {}", query);
2632 }
2633
2634 let rows = self.tx.fetch_all(&query, args).await?;
2635 let mut result = Vec::with_capacity(rows.len());
2636 for row in rows {
2637 result.push(R::from_any_row(&row)?);
2638 }
2639 Ok(result)
2640 }
2641
2642 /// Executes the query and returns only the first result.
2643 ///
2644 /// Automatically applies `LIMIT 1` if no limit is set.
2645 ///
2646 /// # Type Parameters
2647 ///
2648 /// * `R` - The result type. Must implement `FromAnyRow` and `AnyImpl`.
2649 ///
2650 /// # Returns
2651 ///
2652 /// * `Ok(R)` - The first matching record
2653 /// * `Err(sqlx::Error::RowNotFound)` - If no records match
2654 ///
2655 /// # Example
2656 ///
2657 /// ```rust,ignore
2658 /// let user: User = db.model::<User>()
2659 /// .filter("id", Op::Eq, 1)
2660 /// .first()
2661 /// .await?;
2662 /// // SQL: SELECT * FROM "user" WHERE "id" = 1 LIMIT 1
2663 /// ```
2664 pub async fn first<R>(mut self) -> Result<R, sqlx::Error>
2665 where
2666 R: FromAnyRow + AnyImpl + Send + Unpin,
2667 {
2668 self.apply_soft_delete_filter();
2669 let mut query = String::new();
2670 let mut args = AnyArguments::default();
2671 let mut arg_counter = 1;
2672
2673 // Force limit 1 if not set
2674 if self.limit.is_none() {
2675 self.limit = Some(1);
2676 }
2677
2678 // Apply PK ordering fallback if no order is set
2679 if self.order_clauses.is_empty() {
2680 let table_id = self.get_table_identifier();
2681 let pk_columns: Vec<String> = <T as Model>::columns()
2682 .iter()
2683 .filter(|c| c.is_primary_key)
2684 .map(|c| format!("\"{}\".\"{}\"", table_id, c.name.strip_prefix("r#").unwrap_or(c.name).to_snake_case()))
2685 .collect();
2686
2687 if !pk_columns.is_empty() {
2688 self.order_clauses.push(pk_columns.iter().map(|col| format!("{} ASC", col)).collect::<Vec<_>>().join(", "));
2689 }
2690 }
2691
2692 self.write_select_sql::<R>(&mut query, &mut args, &mut arg_counter);
2693
2694 if self.debug_mode {
2695 log::debug!("SQL: {}", query);
2696 }
2697
2698 let row = self.tx.fetch_one(&query, args).await?;
2699 R::from_any_row(&row)
2700 }
2701
2702 /// Executes the query and returns a single scalar value.
2703 ///
2704 /// This method is useful for fetching single values like counts, max/min values,
2705 /// or specific columns without mapping to a struct or tuple.
2706 ///
2707 /// # Type Parameters
2708 ///
2709 /// * `O` - The output type. Must implement `FromAnyRow`, `AnyImpl`, `Send` and `Unpin`.
2710 ///
2711 /// # Example
2712 ///
2713 /// ```rust,ignore
2714 /// // Get count of users
2715 /// let count: i64 = db.model::<User>()
2716 /// .select("count(*)")
2717 /// .scalar()
2718 /// .await?;
2719 ///
2720 /// // Get specific field
2721 /// let username: String = db.model::<User>()
2722 /// .filter("id", "=", 1)
2723 /// .select("username")
2724 /// .scalar()
2725 /// .await?;
2726 /// ```
2727 pub async fn scalar<O>(mut self) -> Result<O, sqlx::Error>
2728 where
2729 O: FromAnyRow + AnyImpl + Send + Unpin,
2730 {
2731 self.apply_soft_delete_filter();
2732 let mut query = String::new();
2733 let mut args = AnyArguments::default();
2734 let mut arg_counter = 1;
2735
2736 // Force limit 1 if not set
2737 if self.limit.is_none() {
2738 self.limit = Some(1);
2739 }
2740
2741 self.write_select_sql::<O>(&mut query, &mut args, &mut arg_counter);
2742
2743 if self.debug_mode {
2744 log::debug!("SQL: {}", query);
2745 }
2746
2747 let row = self.tx.fetch_one(&query, args).await?;
2748 O::from_any_row(&row)
2749 }
2750
2751 /// Updates a single column in the database.
2752 ///
2753 /// # Arguments
2754 ///
2755 /// * `col` - The column name to update
2756 /// * `value` - The new value
2757 ///
2758 /// # Returns
2759 ///
2760 /// * `Ok(u64)` - The number of rows affected
2761 pub fn update<'b, V>(&'b mut self, col: &str, value: V) -> BoxFuture<'b, Result<u64, sqlx::Error>>
2762 where
2763 V: ToUpdateValue + Send + Sync,
2764 {
2765 let mut map = std::collections::HashMap::new();
2766 map.insert(col.to_string(), value.to_update_value());
2767 self.execute_update(map)
2768 }
2769
2770 /// Updates all columns based on the model instance.
2771 ///
2772 /// This method updates all active columns of the table with values from the provided model.
2773 ///
2774 /// # Arguments
2775 ///
2776 /// * `model` - The model instance containing new values
2777 ///
2778 /// # Returns
2779 ///
2780 /// * `Ok(u64)` - The number of rows affected
2781 pub fn updates<'b>(&'b mut self, model: &T) -> BoxFuture<'b, Result<u64, sqlx::Error>> {
2782 self.execute_update(Model::to_map(model))
2783 }
2784
2785 /// Updates columns based on a partial model (struct implementing AnyImpl).
2786 ///
2787 /// This allows updating a subset of columns using a custom struct.
2788 /// The struct must implement `AnyImpl` (usually via `#[derive(FromAnyRow)]`).
2789 ///
2790 /// # Arguments
2791 ///
2792 /// * `partial` - The partial model containing new values
2793 ///
2794 /// # Returns
2795 ///
2796 /// * `Ok(u64)` - The number of rows affected
2797 pub fn update_partial<'b, P: AnyImpl>(&'b mut self, partial: &P) -> BoxFuture<'b, Result<u64, sqlx::Error>> {
2798 self.execute_update(AnyImpl::to_map(partial))
2799 }
2800
2801 /// Updates a column using a raw SQL expression.
2802 ///
2803 /// This allows for complex updates like incrementing values or using database functions.
2804 /// You can use a `?` placeholder in the expression and provide a value to bind.
2805 ///
2806 /// # Arguments
2807 ///
2808 /// * `col` - The column name to update
2809 /// * `expr` - The raw SQL expression (e.g., "age + 1" or "age + ?")
2810 /// * `value` - The value to bind for the placeholder
2811 ///
2812 /// # Example
2813 ///
2814 /// ```rust,ignore
2815 /// // Increment age by 1
2816 /// db.model::<User>()
2817 /// .filter("id", "=", 1)
2818 /// .update_raw("age", "age + 1", 0)
2819 /// .await?;
2820 ///
2821 /// // Increment age by a variable
2822 /// db.model::<User>()
2823 /// .filter("id", "=", 1)
2824 /// .update_raw("age", "age + ?", 5)
2825 /// .await?;
2826 /// ```
2827 pub fn update_raw<'b, V>(
2828 &'b mut self,
2829 col: &str,
2830 expr: &str,
2831 value: V,
2832 ) -> BoxFuture<'b, Result<u64, sqlx::Error>>
2833 where
2834 V: 'static + for<'q> Encode<'q, Any> + Type<Any> + Send + Sync + Clone,
2835 {
2836 self.apply_soft_delete_filter();
2837
2838 let col_name_clean = col.strip_prefix("r#").unwrap_or(col).to_snake_case();
2839 let expr_owned = expr.to_string();
2840 let value_owned = value.clone();
2841
2842 Box::pin(async move {
2843 let table_name = self.table_name.to_snake_case();
2844 let mut query = format!("UPDATE \"{}\" ", table_name);
2845 if let Some(alias) = &self.alias {
2846 query.push_str(&format!("AS {} ", alias));
2847 }
2848 query.push_str("SET ");
2849
2850 let mut arg_counter = 1;
2851 let mut args = AnyArguments::default();
2852
2853 let mut processed_expr = expr_owned.clone();
2854 let mut has_placeholder = false;
2855
2856 if processed_expr.contains('?') {
2857 has_placeholder = true;
2858 if matches!(self.driver, Drivers::Postgres) {
2859 while let Some(pos) = processed_expr.find('?') {
2860 let placeholder = format!("${}", arg_counter);
2861 arg_counter += 1;
2862 processed_expr.replace_range(pos..pos + 1, &placeholder);
2863 }
2864 }
2865 }
2866
2867 if has_placeholder {
2868 let _ = args.add(value_owned);
2869 }
2870
2871 query.push_str(&format!("\"{}\" = {}", col_name_clean, processed_expr));
2872 query.push_str(" WHERE 1=1");
2873
2874 for clause in &self.where_clauses {
2875 clause(&mut query, &mut args, &self.driver, &mut arg_counter);
2876 }
2877
2878 if self.debug_mode {
2879 log::debug!("SQL: {}", query);
2880 }
2881
2882 let result = self.tx.execute(&query, args).await?;
2883 Ok(result.rows_affected())
2884 })
2885 }
2886
2887 /// Internal helper to apply soft delete filter to where clauses if necessary.
2888 fn apply_soft_delete_filter(&mut self) {
2889 if !self.with_deleted {
2890 if let Some(soft_delete_col) = self.columns_info.iter().find(|c| c.soft_delete).map(|c| c.name) {
2891 let col_owned = soft_delete_col.to_string();
2892 let clause: FilterFn = Box::new(move |query, _args, _driver, _arg_counter| {
2893 query.push_str(" AND ");
2894 query.push_str(&format!("\"{}\"", col_owned));
2895 query.push_str(" IS NULL");
2896 });
2897 self.where_clauses.push(clause);
2898 }
2899 }
2900 }
2901
2902 /// Internal helper to execute an UPDATE query from a map of values.
2903 fn execute_update<'b>(
2904 &'b mut self,
2905 data_map: std::collections::HashMap<String, Option<String>>,
2906 ) -> BoxFuture<'b, Result<u64, sqlx::Error>> {
2907 self.apply_soft_delete_filter();
2908
2909 Box::pin(async move {
2910 let table_name = self.table_name.to_snake_case();
2911 let mut query = format!("UPDATE \"{}\" ", table_name);
2912 if let Some(alias) = &self.alias {
2913 query.push_str(&format!("{} ", alias));
2914 }
2915 query.push_str("SET ");
2916
2917 let mut bindings: Vec<(Option<String>, &str)> = Vec::new();
2918 let mut set_clauses = Vec::new();
2919
2920 // Maintain argument counter for PostgreSQL ($1, $2, ...)
2921 let mut arg_counter = 1;
2922
2923 // Build SET clause
2924 for (col_name, value) in data_map {
2925 // Strip the "r#" prefix if present
2926 let col_name_clean = col_name.strip_prefix("r#").unwrap_or(&col_name).to_snake_case();
2927
2928 // Find the SQL type for this column from the Model metadata
2929 let sql_type_opt = self
2930 .columns_info
2931 .iter()
2932 .find(|c| c.name == col_name || c.name == col_name_clean)
2933 .map(|c| c.sql_type);
2934
2935 let sql_type = match sql_type_opt {
2936 Some(t) => t,
2937 None => continue,
2938 };
2939
2940 // Generate placeholder
2941 let placeholder = match self.driver {
2942 Drivers::Postgres => {
2943 let idx = arg_counter;
2944 arg_counter += 1;
2945
2946 if temporal::is_temporal_type(sql_type) {
2947 format!("${}{}", idx, temporal::get_postgres_type_cast(sql_type))
2948 } else {
2949 match sql_type {
2950 "UUID" => format!("${}::UUID", idx),
2951 "JSONB" | "jsonb" => format!("${}::JSONB", idx),
2952 _ => format!("${}", idx),
2953 }
2954 }
2955 }
2956 _ => "?".to_string(),
2957 };
2958
2959 set_clauses.push(format!("\"{}\" = {}", col_name_clean, placeholder));
2960 bindings.push((value, sql_type));
2961 }
2962
2963 // If no fields to update, return 0
2964 if set_clauses.is_empty() {
2965 return Ok(0);
2966 }
2967
2968 query.push_str(&set_clauses.join(", "));
2969
2970 // Build WHERE clause
2971 query.push_str(" WHERE 1=1");
2972
2973 let mut args = AnyArguments::default();
2974
2975 // Bind SET values
2976 for (val_opt, sql_type) in bindings {
2977 if let Some(val_str) = val_opt {
2978 if args.bind_value(&val_str, sql_type, &self.driver).is_err() {
2979 let _ = args.add(val_str);
2980 }
2981 } else {
2982 let _ = args.add(None::<String>);
2983 }
2984 }
2985
2986 // Apply WHERE clauses (appending to args and query)
2987 for clause in &self.where_clauses {
2988 clause(&mut query, &mut args, &self.driver, &mut arg_counter);
2989 }
2990
2991 // Print SQL query to logs if debug mode is active
2992 if self.debug_mode {
2993 log::debug!("SQL: {}", query);
2994 }
2995
2996 // Execute the UPDATE query
2997 let result = self.tx.execute(&query, args).await?;
2998
2999 Ok(result.rows_affected())
3000 })
3001 }
3002
3003 /// Executes a DELETE query based on the current filters.
3004 ///
3005 /// Performs a soft delete if the model has a soft delete column,
3006 /// otherwise performs a permanent hard delete.
3007 ///
3008 /// # Returns
3009 ///
3010 /// * `Ok(u64)` - The number of rows deleted (or soft-deleted)
3011 /// * `Err(sqlx::Error)` - Database error
3012 ///
3013 /// # Example
3014 ///
3015 /// ```rust,ignore
3016 /// db.model::<User>()
3017 /// .filter("id", Op::Eq, 1)
3018 /// .delete()
3019 /// .await?;
3020 /// // SQL (Soft): UPDATE "user" SET "deleted_at" = NOW() WHERE "id" = 1
3021 /// // SQL (Hard): DELETE FROM "user" WHERE "id" = 1
3022 /// ```
3023 pub async fn delete(self) -> Result<u64, sqlx::Error> {
3024 // Check for soft delete column
3025 let soft_delete_col = self.columns_info.iter().find(|c| c.soft_delete).map(|c| c.name);
3026
3027 if let Some(col) = soft_delete_col {
3028 // Soft Delete: Update the column to current timestamp
3029 let table_name = self.table_name.to_snake_case();
3030 let mut query = format!("UPDATE \"{}\" ", table_name);
3031 if let Some(alias) = &self.alias {
3032 query.push_str(&format!("{} ", alias));
3033 }
3034 query.push_str(&format!("SET \"{}\" = ", col));
3035
3036 match self.driver {
3037 Drivers::Postgres => query.push_str("NOW()"),
3038 Drivers::SQLite => query.push_str("strftime('%Y-%m-%dT%H:%M:%SZ', 'now')"),
3039 Drivers::MySQL => query.push_str("NOW()"),
3040 }
3041
3042 query.push_str(" WHERE 1=1");
3043
3044 let mut args = AnyArguments::default();
3045 let mut arg_counter = 1;
3046
3047 // Apply filters
3048 for clause in &self.where_clauses {
3049 clause(&mut query, &mut args, &self.driver, &mut arg_counter);
3050 }
3051
3052 // Print SQL query to logs if debug mode is active
3053 if self.debug_mode {
3054 log::debug!("SQL: {}", query);
3055 }
3056
3057 let result = self.tx.execute(&query, args).await?;
3058 Ok(result.rows_affected())
3059 } else {
3060 // Standard Delete (no soft delete column)
3061 let mut query = String::from("DELETE FROM \"");
3062 query.push_str(&self.table_name.to_snake_case());
3063 query.push_str("\" WHERE 1=1");
3064
3065 let mut args = AnyArguments::default();
3066 let mut arg_counter = 1;
3067
3068 for clause in &self.where_clauses {
3069 clause(&mut query, &mut args, &self.driver, &mut arg_counter);
3070 }
3071
3072 // Print SQL query to logs if debug mode is active
3073 if self.debug_mode {
3074 log::debug!("SQL: {}", query);
3075 }
3076
3077 let result = self.tx.execute(&query, args).await?;
3078 Ok(result.rows_affected())
3079 }
3080 }
3081
3082 /// Permanently removes records from the database.
3083 ///
3084 /// # Returns
3085 ///
3086 /// * `Ok(u64)` - The number of rows deleted
3087 /// * `Err(sqlx::Error)` - Database error
3088 ///
3089 /// # Example
3090 ///
3091 /// ```rust,ignore
3092 /// db.model::<User>()
3093 /// .filter("id", Op::Eq, 1)
3094 /// .hard_delete()
3095 /// .await?;
3096 /// // SQL: DELETE FROM "user" WHERE "id" = 1
3097 /// ```
3098 pub async fn hard_delete(self) -> Result<u64, sqlx::Error> {
3099 let mut query = String::from("DELETE FROM \"");
3100 query.push_str(&self.table_name.to_snake_case());
3101 query.push_str("\" WHERE 1=1");
3102
3103 let mut args = AnyArguments::default();
3104 let mut arg_counter = 1;
3105
3106 for clause in &self.where_clauses {
3107 clause(&mut query, &mut args, &self.driver, &mut arg_counter);
3108 }
3109
3110 // Print SQL query to logs if debug mode is active
3111 if self.debug_mode {
3112 log::debug!("SQL: {}", query);
3113 }
3114
3115 let result = self.tx.execute(&query, args).await?;
3116 Ok(result.rows_affected())
3117 }
3118}