reinhardt_db/orm/query.rs
1//! Unified query interface facade
2//!
3//! This module provides a unified entry point for querying functionality.
4//! By default, it exports the expression-based query API (SQLAlchemy-style).
5
6use super::FieldSelector;
7use crate::orm::query_fields::GroupByFields;
8use crate::orm::query_fields::aggregate::{AggregateExpr, ComparisonExpr};
9use crate::orm::query_fields::comparison::FieldComparison;
10use crate::orm::query_fields::compiler::QueryFieldCompiler;
11use reinhardt_query::prelude::{
12 Alias, BinOper, ColumnRef, Condition, Expr, ExprTrait, Func, JoinType as SeaJoinType, Order,
13 PostgresQueryBuilder, Query, QueryStatementBuilder, SelectStatement, SimpleExpr,
14};
15use reinhardt_query::types::PgBinOper;
16use serde::{Deserialize, Serialize};
17use smallvec::SmallVec;
18use std::collections::HashMap;
19use uuid::Uuid;
20
21// Django QuerySet API types
22#[derive(Debug, Clone, Serialize, Deserialize)]
23/// Defines possible filter operator values.
24pub enum FilterOperator {
25 /// Eq variant.
26 Eq,
27 /// Ne variant.
28 Ne,
29 /// Gt variant.
30 Gt,
31 /// Gte variant.
32 Gte,
33 /// Lt variant.
34 Lt,
35 /// Lte variant.
36 Lte,
37 /// In variant.
38 In,
39 /// NotIn variant.
40 NotIn,
41 /// Contains variant.
42 Contains,
43 /// StartsWith variant.
44 StartsWith,
45 /// EndsWith variant.
46 EndsWith,
47 // PostgreSQL array operators
48 /// Array contains all elements (@>)
49 ArrayContains,
50 /// Array is contained by (<@)
51 ArrayContainedBy,
52 /// Arrays overlap (&&) - at least one common element
53 ArrayOverlap,
54 // PostgreSQL full-text search
55 /// Full-text search match (@@)
56 FullTextMatch,
57 // PostgreSQL JSONB operators
58 /// JSONB contains (@>)
59 JsonbContains,
60 /// JSONB is contained by (<@)
61 JsonbContainedBy,
62 /// JSONB key exists (?)
63 JsonbKeyExists,
64 /// JSONB any key exists (?|)
65 JsonbAnyKeyExists,
66 /// JSONB all keys exist (?&)
67 JsonbAllKeysExist,
68 /// JSONB path exists (@?)
69 JsonbPathExists,
70 // Other operators
71 /// Is null check
72 IsNull,
73 /// Is not null check
74 IsNotNull,
75 /// Range contains value (@>)
76 RangeContains,
77 /// Value is within range (<@)
78 RangeContainedBy,
79 /// Range overlaps (&&)
80 RangeOverlaps,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
84/// Defines possible filter value values.
85pub enum FilterValue {
86 /// String variant.
87 String(String),
88 /// Integer variant.
89 Integer(i64),
90 /// Alias for Integer (for compatibility with test code)
91 Int(i64),
92 /// Float variant.
93 Float(f64),
94 /// Boolean variant.
95 Boolean(bool),
96 /// Alias for Boolean (for compatibility with test code)
97 Bool(bool),
98 /// Null variant.
99 Null,
100 /// Array variant.
101 Array(Vec<String>),
102 /// Field reference for field-to-field comparisons (e.g., WHERE discount_price < total_price)
103 FieldRef(super::expressions::F),
104 /// Arithmetic expression (e.g., WHERE total != unit_price * quantity)
105 Expression(super::annotation::Expression),
106 /// Outer query reference for correlated subqueries (e.g., WHERE books.author_id = OuterRef("authors.id"))
107 OuterRef(super::expressions::OuterRef),
108}
109
110#[derive(Debug, Clone)]
111/// Represents a filter.
112pub struct Filter {
113 /// The field.
114 pub field: String,
115 /// The operator.
116 pub operator: FilterOperator,
117 /// The value.
118 pub value: FilterValue,
119}
120
121impl Filter {
122 /// Creates a new instance.
123 pub fn new(field: impl Into<String>, operator: FilterOperator, value: FilterValue) -> Self {
124 Self {
125 field: field.into(),
126 operator,
127 value,
128 }
129 }
130}
131
132/// Values that can be used in UPDATE statements
133#[derive(Debug, Clone)]
134pub enum UpdateValue {
135 /// String variant.
136 String(String),
137 /// Integer variant.
138 Integer(i64),
139 /// Float variant.
140 Float(f64),
141 /// Boolean variant.
142 Boolean(bool),
143 /// Null variant.
144 Null,
145 /// Field reference for field-to-field updates (e.g., SET discount_price = total_price)
146 FieldRef(super::expressions::F),
147 /// Arithmetic expression (e.g., SET total = unit_price * quantity)
148 Expression(super::annotation::Expression),
149}
150
151/// Composite filter condition supporting AND/OR logic
152///
153/// This enum allows building complex filter expressions with nested AND/OR conditions.
154/// It's particularly useful for search functionality that needs to match across
155/// multiple fields using OR logic.
156///
157/// # Examples
158///
159/// ```
160/// use reinhardt_db::orm::{Filter, FilterCondition, FilterOperator, FilterValue};
161///
162/// // Simple single filter
163/// let single = FilterCondition::Single(Filter::new(
164/// "name".to_string(),
165/// FilterOperator::Eq,
166/// FilterValue::String("Alice".to_string()),
167/// ));
168///
169/// // OR condition across multiple fields (useful for search)
170/// let search = FilterCondition::Or(vec![
171/// FilterCondition::Single(Filter::new(
172/// "name".to_string(),
173/// FilterOperator::Contains,
174/// FilterValue::String("alice".to_string()),
175/// )),
176/// FilterCondition::Single(Filter::new(
177/// "email".to_string(),
178/// FilterOperator::Contains,
179/// FilterValue::String("alice".to_string()),
180/// )),
181/// ]);
182///
183/// // Complex nested condition: (status = 'active') AND (name LIKE '%alice%' OR email LIKE '%alice%')
184/// let complex = FilterCondition::And(vec![
185/// FilterCondition::Single(Filter::new(
186/// "status".to_string(),
187/// FilterOperator::Eq,
188/// FilterValue::String("active".to_string()),
189/// )),
190/// search,
191/// ]);
192/// ```
193#[derive(Debug, Clone)]
194pub enum FilterCondition {
195 /// A single filter expression
196 Single(Filter),
197 /// All conditions must match (AND logic)
198 And(Vec<FilterCondition>),
199 /// Any condition must match (OR logic)
200 Or(Vec<FilterCondition>),
201 /// Negates the inner condition (NOT logic)
202 Not(Box<FilterCondition>),
203}
204
205impl FilterCondition {
206 /// Create a single filter condition
207 pub fn single(filter: Filter) -> Self {
208 Self::Single(filter)
209 }
210
211 /// Create an AND condition from multiple conditions
212 pub fn and(conditions: Vec<FilterCondition>) -> Self {
213 Self::And(conditions)
214 }
215
216 /// Create an OR condition from multiple conditions
217 pub fn or(conditions: Vec<FilterCondition>) -> Self {
218 Self::Or(conditions)
219 }
220
221 /// Create a NOT condition that negates the given condition
222 ///
223 /// # Examples
224 ///
225 /// ```
226 /// use reinhardt_db::orm::{Filter, FilterCondition, FilterOperator, FilterValue};
227 ///
228 /// let condition = FilterCondition::not(
229 /// FilterCondition::Single(Filter::new(
230 /// "is_active".to_string(),
231 /// FilterOperator::Eq,
232 /// FilterValue::Boolean(true),
233 /// ))
234 /// );
235 /// ```
236 // This method is intentionally named `not` for API consistency with Django's Q object.
237 // It does not implement std::ops::Not because it constructs a FilterCondition variant,
238 // not a boolean negation.
239 #[allow(clippy::should_implement_trait)]
240 pub fn not(condition: FilterCondition) -> Self {
241 Self::Not(Box::new(condition))
242 }
243
244 /// Create an OR condition from multiple filters (convenience method for search)
245 ///
246 /// This is particularly useful for implementing search across multiple fields.
247 ///
248 /// # Examples
249 ///
250 /// ```
251 /// use reinhardt_db::orm::{Filter, FilterCondition, FilterOperator, FilterValue};
252 ///
253 /// let search_filters = vec![
254 /// Filter::new("name".to_string(), FilterOperator::Contains, FilterValue::String("test".to_string())),
255 /// Filter::new("email".to_string(), FilterOperator::Contains, FilterValue::String("test".to_string())),
256 /// ];
257 /// let or_condition = FilterCondition::or_filters(search_filters);
258 /// ```
259 pub fn or_filters(filters: Vec<Filter>) -> Self {
260 Self::Or(filters.into_iter().map(FilterCondition::Single).collect())
261 }
262
263 /// Create an AND condition from multiple filters
264 pub fn and_filters(filters: Vec<Filter>) -> Self {
265 Self::And(filters.into_iter().map(FilterCondition::Single).collect())
266 }
267
268 /// Check if this condition is empty (no actual filters)
269 pub fn is_empty(&self) -> bool {
270 match self {
271 FilterCondition::Single(_) => false,
272 FilterCondition::And(conditions) | FilterCondition::Or(conditions) => {
273 conditions.is_empty() || conditions.iter().all(|c| c.is_empty())
274 }
275 FilterCondition::Not(condition) => condition.is_empty(),
276 }
277 }
278}
279
280// From implementations for FilterValue
281impl From<String> for FilterValue {
282 fn from(s: String) -> Self {
283 FilterValue::String(s)
284 }
285}
286
287impl From<&str> for FilterValue {
288 fn from(s: &str) -> Self {
289 FilterValue::String(s.to_string())
290 }
291}
292
293impl From<i64> for FilterValue {
294 fn from(i: i64) -> Self {
295 FilterValue::Integer(i)
296 }
297}
298
299impl From<i32> for FilterValue {
300 fn from(i: i32) -> Self {
301 FilterValue::Integer(i as i64)
302 }
303}
304
305impl From<f64> for FilterValue {
306 fn from(f: f64) -> Self {
307 FilterValue::Float(f)
308 }
309}
310
311impl From<bool> for FilterValue {
312 fn from(b: bool) -> Self {
313 FilterValue::Boolean(b)
314 }
315}
316
317impl From<uuid::Uuid> for FilterValue {
318 fn from(u: uuid::Uuid) -> Self {
319 FilterValue::String(u.to_string())
320 }
321}
322
323#[derive(Debug, Clone)]
324/// Represents a orm query.
325pub struct OrmQuery {
326 filters: Vec<Filter>,
327}
328
329impl OrmQuery {
330 /// Creates a new instance.
331 pub fn new() -> Self {
332 Self {
333 filters: Vec::new(),
334 }
335 }
336
337 /// Performs the filter operation.
338 pub fn filter(mut self, filter: Filter) -> Self {
339 self.filters.push(filter);
340 self
341 }
342}
343
344impl Default for OrmQuery {
345 fn default() -> Self {
346 Self::new()
347 }
348}
349
350/// JOIN clause specification for QuerySet
351#[derive(Clone, Debug)]
352struct JoinClause {
353 /// The type of JOIN (INNER, LEFT, RIGHT, CROSS)
354 join_type: super::sqlalchemy_query::JoinType,
355 /// The name of the table to join
356 target_table: String,
357 /// Optional alias for the target table (for self-joins)
358 target_alias: Option<String>,
359 /// The ON condition as a SQL expression string
360 /// Format: "left_table.left_field = right_table.right_field"
361 /// Can include table aliases for self-joins (e.g., "u1.id < u2.id")
362 on_condition: String,
363}
364
365/// Aggregate function types for HAVING clauses
366#[derive(Clone, Debug)]
367enum AggregateFunc {
368 Avg,
369 Count,
370 Sum,
371 Min,
372 Max,
373}
374
375/// Comparison operators for HAVING clauses
376#[derive(Clone, Debug)]
377pub enum ComparisonOp {
378 /// Eq variant.
379 Eq,
380 /// Ne variant.
381 Ne,
382 /// Gt variant.
383 Gt,
384 /// Gte variant.
385 Gte,
386 /// Lt variant.
387 Lt,
388 /// Lte variant.
389 Lte,
390}
391
392/// Value types for aggregate comparisons in HAVING clauses
393#[derive(Clone, Debug)]
394enum AggregateValue {
395 Int(i64),
396 Float(f64),
397}
398
399/// HAVING clause condition specification
400#[derive(Clone, Debug)]
401enum HavingCondition {
402 /// Compare an aggregate function result with a value
403 /// Example: HAVING AVG(price) > 1500.0
404 AggregateCompare {
405 func: AggregateFunc,
406 field: String,
407 operator: ComparisonOp,
408 value: AggregateValue,
409 },
410}
411
412/// Subquery condition specification for WHERE clause
413#[derive(Clone, Debug)]
414enum SubqueryCondition {
415 /// WHERE field IN (subquery)
416 /// Example: WHERE author_id IN (SELECT id FROM authors WHERE name = 'John')
417 In { field: String, subquery: String },
418 /// WHERE field NOT IN (subquery)
419 NotIn { field: String, subquery: String },
420 /// WHERE EXISTS (subquery)
421 /// Example: WHERE EXISTS (SELECT 1 FROM books WHERE author_id = authors.id)
422 Exists { subquery: String },
423 /// WHERE NOT EXISTS (subquery)
424 NotExists { subquery: String },
425}
426
427#[derive(Clone)]
428/// Represents a query set.
429pub struct QuerySet<T>
430where
431 T: super::Model,
432{
433 _phantom: std::marker::PhantomData<T>,
434 filters: SmallVec<[Filter; 10]>,
435 select_related_fields: Vec<String>,
436 prefetch_related_fields: Vec<String>,
437 order_by_fields: Vec<String>,
438 distinct_enabled: bool,
439 selected_fields: Option<Vec<String>>,
440 deferred_fields: Vec<String>,
441 annotations: Vec<super::annotation::Annotation>,
442 manager: Option<std::sync::Arc<super::manager::Manager<T>>>,
443 limit: Option<usize>,
444 offset: Option<usize>,
445 ctes: super::cte::CTECollection,
446 lateral_joins: super::lateral_join::LateralJoins,
447 joins: Vec<JoinClause>,
448 group_by_fields: Vec<String>,
449 having_conditions: Vec<HavingCondition>,
450 subquery_conditions: Vec<SubqueryCondition>,
451 from_alias: Option<String>,
452 /// Subquery SQL for FROM clause (derived table)
453 /// When set, the FROM clause will use this subquery instead of the model's table
454 from_subquery_sql: Option<String>,
455}
456
457impl<T> QuerySet<T>
458where
459 T: super::Model,
460{
461 /// Creates a new instance.
462 pub fn new() -> Self {
463 Self {
464 _phantom: std::marker::PhantomData,
465 filters: SmallVec::new(),
466 select_related_fields: Vec::new(),
467 prefetch_related_fields: Vec::new(),
468 order_by_fields: Vec::new(),
469 distinct_enabled: false,
470 selected_fields: None,
471 deferred_fields: Vec::new(),
472 annotations: Vec::new(),
473 manager: None,
474 limit: None,
475 offset: None,
476 ctes: super::cte::CTECollection::new(),
477 lateral_joins: super::lateral_join::LateralJoins::new(),
478 joins: Vec::new(),
479 group_by_fields: Vec::new(),
480 having_conditions: Vec::new(),
481 subquery_conditions: Vec::new(),
482 from_alias: None,
483 from_subquery_sql: None,
484 }
485 }
486
487 /// Sets the manager and returns self for chaining.
488 pub fn with_manager(manager: std::sync::Arc<super::manager::Manager<T>>) -> Self {
489 Self {
490 _phantom: std::marker::PhantomData,
491 filters: SmallVec::new(),
492 select_related_fields: Vec::new(),
493 prefetch_related_fields: Vec::new(),
494 order_by_fields: Vec::new(),
495 distinct_enabled: false,
496 selected_fields: None,
497 deferred_fields: Vec::new(),
498 annotations: Vec::new(),
499 manager: Some(manager),
500 limit: None,
501 offset: None,
502 ctes: super::cte::CTECollection::new(),
503 lateral_joins: super::lateral_join::LateralJoins::new(),
504 joins: Vec::new(),
505 group_by_fields: Vec::new(),
506 having_conditions: Vec::new(),
507 subquery_conditions: Vec::new(),
508 from_alias: None,
509 from_subquery_sql: None,
510 }
511 }
512
513 /// Performs the filter operation.
514 pub fn filter(mut self, filter: Filter) -> Self {
515 self.filters.push(filter);
516 self
517 }
518
519 /// Create a QuerySet from a subquery (FROM clause subquery / derived table)
520 ///
521 /// This method creates a new QuerySet that uses a subquery as its data source
522 /// instead of a regular table. The subquery becomes a derived table in the FROM clause.
523 ///
524 /// # Type Parameters
525 ///
526 /// * `M` - The model type for the subquery
527 /// * `F` - A closure that builds the subquery
528 ///
529 /// # Parameters
530 ///
531 /// * `builder` - A closure that receives a fresh `QuerySet<M>` and returns a configured QuerySet
532 /// * `alias` - The alias for the derived table (required for FROM subqueries)
533 ///
534 /// # Examples
535 ///
536 /// ```
537 /// # use reinhardt_db::orm::{Model, QuerySet};
538 /// # use reinhardt_db::orm::annotation::{Annotation, AnnotationValue};
539 /// # use reinhardt_db::orm::aggregation::Aggregate;
540 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
541 /// # use reinhardt_db::orm::GroupByFields;
542 /// # use serde::{Serialize, Deserialize};
543 /// # #[derive(Clone, Serialize, Deserialize)]
544 /// # struct Book { id: Option<i64>, author_id: Option<i64> }
545 /// # #[derive(Clone)]
546 /// # struct BookFields;
547 /// # impl reinhardt_db::orm::model::FieldSelector for BookFields {
548 /// # fn with_alias(self, _alias: &str) -> Self { self }
549 /// # }
550 /// # impl Model for Book {
551 /// # type PrimaryKey = i64;
552 /// # type Fields = BookFields;
553 /// # fn table_name() -> &'static str { "books" }
554 /// # fn new_fields() -> Self::Fields { BookFields }
555 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
556 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
557 /// # }
558 /// // Query from a derived table showing author book counts
559 /// let results = QuerySet::<Book>::from_subquery(
560 /// |subq: QuerySet<Book>| {
561 /// subq.values(&["author_id"])
562 /// .annotate(Annotation::new("book_count", AnnotationValue::Aggregate(Aggregate::count_all())))
563 /// },
564 /// "book_stats"
565 /// )
566 /// .filter(Filter::new("book_count", FilterOperator::Gt, FilterValue::Int(1)))
567 /// .to_sql();
568 /// // Generates: SELECT * FROM (SELECT author_id, COUNT(*) AS book_count FROM books GROUP BY author_id) AS book_stats WHERE book_count > 1
569 /// ```
570 pub fn from_subquery<M, F>(builder: F, alias: &str) -> Self
571 where
572 M: super::Model + 'static,
573 F: FnOnce(QuerySet<M>) -> QuerySet<M>,
574 {
575 // Create a fresh QuerySet for the subquery model
576 let subquery_qs = QuerySet::<M>::new();
577 // Apply the builder to configure the subquery
578 let configured_subquery = builder(subquery_qs);
579 // Generate SQL for the subquery (wrapped in parentheses)
580 let subquery_sql = configured_subquery.as_subquery();
581
582 // Create a new QuerySet with the subquery as FROM source
583 Self {
584 _phantom: std::marker::PhantomData,
585 filters: SmallVec::new(),
586 select_related_fields: Vec::new(),
587 prefetch_related_fields: Vec::new(),
588 order_by_fields: Vec::new(),
589 distinct_enabled: false,
590 selected_fields: None,
591 deferred_fields: Vec::new(),
592 annotations: Vec::new(),
593 manager: None,
594 limit: None,
595 offset: None,
596 ctes: super::cte::CTECollection::new(),
597 lateral_joins: super::lateral_join::LateralJoins::new(),
598 joins: Vec::new(),
599 group_by_fields: Vec::new(),
600 having_conditions: Vec::new(),
601 subquery_conditions: Vec::new(),
602 from_alias: Some(alias.to_string()),
603 from_subquery_sql: Some(subquery_sql),
604 }
605 }
606
607 /// Add an INNER JOIN to the query
608 ///
609 /// Performs an INNER JOIN between the current model (T) and another model (R).
610 /// Only rows with matching values in both tables are included in the result.
611 ///
612 /// # Type Parameters
613 ///
614 /// * `R` - The model type to join with (must implement `Model` trait)
615 ///
616 /// # Parameters
617 ///
618 /// * `left_field` - The field name from the left table (current model T)
619 /// * `right_field` - The field name from the right table (model R)
620 ///
621 /// # Examples
622 ///
623 /// ```no_run
624 /// # use reinhardt_db::orm::Model;
625 /// # use serde::{Serialize, Deserialize};
626 /// # #[derive(Clone, Serialize, Deserialize)]
627 /// # struct User { id: Option<i64> }
628 /// # #[derive(Clone)]
629 /// # struct UserFields;
630 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
631 /// # fn with_alias(self, _alias: &str) -> Self { self }
632 /// # }
633 /// # impl Model for User {
634 /// # type PrimaryKey = i64;
635 /// # type Fields = UserFields;
636 /// # fn table_name() -> &'static str { "users" }
637 /// # fn new_fields() -> Self::Fields { UserFields }
638 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
639 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
640 /// # }
641 /// # #[derive(Clone, Serialize, Deserialize)]
642 /// # struct Post { id: Option<i64>, user_id: Option<i64> }
643 /// # #[derive(Clone)]
644 /// # struct PostFields;
645 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
646 /// # fn with_alias(self, _alias: &str) -> Self { self }
647 /// # }
648 /// # impl Model for Post {
649 /// # type PrimaryKey = i64;
650 /// # type Fields = PostFields;
651 /// # fn table_name() -> &'static str { "posts" }
652 /// # fn new_fields() -> Self::Fields { PostFields }
653 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
654 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
655 /// # }
656 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
657 /// // Join User and Post on user.id = post.user_id
658 /// let sql = User::objects()
659 /// .all()
660 /// .inner_join::<Post>("id", "user_id")
661 /// .to_sql();
662 /// # Ok(())
663 /// # }
664 /// ```
665 pub fn inner_join<R: super::Model>(mut self, left_field: &str, right_field: &str) -> Self {
666 let condition = format!(
667 "{}.{} = {}.{}",
668 T::table_name(),
669 left_field,
670 R::table_name(),
671 right_field
672 );
673
674 self.joins.push(JoinClause {
675 join_type: super::sqlalchemy_query::JoinType::Inner,
676 target_table: R::table_name().to_string(),
677 target_alias: None,
678 on_condition: condition,
679 });
680
681 self
682 }
683
684 /// Add a LEFT OUTER JOIN to the query
685 ///
686 /// Performs a LEFT OUTER JOIN between the current model (T) and another model (R).
687 /// All rows from the left table are included, with matching rows from the right table
688 /// or NULL values if no match is found.
689 ///
690 /// # Examples
691 ///
692 /// ```no_run
693 /// # use reinhardt_db::orm::Model;
694 /// # use serde::{Serialize, Deserialize};
695 /// # #[derive(Clone, Serialize, Deserialize)]
696 /// # struct User { id: Option<i64> }
697 /// # #[derive(Clone)]
698 /// # struct UserFields;
699 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
700 /// # fn with_alias(self, _alias: &str) -> Self { self }
701 /// # }
702 /// # impl Model for User {
703 /// # type PrimaryKey = i64;
704 /// # type Fields = UserFields;
705 /// # fn table_name() -> &'static str { "users" }
706 /// # fn new_fields() -> Self::Fields { UserFields }
707 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
708 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
709 /// # }
710 /// # #[derive(Clone, Serialize, Deserialize)]
711 /// # struct Post { id: Option<i64>, user_id: Option<i64> }
712 /// # #[derive(Clone)]
713 /// # struct PostFields;
714 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
715 /// # fn with_alias(self, _alias: &str) -> Self { self }
716 /// # }
717 /// # impl Model for Post {
718 /// # type PrimaryKey = i64;
719 /// # type Fields = PostFields;
720 /// # fn table_name() -> &'static str { "posts" }
721 /// # fn new_fields() -> Self::Fields { PostFields }
722 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
723 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
724 /// # }
725 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
726 /// // Left join User and Post
727 /// let sql = User::objects()
728 /// .all()
729 /// .left_join::<Post>("id", "user_id")
730 /// .to_sql();
731 /// # Ok(())
732 /// # }
733 /// ```
734 pub fn left_join<R: super::Model>(mut self, left_field: &str, right_field: &str) -> Self {
735 let condition = format!(
736 "{}.{} = {}.{}",
737 T::table_name(),
738 left_field,
739 R::table_name(),
740 right_field
741 );
742
743 self.joins.push(JoinClause {
744 join_type: super::sqlalchemy_query::JoinType::Left,
745 target_table: R::table_name().to_string(),
746 target_alias: None,
747 on_condition: condition,
748 });
749
750 self
751 }
752
753 /// Add a RIGHT OUTER JOIN to the query
754 ///
755 /// Performs a RIGHT OUTER JOIN between the current model (T) and another model (R).
756 /// All rows from the right table are included, with matching rows from the left table
757 /// or NULL values if no match is found.
758 ///
759 /// # Examples
760 ///
761 /// ```no_run
762 /// # use reinhardt_db::orm::Model;
763 /// # use serde::{Serialize, Deserialize};
764 /// # #[derive(Clone, Serialize, Deserialize)]
765 /// # struct User { id: Option<i64> }
766 /// # #[derive(Clone)]
767 /// # struct UserFields;
768 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
769 /// # fn with_alias(self, _alias: &str) -> Self { self }
770 /// # }
771 /// # impl Model for User {
772 /// # type PrimaryKey = i64;
773 /// # type Fields = UserFields;
774 /// # fn table_name() -> &'static str { "users" }
775 /// # fn new_fields() -> Self::Fields { UserFields }
776 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
777 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
778 /// # }
779 /// # #[derive(Clone, Serialize, Deserialize)]
780 /// # struct Post { id: Option<i64>, user_id: Option<i64> }
781 /// # #[derive(Clone)]
782 /// # struct PostFields;
783 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
784 /// # fn with_alias(self, _alias: &str) -> Self { self }
785 /// # }
786 /// # impl Model for Post {
787 /// # type PrimaryKey = i64;
788 /// # type Fields = PostFields;
789 /// # fn table_name() -> &'static str { "posts" }
790 /// # fn new_fields() -> Self::Fields { PostFields }
791 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
792 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
793 /// # }
794 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
795 /// // Right join User and Post
796 /// let sql = User::objects()
797 /// .all()
798 /// .right_join::<Post>("id", "user_id")
799 /// .to_sql();
800 /// # Ok(())
801 /// # }
802 /// ```
803 pub fn right_join<R: super::Model>(mut self, left_field: &str, right_field: &str) -> Self {
804 let condition = format!(
805 "{}.{} = {}.{}",
806 T::table_name(),
807 left_field,
808 R::table_name(),
809 right_field
810 );
811
812 self.joins.push(JoinClause {
813 join_type: super::sqlalchemy_query::JoinType::Right,
814 target_table: R::table_name().to_string(),
815 target_alias: None,
816 on_condition: condition,
817 });
818
819 self
820 }
821
822 /// Add a CROSS JOIN to the query
823 ///
824 /// Performs a CROSS JOIN between the current model (T) and another model (R).
825 /// Produces the Cartesian product of both tables (all possible combinations).
826 /// No ON condition is needed for CROSS JOIN.
827 ///
828 /// # Examples
829 ///
830 /// ```no_run
831 /// # use reinhardt_db::orm::Model;
832 /// # use serde::{Serialize, Deserialize};
833 /// # #[derive(Clone, Serialize, Deserialize)]
834 /// # struct User { id: Option<i64> }
835 /// # #[derive(Clone)]
836 /// # struct UserFields;
837 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
838 /// # fn with_alias(self, _alias: &str) -> Self { self }
839 /// # }
840 /// # impl Model for User {
841 /// # type PrimaryKey = i64;
842 /// # type Fields = UserFields;
843 /// # fn table_name() -> &'static str { "users" }
844 /// # fn new_fields() -> Self::Fields { UserFields }
845 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
846 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
847 /// # }
848 /// # #[derive(Clone, Serialize, Deserialize)]
849 /// # struct Category { id: Option<i64> }
850 /// # #[derive(Clone)]
851 /// # struct CategoryFields;
852 /// # impl reinhardt_db::orm::model::FieldSelector for CategoryFields {
853 /// # fn with_alias(self, _alias: &str) -> Self { self }
854 /// # }
855 /// # impl Model for Category {
856 /// # type PrimaryKey = i64;
857 /// # type Fields = CategoryFields;
858 /// # fn table_name() -> &'static str { "categories" }
859 /// # fn new_fields() -> Self::Fields { CategoryFields }
860 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
861 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
862 /// # }
863 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
864 /// // Cross join User and Category
865 /// let sql = User::objects()
866 /// .all()
867 /// .cross_join::<Category>()
868 /// .to_sql();
869 /// # Ok(())
870 /// # }
871 /// ```
872 pub fn cross_join<R: super::Model>(mut self) -> Self {
873 self.joins.push(JoinClause {
874 join_type: super::sqlalchemy_query::JoinType::Inner, // CROSS JOIN uses Inner with empty condition
875 target_table: R::table_name().to_string(),
876 target_alias: None,
877 on_condition: String::new(), // Empty condition for CROSS JOIN
878 });
879
880 self
881 }
882
883 /// Set an alias for the base table (FROM clause)
884 ///
885 /// This is useful for self-joins where you need to reference the same table multiple times.
886 ///
887 /// # Parameters
888 ///
889 /// * `alias` - The alias name for the base table
890 ///
891 /// # Examples
892 ///
893 /// ```
894 /// # use reinhardt_db::orm::Model;
895 /// # use reinhardt_db::orm::query_fields::Field;
896 /// # use reinhardt_db::orm::FieldSelector;
897 /// # use serde::{Serialize, Deserialize};
898 /// # #[derive(Clone, Serialize, Deserialize)]
899 /// # struct User { id: Option<i64> }
900 /// #
901 /// # #[derive(Clone)]
902 /// # struct UserFields {
903 /// # pub id: Field<User, i64>,
904 /// # }
905 /// # impl UserFields {
906 /// # pub fn new() -> Self {
907 /// # Self { id: Field::new(vec!["id"]) }
908 /// # }
909 /// # }
910 /// # impl FieldSelector for UserFields {
911 /// # fn with_alias(mut self, alias: &str) -> Self {
912 /// # self.id = self.id.with_alias(alias);
913 /// # self
914 /// # }
915 /// # }
916 /// # impl Model for User {
917 /// # type PrimaryKey = i64;
918 /// # type Fields = UserFields;
919 /// # fn table_name() -> &'static str { "users" }
920 /// # fn new_fields() -> Self::Fields { UserFields::new() }
921 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
922 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
923 /// # }
924 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
925 /// // Self-join: find user pairs
926 /// let sql = User::objects()
927 /// .all()
928 /// .from_as("u1")
929 /// .inner_join_as::<User, _>("u1", "u2", |left, right| left.id.field_lt(right.id))
930 /// .to_sql();
931 /// # Ok(())
932 /// # }
933 /// ```
934 pub fn from_as(mut self, alias: &str) -> Self {
935 self.from_alias = Some(alias.to_string());
936 self
937 }
938
939 /// Add an INNER JOIN with custom condition
940 ///
941 /// Performs an INNER JOIN with a custom ON condition expression.
942 /// Use this when you need complex join conditions beyond simple equality.
943 ///
944 /// # Type Parameters
945 ///
946 /// * `R` - The model type to join with (must implement `Model` trait)
947 ///
948 /// # Parameters
949 ///
950 /// * `condition` - Custom SQL condition for the JOIN (e.g., "users.id = posts.user_id AND posts.status = 'published'")
951 ///
952 /// # Examples
953 ///
954 /// ```no_run
955 /// # use reinhardt_db::orm::Model;
956 /// # use serde::{Serialize, Deserialize};
957 /// # #[derive(Clone, Serialize, Deserialize)]
958 /// # struct User { id: Option<i64> }
959 /// # #[derive(Clone)]
960 /// # struct UserFields;
961 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
962 /// # fn with_alias(self, _alias: &str) -> Self { self }
963 /// # }
964 /// # impl Model for User {
965 /// # type PrimaryKey = i64;
966 /// # type Fields = UserFields;
967 /// # fn table_name() -> &'static str { "users" }
968 /// # fn new_fields() -> Self::Fields { UserFields }
969 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
970 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
971 /// # }
972 /// # #[derive(Clone, Serialize, Deserialize)]
973 /// # struct Post { id: Option<i64>, user_id: Option<i64> }
974 /// # #[derive(Clone)]
975 /// # struct PostFields;
976 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
977 /// # fn with_alias(self, _alias: &str) -> Self { self }
978 /// # }
979 /// # impl Model for Post {
980 /// # type PrimaryKey = i64;
981 /// # type Fields = PostFields;
982 /// # fn table_name() -> &'static str { "posts" }
983 /// # fn new_fields() -> Self::Fields { PostFields }
984 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
985 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
986 /// # }
987 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
988 /// // Join with complex condition
989 /// let sql = User::objects()
990 /// .all()
991 /// .inner_join_on::<Post>("users.id = posts.user_id AND posts.title LIKE 'First%'")
992 /// .to_sql();
993 /// # Ok(())
994 /// # }
995 /// ```
996 pub fn inner_join_on<R: super::Model>(mut self, condition: &str) -> Self {
997 self.joins.push(JoinClause {
998 join_type: super::sqlalchemy_query::JoinType::Inner,
999 target_table: R::table_name().to_string(),
1000 target_alias: None,
1001 on_condition: condition.to_string(),
1002 });
1003
1004 self
1005 }
1006
1007 /// Add a LEFT OUTER JOIN with custom condition
1008 ///
1009 /// Similar to `inner_join_on()` but performs a LEFT OUTER JOIN.
1010 ///
1011 /// # Type Parameters
1012 ///
1013 /// * `R` - The model type to join with (must implement `Model` trait)
1014 ///
1015 /// # Parameters
1016 ///
1017 /// * `condition` - Custom SQL condition for the JOIN
1018 ///
1019 /// # Examples
1020 ///
1021 /// ```no_run
1022 /// # use reinhardt_db::orm::Model;
1023 /// # use serde::{Serialize, Deserialize};
1024 /// # #[derive(Clone, Serialize, Deserialize)]
1025 /// # struct User { id: Option<i64> }
1026 /// # #[derive(Clone)]
1027 /// # struct UserFields;
1028 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
1029 /// # fn with_alias(self, _alias: &str) -> Self { self }
1030 /// # }
1031 /// # impl Model for User {
1032 /// # type PrimaryKey = i64;
1033 /// # type Fields = UserFields;
1034 /// # fn table_name() -> &'static str { "users" }
1035 /// # fn new_fields() -> Self::Fields { UserFields }
1036 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1037 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1038 /// # }
1039 /// # #[derive(Clone, Serialize, Deserialize)]
1040 /// # struct Post { id: Option<i64>, user_id: Option<i64> }
1041 /// # #[derive(Clone)]
1042 /// # struct PostFields;
1043 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
1044 /// # fn with_alias(self, _alias: &str) -> Self { self }
1045 /// # }
1046 /// # impl Model for Post {
1047 /// # type PrimaryKey = i64;
1048 /// # type Fields = PostFields;
1049 /// # fn table_name() -> &'static str { "posts" }
1050 /// # fn new_fields() -> Self::Fields { PostFields }
1051 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1052 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1053 /// # }
1054 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1055 /// let sql = User::objects()
1056 /// .all()
1057 /// .left_join_on::<Post>("users.id = posts.user_id AND posts.published = true")
1058 /// .to_sql();
1059 /// # Ok(())
1060 /// # }
1061 /// ```
1062 pub fn left_join_on<R: super::Model>(mut self, condition: &str) -> Self {
1063 self.joins.push(JoinClause {
1064 join_type: super::sqlalchemy_query::JoinType::Left,
1065 target_table: R::table_name().to_string(),
1066 target_alias: None,
1067 on_condition: condition.to_string(),
1068 });
1069
1070 self
1071 }
1072
1073 /// Add a RIGHT OUTER JOIN with custom condition
1074 ///
1075 /// Similar to `inner_join_on()` but performs a RIGHT OUTER JOIN.
1076 ///
1077 /// # Type Parameters
1078 ///
1079 /// * `R` - The model type to join with (must implement `Model` trait)
1080 ///
1081 /// # Parameters
1082 ///
1083 /// * `condition` - Custom SQL condition for the JOIN
1084 ///
1085 /// # Examples
1086 ///
1087 /// ```no_run
1088 /// # use reinhardt_db::orm::Model;
1089 /// # use serde::{Serialize, Deserialize};
1090 /// # #[derive(Clone, Serialize, Deserialize)]
1091 /// # struct User { id: Option<i64> }
1092 /// # #[derive(Clone)]
1093 /// # struct UserFields;
1094 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
1095 /// # fn with_alias(self, _alias: &str) -> Self { self }
1096 /// # }
1097 /// # impl Model for User {
1098 /// # type PrimaryKey = i64;
1099 /// # type Fields = UserFields;
1100 /// # fn table_name() -> &'static str { "users" }
1101 /// # fn new_fields() -> Self::Fields { UserFields }
1102 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1103 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1104 /// # }
1105 /// # #[derive(Clone, Serialize, Deserialize)]
1106 /// # struct Post { id: Option<i64>, user_id: Option<i64> }
1107 /// # #[derive(Clone)]
1108 /// # struct PostFields;
1109 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
1110 /// # fn with_alias(self, _alias: &str) -> Self { self }
1111 /// # }
1112 /// # impl Model for Post {
1113 /// # type PrimaryKey = i64;
1114 /// # type Fields = PostFields;
1115 /// # fn table_name() -> &'static str { "posts" }
1116 /// # fn new_fields() -> Self::Fields { PostFields }
1117 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1118 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1119 /// # }
1120 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1121 /// let sql = User::objects()
1122 /// .all()
1123 /// .right_join_on::<Post>("users.id = posts.user_id AND users.active = true")
1124 /// .to_sql();
1125 /// # Ok(())
1126 /// # }
1127 /// ```
1128 pub fn right_join_on<R: super::Model>(mut self, condition: &str) -> Self {
1129 self.joins.push(JoinClause {
1130 join_type: super::sqlalchemy_query::JoinType::Right,
1131 target_table: R::table_name().to_string(),
1132 target_alias: None,
1133 on_condition: condition.to_string(),
1134 });
1135
1136 self
1137 }
1138
1139 /// Add an INNER JOIN with table alias
1140 ///
1141 /// Performs an INNER JOIN with an alias for the target table.
1142 /// Useful for self-joins or when you need to reference the same table multiple times.
1143 ///
1144 /// # Type Parameters
1145 ///
1146 /// * `R` - The model type to join with (must implement `Model` trait)
1147 /// * `F` - Closure that builds the JOIN ON condition
1148 ///
1149 /// # Parameters
1150 ///
1151 /// * `alias` - Alias name for the target table
1152 /// * `condition_fn` - Closure that receives a `JoinOnBuilder` and returns it with the condition set
1153 ///
1154 /// # Examples
1155 ///
1156 /// ```
1157 /// # use reinhardt_db::orm::Model;
1158 /// # use reinhardt_db::orm::query_fields::Field;
1159 /// # use reinhardt_db::orm::FieldSelector;
1160 /// # use serde::{Serialize, Deserialize};
1161 /// # #[derive(Clone, Serialize, Deserialize)]
1162 /// # struct User { id: Option<i64> }
1163 /// #
1164 /// # #[derive(Clone)]
1165 /// # struct UserFields {
1166 /// # pub id: Field<User, i64>,
1167 /// # }
1168 /// # impl UserFields {
1169 /// # pub fn new() -> Self {
1170 /// # Self { id: Field::new(vec!["id"]) }
1171 /// # }
1172 /// # }
1173 /// # impl FieldSelector for UserFields {
1174 /// # fn with_alias(mut self, alias: &str) -> Self {
1175 /// # self.id = self.id.with_alias(alias);
1176 /// # self
1177 /// # }
1178 /// # }
1179 /// # impl Model for User {
1180 /// # type PrimaryKey = i64;
1181 /// # type Fields = UserFields;
1182 /// # fn table_name() -> &'static str { "users" }
1183 /// # fn new_fields() -> Self::Fields { UserFields::new() }
1184 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1185 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1186 /// # }
1187 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1188 /// // Self-join: find user pairs where user1.id < user2.id
1189 /// let sql = User::objects()
1190 /// .all()
1191 /// .inner_join_as::<User, _>("u1", "u2", |u1, u2| u1.id.field_lt(u2.id))
1192 /// .to_sql();
1193 /// # Ok(())
1194 /// # }
1195 /// ```
1196 /// # Breaking Change
1197 ///
1198 /// The signature of this method has been changed from string-based JOIN conditions
1199 /// to type-safe field comparisons.
1200 pub fn inner_join_as<R: super::Model, F>(
1201 mut self,
1202 left_alias: &str,
1203 right_alias: &str,
1204 condition_fn: F,
1205 ) -> Self
1206 where
1207 F: FnOnce(T::Fields, R::Fields) -> FieldComparison,
1208 {
1209 // Set base table alias
1210 if self.from_alias.is_none() {
1211 self.from_alias = Some(left_alias.to_string());
1212 }
1213
1214 // Create field selectors and set aliases
1215 let left_fields = T::new_fields().with_alias(left_alias);
1216 let right_fields = R::new_fields().with_alias(right_alias);
1217
1218 // Get comparison expression from closure
1219 let comparison = condition_fn(left_fields, right_fields);
1220
1221 // Convert to SQL
1222 let condition = QueryFieldCompiler::compile_field_comparison(&comparison);
1223
1224 // Add to JoinClause
1225 self.joins.push(JoinClause {
1226 join_type: super::sqlalchemy_query::JoinType::Inner,
1227 target_table: R::table_name().to_string(),
1228 target_alias: Some(right_alias.to_string()),
1229 on_condition: condition,
1230 });
1231
1232 self
1233 }
1234
1235 /// Add a LEFT OUTER JOIN with table alias
1236 ///
1237 /// Similar to `inner_join_as()` but performs a LEFT OUTER JOIN.
1238 ///
1239 /// # Type Parameters
1240 ///
1241 /// * `R` - The model type to join with (must implement `Model` trait)
1242 /// * `F` - Closure that builds the JOIN ON condition
1243 ///
1244 /// # Parameters
1245 ///
1246 /// * `alias` - Alias name for the target table
1247 /// * `condition_fn` - Closure that receives a `JoinOnBuilder` and returns it with the condition set
1248 ///
1249 /// # Examples
1250 ///
1251 /// ```
1252 /// # use reinhardt_db::orm::Model;
1253 /// # use reinhardt_db::orm::query_fields::Field;
1254 /// # use reinhardt_db::orm::FieldSelector;
1255 /// # use serde::{Serialize, Deserialize};
1256 /// # #[derive(Clone, Serialize, Deserialize)]
1257 /// # struct User { id: Option<i64> }
1258 /// #
1259 /// # #[derive(Clone)]
1260 /// # struct UserFields {
1261 /// # pub id: Field<User, i64>,
1262 /// # pub manager_id: Field<User, i64>,
1263 /// # }
1264 /// # impl UserFields {
1265 /// # pub fn new() -> Self {
1266 /// # Self {
1267 /// # id: Field::new(vec!["id"]),
1268 /// # manager_id: Field::new(vec!["manager_id"]),
1269 /// # }
1270 /// # }
1271 /// # }
1272 /// # impl FieldSelector for UserFields {
1273 /// # fn with_alias(mut self, alias: &str) -> Self {
1274 /// # self.id = self.id.with_alias(alias);
1275 /// # self.manager_id = self.manager_id.with_alias(alias);
1276 /// # self
1277 /// # }
1278 /// # }
1279 /// # impl Model for User {
1280 /// # type PrimaryKey = i64;
1281 /// # type Fields = UserFields;
1282 /// # fn table_name() -> &'static str { "users" }
1283 /// # fn new_fields() -> Self::Fields { UserFields::new() }
1284 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1285 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1286 /// # }
1287 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1288 /// // Self-join with LEFT JOIN: find employees and their managers
1289 /// let sql = User::objects()
1290 /// .all()
1291 /// .left_join_as::<User, _>("u1", "u2", |u1, u2| u2.id.field_eq(u1.manager_id))
1292 /// .to_sql();
1293 /// # Ok(())
1294 /// # }
1295 /// ```
1296 /// # Breaking Change
1297 ///
1298 /// This method signature has been changed from string-based JOIN conditions
1299 /// to type-safe field comparisons.
1300 pub fn left_join_as<R: super::Model, F>(
1301 mut self,
1302 left_alias: &str,
1303 right_alias: &str,
1304 condition_fn: F,
1305 ) -> Self
1306 where
1307 F: FnOnce(T::Fields, R::Fields) -> FieldComparison,
1308 {
1309 // Set base table alias
1310 if self.from_alias.is_none() {
1311 self.from_alias = Some(left_alias.to_string());
1312 }
1313
1314 // Create field selectors with aliases
1315 let left_fields = T::new_fields().with_alias(left_alias);
1316 let right_fields = R::new_fields().with_alias(right_alias);
1317
1318 // Get comparison from closure
1319 let comparison = condition_fn(left_fields, right_fields);
1320
1321 // Convert to SQL
1322 let condition = QueryFieldCompiler::compile_field_comparison(&comparison);
1323
1324 // Add to JoinClause
1325 self.joins.push(JoinClause {
1326 join_type: super::sqlalchemy_query::JoinType::Left,
1327 target_table: R::table_name().to_string(),
1328 target_alias: Some(right_alias.to_string()),
1329 on_condition: condition,
1330 });
1331
1332 self
1333 }
1334
1335 /// Add a RIGHT OUTER JOIN with table alias
1336 ///
1337 /// Similar to `inner_join_as()` but performs a RIGHT OUTER JOIN.
1338 ///
1339 /// # Type Parameters
1340 ///
1341 /// * `R` - The model type to join with (must implement `Model` trait)
1342 /// * `F` - Closure that builds the JOIN ON condition
1343 ///
1344 /// # Parameters
1345 ///
1346 /// * `alias` - Alias name for the target table
1347 /// * `condition_fn` - Closure that receives a `JoinOnBuilder` and returns it with the condition set
1348 ///
1349 /// # Examples
1350 ///
1351 /// ```
1352 /// # use reinhardt_db::orm::Model;
1353 /// # use reinhardt_db::orm::query_fields::Field;
1354 /// # use reinhardt_db::orm::FieldSelector;
1355 /// # use serde::{Serialize, Deserialize};
1356 /// # #[derive(Clone, Serialize, Deserialize)]
1357 /// # struct User { id: Option<i64> }
1358 /// #
1359 /// # #[derive(Clone)]
1360 /// # struct UserFields {
1361 /// # pub id: Field<User, i64>,
1362 /// # pub department_id: Field<User, i64>,
1363 /// # }
1364 /// # impl UserFields {
1365 /// # pub fn new() -> Self {
1366 /// # Self {
1367 /// # id: Field::new(vec!["id"]),
1368 /// # department_id: Field::new(vec!["department_id"]),
1369 /// # }
1370 /// # }
1371 /// # }
1372 /// # impl FieldSelector for UserFields {
1373 /// # fn with_alias(mut self, alias: &str) -> Self {
1374 /// # self.id = self.id.with_alias(alias);
1375 /// # self.department_id = self.department_id.with_alias(alias);
1376 /// # self
1377 /// # }
1378 /// # }
1379 /// # impl Model for User {
1380 /// # type PrimaryKey = i64;
1381 /// # type Fields = UserFields;
1382 /// # fn table_name() -> &'static str { "users" }
1383 /// # fn new_fields() -> Self::Fields { UserFields::new() }
1384 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1385 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1386 /// # }
1387 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1388 /// // RIGHT JOIN: find all departments even if no users belong to them
1389 /// let sql = User::objects()
1390 /// .all()
1391 /// .right_join_as::<User, _>("u1", "u2", |u1, u2| u2.id.field_eq(u1.department_id))
1392 /// .to_sql();
1393 /// # Ok(())
1394 /// # }
1395 /// ```
1396 /// # Breaking Change
1397 ///
1398 /// This method signature has been changed from string-based JOIN conditions
1399 /// to type-safe field comparisons.
1400 pub fn right_join_as<R: super::Model, F>(
1401 mut self,
1402 left_alias: &str,
1403 right_alias: &str,
1404 condition_fn: F,
1405 ) -> Self
1406 where
1407 F: FnOnce(T::Fields, R::Fields) -> FieldComparison,
1408 {
1409 // Set base table alias
1410 if self.from_alias.is_none() {
1411 self.from_alias = Some(left_alias.to_string());
1412 }
1413
1414 // Create field selectors with aliases
1415 let left_fields = T::new_fields().with_alias(left_alias);
1416 let right_fields = R::new_fields().with_alias(right_alias);
1417
1418 // Get comparison from closure
1419 let comparison = condition_fn(left_fields, right_fields);
1420
1421 // Convert to SQL
1422 let condition = QueryFieldCompiler::compile_field_comparison(&comparison);
1423
1424 // Add to JoinClause
1425 self.joins.push(JoinClause {
1426 join_type: super::sqlalchemy_query::JoinType::Right,
1427 target_table: R::table_name().to_string(),
1428 target_alias: Some(right_alias.to_string()),
1429 on_condition: condition,
1430 });
1431
1432 self
1433 }
1434
1435 /// Add GROUP BY clause to the query
1436 ///
1437 /// Groups rows that have the same values in specified columns into summary rows.
1438 /// Typically used with aggregate functions (COUNT, MAX, MIN, SUM, AVG).
1439 ///
1440 /// # Type Parameters
1441 ///
1442 /// * `F` - Closure that builds the GROUP BY field list
1443 ///
1444 /// # Parameters
1445 ///
1446 /// * `builder_fn` - Closure that receives a `GroupByBuilder` and returns it with fields set
1447 ///
1448 /// # Examples
1449 ///
1450 /// ```
1451 /// # use reinhardt_db::orm::{Model, query_fields::{Field, GroupByFields}, FieldSelector};
1452 /// # use serde::{Serialize, Deserialize};
1453 /// # #[derive(Clone, Serialize, Deserialize)]
1454 /// # struct Book { id: Option<i64> }
1455 /// #
1456 /// # #[derive(Clone)]
1457 /// # struct BookFields {
1458 /// # pub author_id: Field<Book, i64>,
1459 /// # }
1460 /// # impl BookFields {
1461 /// # pub fn new() -> Self {
1462 /// # Self { author_id: Field::new(vec!["author_id"]) }
1463 /// # }
1464 /// # }
1465 /// # impl FieldSelector for BookFields {
1466 /// # fn with_alias(mut self, alias: &str) -> Self {
1467 /// # self.author_id = self.author_id.with_alias(alias);
1468 /// # self
1469 /// # }
1470 /// # }
1471 /// # impl Model for Book {
1472 /// # type PrimaryKey = i64;
1473 /// # type Fields = BookFields;
1474 /// # fn table_name() -> &'static str { "books" }
1475 /// # fn new_fields() -> Self::Fields { BookFields::new() }
1476 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1477 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1478 /// # }
1479 /// # #[derive(Clone, Serialize, Deserialize)]
1480 /// # struct Sale { id: Option<i64> }
1481 /// #
1482 /// # #[derive(Clone)]
1483 /// # struct SaleFields {
1484 /// # pub region: Field<Sale, String>,
1485 /// # pub product_category: Field<Sale, String>,
1486 /// # }
1487 /// # impl SaleFields {
1488 /// # pub fn new() -> Self {
1489 /// # Self {
1490 /// # region: Field::new(vec!["region"]),
1491 /// # product_category: Field::new(vec!["product_category"]),
1492 /// # }
1493 /// # }
1494 /// # }
1495 /// # impl FieldSelector for SaleFields {
1496 /// # fn with_alias(mut self, alias: &str) -> Self {
1497 /// # self.region = self.region.with_alias(alias);
1498 /// # self.product_category = self.product_category.with_alias(alias);
1499 /// # self
1500 /// # }
1501 /// # }
1502 /// # impl Model for Sale {
1503 /// # type PrimaryKey = i64;
1504 /// # type Fields = SaleFields;
1505 /// # fn table_name() -> &'static str { "sales" }
1506 /// # fn new_fields() -> Self::Fields { SaleFields::new() }
1507 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1508 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1509 /// # }
1510 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1511 /// // Group by single field
1512 /// let sql1 = Book::objects()
1513 /// .all()
1514 /// .group_by(|fields| GroupByFields::new().add(&fields.author_id))
1515 /// .to_sql();
1516 ///
1517 /// // Group by multiple fields (chain .add())
1518 /// let sql2 = Sale::objects()
1519 /// .all()
1520 /// .group_by(|fields| GroupByFields::new().add(&fields.region).add(&fields.product_category))
1521 /// .to_sql();
1522 /// # Ok(())
1523 /// # }
1524 /// ```
1525 /// # Breaking Change
1526 ///
1527 /// This method signature has been changed from string-based field selection
1528 /// to type-safe field selectors.
1529 pub fn group_by<F>(mut self, selector_fn: F) -> Self
1530 where
1531 F: FnOnce(T::Fields) -> GroupByFields,
1532 {
1533 let fields = T::new_fields();
1534 let group_by_fields = selector_fn(fields);
1535 self.group_by_fields = group_by_fields.build();
1536 self
1537 }
1538
1539 /// Add HAVING clause for AVG aggregate
1540 ///
1541 /// Filters grouped rows based on the average value of a field.
1542 ///
1543 /// # Type Parameters
1544 ///
1545 /// * `F` - Closure that selects the field
1546 ///
1547 /// # Parameters
1548 ///
1549 /// * `field_fn` - Closure that receives a `HavingFieldSelector` and returns it with the field set
1550 /// * `operator` - Comparison operator (Eq, Ne, Gt, Gte, Lt, Lte)
1551 /// * `value` - Value to compare against
1552 ///
1553 /// # Examples
1554 ///
1555 /// ```
1556 /// # use reinhardt_db::orm::{Model, query_fields::{Field, GroupByFields}, FieldSelector};
1557 /// # use serde::{Serialize, Deserialize};
1558 /// # #[derive(Clone, Serialize, Deserialize)]
1559 /// # struct Author { id: Option<i64> }
1560 /// #
1561 /// # #[derive(Clone)]
1562 /// # struct AuthorFields {
1563 /// # pub author_id: Field<Author, i64>,
1564 /// # pub price: Field<Author, f64>,
1565 /// # }
1566 /// # impl AuthorFields {
1567 /// # pub fn new() -> Self {
1568 /// # Self {
1569 /// # author_id: Field::new(vec!["author_id"]),
1570 /// # price: Field::new(vec!["price"]),
1571 /// # }
1572 /// # }
1573 /// # }
1574 /// # impl FieldSelector for AuthorFields {
1575 /// # fn with_alias(mut self, alias: &str) -> Self {
1576 /// # self.author_id = self.author_id.with_alias(alias);
1577 /// # self.price = self.price.with_alias(alias);
1578 /// # self
1579 /// # }
1580 /// # }
1581 /// # impl Model for Author {
1582 /// # type PrimaryKey = i64;
1583 /// # type Fields = AuthorFields;
1584 /// # fn table_name() -> &'static str { "authors" }
1585 /// # fn new_fields() -> Self::Fields { AuthorFields::new() }
1586 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1587 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1588 /// # }
1589 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1590 /// // Find authors with average book price > 1500
1591 /// let sql = Author::objects()
1592 /// .all()
1593 /// .group_by(|fields| GroupByFields::new().add(&fields.author_id))
1594 /// .having_avg(|fields| &fields.price, |avg| avg.gt(1500.0))
1595 /// .to_sql();
1596 /// # Ok(())
1597 /// # }
1598 /// ```
1599 /// # Breaking Change
1600 ///
1601 /// This method signature has been changed to use type-safe field selectors
1602 /// and aggregate expressions.
1603 pub fn having_avg<FS, FE, NT>(mut self, field_selector: FS, expr_fn: FE) -> Self
1604 where
1605 FS: FnOnce(&T::Fields) -> &super::query_fields::Field<T, NT>,
1606 NT: super::query_fields::NumericType,
1607 FE: FnOnce(AggregateExpr) -> ComparisonExpr,
1608 {
1609 let fields = T::new_fields();
1610 let field = field_selector(&fields);
1611 let field_path = field.path().join(".");
1612
1613 let avg_expr = AggregateExpr::avg(&field_path);
1614 let comparison = expr_fn(avg_expr);
1615
1616 // Extract components for HavingCondition
1617 let operator = match comparison.op {
1618 super::query_fields::comparison::ComparisonOperator::Eq => ComparisonOp::Eq,
1619 super::query_fields::comparison::ComparisonOperator::Ne => ComparisonOp::Ne,
1620 super::query_fields::comparison::ComparisonOperator::Gt => ComparisonOp::Gt,
1621 super::query_fields::comparison::ComparisonOperator::Gte => ComparisonOp::Gte,
1622 super::query_fields::comparison::ComparisonOperator::Lt => ComparisonOp::Lt,
1623 super::query_fields::comparison::ComparisonOperator::Lte => ComparisonOp::Lte,
1624 };
1625
1626 let value = match comparison.value {
1627 super::query_fields::aggregate::ComparisonValue::Int(i) => {
1628 AggregateValue::Float(i as f64)
1629 }
1630 super::query_fields::aggregate::ComparisonValue::Float(f) => AggregateValue::Float(f),
1631 };
1632
1633 self.having_conditions
1634 .push(HavingCondition::AggregateCompare {
1635 func: AggregateFunc::Avg,
1636 field: comparison.aggregate.field().to_string(),
1637 operator,
1638 value,
1639 });
1640 self
1641 }
1642
1643 /// Add HAVING clause for COUNT aggregate
1644 ///
1645 /// Filters grouped rows based on the count of rows in each group.
1646 ///
1647 /// # Type Parameters
1648 ///
1649 /// * `F` - Closure that selects the field
1650 ///
1651 /// # Parameters
1652 ///
1653 /// * `field_fn` - Closure that receives a `HavingFieldSelector` and returns it with the field set
1654 /// * `operator` - Comparison operator (Eq, Ne, Gt, Gte, Lt, Lte)
1655 /// * `value` - Value to compare against
1656 ///
1657 /// # Examples
1658 ///
1659 /// ```
1660 /// # use reinhardt_db::orm::{Model, query_fields::{Field, GroupByFields}, FieldSelector};
1661 /// # use serde::{Serialize, Deserialize};
1662 /// # #[derive(Clone, Serialize, Deserialize)]
1663 /// # struct Author { id: Option<i64> }
1664 /// #
1665 /// # #[derive(Clone)]
1666 /// # struct AuthorFields {
1667 /// # pub author_id: Field<Author, i64>,
1668 /// # }
1669 /// # impl AuthorFields {
1670 /// # pub fn new() -> Self {
1671 /// # Self { author_id: Field::new(vec!["author_id"]) }
1672 /// # }
1673 /// # }
1674 /// # impl FieldSelector for AuthorFields {
1675 /// # fn with_alias(mut self, alias: &str) -> Self {
1676 /// # self.author_id = self.author_id.with_alias(alias);
1677 /// # self
1678 /// # }
1679 /// # }
1680 /// # impl Model for Author {
1681 /// # type PrimaryKey = i64;
1682 /// # type Fields = AuthorFields;
1683 /// # fn table_name() -> &'static str { "authors" }
1684 /// # fn new_fields() -> Self::Fields { AuthorFields::new() }
1685 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1686 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1687 /// # }
1688 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1689 /// // Find authors with more than 5 books
1690 /// let sql = Author::objects()
1691 /// .all()
1692 /// .group_by(|fields| GroupByFields::new().add(&fields.author_id))
1693 /// .having_count(|count| count.gt(5))
1694 /// .to_sql();
1695 /// # Ok(())
1696 /// # }
1697 /// ```
1698 /// # Breaking Change
1699 ///
1700 /// This method signature has been changed to use type-safe aggregate expressions.
1701 pub fn having_count<F>(mut self, expr_fn: F) -> Self
1702 where
1703 F: FnOnce(AggregateExpr) -> ComparisonExpr,
1704 {
1705 let count_expr = AggregateExpr::count("*");
1706 let comparison = expr_fn(count_expr);
1707
1708 // Extract components for HavingCondition
1709 let operator = match comparison.op {
1710 super::query_fields::comparison::ComparisonOperator::Eq => ComparisonOp::Eq,
1711 super::query_fields::comparison::ComparisonOperator::Ne => ComparisonOp::Ne,
1712 super::query_fields::comparison::ComparisonOperator::Gt => ComparisonOp::Gt,
1713 super::query_fields::comparison::ComparisonOperator::Gte => ComparisonOp::Gte,
1714 super::query_fields::comparison::ComparisonOperator::Lt => ComparisonOp::Lt,
1715 super::query_fields::comparison::ComparisonOperator::Lte => ComparisonOp::Lte,
1716 };
1717
1718 let value = match comparison.value {
1719 super::query_fields::aggregate::ComparisonValue::Int(i) => AggregateValue::Int(i),
1720 super::query_fields::aggregate::ComparisonValue::Float(f) => AggregateValue::Float(f),
1721 };
1722
1723 self.having_conditions
1724 .push(HavingCondition::AggregateCompare {
1725 func: AggregateFunc::Count,
1726 field: comparison.aggregate.field().to_string(),
1727 operator,
1728 value,
1729 });
1730 self
1731 }
1732
1733 /// Add HAVING clause for SUM aggregate
1734 ///
1735 /// Filters grouped rows based on the sum of values in a field.
1736 ///
1737 /// # Type Parameters
1738 ///
1739 /// * `F` - Closure that selects the field
1740 ///
1741 /// # Parameters
1742 ///
1743 /// * `field_fn` - Closure that receives a `HavingFieldSelector` and returns it with the field set
1744 /// * `operator` - Comparison operator (Eq, Ne, Gt, Gte, Lt, Lte)
1745 /// * `value` - Value to compare against
1746 ///
1747 /// # Examples
1748 ///
1749 /// ```
1750 /// # use reinhardt_db::orm::{Model, query_fields::{Field, GroupByFields}, FieldSelector};
1751 /// # use serde::{Serialize, Deserialize};
1752 /// # #[derive(Clone, Serialize, Deserialize)]
1753 /// # struct Product { id: Option<i64> }
1754 /// #
1755 /// # #[derive(Clone)]
1756 /// # struct ProductFields {
1757 /// # pub category: Field<Product, String>,
1758 /// # pub sales_amount: Field<Product, f64>,
1759 /// # }
1760 /// # impl ProductFields {
1761 /// # pub fn new() -> Self {
1762 /// # Self {
1763 /// # category: Field::new(vec!["category"]),
1764 /// # sales_amount: Field::new(vec!["sales_amount"]),
1765 /// # }
1766 /// # }
1767 /// # }
1768 /// # impl FieldSelector for ProductFields {
1769 /// # fn with_alias(mut self, alias: &str) -> Self {
1770 /// # self.category = self.category.with_alias(alias);
1771 /// # self.sales_amount = self.sales_amount.with_alias(alias);
1772 /// # self
1773 /// # }
1774 /// # }
1775 /// # impl Model for Product {
1776 /// # type PrimaryKey = i64;
1777 /// # type Fields = ProductFields;
1778 /// # fn table_name() -> &'static str { "products" }
1779 /// # fn new_fields() -> Self::Fields { ProductFields::new() }
1780 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1781 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1782 /// # }
1783 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1784 /// // Find categories with total sales > 10000
1785 /// let sql = Product::objects()
1786 /// .all()
1787 /// .group_by(|fields| GroupByFields::new().add(&fields.category))
1788 /// .having_sum(|fields| &fields.sales_amount, |sum| sum.gt(10000.0))
1789 /// .to_sql();
1790 /// # Ok(())
1791 /// # }
1792 /// ```
1793 /// # Breaking Change
1794 ///
1795 /// This method signature has been changed to use type-safe field selectors.
1796 pub fn having_sum<FS, FE, NT>(mut self, field_selector: FS, expr_fn: FE) -> Self
1797 where
1798 FS: FnOnce(&T::Fields) -> &super::query_fields::Field<T, NT>,
1799 NT: super::query_fields::NumericType,
1800 FE: FnOnce(AggregateExpr) -> ComparisonExpr,
1801 {
1802 let fields = T::new_fields();
1803 let field = field_selector(&fields);
1804 let field_path = field.path().join(".");
1805
1806 let sum_expr = AggregateExpr::sum(&field_path);
1807 let comparison = expr_fn(sum_expr);
1808
1809 let operator = match comparison.op {
1810 super::query_fields::comparison::ComparisonOperator::Eq => ComparisonOp::Eq,
1811 super::query_fields::comparison::ComparisonOperator::Ne => ComparisonOp::Ne,
1812 super::query_fields::comparison::ComparisonOperator::Gt => ComparisonOp::Gt,
1813 super::query_fields::comparison::ComparisonOperator::Gte => ComparisonOp::Gte,
1814 super::query_fields::comparison::ComparisonOperator::Lt => ComparisonOp::Lt,
1815 super::query_fields::comparison::ComparisonOperator::Lte => ComparisonOp::Lte,
1816 };
1817
1818 let value = match comparison.value {
1819 super::query_fields::aggregate::ComparisonValue::Int(i) => AggregateValue::Int(i),
1820 super::query_fields::aggregate::ComparisonValue::Float(f) => AggregateValue::Float(f),
1821 };
1822
1823 self.having_conditions
1824 .push(HavingCondition::AggregateCompare {
1825 func: AggregateFunc::Sum,
1826 field: comparison.aggregate.field().to_string(),
1827 operator,
1828 value,
1829 });
1830 self
1831 }
1832
1833 /// Add HAVING clause for MIN aggregate
1834 ///
1835 /// Filters grouped rows based on the minimum value in a field.
1836 ///
1837 /// # Breaking Change
1838 ///
1839 /// This method signature has been changed to use type-safe field selectors.
1840 ///
1841 /// # Type Parameters
1842 ///
1843 /// * `FS` - Field selector closure that returns a reference to a numeric field
1844 /// * `FE` - Expression closure that builds the comparison expression
1845 ///
1846 /// # Parameters
1847 ///
1848 /// * `field_selector` - Closure that selects the field from the model
1849 /// * `expr_fn` - Closure that builds the comparison expression using method chaining
1850 ///
1851 /// # Examples
1852 ///
1853 /// ```
1854 /// # use reinhardt_db::orm::{Model, query_fields::{Field, GroupByFields}, FieldSelector};
1855 /// # use serde::{Serialize, Deserialize};
1856 /// # #[derive(Clone, Serialize, Deserialize)]
1857 /// # struct Author { id: Option<i64> }
1858 /// #
1859 /// # #[derive(Clone)]
1860 /// # struct AuthorFields {
1861 /// # pub author_id: Field<Author, i64>,
1862 /// # pub price: Field<Author, f64>,
1863 /// # }
1864 /// # impl AuthorFields {
1865 /// # pub fn new() -> Self {
1866 /// # Self {
1867 /// # author_id: Field::new(vec!["author_id"]),
1868 /// # price: Field::new(vec!["price"]),
1869 /// # }
1870 /// # }
1871 /// # }
1872 /// # impl FieldSelector for AuthorFields {
1873 /// # fn with_alias(mut self, alias: &str) -> Self {
1874 /// # self.author_id = self.author_id.with_alias(alias);
1875 /// # self.price = self.price.with_alias(alias);
1876 /// # self
1877 /// # }
1878 /// # }
1879 /// # impl Model for Author {
1880 /// # type PrimaryKey = i64;
1881 /// # type Fields = AuthorFields;
1882 /// # fn table_name() -> &'static str { "authors" }
1883 /// # fn new_fields() -> Self::Fields { AuthorFields::new() }
1884 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1885 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1886 /// # }
1887 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1888 /// // Find authors where minimum book price > 1000
1889 /// let sql = Author::objects()
1890 /// .all()
1891 /// .group_by(|fields| GroupByFields::new().add(&fields.author_id))
1892 /// .having_min(|fields| &fields.price, |min| min.gt(1000.0))
1893 /// .to_sql();
1894 /// # Ok(())
1895 /// # }
1896 /// ```
1897 pub fn having_min<FS, FE, NT>(mut self, field_selector: FS, expr_fn: FE) -> Self
1898 where
1899 FS: FnOnce(&T::Fields) -> &super::query_fields::Field<T, NT>,
1900 NT: super::query_fields::NumericType,
1901 FE: FnOnce(AggregateExpr) -> ComparisonExpr,
1902 {
1903 let fields = T::new_fields();
1904 let field = field_selector(&fields);
1905 let field_path = field.path().join(".");
1906
1907 let min_expr = AggregateExpr::min(&field_path);
1908 let comparison = expr_fn(min_expr);
1909
1910 let operator = match comparison.op {
1911 super::query_fields::comparison::ComparisonOperator::Eq => ComparisonOp::Eq,
1912 super::query_fields::comparison::ComparisonOperator::Ne => ComparisonOp::Ne,
1913 super::query_fields::comparison::ComparisonOperator::Gt => ComparisonOp::Gt,
1914 super::query_fields::comparison::ComparisonOperator::Gte => ComparisonOp::Gte,
1915 super::query_fields::comparison::ComparisonOperator::Lt => ComparisonOp::Lt,
1916 super::query_fields::comparison::ComparisonOperator::Lte => ComparisonOp::Lte,
1917 };
1918
1919 let value = match comparison.value {
1920 super::query_fields::aggregate::ComparisonValue::Int(i) => AggregateValue::Int(i),
1921 super::query_fields::aggregate::ComparisonValue::Float(f) => AggregateValue::Float(f),
1922 };
1923
1924 self.having_conditions
1925 .push(HavingCondition::AggregateCompare {
1926 func: AggregateFunc::Min,
1927 field: comparison.aggregate.field().to_string(),
1928 operator,
1929 value,
1930 });
1931 self
1932 }
1933
1934 /// Add HAVING clause for MAX aggregate
1935 ///
1936 /// Filters grouped rows based on the maximum value in a field.
1937 ///
1938 /// # Breaking Change
1939 ///
1940 /// This method signature has been changed to use type-safe field selectors.
1941 ///
1942 /// # Type Parameters
1943 ///
1944 /// * `FS` - Field selector closure that returns a reference to a numeric field
1945 /// * `FE` - Expression closure that builds the comparison expression
1946 ///
1947 /// # Parameters
1948 ///
1949 /// * `field_selector` - Closure that selects the field from the model
1950 /// * `expr_fn` - Closure that builds the comparison expression using method chaining
1951 ///
1952 /// # Examples
1953 ///
1954 /// ```
1955 /// # use reinhardt_db::orm::{Model, query_fields::{Field, GroupByFields}, FieldSelector};
1956 /// # use serde::{Serialize, Deserialize};
1957 /// # #[derive(Clone, Serialize, Deserialize)]
1958 /// # struct Author { id: Option<i64> }
1959 /// #
1960 /// # #[derive(Clone)]
1961 /// # struct AuthorFields {
1962 /// # pub author_id: Field<Author, i64>,
1963 /// # pub price: Field<Author, f64>,
1964 /// # }
1965 /// # impl AuthorFields {
1966 /// # pub fn new() -> Self {
1967 /// # Self {
1968 /// # author_id: Field::new(vec!["author_id"]),
1969 /// # price: Field::new(vec!["price"]),
1970 /// # }
1971 /// # }
1972 /// # }
1973 /// # impl FieldSelector for AuthorFields {
1974 /// # fn with_alias(mut self, alias: &str) -> Self {
1975 /// # self.author_id = self.author_id.with_alias(alias);
1976 /// # self.price = self.price.with_alias(alias);
1977 /// # self
1978 /// # }
1979 /// # }
1980 /// # impl Model for Author {
1981 /// # type PrimaryKey = i64;
1982 /// # type Fields = AuthorFields;
1983 /// # fn table_name() -> &'static str { "authors" }
1984 /// # fn new_fields() -> Self::Fields { AuthorFields::new() }
1985 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
1986 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
1987 /// # }
1988 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
1989 /// // Find authors where maximum book price < 5000
1990 /// let sql = Author::objects()
1991 /// .all()
1992 /// .group_by(|fields| GroupByFields::new().add(&fields.author_id))
1993 /// .having_max(|fields| &fields.price, |max| max.lt(5000.0))
1994 /// .to_sql();
1995 /// # Ok(())
1996 /// # }
1997 /// ```
1998 pub fn having_max<FS, FE, NT>(mut self, field_selector: FS, expr_fn: FE) -> Self
1999 where
2000 FS: FnOnce(&T::Fields) -> &super::query_fields::Field<T, NT>,
2001 NT: super::query_fields::NumericType,
2002 FE: FnOnce(AggregateExpr) -> ComparisonExpr,
2003 {
2004 let fields = T::new_fields();
2005 let field = field_selector(&fields);
2006 let field_path = field.path().join(".");
2007
2008 let max_expr = AggregateExpr::max(&field_path);
2009 let comparison = expr_fn(max_expr);
2010
2011 let operator = match comparison.op {
2012 super::query_fields::comparison::ComparisonOperator::Eq => ComparisonOp::Eq,
2013 super::query_fields::comparison::ComparisonOperator::Ne => ComparisonOp::Ne,
2014 super::query_fields::comparison::ComparisonOperator::Gt => ComparisonOp::Gt,
2015 super::query_fields::comparison::ComparisonOperator::Gte => ComparisonOp::Gte,
2016 super::query_fields::comparison::ComparisonOperator::Lt => ComparisonOp::Lt,
2017 super::query_fields::comparison::ComparisonOperator::Lte => ComparisonOp::Lte,
2018 };
2019
2020 let value = match comparison.value {
2021 super::query_fields::aggregate::ComparisonValue::Int(i) => AggregateValue::Int(i),
2022 super::query_fields::aggregate::ComparisonValue::Float(f) => AggregateValue::Float(f),
2023 };
2024
2025 self.having_conditions
2026 .push(HavingCondition::AggregateCompare {
2027 func: AggregateFunc::Max,
2028 field: comparison.aggregate.field().to_string(),
2029 operator,
2030 value,
2031 });
2032 self
2033 }
2034
2035 /// Add WHERE IN (subquery) condition
2036 ///
2037 /// Filters rows where the specified field's value is in the result set of a subquery.
2038 ///
2039 /// # Type Parameters
2040 ///
2041 /// * `R` - The model type used in the subquery (must implement `Model` trait)
2042 /// * `F` - Function that builds the subquery QuerySet
2043 ///
2044 /// # Examples
2045 ///
2046 /// ```no_run
2047 /// # use reinhardt_db::orm::Model;
2048 /// # use reinhardt_db::orm::{QuerySet, Filter, FilterOperator, FilterValue};
2049 /// # use serde::{Serialize, Deserialize};
2050 /// # #[derive(Clone, Serialize, Deserialize)]
2051 /// # struct Author { id: Option<i64> }
2052 /// # #[derive(Clone)]
2053 /// # struct AuthorFields;
2054 /// # impl reinhardt_db::orm::model::FieldSelector for AuthorFields {
2055 /// # fn with_alias(self, _alias: &str) -> Self { self }
2056 /// # }
2057 /// # impl Model for Author {
2058 /// # type PrimaryKey = i64;
2059 /// # type Fields = AuthorFields;
2060 /// # fn table_name() -> &'static str { "authors" }
2061 /// # fn new_fields() -> Self::Fields { AuthorFields }
2062 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2063 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2064 /// # }
2065 /// # #[derive(Clone, Serialize, Deserialize)]
2066 /// # struct Book { id: Option<i64> }
2067 /// # #[derive(Clone)]
2068 /// # struct BookFields;
2069 /// # impl reinhardt_db::orm::model::FieldSelector for BookFields {
2070 /// # fn with_alias(self, _alias: &str) -> Self { self }
2071 /// # }
2072 /// # impl Model for Book {
2073 /// # type PrimaryKey = i64;
2074 /// # type Fields = BookFields;
2075 /// # fn table_name() -> &'static str { "books" }
2076 /// # fn new_fields() -> Self::Fields { BookFields }
2077 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2078 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2079 /// # }
2080 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2081 /// // Find authors who have books priced over 1500
2082 /// let authors = Author::objects()
2083 /// .filter_in_subquery("id", |subq: QuerySet<Book>| {
2084 /// subq.filter(Filter::new("price", FilterOperator::Gt, FilterValue::Int(1500)))
2085 /// .values(&["author_id"])
2086 /// })
2087 /// .all()
2088 /// .await?;
2089 /// # Ok(())
2090 /// # }
2091 /// ```
2092 pub fn filter_in_subquery<R: super::Model, F>(mut self, field: &str, subquery_fn: F) -> Self
2093 where
2094 F: FnOnce(QuerySet<R>) -> QuerySet<R>,
2095 {
2096 let subquery_qs = subquery_fn(QuerySet::<R>::new());
2097 let subquery_sql = subquery_qs.as_subquery();
2098
2099 self.subquery_conditions.push(SubqueryCondition::In {
2100 field: field.to_string(),
2101 subquery: subquery_sql,
2102 });
2103
2104 self
2105 }
2106
2107 /// Add WHERE NOT IN (subquery) condition
2108 ///
2109 /// Filters rows where the specified field's value is NOT in the result set of a subquery.
2110 ///
2111 /// # Examples
2112 ///
2113 /// ```no_run
2114 /// # use reinhardt_db::orm::Model;
2115 /// # use reinhardt_db::orm::{QuerySet, Filter, FilterOperator, FilterValue};
2116 /// # use serde::{Serialize, Deserialize};
2117 /// # #[derive(Clone, Serialize, Deserialize)]
2118 /// # struct Author { id: Option<i64> }
2119 /// # #[derive(Clone)]
2120 /// # struct AuthorFields;
2121 /// # impl reinhardt_db::orm::model::FieldSelector for AuthorFields {
2122 /// # fn with_alias(self, _alias: &str) -> Self { self }
2123 /// # }
2124 /// # impl Model for Author {
2125 /// # type PrimaryKey = i64;
2126 /// # type Fields = AuthorFields;
2127 /// # fn table_name() -> &'static str { "authors" }
2128 /// # fn new_fields() -> Self::Fields { AuthorFields }
2129 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2130 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2131 /// # }
2132 /// # #[derive(Clone, Serialize, Deserialize)]
2133 /// # struct Book { id: Option<i64> }
2134 /// # #[derive(Clone)]
2135 /// # struct BookFields;
2136 /// # impl reinhardt_db::orm::model::FieldSelector for BookFields {
2137 /// # fn with_alias(self, _alias: &str) -> Self { self }
2138 /// # }
2139 /// # impl Model for Book {
2140 /// # type PrimaryKey = i64;
2141 /// # type Fields = BookFields;
2142 /// # fn table_name() -> &'static str { "books" }
2143 /// # fn new_fields() -> Self::Fields { BookFields }
2144 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2145 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2146 /// # }
2147 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2148 /// // Find authors who have NO books priced over 1500
2149 /// let authors = Author::objects()
2150 /// .filter_not_in_subquery("id", |subq: QuerySet<Book>| {
2151 /// subq.filter(Filter::new("price", FilterOperator::Gt, FilterValue::Int(1500)))
2152 /// .values(&["author_id"])
2153 /// })
2154 /// .all()
2155 /// .await?;
2156 /// # Ok(())
2157 /// # }
2158 /// ```
2159 pub fn filter_not_in_subquery<R: super::Model, F>(mut self, field: &str, subquery_fn: F) -> Self
2160 where
2161 F: FnOnce(QuerySet<R>) -> QuerySet<R>,
2162 {
2163 let subquery_qs = subquery_fn(QuerySet::<R>::new());
2164 let subquery_sql = subquery_qs.as_subquery();
2165
2166 self.subquery_conditions.push(SubqueryCondition::NotIn {
2167 field: field.to_string(),
2168 subquery: subquery_sql,
2169 });
2170
2171 self
2172 }
2173
2174 /// Add WHERE EXISTS (subquery) condition
2175 ///
2176 /// Filters rows where the subquery returns at least one row.
2177 /// Typically used with correlated subqueries.
2178 ///
2179 /// # Examples
2180 ///
2181 /// ```no_run
2182 /// # use reinhardt_db::orm::Model;
2183 /// # use reinhardt_db::orm::{QuerySet, Filter, FilterOperator, FilterValue};
2184 /// # use serde::{Serialize, Deserialize};
2185 /// # #[derive(Clone, Serialize, Deserialize)]
2186 /// # struct Author { id: Option<i64> }
2187 /// # #[derive(Clone)]
2188 /// # struct AuthorFields;
2189 /// # impl reinhardt_db::orm::model::FieldSelector for AuthorFields {
2190 /// # fn with_alias(self, _alias: &str) -> Self { self }
2191 /// # }
2192 /// # impl Model for Author {
2193 /// # type PrimaryKey = i64;
2194 /// # type Fields = AuthorFields;
2195 /// # fn table_name() -> &'static str { "authors" }
2196 /// # fn new_fields() -> Self::Fields { AuthorFields }
2197 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2198 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2199 /// # }
2200 /// # #[derive(Clone, Serialize, Deserialize)]
2201 /// # struct Book { id: Option<i64> }
2202 /// # #[derive(Clone)]
2203 /// # struct BookFields;
2204 /// # impl reinhardt_db::orm::model::FieldSelector for BookFields {
2205 /// # fn with_alias(self, _alias: &str) -> Self { self }
2206 /// # }
2207 /// # impl Model for Book {
2208 /// # type PrimaryKey = i64;
2209 /// # type Fields = BookFields;
2210 /// # fn table_name() -> &'static str { "books" }
2211 /// # fn new_fields() -> Self::Fields { BookFields }
2212 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2213 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2214 /// # }
2215 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2216 /// use reinhardt_db::orm::F;
2217 /// // Find authors who have at least one book
2218 /// let authors = Author::objects()
2219 /// .filter_exists(|subq: QuerySet<Book>| {
2220 /// subq.filter(Filter::new("author_id", FilterOperator::Eq, FilterValue::FieldRef(F::new("authors.id"))))
2221 /// })
2222 /// .all()
2223 /// .await?;
2224 /// # Ok(())
2225 /// # }
2226 /// ```
2227 pub fn filter_exists<R: super::Model, F>(mut self, subquery_fn: F) -> Self
2228 where
2229 F: FnOnce(QuerySet<R>) -> QuerySet<R>,
2230 {
2231 let subquery_qs = subquery_fn(QuerySet::<R>::new());
2232 let subquery_sql = subquery_qs.as_subquery();
2233
2234 self.subquery_conditions.push(SubqueryCondition::Exists {
2235 subquery: subquery_sql,
2236 });
2237
2238 self
2239 }
2240
2241 /// Add WHERE NOT EXISTS (subquery) condition
2242 ///
2243 /// Filters rows where the subquery returns no rows.
2244 /// Typically used with correlated subqueries.
2245 ///
2246 /// # Examples
2247 ///
2248 /// ```no_run
2249 /// # use reinhardt_db::orm::Model;
2250 /// # use reinhardt_db::orm::{QuerySet, Filter, FilterOperator, FilterValue};
2251 /// # use serde::{Serialize, Deserialize};
2252 /// # #[derive(Clone, Serialize, Deserialize)]
2253 /// # struct Author { id: Option<i64> }
2254 /// # #[derive(Clone)]
2255 /// # struct AuthorFields;
2256 /// # impl reinhardt_db::orm::model::FieldSelector for AuthorFields {
2257 /// # fn with_alias(self, _alias: &str) -> Self { self }
2258 /// # }
2259 /// # impl Model for Author {
2260 /// # type PrimaryKey = i64;
2261 /// # type Fields = AuthorFields;
2262 /// # fn table_name() -> &'static str { "authors" }
2263 /// # fn new_fields() -> Self::Fields { AuthorFields }
2264 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2265 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2266 /// # }
2267 /// # #[derive(Clone, Serialize, Deserialize)]
2268 /// # struct Book { id: Option<i64> }
2269 /// # #[derive(Clone)]
2270 /// # struct BookFields;
2271 /// # impl reinhardt_db::orm::model::FieldSelector for BookFields {
2272 /// # fn with_alias(self, _alias: &str) -> Self { self }
2273 /// # }
2274 /// # impl Model for Book {
2275 /// # type PrimaryKey = i64;
2276 /// # type Fields = BookFields;
2277 /// # fn table_name() -> &'static str { "books" }
2278 /// # fn new_fields() -> Self::Fields { BookFields }
2279 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2280 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2281 /// # }
2282 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2283 /// use reinhardt_db::orm::F;
2284 /// // Find authors who have NO books
2285 /// let authors = Author::objects()
2286 /// .filter_not_exists(|subq: QuerySet<Book>| {
2287 /// subq.filter(Filter::new("author_id", FilterOperator::Eq, FilterValue::FieldRef(F::new("authors.id"))))
2288 /// })
2289 /// .all()
2290 /// .await?;
2291 /// # Ok(())
2292 /// # }
2293 /// ```
2294 pub fn filter_not_exists<R: super::Model, F>(mut self, subquery_fn: F) -> Self
2295 where
2296 F: FnOnce(QuerySet<R>) -> QuerySet<R>,
2297 {
2298 let subquery_qs = subquery_fn(QuerySet::<R>::new());
2299 let subquery_sql = subquery_qs.as_subquery();
2300
2301 self.subquery_conditions.push(SubqueryCondition::NotExists {
2302 subquery: subquery_sql,
2303 });
2304
2305 self
2306 }
2307
2308 /// Add a Common Table Expression (WITH clause) to the query
2309 ///
2310 /// CTEs allow you to define named subqueries that can be referenced
2311 /// in the main query. This is useful for complex queries that need
2312 /// to reference the same subquery multiple times or for recursive queries.
2313 ///
2314 /// # Examples
2315 ///
2316 /// ```no_run
2317 /// # use reinhardt_db::orm::Model;
2318 /// # use reinhardt_db::orm::cte::CTE;
2319 /// # use serde::{Serialize, Deserialize};
2320 /// # #[derive(Clone, Serialize, Deserialize)]
2321 /// # struct Employee { id: Option<i64> }
2322 /// # #[derive(Clone)]
2323 /// # struct EmployeeFields;
2324 /// # impl reinhardt_db::orm::model::FieldSelector for EmployeeFields {
2325 /// # fn with_alias(self, _alias: &str) -> Self { self }
2326 /// # }
2327 /// # impl Model for Employee {
2328 /// # type PrimaryKey = i64;
2329 /// # type Fields = EmployeeFields;
2330 /// # fn table_name() -> &'static str { "employees" }
2331 /// # fn new_fields() -> Self::Fields { EmployeeFields }
2332 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2333 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2334 /// # }
2335 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2336 /// // Simple CTE
2337 /// let high_earners = CTE::new("high_earners", "SELECT * FROM employees WHERE salary > 100000");
2338 /// let results = Employee::objects()
2339 /// .with_cte(high_earners)
2340 /// .all()
2341 /// .await?;
2342 ///
2343 /// // Recursive CTE for hierarchical data
2344 /// let hierarchy = CTE::new(
2345 /// "org_hierarchy",
2346 /// "SELECT id, name, manager_id, 1 as level FROM employees WHERE manager_id IS NULL \
2347 /// UNION ALL \
2348 /// SELECT e.id, e.name, e.manager_id, h.level + 1 \
2349 /// FROM employees e JOIN org_hierarchy h ON e.manager_id = h.id"
2350 /// ).recursive();
2351 ///
2352 /// let org = Employee::objects()
2353 /// .with_cte(hierarchy)
2354 /// .all()
2355 /// .await?;
2356 /// # Ok(())
2357 /// # }
2358 /// ```
2359 pub fn with_cte(mut self, cte: super::cte::CTE) -> Self {
2360 self.ctes.add(cte);
2361 self
2362 }
2363
2364 /// Add a LATERAL JOIN to the query
2365 ///
2366 /// LATERAL JOINs allow correlated subqueries in the FROM clause,
2367 /// where the subquery can reference columns from preceding tables.
2368 /// This is useful for "top-N per group" queries and similar patterns.
2369 ///
2370 /// **Note**: LATERAL JOIN is supported in PostgreSQL 9.3+, MySQL 8.0.14+,
2371 /// but NOT in SQLite.
2372 ///
2373 /// # Examples
2374 ///
2375 /// ```no_run
2376 /// # use reinhardt_db::orm::Model;
2377 /// # use serde::{Serialize, Deserialize};
2378 /// # #[derive(Clone, Serialize, Deserialize)]
2379 /// # struct Customer { id: Option<i64> }
2380 /// # #[derive(Clone)]
2381 /// # struct CustomerFields;
2382 /// # impl reinhardt_db::orm::model::FieldSelector for CustomerFields {
2383 /// # fn with_alias(self, _alias: &str) -> Self { self }
2384 /// # }
2385 /// # impl Model for Customer {
2386 /// # type PrimaryKey = i64;
2387 /// # type Fields = CustomerFields;
2388 /// # fn table_name() -> &'static str { "customers" }
2389 /// # fn new_fields() -> Self::Fields { CustomerFields }
2390 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
2391 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
2392 /// # }
2393 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
2394 /// use reinhardt_db::orm::lateral_join::{LateralJoin, LateralJoinPatterns};
2395 ///
2396 /// // Get top 3 orders per customer
2397 /// let top_orders = LateralJoinPatterns::top_n_per_group(
2398 /// "recent_orders",
2399 /// "orders",
2400 /// "customer_id",
2401 /// "customers",
2402 /// "created_at DESC",
2403 /// 3,
2404 /// );
2405 ///
2406 /// let results = Customer::objects()
2407 /// .all()
2408 /// .with_lateral_join(top_orders)
2409 /// .all()
2410 /// .await?;
2411 ///
2412 /// // Get latest order per customer
2413 /// let latest = LateralJoinPatterns::latest_per_parent(
2414 /// "latest_order",
2415 /// "orders",
2416 /// "customer_id",
2417 /// "customers",
2418 /// "created_at",
2419 /// );
2420 ///
2421 /// let customers_with_orders = Customer::objects()
2422 /// .all()
2423 /// .with_lateral_join(latest)
2424 /// .all()
2425 /// .await?;
2426 /// # Ok(())
2427 /// # }
2428 /// ```
2429 pub fn with_lateral_join(mut self, join: super::lateral_join::LateralJoin) -> Self {
2430 self.lateral_joins.add(join);
2431 self
2432 }
2433
2434 /// Build WHERE condition using reinhardt-query from accumulated filters
2435 fn build_where_condition(&self) -> Option<Condition> {
2436 // Early return only if both filters and subquery_conditions are empty
2437 if self.filters.is_empty() && self.subquery_conditions.is_empty() {
2438 return None;
2439 }
2440
2441 let mut cond = Condition::all();
2442
2443 for filter in &self.filters {
2444 let col = Expr::col(parse_column_reference(&filter.field));
2445
2446 let expr = match (&filter.operator, &filter.value) {
2447 // Field-to-field comparisons (must come before generic patterns)
2448 (FilterOperator::Eq, FilterValue::FieldRef(f)) => {
2449 col.eq(Expr::col(Alias::new(&f.field)))
2450 }
2451 (FilterOperator::Ne, FilterValue::FieldRef(f)) => {
2452 col.ne(Expr::col(Alias::new(&f.field)))
2453 }
2454 (FilterOperator::Gt, FilterValue::FieldRef(f)) => {
2455 col.gt(Expr::col(Alias::new(&f.field)))
2456 }
2457 (FilterOperator::Gte, FilterValue::FieldRef(f)) => {
2458 col.gte(Expr::col(Alias::new(&f.field)))
2459 }
2460 (FilterOperator::Lt, FilterValue::FieldRef(f)) => {
2461 col.lt(Expr::col(Alias::new(&f.field)))
2462 }
2463 (FilterOperator::Lte, FilterValue::FieldRef(f)) => {
2464 col.lte(Expr::col(Alias::new(&f.field)))
2465 }
2466 // OuterRef comparisons for correlated subqueries
2467 (FilterOperator::Eq, FilterValue::OuterRef(outer)) => {
2468 // For correlated subqueries, reference outer query field
2469 // e.g., WHERE books.author_id = authors.id (where authors is from outer query)
2470 col.eq(Expr::col(parse_column_reference(&outer.field)))
2471 }
2472 (FilterOperator::Ne, FilterValue::OuterRef(outer)) => {
2473 col.ne(Expr::col(parse_column_reference(&outer.field)))
2474 }
2475 (FilterOperator::Gt, FilterValue::OuterRef(outer)) => {
2476 col.gt(Expr::col(parse_column_reference(&outer.field)))
2477 }
2478 (FilterOperator::Gte, FilterValue::OuterRef(outer)) => {
2479 col.gte(Expr::col(parse_column_reference(&outer.field)))
2480 }
2481 (FilterOperator::Lt, FilterValue::OuterRef(outer)) => {
2482 col.lt(Expr::col(parse_column_reference(&outer.field)))
2483 }
2484 (FilterOperator::Lte, FilterValue::OuterRef(outer)) => {
2485 col.lte(Expr::col(parse_column_reference(&outer.field)))
2486 }
2487 // Expression comparisons (F("a") * F("b") etc.)
2488 (FilterOperator::Eq, FilterValue::Expression(expr)) => {
2489 col.eq(Self::expression_to_query_expr(expr))
2490 }
2491 (FilterOperator::Ne, FilterValue::Expression(expr)) => {
2492 col.ne(Self::expression_to_query_expr(expr))
2493 }
2494 (FilterOperator::Gt, FilterValue::Expression(expr)) => {
2495 col.gt(Self::expression_to_query_expr(expr))
2496 }
2497 (FilterOperator::Gte, FilterValue::Expression(expr)) => {
2498 col.gte(Self::expression_to_query_expr(expr))
2499 }
2500 (FilterOperator::Lt, FilterValue::Expression(expr)) => {
2501 col.lt(Self::expression_to_query_expr(expr))
2502 }
2503 (FilterOperator::Lte, FilterValue::Expression(expr)) => {
2504 col.lte(Self::expression_to_query_expr(expr))
2505 }
2506 // NULL checks
2507 (FilterOperator::Eq, FilterValue::Null) => col.is_null(),
2508 (FilterOperator::Ne, FilterValue::Null) => col.is_not_null(),
2509 // Generic value comparisons (catch-all for other FilterValue types)
2510 (FilterOperator::Eq, v) => col.eq(Self::filter_value_to_sea_value(v)),
2511 (FilterOperator::Ne, v) => col.ne(Self::filter_value_to_sea_value(v)),
2512 (FilterOperator::Gt, v) => col.gt(Self::filter_value_to_sea_value(v)),
2513 (FilterOperator::Gte, v) => col.gte(Self::filter_value_to_sea_value(v)),
2514 (FilterOperator::Lt, v) => col.lt(Self::filter_value_to_sea_value(v)),
2515 (FilterOperator::Lte, v) => col.lte(Self::filter_value_to_sea_value(v)),
2516 (FilterOperator::In, FilterValue::String(s)) => {
2517 let values = Self::parse_array_string(s);
2518 col.is_in(values)
2519 }
2520 (FilterOperator::In, FilterValue::Array(arr)) => {
2521 col.is_in(arr.iter().map(|s| s.as_str()).collect::<Vec<_>>())
2522 }
2523 (FilterOperator::NotIn, FilterValue::String(s)) => {
2524 let values = Self::parse_array_string(s);
2525 col.is_not_in(values)
2526 }
2527 (FilterOperator::NotIn, FilterValue::Array(arr)) => {
2528 col.is_not_in(arr.iter().map(|s| s.as_str()).collect::<Vec<_>>())
2529 }
2530 (FilterOperator::Contains, FilterValue::String(s)) => col.like(format!("%{}%", s)),
2531 (FilterOperator::Contains, FilterValue::Array(arr)) => {
2532 col.like(format!("%{}%", arr.first().unwrap_or(&String::new())))
2533 }
2534 (FilterOperator::StartsWith, FilterValue::String(s)) => col.like(format!("{}%", s)),
2535 (FilterOperator::StartsWith, FilterValue::Array(arr)) => {
2536 col.like(format!("{}%", arr.first().unwrap_or(&String::new())))
2537 }
2538 (FilterOperator::EndsWith, FilterValue::String(s)) => col.like(format!("%{}", s)),
2539 (FilterOperator::EndsWith, FilterValue::Array(arr)) => {
2540 col.like(format!("%{}", arr.first().unwrap_or(&String::new())))
2541 }
2542 // Handle Integer, Float, Boolean for text operators
2543 (FilterOperator::Contains, FilterValue::Integer(i) | FilterValue::Int(i)) => {
2544 col.like(format!("%{}%", i))
2545 }
2546 (FilterOperator::Contains, FilterValue::Float(f)) => col.like(format!("%{}%", f)),
2547 (FilterOperator::Contains, FilterValue::Boolean(b) | FilterValue::Bool(b)) => {
2548 col.like(format!("%{}%", b))
2549 }
2550 (FilterOperator::Contains, FilterValue::Null) => col.like("%"),
2551 (FilterOperator::StartsWith, FilterValue::Integer(i) | FilterValue::Int(i)) => {
2552 col.like(format!("{}%", i))
2553 }
2554 (FilterOperator::StartsWith, FilterValue::Float(f)) => col.like(format!("{}%", f)),
2555 (FilterOperator::StartsWith, FilterValue::Boolean(b) | FilterValue::Bool(b)) => {
2556 col.like(format!("{}%", b))
2557 }
2558 (FilterOperator::StartsWith, FilterValue::Null) => col.like("%"),
2559 (FilterOperator::EndsWith, FilterValue::Integer(i) | FilterValue::Int(i)) => {
2560 col.like(format!("%{}", i))
2561 }
2562 (FilterOperator::EndsWith, FilterValue::Float(f)) => col.like(format!("%{}", f)),
2563 (FilterOperator::EndsWith, FilterValue::Boolean(b) | FilterValue::Bool(b)) => {
2564 col.like(format!("%{}", b))
2565 }
2566 (FilterOperator::EndsWith, FilterValue::Null) => col.like("%"),
2567 // Handle In/NotIn for non-String types
2568 (FilterOperator::In, FilterValue::Integer(i) | FilterValue::Int(i)) => {
2569 col.is_in(vec![*i])
2570 }
2571 (FilterOperator::In, FilterValue::Float(f)) => col.is_in(vec![*f]),
2572 (FilterOperator::In, FilterValue::Boolean(b) | FilterValue::Bool(b)) => {
2573 col.is_in(vec![*b])
2574 }
2575 (FilterOperator::In, FilterValue::Null) => {
2576 col.is_in(vec![reinhardt_query::value::Value::Int(None)])
2577 }
2578 (FilterOperator::NotIn, FilterValue::Integer(i) | FilterValue::Int(i)) => {
2579 col.is_not_in(vec![*i])
2580 }
2581 (FilterOperator::NotIn, FilterValue::Float(f)) => col.is_not_in(vec![*f]),
2582 (FilterOperator::NotIn, FilterValue::Boolean(b) | FilterValue::Bool(b)) => {
2583 col.is_not_in(vec![*b])
2584 }
2585 (FilterOperator::NotIn, FilterValue::Null) => {
2586 col.is_not_in(vec![reinhardt_query::value::Value::Int(None)])
2587 }
2588 // IsNull/IsNotNull operators
2589 (FilterOperator::IsNull, _) => col.is_null(),
2590 (FilterOperator::IsNotNull, _) => col.is_not_null(),
2591 // PostgreSQL Array operators (using custom SQL)
2592 (FilterOperator::ArrayContains, FilterValue::Array(arr)) => {
2593 // field @> ARRAY[?, ?] - parameterized
2594 let placeholders = arr.iter().map(|_| "?").collect::<Vec<_>>().join(", ");
2595 Expr::cust_with_values(
2596 format!(
2597 "{} @> ARRAY[{}]",
2598 quote_identifier(&filter.field),
2599 placeholders
2600 ),
2601 arr.iter().cloned(),
2602 )
2603 .into_simple_expr()
2604 }
2605 (FilterOperator::ArrayContainedBy, FilterValue::Array(arr)) => {
2606 // field <@ ARRAY[?, ?] - parameterized
2607 let placeholders = arr.iter().map(|_| "?").collect::<Vec<_>>().join(", ");
2608 Expr::cust_with_values(
2609 format!(
2610 "{} <@ ARRAY[{}]",
2611 quote_identifier(&filter.field),
2612 placeholders
2613 ),
2614 arr.iter().cloned(),
2615 )
2616 .into_simple_expr()
2617 }
2618 (FilterOperator::ArrayOverlap, FilterValue::Array(arr)) => {
2619 // field && ARRAY[?, ?] - parameterized
2620 let placeholders = arr.iter().map(|_| "?").collect::<Vec<_>>().join(", ");
2621 Expr::cust_with_values(
2622 format!(
2623 "{} && ARRAY[{}]",
2624 quote_identifier(&filter.field),
2625 placeholders
2626 ),
2627 arr.iter().cloned(),
2628 )
2629 .into_simple_expr()
2630 }
2631 // PostgreSQL Full-text search
2632 (FilterOperator::FullTextMatch, FilterValue::String(query)) => {
2633 // field @@ plainto_tsquery('english', ?) - parameterized
2634 Expr::cust_with_values(
2635 format!(
2636 "{} @@ plainto_tsquery('english', ?)",
2637 quote_identifier(&filter.field)
2638 ),
2639 [query.clone()],
2640 )
2641 .into_simple_expr()
2642 }
2643 // PostgreSQL JSONB operators
2644 (FilterOperator::JsonbContains, FilterValue::String(json)) => {
2645 // field @> ?::jsonb - parameterized
2646 Expr::cust_with_values(
2647 format!("{} @> ?::jsonb", quote_identifier(&filter.field)),
2648 [json.clone()],
2649 )
2650 .into_simple_expr()
2651 }
2652 (FilterOperator::JsonbContainedBy, FilterValue::String(json)) => {
2653 // field <@ ?::jsonb - parameterized
2654 Expr::cust_with_values(
2655 format!("{} <@ ?::jsonb", quote_identifier(&filter.field)),
2656 [json.clone()],
2657 )
2658 .into_simple_expr()
2659 }
2660 (FilterOperator::JsonbKeyExists, FilterValue::String(key)) => {
2661 // field ? 'key' - using PgBinOper for safe parameterization
2662 Expr::cust(quote_identifier(&filter.field))
2663 .into_simple_expr()
2664 .binary(
2665 BinOper::PgOperator(PgBinOper::JsonContainsKey),
2666 SimpleExpr::from(key.clone()),
2667 )
2668 }
2669 (FilterOperator::JsonbAnyKeyExists, FilterValue::Array(keys)) => {
2670 // field ?| array[?, ?] - using PgBinOper for safe parameterization
2671 let placeholders = keys.iter().map(|_| "?").collect::<Vec<_>>().join(", ");
2672 let array_expr = Expr::cust_with_values(
2673 format!("array[{}]", placeholders),
2674 keys.iter().cloned(),
2675 )
2676 .into_simple_expr();
2677 Expr::cust(quote_identifier(&filter.field))
2678 .into_simple_expr()
2679 .binary(
2680 BinOper::PgOperator(PgBinOper::JsonContainsAnyKey),
2681 array_expr,
2682 )
2683 }
2684 (FilterOperator::JsonbAllKeysExist, FilterValue::Array(keys)) => {
2685 // field ?& array[?, ?] - using PgBinOper for safe parameterization
2686 let placeholders = keys.iter().map(|_| "?").collect::<Vec<_>>().join(", ");
2687 let array_expr = Expr::cust_with_values(
2688 format!("array[{}]", placeholders),
2689 keys.iter().cloned(),
2690 )
2691 .into_simple_expr();
2692 Expr::cust(quote_identifier(&filter.field))
2693 .into_simple_expr()
2694 .binary(
2695 BinOper::PgOperator(PgBinOper::JsonContainsAllKeys),
2696 array_expr,
2697 )
2698 }
2699 (FilterOperator::JsonbPathExists, FilterValue::String(path)) => {
2700 // field @? ? - parameterized
2701 Expr::cust_with_values(
2702 format!("{} @? ?", quote_identifier(&filter.field)),
2703 [path.clone()],
2704 )
2705 .into_simple_expr()
2706 }
2707 // PostgreSQL Range operators
2708 (FilterOperator::RangeContains, v) => {
2709 // field @> ? - parameterized
2710 let val = Self::filter_value_to_sql_string(v);
2711 Expr::cust_with_values(
2712 format!("{} @> ?", quote_identifier(&filter.field)),
2713 [val],
2714 )
2715 .into_simple_expr()
2716 }
2717 (FilterOperator::RangeContainedBy, FilterValue::String(range)) => {
2718 // field <@ ? - parameterized
2719 Expr::cust_with_values(
2720 format!("{} <@ ?", quote_identifier(&filter.field)),
2721 [range.clone()],
2722 )
2723 .into_simple_expr()
2724 }
2725 (FilterOperator::RangeOverlaps, FilterValue::String(range)) => {
2726 // field && ? - parameterized
2727 Expr::cust_with_values(
2728 format!("{} && ?", quote_identifier(&filter.field)),
2729 [range.clone()],
2730 )
2731 .into_simple_expr()
2732 }
2733 // Fallback for unsupported combinations
2734 _ => {
2735 // Default to equality for unhandled cases
2736 col.eq(Self::filter_value_to_sea_value(&filter.value))
2737 }
2738 };
2739
2740 cond = cond.add(expr);
2741 }
2742
2743 // Add subquery conditions
2744 for subq_cond in &self.subquery_conditions {
2745 let expr = match subq_cond {
2746 SubqueryCondition::In { field, subquery } => {
2747 // field IN (subquery)
2748 Expr::cust(format!("{} IN {}", quote_identifier(field), subquery))
2749 .into_simple_expr()
2750 }
2751 SubqueryCondition::NotIn { field, subquery } => {
2752 // field NOT IN (subquery)
2753 Expr::cust(format!("{} NOT IN {}", quote_identifier(field), subquery))
2754 .into_simple_expr()
2755 }
2756 SubqueryCondition::Exists { subquery } => {
2757 // EXISTS (subquery)
2758 Expr::cust(format!("EXISTS {}", subquery)).into_simple_expr()
2759 }
2760 SubqueryCondition::NotExists { subquery } => {
2761 // NOT EXISTS (subquery)
2762 Expr::cust(format!("NOT EXISTS {}", subquery)).into_simple_expr()
2763 }
2764 };
2765
2766 cond = cond.add(expr);
2767 }
2768
2769 Some(cond)
2770 }
2771
2772 /// Convert FilterValue to reinhardt_query::value::Value
2773 /// Convert Expression to reinhardt-query Expr for use in WHERE clauses
2774 ///
2775 /// Uses Expr::cust() for arithmetic operations as reinhardt-query doesn't provide
2776 /// multiply/divide/etc. methods. SQL injection risk is low since F() only
2777 /// accepts field names.
2778 fn expression_to_query_expr(expr: &super::annotation::Expression) -> Expr {
2779 use crate::orm::annotation::Expression;
2780
2781 match expr {
2782 Expression::Add(left, right) => {
2783 let left_sql = Self::annotation_value_to_sql(left);
2784 let right_sql = Self::annotation_value_to_sql(right);
2785 Expr::cust(format!("({} + {})", left_sql, right_sql))
2786 }
2787 Expression::Subtract(left, right) => {
2788 let left_sql = Self::annotation_value_to_sql(left);
2789 let right_sql = Self::annotation_value_to_sql(right);
2790 Expr::cust(format!("({} - {})", left_sql, right_sql))
2791 }
2792 Expression::Multiply(left, right) => {
2793 let left_sql = Self::annotation_value_to_sql(left);
2794 let right_sql = Self::annotation_value_to_sql(right);
2795 Expr::cust(format!("({} * {})", left_sql, right_sql))
2796 }
2797 Expression::Divide(left, right) => {
2798 let left_sql = Self::annotation_value_to_sql(left);
2799 let right_sql = Self::annotation_value_to_sql(right);
2800 Expr::cust(format!("({} / {})", left_sql, right_sql))
2801 }
2802 Expression::Case { whens, default } => {
2803 let mut case_sql = "CASE".to_string();
2804 for when in whens.iter() {
2805 // Use When::to_sql() which generates "WHEN condition THEN value"
2806 case_sql.push_str(&format!(" {}", when.to_sql()));
2807 }
2808 if let Some(default_val) = default {
2809 case_sql.push_str(&format!(
2810 " ELSE {}",
2811 Self::annotation_value_to_sql(default_val)
2812 ));
2813 }
2814 case_sql.push_str(" END");
2815 Expr::cust(case_sql)
2816 }
2817 Expression::Coalesce(values) => {
2818 let value_sqls = values
2819 .iter()
2820 .map(|v| Self::annotation_value_to_sql(v))
2821 .collect::<Vec<_>>()
2822 .join(", ");
2823 Expr::cust(format!("COALESCE({})", value_sqls))
2824 }
2825 }
2826 }
2827
2828 /// Convert AnnotationValue to SQL string for custom expressions
2829 ///
2830 /// Delegates to the `AnnotationValue::to_sql()` method which provides
2831 /// complete SQL generation for all annotation value types.
2832 fn annotation_value_to_sql(value: &super::annotation::AnnotationValue) -> String {
2833 value.to_sql()
2834 }
2835
2836 fn filter_value_to_sea_value(v: &FilterValue) -> reinhardt_query::value::Value {
2837 match v {
2838 FilterValue::String(s) => {
2839 // Try to parse as UUID first for proper PostgreSQL uuid column handling
2840 if let Ok(uuid) = Uuid::parse_str(s) {
2841 reinhardt_query::value::Value::Uuid(Some(Box::new(uuid)))
2842 } else {
2843 s.clone().into()
2844 }
2845 }
2846 FilterValue::Integer(i) | FilterValue::Int(i) => (*i).into(),
2847 FilterValue::Float(f) => (*f).into(),
2848 FilterValue::Boolean(b) | FilterValue::Bool(b) => (*b).into(),
2849 FilterValue::Null => reinhardt_query::value::Value::Int(None),
2850 FilterValue::Array(arr) => arr.join(",").into(),
2851 // FieldRef, Expression, and OuterRef are typically handled separately
2852 // in build_where_condition(), but provide proper conversion as fallback
2853 FilterValue::FieldRef(f) => f.field.clone().into(),
2854 FilterValue::Expression(expr) => expr.to_sql().into(),
2855 FilterValue::OuterRef(outer_ref) => outer_ref.field.clone().into(),
2856 }
2857 }
2858
2859 /// Convert FilterValue to SQL-safe string representation
2860 /// Used for custom SQL expressions (PostgreSQL operators)
2861 fn filter_value_to_sql_string(v: &FilterValue) -> String {
2862 match v {
2863 FilterValue::String(s) => format!("'{}'", s.replace('\'', "''")),
2864 FilterValue::Integer(i) | FilterValue::Int(i) => i.to_string(),
2865 FilterValue::Float(f) => f.to_string(),
2866 FilterValue::Boolean(b) | FilterValue::Bool(b) => {
2867 if *b { "TRUE" } else { "FALSE" }.to_string()
2868 }
2869 FilterValue::Null => "NULL".to_string(),
2870 FilterValue::Array(arr) => {
2871 // Format as PostgreSQL array literal
2872 let elements = arr
2873 .iter()
2874 .map(|s| format!("'{}'", s.replace('\'', "''")))
2875 .collect::<Vec<_>>();
2876 format!("ARRAY[{}]", elements.join(", "))
2877 }
2878 FilterValue::FieldRef(f) => f.field.clone(),
2879 FilterValue::Expression(expr) => expr.to_sql(),
2880 FilterValue::OuterRef(outer_ref) => outer_ref.field.clone(),
2881 }
2882 }
2883
2884 /// Convert FilterValue to String representation
2885 // Allow dead_code: internal conversion helper for filter value stringification in queries
2886 #[allow(dead_code)]
2887 fn value_to_string(v: &FilterValue) -> String {
2888 match v {
2889 FilterValue::String(s) => s.clone(),
2890 FilterValue::Integer(i) | FilterValue::Int(i) => i.to_string(),
2891 FilterValue::Float(f) => f.to_string(),
2892 FilterValue::Boolean(b) | FilterValue::Bool(b) => b.to_string(),
2893 FilterValue::Null => String::new(),
2894 FilterValue::Array(arr) => arr.join(","),
2895 FilterValue::FieldRef(f) => f.field.clone(),
2896 FilterValue::Expression(expr) => expr.to_sql(),
2897 FilterValue::OuterRef(outer_ref) => outer_ref.field.clone(),
2898 }
2899 }
2900
2901 /// Parse array string into `Vec<reinhardt_query::value::Value>`
2902 /// Supports comma-separated values or JSON array format
2903 fn parse_array_string(s: &str) -> Vec<reinhardt_query::value::Value> {
2904 let trimmed = s.trim();
2905
2906 // Try parsing as JSON array first
2907 if trimmed.starts_with('[')
2908 && trimmed.ends_with(']')
2909 && let Ok(arr) = serde_json::from_str::<Vec<serde_json::Value>>(trimmed)
2910 {
2911 return arr
2912 .iter()
2913 .map(|v| match v {
2914 serde_json::Value::String(s) => s.clone().into(),
2915 serde_json::Value::Number(n) => {
2916 if let Some(i) = n.as_i64() {
2917 i.into()
2918 } else if let Some(f) = n.as_f64() {
2919 f.into()
2920 } else {
2921 n.to_string().into()
2922 }
2923 }
2924 serde_json::Value::Bool(b) => (*b).into(),
2925 _ => v.to_string().into(),
2926 })
2927 .collect();
2928 }
2929
2930 // Fallback to comma-separated parsing
2931 trimmed
2932 .split(',')
2933 .map(|s| s.trim())
2934 .filter(|s| !s.is_empty())
2935 .map(|s| s.to_string().into())
2936 .collect()
2937 }
2938
2939 /// Convert FilterValue to array of reinhardt_query::value::Value
2940 // Allow dead_code: internal conversion for IN clause array parameter binding
2941 #[allow(dead_code)]
2942 fn value_to_array(v: &FilterValue) -> Vec<reinhardt_query::value::Value> {
2943 match v {
2944 FilterValue::String(s) => Self::parse_array_string(s),
2945 FilterValue::Integer(i) | FilterValue::Int(i) => vec![(*i).into()],
2946 FilterValue::Float(f) => vec![(*f).into()],
2947 FilterValue::Boolean(b) | FilterValue::Bool(b) => vec![(*b).into()],
2948 FilterValue::Null => vec![reinhardt_query::value::Value::Int(None)],
2949 FilterValue::Array(arr) => arr.iter().map(|s| s.clone().into()).collect(),
2950 FilterValue::FieldRef(f) => vec![f.field.clone().into()],
2951 FilterValue::Expression(expr) => vec![expr.to_sql().into()],
2952 FilterValue::OuterRef(outer) => vec![outer.field.clone().into()],
2953 }
2954 }
2955
2956 /// Build WHERE clause from accumulated filters
2957 ///
2958 /// # Deprecation Note
2959 ///
2960 /// This method is maintained for backward compatibility with existing code that
2961 /// expects a string-based WHERE clause. New code should use `build_where_condition()`
2962 /// which returns a `Condition` object that can be directly added to reinhardt-query statements.
2963 ///
2964 /// This method generates a complete SELECT statement internally and extracts only
2965 /// the WHERE portion, which is less efficient than using `build_where_condition()`.
2966 // Allow dead_code: backward-compatible string-based WHERE clause builder for legacy code paths
2967 #[allow(dead_code)]
2968 fn build_where_clause(&self) -> (String, Vec<String>) {
2969 if self.filters.is_empty() {
2970 return (String::new(), Vec::new());
2971 }
2972
2973 // Build reinhardt-query condition
2974 let mut stmt = Query::select();
2975 stmt.from(Alias::new("dummy"));
2976
2977 if let Some(cond) = self.build_where_condition() {
2978 stmt.cond_where(cond);
2979 }
2980
2981 // Convert to SQL string with inline values
2982 use reinhardt_query::prelude::PostgresQueryBuilder;
2983 let sql = stmt.to_string(PostgresQueryBuilder);
2984
2985 // Extract WHERE clause portion by finding the WHERE keyword
2986 let where_clause = if let Some(idx) = sql.find(" WHERE ") {
2987 sql[idx..].to_string()
2988 } else {
2989 String::new()
2990 };
2991
2992 (where_clause, Vec::new())
2993 }
2994
2995 /// Eagerly load related objects using JOIN queries
2996 ///
2997 /// This method performs SQL JOINs to fetch related objects in a single query,
2998 /// reducing the number of database round-trips and preventing N+1 query problems.
2999 ///
3000 /// # Performance
3001 ///
3002 /// Best for one-to-one and many-to-one relationships where JOIN won't create
3003 /// significant data duplication. For one-to-many and many-to-many relationships,
3004 /// consider using `prefetch_related()` instead.
3005 ///
3006 /// # Examples
3007 ///
3008 /// ```no_run
3009 /// # use reinhardt_db::orm::Model;
3010 /// # use serde::{Serialize, Deserialize};
3011 /// # #[derive(Clone, Serialize, Deserialize)]
3012 /// # struct Post { id: Option<i64>, author: Author, category: Category }
3013 /// # #[derive(Clone, Serialize, Deserialize)]
3014 /// # struct Author { name: String }
3015 /// # #[derive(Clone, Serialize, Deserialize)]
3016 /// # struct Category { name: String }
3017 /// # #[derive(Clone)]
3018 /// # struct PostFields;
3019 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
3020 /// # fn with_alias(self, _alias: &str) -> Self { self }
3021 /// # }
3022 /// # impl Model for Post {
3023 /// # type PrimaryKey = i64;
3024 /// # type Fields = PostFields;
3025 /// # fn table_name() -> &'static str { "posts" }
3026 /// # fn new_fields() -> Self::Fields { PostFields }
3027 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3028 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3029 /// # }
3030 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3031 /// // Single query with JOINs instead of N+1 queries
3032 /// let posts = Post::objects()
3033 /// .select_related(&["author", "category"])
3034 /// .all()
3035 /// .await?;
3036 ///
3037 /// // Each post has author and category pre-loaded
3038 /// for post in posts {
3039 /// println!("Author: {}", post.author.name); // No additional query
3040 /// }
3041 /// # Ok(())
3042 /// # }
3043 /// ```
3044 pub fn select_related(mut self, fields: &[&str]) -> Self {
3045 self.select_related_fields = fields.iter().map(|s| s.to_string()).collect();
3046 self
3047 }
3048
3049 /// Generate SELECT query with JOIN clauses for select_related fields
3050 ///
3051 /// Returns reinhardt-query SelectStatement with LEFT JOIN for each related field to enable eager loading.
3052 ///
3053 /// # Examples
3054 ///
3055 /// ```no_run
3056 /// # use reinhardt_db::orm::Model;
3057 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
3058 /// # use serde::{Serialize, Deserialize};
3059 /// # #[derive(Clone, Serialize, Deserialize)]
3060 /// # struct Post { id: Option<i64> }
3061 /// # #[derive(Clone)]
3062 /// # struct PostFields;
3063 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
3064 /// # fn with_alias(self, _alias: &str) -> Self { self }
3065 /// # }
3066 /// # impl Model for Post {
3067 /// # type PrimaryKey = i64;
3068 /// # type Fields = PostFields;
3069 /// # fn table_name() -> &'static str { "posts" }
3070 /// # fn new_fields() -> Self::Fields { PostFields }
3071 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3072 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3073 /// # }
3074 /// let queryset = Post::objects()
3075 /// .select_related(&["author", "category"])
3076 /// .filter(Filter::new("published", FilterOperator::Eq, FilterValue::Boolean(true)));
3077 ///
3078 /// let stmt = queryset.select_related_query();
3079 /// // Generates:
3080 /// // SELECT posts.*, author.*, category.* FROM posts
3081 /// // LEFT JOIN users AS author ON posts.author_id = author.id
3082 /// // LEFT JOIN categories AS category ON posts.category_id = category.id
3083 /// // WHERE posts.published = $1
3084 /// ```
3085 pub fn select_related_query(&self) -> SelectStatement {
3086 let table_name = T::table_name();
3087 let mut stmt = Query::select();
3088
3089 // Apply FROM clause with optional alias
3090 if let Some(ref alias) = self.from_alias {
3091 stmt.from_as(Alias::new(table_name), Alias::new(alias));
3092 } else {
3093 stmt.from(Alias::new(table_name));
3094 }
3095
3096 // Apply DISTINCT if enabled
3097 if self.distinct_enabled {
3098 stmt.distinct();
3099 }
3100
3101 // Add main table columns
3102 stmt.column(ColumnRef::table_asterisk(Alias::new(table_name)));
3103
3104 // Add LEFT JOIN for each related field
3105 for related_field in &self.select_related_fields {
3106 // Convention: related_field is the field name in the model
3107 // We assume FK field is "{related_field}_id" and join to "{related_field}s" table
3108 let fk_field = Alias::new(format!("{}_id", related_field));
3109 let related_table = Alias::new(format!("{}s", related_field));
3110 let related_alias = Alias::new(related_field);
3111
3112 // LEFT JOIN related_table AS related_field ON table.fk_field = related_field.id
3113 stmt.left_join(
3114 related_table,
3115 Expr::col((Alias::new(table_name), fk_field))
3116 .equals((related_alias.clone(), Alias::new("id"))),
3117 );
3118
3119 // Add related table columns to SELECT
3120 stmt.column(ColumnRef::table_asterisk(related_alias));
3121 }
3122
3123 // Apply manual JOINs
3124 for join in &self.joins {
3125 if join.on_condition.is_empty() {
3126 // CROSS JOIN (no ON condition)
3127 if let Some(ref alias) = join.target_alias {
3128 stmt.cross_join((Alias::new(&join.target_table), Alias::new(alias)));
3129 } else {
3130 stmt.cross_join(Alias::new(&join.target_table));
3131 }
3132 } else {
3133 // Convert reinhardt JoinType to reinhardt-query JoinType
3134 let sea_join_type = match join.join_type {
3135 super::sqlalchemy_query::JoinType::Inner => SeaJoinType::InnerJoin,
3136 super::sqlalchemy_query::JoinType::Left => SeaJoinType::LeftJoin,
3137 super::sqlalchemy_query::JoinType::Right => SeaJoinType::RightJoin,
3138 super::sqlalchemy_query::JoinType::Full => SeaJoinType::FullOuterJoin,
3139 };
3140
3141 // Build the join with optional alias
3142 if let Some(ref alias) = join.target_alias {
3143 stmt.join(
3144 sea_join_type,
3145 (Alias::new(&join.target_table), Alias::new(alias)),
3146 Expr::cust(join.on_condition.clone()),
3147 );
3148 } else {
3149 stmt.join(
3150 sea_join_type,
3151 Alias::new(&join.target_table),
3152 Expr::cust(join.on_condition.clone()),
3153 );
3154 }
3155 }
3156 }
3157
3158 // Apply WHERE conditions
3159 if let Some(cond) = self.build_where_condition() {
3160 stmt.cond_where(cond);
3161 }
3162
3163 // Apply GROUP BY
3164 for group_field in &self.group_by_fields {
3165 let col_ref = parse_column_reference(group_field);
3166 stmt.group_by_col(col_ref);
3167 }
3168
3169 // Apply HAVING
3170 for having_cond in &self.having_conditions {
3171 match having_cond {
3172 HavingCondition::AggregateCompare {
3173 func,
3174 field,
3175 operator,
3176 value,
3177 } => {
3178 // Build aggregate function expression
3179 let agg_expr = match func {
3180 AggregateFunc::Avg => {
3181 Func::avg(Expr::col(Alias::new(field)).into_simple_expr())
3182 }
3183 AggregateFunc::Count => {
3184 if field == "*" {
3185 Func::count(Expr::asterisk().into_simple_expr())
3186 } else {
3187 Func::count(Expr::col(Alias::new(field)).into_simple_expr())
3188 }
3189 }
3190 AggregateFunc::Sum => {
3191 Func::sum(Expr::col(Alias::new(field)).into_simple_expr())
3192 }
3193 AggregateFunc::Min => {
3194 Func::min(Expr::col(Alias::new(field)).into_simple_expr())
3195 }
3196 AggregateFunc::Max => {
3197 Func::max(Expr::col(Alias::new(field)).into_simple_expr())
3198 }
3199 };
3200
3201 // Build comparison expression
3202 let having_expr = match operator {
3203 ComparisonOp::Eq => match value {
3204 AggregateValue::Int(v) => agg_expr.eq(*v),
3205 AggregateValue::Float(v) => agg_expr.eq(*v),
3206 },
3207 ComparisonOp::Ne => match value {
3208 AggregateValue::Int(v) => agg_expr.ne(*v),
3209 AggregateValue::Float(v) => agg_expr.ne(*v),
3210 },
3211 ComparisonOp::Gt => match value {
3212 AggregateValue::Int(v) => agg_expr.gt(*v),
3213 AggregateValue::Float(v) => agg_expr.gt(*v),
3214 },
3215 ComparisonOp::Gte => match value {
3216 AggregateValue::Int(v) => agg_expr.gte(*v),
3217 AggregateValue::Float(v) => agg_expr.gte(*v),
3218 },
3219 ComparisonOp::Lt => match value {
3220 AggregateValue::Int(v) => agg_expr.lt(*v),
3221 AggregateValue::Float(v) => agg_expr.lt(*v),
3222 },
3223 ComparisonOp::Lte => match value {
3224 AggregateValue::Int(v) => agg_expr.lte(*v),
3225 AggregateValue::Float(v) => agg_expr.lte(*v),
3226 },
3227 };
3228
3229 stmt.and_having(having_expr);
3230 }
3231 }
3232 }
3233
3234 // Apply ORDER BY
3235 for order_field in &self.order_by_fields {
3236 let (field, is_desc) = if let Some(stripped) = order_field.strip_prefix('-') {
3237 (stripped, true)
3238 } else {
3239 (order_field.as_str(), false)
3240 };
3241
3242 let col_ref = parse_column_reference(field);
3243 let expr = Expr::col(col_ref);
3244 if is_desc {
3245 stmt.order_by_expr(expr, Order::Desc);
3246 } else {
3247 stmt.order_by_expr(expr, Order::Asc);
3248 }
3249 }
3250
3251 // Apply LIMIT/OFFSET
3252 if let Some(limit) = self.limit {
3253 stmt.limit(limit as u64);
3254 }
3255 if let Some(offset) = self.offset {
3256 stmt.offset(offset as u64);
3257 }
3258
3259 stmt.to_owned()
3260 }
3261
3262 /// Eagerly load related objects using separate queries
3263 ///
3264 /// This method performs separate SQL queries for related objects and joins them
3265 /// in memory, which is more efficient than JOINs for one-to-many and many-to-many
3266 /// relationships that would create significant data duplication.
3267 ///
3268 /// # Performance
3269 ///
3270 /// Best for one-to-many and many-to-many relationships where JOINs would create
3271 /// data duplication (e.g., a post with 100 comments would duplicate post data 100 times).
3272 /// Uses 1 + N queries where N is the number of prefetch_related fields.
3273 ///
3274 /// # Examples
3275 ///
3276 /// ```no_run
3277 /// # use reinhardt_db::orm::Model;
3278 /// # use serde::{Serialize, Deserialize};
3279 /// # #[derive(Clone, Serialize, Deserialize)]
3280 /// # struct Post { id: Option<i64>, comments: Vec<Comment>, tags: Vec<Tag> }
3281 /// # #[derive(Clone, Serialize, Deserialize)]
3282 /// # struct Comment { text: String }
3283 /// # #[derive(Clone, Serialize, Deserialize)]
3284 /// # struct Tag { name: String }
3285 /// # #[derive(Clone)]
3286 /// # struct PostFields;
3287 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
3288 /// # fn with_alias(self, _alias: &str) -> Self { self }
3289 /// # }
3290 /// # impl Model for Post {
3291 /// # type PrimaryKey = i64;
3292 /// # type Fields = PostFields;
3293 /// # fn table_name() -> &'static str { "posts" }
3294 /// # fn new_fields() -> Self::Fields { PostFields }
3295 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3296 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3297 /// # }
3298 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3299 /// // 2 queries total instead of N+1 queries
3300 /// let posts = Post::objects()
3301 /// .prefetch_related(&["comments", "tags"])
3302 /// .all()
3303 /// .await?;
3304 ///
3305 /// // Each post has comments and tags pre-loaded
3306 /// for post in posts {
3307 /// for comment in &post.comments {
3308 /// println!("Comment: {}", comment.text); // No additional query
3309 /// }
3310 /// }
3311 /// # Ok(())
3312 /// # }
3313 /// ```
3314 pub fn prefetch_related(mut self, fields: &[&str]) -> Self {
3315 self.prefetch_related_fields = fields.iter().map(|s| s.to_string()).collect();
3316 self
3317 }
3318
3319 /// Generate SELECT queries for prefetch_related fields
3320 ///
3321 /// Returns a vector of (field_name, SelectStatement) tuples, one for each prefetch field.
3322 /// Each query fetches related objects using IN clause with collected primary keys.
3323 ///
3324 /// # Examples
3325 ///
3326 /// ```no_run
3327 /// # use reinhardt_db::orm::Model;
3328 /// # use serde::{Serialize, Deserialize};
3329 /// # #[derive(Clone, Serialize, Deserialize)]
3330 /// # struct Post { id: Option<i64> }
3331 /// # #[derive(Clone)]
3332 /// # struct PostFields;
3333 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
3334 /// # fn with_alias(self, _alias: &str) -> Self { self }
3335 /// # }
3336 /// # impl Model for Post {
3337 /// # type PrimaryKey = i64;
3338 /// # type Fields = PostFields;
3339 /// # fn table_name() -> &'static str { "posts" }
3340 /// # fn new_fields() -> Self::Fields { PostFields }
3341 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3342 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3343 /// # }
3344 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3345 /// let queryset = Post::objects()
3346 /// .prefetch_related(&["comments", "tags"]);
3347 ///
3348 /// let main_results = queryset.all().await?; // Main query
3349 /// let pk_values = vec![1, 2, 3]; // Collected from main results
3350 ///
3351 /// let prefetch_queries = queryset.prefetch_related_queries(&pk_values);
3352 /// // Returns SelectStatements for:
3353 /// // 1. comments: SELECT * FROM comments WHERE post_id IN ($1, $2, $3)
3354 /// // 2. tags: SELECT tags.* FROM tags
3355 /// // INNER JOIN post_tags ON tags.id = post_tags.tag_id
3356 /// // WHERE post_tags.post_id IN ($1, $2, $3)
3357 /// # Ok(())
3358 /// # }
3359 /// ```
3360 pub fn prefetch_related_queries(&self, pk_values: &[i64]) -> Vec<(String, SelectStatement)> {
3361 if pk_values.is_empty() {
3362 return Vec::new();
3363 }
3364
3365 let mut queries = Vec::new();
3366
3367 for related_field in &self.prefetch_related_fields {
3368 // Determine if this is a many-to-many relation or one-to-many
3369 // by querying the model's relationship metadata
3370 let is_m2m = self.is_many_to_many_relation(related_field);
3371
3372 let stmt = if is_m2m {
3373 self.prefetch_many_to_many_query(related_field, pk_values)
3374 } else {
3375 self.prefetch_one_to_many_query(related_field, pk_values)
3376 };
3377
3378 queries.push((related_field.clone(), stmt));
3379 }
3380
3381 queries
3382 }
3383
3384 /// Check if a related field is a many-to-many relation
3385 ///
3386 /// Determines relationship type by querying the model's metadata.
3387 /// Returns true if the relationship is defined as ManyToMany in the model metadata.
3388 fn is_many_to_many_relation(&self, related_field: &str) -> bool {
3389 // Get relationship metadata from the model
3390 let relations = T::relationship_metadata();
3391
3392 // Find the relationship with the matching name
3393 relations
3394 .iter()
3395 .find(|rel| rel.name == related_field)
3396 .map(|rel| rel.relationship_type == super::relationship::RelationshipType::ManyToMany)
3397 .unwrap_or(false)
3398 }
3399
3400 /// Generate query for one-to-many prefetch
3401 ///
3402 /// Generates: SELECT * FROM related_table WHERE fk_field IN (pk_values)
3403 fn prefetch_one_to_many_query(
3404 &self,
3405 related_field: &str,
3406 pk_values: &[i64],
3407 ) -> SelectStatement {
3408 let table_name = T::table_name();
3409 let related_table = Alias::new(format!("{}s", related_field));
3410 let fk_field = Alias::new(format!("{}_id", table_name.trim_end_matches('s')));
3411
3412 let mut stmt = Query::select();
3413 stmt.from(related_table).column(ColumnRef::Asterisk);
3414
3415 // Add IN clause with pk_values
3416 let values: Vec<reinhardt_query::value::Value> =
3417 pk_values.iter().map(|&id| id.into()).collect();
3418 stmt.and_where(Expr::col(fk_field).is_in(values));
3419
3420 stmt.to_owned()
3421 }
3422
3423 /// Generate query for many-to-many prefetch
3424 ///
3425 /// Generates: SELECT related.*, junction.main_id FROM related
3426 /// INNER JOIN junction ON related.id = junction.related_id
3427 /// WHERE junction.main_id IN (pk_values)
3428 fn prefetch_many_to_many_query(
3429 &self,
3430 related_field: &str,
3431 pk_values: &[i64],
3432 ) -> SelectStatement {
3433 let table_name = T::table_name();
3434 let junction_table = Alias::new(format!("{}_{}", table_name, related_field));
3435 let related_table = Alias::new(format!("{}s", related_field));
3436 let junction_main_fk = Alias::new(format!("{}_id", table_name.trim_end_matches('s')));
3437 let junction_related_fk = Alias::new(format!("{}_id", related_field));
3438
3439 let mut stmt = Query::select();
3440 stmt.from(related_table.clone())
3441 .column(ColumnRef::table_asterisk(related_table.clone()))
3442 .column((junction_table.clone(), junction_main_fk.clone()))
3443 .inner_join(
3444 junction_table.clone(),
3445 Expr::col((related_table.clone(), Alias::new("id")))
3446 .equals((junction_table.clone(), junction_related_fk)),
3447 );
3448
3449 // Add IN clause with pk_values
3450 let values: Vec<reinhardt_query::value::Value> =
3451 pk_values.iter().map(|&id| id.into()).collect();
3452 stmt.and_where(Expr::col((junction_table, junction_main_fk)).is_in(values));
3453
3454 stmt.to_owned()
3455 }
3456
3457 /// Execute the queryset and return all matching records
3458 ///
3459 /// Fetches all records from the database that match the accumulated filters.
3460 /// If `select_related` fields are specified, performs JOIN queries for eager loading.
3461 ///
3462 /// # Examples
3463 ///
3464 /// ```no_run
3465 /// # use reinhardt_db::orm::Model;
3466 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
3467 /// # use serde::{Serialize, Deserialize};
3468 /// # #[derive(Clone, Serialize, Deserialize)]
3469 /// # struct User { id: Option<i64> }
3470 /// # #[derive(Clone)]
3471 /// # struct UserFields;
3472 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
3473 /// # fn with_alias(self, _alias: &str) -> Self { self }
3474 /// # }
3475 /// # impl Model for User {
3476 /// # type PrimaryKey = i64;
3477 /// # type Fields = UserFields;
3478 /// # fn table_name() -> &'static str { "users" }
3479 /// # fn new_fields() -> Self::Fields { UserFields }
3480 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3481 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3482 /// # }
3483 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3484 /// // Fetch all users (Manager.all() returns QuerySet, then call .all().await)
3485 /// let users = User::objects().all().all().await?;
3486 ///
3487 /// // Fetch filtered users with eager loading
3488 /// let active_users = User::objects()
3489 /// .filter(
3490 /// "is_active",
3491 /// FilterOperator::Eq,
3492 /// FilterValue::Boolean(true),
3493 /// )
3494 /// .select_related(&["profile"])
3495 /// .all()
3496 /// .await?;
3497 /// # Ok(())
3498 /// # }
3499 /// ```
3500 ///
3501 /// # Errors
3502 ///
3503 /// Returns an error if:
3504 /// - Database connection fails
3505 /// - SQL execution fails
3506 /// - Deserialization of results fails
3507 pub async fn all(&self) -> reinhardt_core::exception::Result<Vec<T>>
3508 where
3509 T: serde::de::DeserializeOwned,
3510 {
3511 let conn = super::manager::get_connection().await?;
3512
3513 let stmt = if self.select_related_fields.is_empty() {
3514 // Simple SELECT without JOINs
3515 let mut stmt = Query::select();
3516 stmt.from(Alias::new(T::table_name()));
3517
3518 // Column selection considering selected_fields and deferred_fields
3519 if let Some(ref fields) = self.selected_fields {
3520 for field in fields {
3521 // Detect raw SQL expressions (like COUNT(*), AVG(price), etc.)
3522 if field.contains('(') && field.contains(')') {
3523 // Use expr() for raw SQL expressions - clone to satisfy lifetime
3524 stmt.expr(Expr::cust(field.clone()));
3525 } else {
3526 // Regular column reference
3527 let col_ref = parse_column_reference(field);
3528 stmt.column(col_ref);
3529 }
3530 }
3531 } else if !self.deferred_fields.is_empty() {
3532 let all_fields = T::field_metadata();
3533 for field in all_fields {
3534 if !self.deferred_fields.contains(&field.name) {
3535 let col_ref = parse_column_reference(&field.name);
3536 stmt.column(col_ref);
3537 }
3538 }
3539 } else {
3540 stmt.column(ColumnRef::Asterisk);
3541 }
3542
3543 if let Some(cond) = self.build_where_condition() {
3544 stmt.cond_where(cond);
3545 }
3546
3547 // Apply ORDER BY clause
3548 for order_field in &self.order_by_fields {
3549 let (field, is_desc) = if let Some(stripped) = order_field.strip_prefix('-') {
3550 (stripped, true)
3551 } else {
3552 (order_field.as_str(), false)
3553 };
3554
3555 let col_ref = parse_column_reference(field);
3556 let expr = Expr::col(col_ref);
3557 if is_desc {
3558 stmt.order_by_expr(expr, Order::Desc);
3559 } else {
3560 stmt.order_by_expr(expr, Order::Asc);
3561 }
3562 }
3563
3564 // Apply LIMIT/OFFSET
3565 if let Some(limit) = self.limit {
3566 stmt.limit(limit as u64);
3567 }
3568 if let Some(offset) = self.offset {
3569 stmt.offset(offset as u64);
3570 }
3571
3572 stmt.to_owned()
3573 } else {
3574 // SELECT with JOINs for select_related
3575 self.select_related_query()
3576 };
3577
3578 // Convert statement to SQL with inline values (no placeholders)
3579 let sql = stmt.to_string(PostgresQueryBuilder);
3580
3581 // Execute query and deserialize results
3582 let rows = conn.query(&sql, vec![]).await?;
3583 rows.into_iter()
3584 .map(|row| {
3585 serde_json::from_value(serde_json::to_value(&row.data).map_err(|e| {
3586 reinhardt_core::exception::Error::Database(format!(
3587 "Serialization error: {}",
3588 e
3589 ))
3590 })?)
3591 .map_err(|e| {
3592 reinhardt_core::exception::Error::Database(format!(
3593 "Deserialization error: {}",
3594 e
3595 ))
3596 })
3597 })
3598 .collect()
3599 }
3600
3601 /// Execute the queryset and return the first matching record
3602 ///
3603 /// Returns `None` if no records match the query.
3604 ///
3605 /// # Examples
3606 ///
3607 /// ```no_run
3608 /// # use reinhardt_db::orm::Model;
3609 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
3610 /// # use serde::{Serialize, Deserialize};
3611 /// # #[derive(Clone, Serialize, Deserialize)]
3612 /// # struct User { id: Option<i64>, username: String }
3613 /// # #[derive(Clone)]
3614 /// # struct UserFields;
3615 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
3616 /// # fn with_alias(self, _alias: &str) -> Self { self }
3617 /// # }
3618 /// # impl Model for User {
3619 /// # type PrimaryKey = i64;
3620 /// # type Fields = UserFields;
3621 /// # fn table_name() -> &'static str { "users" }
3622 /// # fn new_fields() -> Self::Fields { UserFields }
3623 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3624 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3625 /// # }
3626 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3627 /// // Fetch first active user
3628 /// let user = User::objects()
3629 /// .filter(
3630 /// "is_active",
3631 /// FilterOperator::Eq,
3632 /// FilterValue::Boolean(true),
3633 /// )
3634 /// .first()
3635 /// .await?;
3636 ///
3637 /// match user {
3638 /// Some(u) => println!("Found user: {}", u.username),
3639 /// None => println!("No active users found"),
3640 /// }
3641 /// # Ok(())
3642 /// # }
3643 /// ```
3644 pub async fn first(&self) -> reinhardt_core::exception::Result<Option<T>>
3645 where
3646 T: serde::de::DeserializeOwned,
3647 {
3648 let mut results = self.all().await?;
3649 Ok(results.drain(..).next())
3650 }
3651
3652 /// Execute the queryset and return a single matching record
3653 ///
3654 /// Returns an error if zero or multiple records are found.
3655 ///
3656 /// # Examples
3657 ///
3658 /// ```no_run
3659 /// # use reinhardt_db::orm::Model;
3660 /// # use reinhardt_db::orm::{FilterOperator, FilterValue};
3661 /// # use serde::{Serialize, Deserialize};
3662 /// # #[derive(Clone, Serialize, Deserialize)]
3663 /// # struct User { id: Option<i64>, email: String }
3664 /// # #[derive(Clone)]
3665 /// # struct UserFields;
3666 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
3667 /// # fn with_alias(self, _alias: &str) -> Self { self }
3668 /// # }
3669 /// # impl Model for User {
3670 /// # type PrimaryKey = i64;
3671 /// # type Fields = UserFields;
3672 /// # fn table_name() -> &'static str { "users" }
3673 /// # fn new_fields() -> Self::Fields { UserFields }
3674 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3675 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3676 /// # }
3677 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3678 /// // Fetch user with specific email (must be unique)
3679 /// let user = User::objects()
3680 /// .filter(
3681 /// "email",
3682 /// FilterOperator::Eq,
3683 /// FilterValue::String("alice@example.com".to_string()),
3684 /// )
3685 /// .get()
3686 /// .await?;
3687 /// # Ok(())
3688 /// # }
3689 /// ```
3690 ///
3691 /// # Errors
3692 ///
3693 /// Returns an error if:
3694 /// - No records match the query
3695 /// - Multiple records match the query
3696 /// - Database connection fails
3697 pub async fn get(&self) -> reinhardt_core::exception::Result<T>
3698 where
3699 T: serde::de::DeserializeOwned,
3700 {
3701 let results = self.all().await?;
3702 match results.len() {
3703 0 => Err(reinhardt_core::exception::Error::Database(
3704 "No record found matching the query".to_string(),
3705 )),
3706 1 => Ok(results.into_iter().next().unwrap()),
3707 n => Err(reinhardt_core::exception::Error::Database(format!(
3708 "Multiple records found ({}), expected exactly one",
3709 n
3710 ))),
3711 }
3712 }
3713
3714 /// Execute the queryset with an explicit database connection and return all records
3715 ///
3716 /// # Examples
3717 ///
3718 /// ```no_run
3719 /// # use reinhardt_db::orm::Model;
3720 /// # use serde::{Serialize, Deserialize};
3721 /// # #[derive(Clone, Serialize, Deserialize)]
3722 /// # struct User { id: Option<i64> }
3723 /// # #[derive(Clone)]
3724 /// # struct UserFields;
3725 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
3726 /// # fn with_alias(self, _alias: &str) -> Self { self }
3727 /// # }
3728 /// # impl Model for User {
3729 /// # type PrimaryKey = i64;
3730 /// # type Fields = UserFields;
3731 /// # fn table_name() -> &'static str { "users" }
3732 /// # fn new_fields() -> Self::Fields { UserFields }
3733 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3734 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3735 /// # }
3736 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3737 /// # let db = reinhardt_db::orm::manager::get_connection().await?;
3738 /// let users = User::objects()
3739 /// .all()
3740 /// .all_with_db(&db)
3741 /// .await?;
3742 /// # Ok(())
3743 /// # }
3744 /// ```
3745 pub async fn all_with_db(
3746 &self,
3747 conn: &super::connection::DatabaseConnection,
3748 ) -> reinhardt_core::exception::Result<Vec<T>>
3749 where
3750 T: serde::de::DeserializeOwned,
3751 {
3752 let stmt = if self.select_related_fields.is_empty() {
3753 let mut stmt = Query::select();
3754 stmt.from(Alias::new(T::table_name()));
3755
3756 // Column selection considering selected_fields and deferred_fields
3757 if let Some(ref fields) = self.selected_fields {
3758 for field in fields {
3759 // Detect raw SQL expressions (like COUNT(*), AVG(price), etc.)
3760 if field.contains('(') && field.contains(')') {
3761 // Use expr() for raw SQL expressions - clone to satisfy lifetime
3762 stmt.expr(Expr::cust(field.clone()));
3763 } else {
3764 // Regular column reference
3765 let col_ref = parse_column_reference(field);
3766 stmt.column(col_ref);
3767 }
3768 }
3769 } else if !self.deferred_fields.is_empty() {
3770 let all_fields = T::field_metadata();
3771 for field in all_fields {
3772 if !self.deferred_fields.contains(&field.name) {
3773 let col_ref = parse_column_reference(&field.name);
3774 stmt.column(col_ref);
3775 }
3776 }
3777 } else {
3778 stmt.column(ColumnRef::Asterisk);
3779 }
3780
3781 if let Some(cond) = self.build_where_condition() {
3782 stmt.cond_where(cond);
3783 }
3784
3785 // Apply ORDER BY clause
3786 for order_field in &self.order_by_fields {
3787 let (field, is_desc) = if let Some(stripped) = order_field.strip_prefix('-') {
3788 (stripped, true)
3789 } else {
3790 (order_field.as_str(), false)
3791 };
3792
3793 let col_ref = parse_column_reference(field);
3794 let expr = Expr::col(col_ref);
3795 if is_desc {
3796 stmt.order_by_expr(expr, Order::Desc);
3797 } else {
3798 stmt.order_by_expr(expr, Order::Asc);
3799 }
3800 }
3801
3802 // Apply LIMIT/OFFSET
3803 if let Some(limit) = self.limit {
3804 stmt.limit(limit as u64);
3805 }
3806 if let Some(offset) = self.offset {
3807 stmt.offset(offset as u64);
3808 }
3809
3810 stmt.to_owned()
3811 } else {
3812 self.select_related_query()
3813 };
3814
3815 let sql = stmt.to_string(PostgresQueryBuilder);
3816
3817 let rows = conn.query(&sql, vec![]).await?;
3818 rows.into_iter()
3819 .map(|row| {
3820 serde_json::from_value(serde_json::to_value(&row.data).map_err(|e| {
3821 reinhardt_core::exception::Error::Database(format!(
3822 "Serialization error: {}",
3823 e
3824 ))
3825 })?)
3826 .map_err(|e| {
3827 reinhardt_core::exception::Error::Database(format!(
3828 "Deserialization error: {}",
3829 e
3830 ))
3831 })
3832 })
3833 .collect()
3834 }
3835
3836 /// Execute the queryset with an explicit database connection and return a single record
3837 ///
3838 /// # Examples
3839 ///
3840 /// ```no_run
3841 /// # use reinhardt_db::orm::Model;
3842 /// # use serde::{Serialize, Deserialize};
3843 /// # #[derive(Clone, Serialize, Deserialize)]
3844 /// # struct User { id: Option<i64> }
3845 /// # #[derive(Clone)]
3846 /// # struct UserFields;
3847 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
3848 /// # fn with_alias(self, _alias: &str) -> Self { self }
3849 /// # }
3850 /// # impl Model for User {
3851 /// # type PrimaryKey = i64;
3852 /// # type Fields = UserFields;
3853 /// # fn table_name() -> &'static str { "users" }
3854 /// # fn new_fields() -> Self::Fields { UserFields }
3855 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3856 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3857 /// # }
3858 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3859 /// # let user_id = 1;
3860 /// let db = reinhardt_db::orm::manager::get_connection().await?;
3861 /// let user = User::objects()
3862 /// .filter("id", reinhardt_db::orm::FilterOperator::Eq, reinhardt_db::orm::FilterValue::Integer(user_id))
3863 /// .get_with_db(&db)
3864 /// .await?;
3865 /// # Ok(())
3866 /// # }
3867 /// ```
3868 pub async fn get_with_db(
3869 &self,
3870 conn: &super::connection::DatabaseConnection,
3871 ) -> reinhardt_core::exception::Result<T>
3872 where
3873 T: serde::de::DeserializeOwned,
3874 {
3875 let results = self.all_with_db(conn).await?;
3876 match results.len() {
3877 0 => Err(reinhardt_core::exception::Error::NotFound(
3878 "No record found matching the query".to_string(),
3879 )),
3880 1 => Ok(results.into_iter().next().unwrap()),
3881 n => Err(reinhardt_core::exception::Error::Database(format!(
3882 "Multiple records found ({}), expected exactly one",
3883 n
3884 ))),
3885 }
3886 }
3887
3888 /// Execute the queryset with an explicit database connection and return the first record
3889 ///
3890 /// # Examples
3891 ///
3892 /// ```no_run
3893 /// # use reinhardt_db::orm::Model;
3894 /// # use serde::{Serialize, Deserialize};
3895 /// # #[derive(Clone, Serialize, Deserialize)]
3896 /// # struct User { id: Option<i64> }
3897 /// # #[derive(Clone)]
3898 /// # struct UserFields;
3899 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
3900 /// # fn with_alias(self, _alias: &str) -> Self { self }
3901 /// # }
3902 /// # impl Model for User {
3903 /// # type PrimaryKey = i64;
3904 /// # type Fields = UserFields;
3905 /// # fn table_name() -> &'static str { "users" }
3906 /// # fn new_fields() -> Self::Fields { UserFields }
3907 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3908 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3909 /// # }
3910 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3911 /// let db = reinhardt_db::orm::manager::get_connection().await?;
3912 /// let user = User::objects()
3913 /// .filter("status", reinhardt_db::orm::FilterOperator::Eq, reinhardt_db::orm::FilterValue::String("active".to_string()))
3914 /// .first_with_db(&db)
3915 /// .await?;
3916 /// # Ok(())
3917 /// # }
3918 /// ```
3919 pub async fn first_with_db(
3920 &self,
3921 conn: &super::connection::DatabaseConnection,
3922 ) -> reinhardt_core::exception::Result<Option<T>>
3923 where
3924 T: serde::de::DeserializeOwned,
3925 {
3926 let mut results = self.all_with_db(conn).await?;
3927 Ok(results.drain(..).next())
3928 }
3929
3930 /// Execute the queryset and return the count of matching records
3931 ///
3932 /// More efficient than calling `all().await?.len()` as it only executes COUNT query.
3933 ///
3934 /// # Examples
3935 ///
3936 /// ```no_run
3937 /// # use reinhardt_db::orm::Model;
3938 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
3939 /// # use serde::{Serialize, Deserialize};
3940 /// # #[derive(Clone, Serialize, Deserialize)]
3941 /// # struct User { id: Option<i64> }
3942 /// # #[derive(Clone)]
3943 /// # struct UserFields;
3944 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
3945 /// # fn with_alias(self, _alias: &str) -> Self { self }
3946 /// # }
3947 /// # impl Model for User {
3948 /// # type PrimaryKey = i64;
3949 /// # type Fields = UserFields;
3950 /// # fn table_name() -> &'static str { "users" }
3951 /// # fn new_fields() -> Self::Fields { UserFields }
3952 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
3953 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
3954 /// # }
3955 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
3956 /// // Count active users
3957 /// let count = User::objects()
3958 /// .filter(
3959 /// "is_active",
3960 /// FilterOperator::Eq,
3961 /// FilterValue::Boolean(true),
3962 /// )
3963 /// .count()
3964 /// .await?;
3965 ///
3966 /// println!("Active users: {}", count);
3967 /// # Ok(())
3968 /// # }
3969 /// ```
3970 pub async fn count(&self) -> reinhardt_core::exception::Result<usize> {
3971 use reinhardt_query::prelude::{Func, PostgresQueryBuilder, QueryBuilder};
3972
3973 let conn = super::manager::get_connection().await?;
3974
3975 // Build COUNT query using reinhardt-query
3976 let mut stmt = Query::select();
3977 stmt.from(Alias::new(T::table_name()))
3978 .expr(Func::count(Expr::asterisk().into_simple_expr()));
3979
3980 // Add WHERE conditions
3981 if let Some(cond) = self.build_where_condition() {
3982 stmt.cond_where(cond);
3983 }
3984
3985 // Convert to SQL and extract parameter values
3986 let (sql, values) = PostgresQueryBuilder.build_select(&stmt);
3987
3988 // Convert reinhardt_query::value::Values to QueryValue
3989 let params = super::execution::convert_values(values);
3990
3991 // Execute query with parameters
3992 let rows = conn.query(&sql, params).await?;
3993 if let Some(row) = rows.first() {
3994 // Extract count from first row
3995 if let Some(count_value) = row.data.get("count")
3996 && let Some(count) = count_value.as_i64()
3997 {
3998 return Ok(count as usize);
3999 }
4000 }
4001
4002 Ok(0)
4003 }
4004
4005 /// Check if any records match the queryset
4006 ///
4007 /// More efficient than calling `count().await? > 0` as it can short-circuit.
4008 ///
4009 /// # Examples
4010 ///
4011 /// ```no_run
4012 /// # use reinhardt_db::orm::Model;
4013 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
4014 /// # use serde::{Serialize, Deserialize};
4015 /// # #[derive(Clone, Serialize, Deserialize)]
4016 /// # struct User { id: Option<i64> }
4017 /// # #[derive(Clone)]
4018 /// # struct UserFields;
4019 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
4020 /// # fn with_alias(self, _alias: &str) -> Self { self }
4021 /// # }
4022 /// # impl Model for User {
4023 /// # type PrimaryKey = i64;
4024 /// # type Fields = UserFields;
4025 /// # fn table_name() -> &'static str { "users" }
4026 /// # fn new_fields() -> Self::Fields { UserFields }
4027 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4028 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4029 /// # }
4030 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
4031 /// // Check if any admin users exist
4032 /// let has_admin = User::objects()
4033 /// .filter(
4034 /// "role",
4035 /// FilterOperator::Eq,
4036 /// FilterValue::String("admin".to_string()),
4037 /// )
4038 /// .exists()
4039 /// .await?;
4040 ///
4041 /// if has_admin {
4042 /// println!("Admin users exist");
4043 /// }
4044 /// # Ok(())
4045 /// # }
4046 /// ```
4047 pub async fn exists(&self) -> reinhardt_core::exception::Result<bool> {
4048 let count = self.count().await?;
4049 Ok(count > 0)
4050 }
4051
4052 /// Create a new object in the database
4053 ///
4054 /// # Examples
4055 ///
4056 /// ```no_run
4057 /// # use reinhardt_db::orm::Model;
4058 /// # use serde::{Serialize, Deserialize};
4059 /// # #[derive(Clone, Serialize, Deserialize)]
4060 /// # struct User { id: Option<i64>, username: String, email: String }
4061 /// # #[derive(Clone)]
4062 /// # struct UserFields;
4063 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
4064 /// # fn with_alias(self, _alias: &str) -> Self { self }
4065 /// # }
4066 /// # impl Model for User {
4067 /// # type PrimaryKey = i64;
4068 /// # type Fields = UserFields;
4069 /// # fn table_name() -> &'static str { "users" }
4070 /// # fn new_fields() -> Self::Fields { UserFields }
4071 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4072 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4073 /// # }
4074 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
4075 /// let user = User {
4076 /// id: None,
4077 /// username: "alice".to_string(),
4078 /// email: "alice@example.com".to_string(),
4079 /// };
4080 /// let created = User::objects().create(&user).await?;
4081 /// # Ok(())
4082 /// # }
4083 /// ```
4084 pub async fn create(&self, object: T) -> reinhardt_core::exception::Result<T>
4085 where
4086 T: super::Model + Clone,
4087 {
4088 // Delegate to Manager::create() which handles all the SQL generation,
4089 // database connection, primary key retrieval, and error handling
4090 match &self.manager {
4091 Some(manager) => manager.create(&object).await,
4092 None => {
4093 // Fallback: create a new manager instance if none exists
4094 let manager = super::manager::Manager::<T>::new();
4095 manager.create(&object).await
4096 }
4097 }
4098 }
4099
4100 /// Generate UPDATE statement using reinhardt-query
4101 pub fn update_query(
4102 &self,
4103 updates: &HashMap<String, UpdateValue>,
4104 ) -> reinhardt_query::prelude::UpdateStatement {
4105 let mut stmt = Query::update();
4106 stmt.table(Alias::new(T::table_name()));
4107
4108 // Add SET clauses
4109 for (field, value) in updates {
4110 let val_expr = match value {
4111 UpdateValue::String(s) => Expr::val(s.clone()),
4112 UpdateValue::Integer(i) => Expr::val(*i),
4113 UpdateValue::Float(f) => Expr::val(*f),
4114 UpdateValue::Boolean(b) => Expr::val(*b),
4115 UpdateValue::Null => Expr::val(reinhardt_query::value::Value::Int(None)),
4116 UpdateValue::FieldRef(f) => Expr::col(Alias::new(&f.field)),
4117 UpdateValue::Expression(expr) => Self::expression_to_query_expr(expr),
4118 };
4119 stmt.value_expr(Alias::new(field), val_expr);
4120 }
4121
4122 // Add WHERE conditions
4123 if let Some(cond) = self.build_where_condition() {
4124 stmt.cond_where(cond);
4125 }
4126
4127 stmt.to_owned()
4128 }
4129
4130 /// Generate UPDATE SQL with WHERE clause and parameter binding
4131 ///
4132 /// Returns SQL with placeholders ($1, $2, etc.) and the values to bind.
4133 ///
4134 /// # Examples
4135 ///
4136 /// ```no_run
4137 /// # use reinhardt_db::orm::Model;
4138 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
4139 /// # use reinhardt_db::orm::query::UpdateValue;
4140 /// # use serde::{Serialize, Deserialize};
4141 /// # #[derive(Clone, Serialize, Deserialize)]
4142 /// # struct User { id: Option<i64> }
4143 /// # #[derive(Clone)]
4144 /// # struct UserFields;
4145 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
4146 /// # fn with_alias(self, _alias: &str) -> Self { self }
4147 /// # }
4148 /// # impl Model for User {
4149 /// # type PrimaryKey = i64;
4150 /// # type Fields = UserFields;
4151 /// # fn table_name() -> &'static str { "users" }
4152 /// # fn new_fields() -> Self::Fields { UserFields }
4153 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4154 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4155 /// # }
4156 /// use std::collections::HashMap;
4157 /// let queryset = User::objects()
4158 /// .filter("id", FilterOperator::Eq, FilterValue::Integer(1));
4159 ///
4160 /// let mut updates = HashMap::new();
4161 /// updates.insert("name".to_string(), UpdateValue::String("Alice".to_string()));
4162 /// updates.insert("email".to_string(), UpdateValue::String("alice@example.com".to_string()));
4163 /// let (sql, params) = queryset.update_sql(&updates);
4164 /// // sql: "UPDATE users SET name = $1, email = $2 WHERE id = $3"
4165 /// // params: ["Alice", "alice@example.com", "1"]
4166 /// ```
4167 pub fn update_sql(&self, updates: &HashMap<String, UpdateValue>) -> (String, Vec<String>) {
4168 let stmt = self.update_query(updates);
4169 use reinhardt_query::prelude::{PostgresQueryBuilder, QueryBuilder};
4170 let (sql, values) = PostgresQueryBuilder.build_update(&stmt);
4171 let params: Vec<String> = values
4172 .iter()
4173 .map(|v| Self::sea_value_to_string(v))
4174 .collect();
4175 (sql, params)
4176 }
4177
4178 /// Convert reinhardt-query Value to String without SQL quoting
4179 fn sea_value_to_string(value: &reinhardt_query::value::Value) -> String {
4180 use reinhardt_query::value::Value;
4181 match value {
4182 Value::Bool(Some(b)) => b.to_string(),
4183 Value::TinyInt(Some(i)) => i.to_string(),
4184 Value::SmallInt(Some(i)) => i.to_string(),
4185 Value::Int(Some(i)) => i.to_string(),
4186 Value::BigInt(Some(i)) => i.to_string(),
4187 Value::TinyUnsigned(Some(i)) => i.to_string(),
4188 Value::SmallUnsigned(Some(i)) => i.to_string(),
4189 Value::Unsigned(Some(i)) => i.to_string(),
4190 Value::BigUnsigned(Some(i)) => i.to_string(),
4191 Value::Float(Some(f)) => f.to_string(),
4192 Value::Double(Some(f)) => f.to_string(),
4193 Value::String(Some(s)) => s.to_string(),
4194 Value::Bytes(Some(b)) => String::from_utf8_lossy(b).to_string(),
4195 _ => String::new(),
4196 }
4197 }
4198
4199 /// Generate DELETE SQL with WHERE clause and parameter binding
4200 ///
4201 /// Returns SQL with placeholders ($1, $2, etc.) and the values to bind.
4202 ///
4203 /// # Safety
4204 ///
4205 /// This method will panic if no filters are set to prevent accidental deletion of all rows.
4206 /// Always use `.filter()` before calling this method.
4207 ///
4208 /// # Examples
4209 ///
4210 /// ```no_run
4211 /// # use reinhardt_db::orm::Model;
4212 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
4213 /// # use serde::{Serialize, Deserialize};
4214 /// # #[derive(Clone, Serialize, Deserialize)]
4215 /// # struct User { id: Option<i64> }
4216 /// # #[derive(Clone)]
4217 /// # struct UserFields;
4218 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
4219 /// # fn with_alias(self, _alias: &str) -> Self { self }
4220 /// # }
4221 /// # impl Model for User {
4222 /// # type PrimaryKey = i64;
4223 /// # type Fields = UserFields;
4224 /// # fn table_name() -> &'static str { "users" }
4225 /// # fn new_fields() -> Self::Fields { UserFields }
4226 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4227 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4228 /// # }
4229 /// let queryset = User::objects()
4230 /// .filter("id", FilterOperator::Eq, FilterValue::Integer(1));
4231 ///
4232 /// let (sql, params) = queryset.delete_sql();
4233 /// // sql: "DELETE FROM users WHERE id = $1"
4234 /// // params: ["1"]
4235 /// ```
4236 /// Generate DELETE statement using reinhardt-query
4237 pub fn delete_query(&self) -> reinhardt_query::prelude::DeleteStatement {
4238 if self.filters.is_empty() {
4239 panic!(
4240 "DELETE without WHERE clause is not allowed. Use .filter() to specify which rows to delete."
4241 );
4242 }
4243
4244 let mut stmt = Query::delete();
4245 stmt.from_table(Alias::new(T::table_name()));
4246
4247 // Add WHERE conditions
4248 if let Some(cond) = self.build_where_condition() {
4249 stmt.cond_where(cond);
4250 }
4251
4252 stmt.to_owned()
4253 }
4254
4255 /// Deletes sql.
4256 pub fn delete_sql(&self) -> (String, Vec<String>) {
4257 let stmt = self.delete_query();
4258 use reinhardt_query::prelude::{PostgresQueryBuilder, QueryBuilder};
4259 let (sql, values) = PostgresQueryBuilder.build_delete(&stmt);
4260 let params: Vec<String> = values
4261 .iter()
4262 .map(|v| Self::sea_value_to_string(v))
4263 .collect();
4264 (sql, params)
4265 }
4266
4267 /// Retrieve a single object by composite primary key
4268 ///
4269 /// This method queries the database using all fields that compose the composite primary key.
4270 /// It validates that all required primary key fields are provided and returns the matching record.
4271 ///
4272 /// # Examples
4273 ///
4274 /// ```no_run
4275 /// # use reinhardt_db::orm::Model;
4276 /// # use reinhardt_db::orm::composite_pk::{CompositePrimaryKey, PkValue};
4277 /// # use serde::{Serialize, Deserialize};
4278 /// # use std::collections::HashMap;
4279 /// # #[derive(Clone, Serialize, Deserialize)]
4280 /// # struct PostTag { post_id: i64, tag_id: i64 }
4281 /// # #[derive(Clone)]
4282 /// # struct PostTagFields;
4283 /// # impl reinhardt_db::orm::model::FieldSelector for PostTagFields {
4284 /// # fn with_alias(self, _alias: &str) -> Self { self }
4285 /// # }
4286 /// # impl Model for PostTag {
4287 /// # type PrimaryKey = i64;
4288 /// # type Fields = PostTagFields;
4289 /// # fn table_name() -> &'static str { "post_tags" }
4290 /// # fn new_fields() -> Self::Fields { PostTagFields }
4291 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { None }
4292 /// # fn set_primary_key(&mut self, _value: Self::PrimaryKey) {}
4293 /// # fn composite_primary_key() -> Option<CompositePrimaryKey> {
4294 /// # CompositePrimaryKey::new(vec!["post_id".to_string(), "tag_id".to_string()]).ok()
4295 /// # }
4296 /// # }
4297 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
4298 /// let mut pk_values = HashMap::new();
4299 /// pk_values.insert("post_id".to_string(), PkValue::Int(1));
4300 /// pk_values.insert("tag_id".to_string(), PkValue::Int(5));
4301 ///
4302 /// let post_tag = PostTag::objects().get_composite(&pk_values).await?;
4303 /// # Ok(())
4304 /// # }
4305 /// ```
4306 ///
4307 /// # Errors
4308 ///
4309 /// Returns an error if:
4310 /// - The model doesn't have a composite primary key
4311 /// - Required primary key fields are missing from the provided values
4312 /// - No matching record is found in the database
4313 /// - Multiple records match (should not happen with a valid composite PK)
4314 pub async fn get_composite(
4315 &self,
4316 pk_values: &HashMap<String, super::composite_pk::PkValue>,
4317 ) -> reinhardt_core::exception::Result<T>
4318 where
4319 T: super::Model + Clone,
4320 {
4321 use reinhardt_query::prelude::{
4322 Alias, BinOper, ColumnRef, Expr, PostgresQueryBuilder, Value,
4323 };
4324
4325 // Get composite primary key definition from the model
4326 let composite_pk = T::composite_primary_key().ok_or_else(|| {
4327 reinhardt_core::exception::Error::Database(
4328 "Model does not have a composite primary key".to_string(),
4329 )
4330 })?;
4331
4332 // Validate that all required PK fields are provided
4333 composite_pk.validate(pk_values).map_err(|e| {
4334 reinhardt_core::exception::Error::Database(format!(
4335 "Composite PK validation failed: {}",
4336 e
4337 ))
4338 })?;
4339
4340 // Build SELECT query using reinhardt-query
4341 let table_name = T::table_name();
4342 let mut query = Query::select();
4343
4344 // Use Alias::new for table name
4345 let table_alias = Alias::new(table_name);
4346 query.from(table_alias).column(ColumnRef::Asterisk);
4347
4348 // Add WHERE conditions for each composite PK field
4349 for field_name in composite_pk.fields() {
4350 let pk_value: &super::composite_pk::PkValue = pk_values.get(field_name).unwrap();
4351 let col_alias = Alias::new(field_name);
4352
4353 match pk_value {
4354 &super::composite_pk::PkValue::Int(v) => {
4355 let condition = Expr::col(col_alias)
4356 .binary(BinOper::Equal, Expr::value(Value::BigInt(Some(v))));
4357 query.and_where(condition);
4358 }
4359 &super::composite_pk::PkValue::Uint(v) => {
4360 let condition = Expr::col(col_alias)
4361 .binary(BinOper::Equal, Expr::value(Value::BigInt(Some(v as i64))));
4362 query.and_where(condition);
4363 }
4364 super::composite_pk::PkValue::String(v) => {
4365 let condition = Expr::col(col_alias).binary(
4366 BinOper::Equal,
4367 Expr::value(Value::String(Some(Box::new(v.clone())))),
4368 );
4369 query.and_where(condition);
4370 }
4371 &super::composite_pk::PkValue::Bool(v) => {
4372 let condition = Expr::col(col_alias)
4373 .binary(BinOper::Equal, Expr::value(Value::Bool(Some(v))));
4374 query.and_where(condition);
4375 }
4376 }
4377 }
4378
4379 // Build SQL with inline values (no placeholders)
4380 let sql = query.to_string(PostgresQueryBuilder);
4381
4382 // Execute query using database connection
4383 let conn = super::manager::get_connection().await?;
4384
4385 // Execute the SELECT query
4386 let rows = conn.query(&sql, vec![]).await?;
4387
4388 // Composite PK queries should return exactly one row
4389 if rows.is_empty() {
4390 return Err(reinhardt_core::exception::Error::Database(
4391 "No record found matching the composite primary key".to_string(),
4392 ));
4393 }
4394
4395 if rows.len() > 1 {
4396 return Err(reinhardt_core::exception::Error::Database(format!(
4397 "Multiple records found ({}) for composite primary key, expected exactly one",
4398 rows.len()
4399 )));
4400 }
4401
4402 // Deserialize the single row into the model
4403 let row = &rows[0];
4404 let value = serde_json::to_value(&row.data).map_err(|e| {
4405 reinhardt_core::exception::Error::Database(format!("Serialization error: {}", e))
4406 })?;
4407
4408 serde_json::from_value(value).map_err(|e| {
4409 reinhardt_core::exception::Error::Database(format!("Deserialization error: {}", e))
4410 })
4411 }
4412
4413 /// Add an annotation to the QuerySet
4414 ///
4415 /// Annotations allow you to add calculated fields to query results using expressions,
4416 /// aggregations, or subqueries. The annotation will be added to the SELECT clause.
4417 ///
4418 /// # Examples
4419 ///
4420 /// ```no_run
4421 /// # use reinhardt_db::orm::Model;
4422 /// # use serde::{Serialize, Deserialize};
4423 /// # #[derive(Clone, Serialize, Deserialize)]
4424 /// # struct User { id: Option<i64> }
4425 /// # #[derive(Clone)]
4426 /// # struct UserFields;
4427 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
4428 /// # fn with_alias(self, _alias: &str) -> Self { self }
4429 /// # }
4430 /// # impl Model for User {
4431 /// # type PrimaryKey = i64;
4432 /// # type Fields = UserFields;
4433 /// # fn table_name() -> &'static str { "users" }
4434 /// # fn new_fields() -> Self::Fields { UserFields }
4435 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4436 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4437 /// # }
4438 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
4439 /// use reinhardt_db::orm::annotation::{Annotation, AnnotationValue};
4440 /// use reinhardt_db::orm::aggregation::Aggregate;
4441 ///
4442 /// // Add aggregate annotation
4443 /// let users = User::objects()
4444 /// .annotate(Annotation::new("total_orders",
4445 /// AnnotationValue::Aggregate(Aggregate::count(Some("orders")))))
4446 /// .all()
4447 /// .await?;
4448 /// # Ok(())
4449 /// # }
4450 /// ```
4451 pub fn annotate(mut self, annotation: super::annotation::Annotation) -> Self {
4452 self.annotations.push(annotation);
4453 self
4454 }
4455
4456 /// Add a subquery annotation to the QuerySet (SELECT clause subquery)
4457 ///
4458 /// This method adds a scalar subquery to the SELECT clause, allowing you to
4459 /// include computed values from related tables without explicit JOINs.
4460 ///
4461 /// # Type Parameters
4462 ///
4463 /// * `M` - The model type for the subquery
4464 /// * `F` - A closure that builds the subquery
4465 ///
4466 /// # Parameters
4467 ///
4468 /// * `name` - The alias for the subquery result column
4469 /// * `builder` - A closure that receives a fresh `QuerySet<M>` and returns a configured QuerySet
4470 ///
4471 /// # Examples
4472 ///
4473 /// ```no_run
4474 /// # use reinhardt_db::orm::Model;
4475 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
4476 /// # use reinhardt_db::orm::OuterRef;
4477 /// # use serde::{Serialize, Deserialize};
4478 /// # #[derive(Clone, Serialize, Deserialize)]
4479 /// # struct Author { id: Option<i64> }
4480 /// # #[derive(Clone)]
4481 /// # struct AuthorFields;
4482 /// # impl reinhardt_db::orm::model::FieldSelector for AuthorFields {
4483 /// # fn with_alias(self, _alias: &str) -> Self { self }
4484 /// # }
4485 /// # impl Model for Author {
4486 /// # type PrimaryKey = i64;
4487 /// # type Fields = AuthorFields;
4488 /// # fn table_name() -> &'static str { "authors" }
4489 /// # fn new_fields() -> Self::Fields { AuthorFields }
4490 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4491 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4492 /// # }
4493 /// # #[derive(Clone, Serialize, Deserialize)]
4494 /// # struct Book { id: Option<i64> }
4495 /// # #[derive(Clone)]
4496 /// # struct BookFields;
4497 /// # impl reinhardt_db::orm::model::FieldSelector for BookFields {
4498 /// # fn with_alias(self, _alias: &str) -> Self { self }
4499 /// # }
4500 /// # impl Model for Book {
4501 /// # type PrimaryKey = i64;
4502 /// # type Fields = BookFields;
4503 /// # fn table_name() -> &'static str { "books" }
4504 /// # fn new_fields() -> Self::Fields { BookFields }
4505 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4506 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4507 /// # }
4508 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
4509 /// // Add book count for each author
4510 /// let authors = Author::objects()
4511 /// .annotate_subquery::<Book, _>("book_count", |subq| {
4512 /// subq.filter(Filter::new(
4513 /// "author_id",
4514 /// FilterOperator::Eq,
4515 /// FilterValue::OuterRef(OuterRef::new("authors.id"))
4516 /// ))
4517 /// .values(&["COUNT(*)"])
4518 /// })
4519 /// .all()
4520 /// .await?;
4521 /// // Generates: SELECT *, (SELECT COUNT(*) FROM books WHERE author_id = authors.id) AS book_count FROM authors
4522 /// # Ok(())
4523 /// # }
4524 /// ```
4525 pub fn annotate_subquery<M, F>(mut self, name: &str, builder: F) -> Self
4526 where
4527 M: super::Model + 'static,
4528 F: FnOnce(QuerySet<M>) -> QuerySet<M>,
4529 {
4530 // Create a fresh QuerySet for the subquery model
4531 let subquery_qs = QuerySet::<M>::new();
4532 // Apply the builder to configure the subquery
4533 let configured_subquery = builder(subquery_qs);
4534 // Generate SQL for the subquery (wrapped in parentheses)
4535 let subquery_sql = configured_subquery.as_subquery();
4536
4537 // Add as annotation using AnnotationValue::Subquery
4538 let annotation = super::annotation::Annotation {
4539 alias: name.to_string(),
4540 value: super::annotation::AnnotationValue::Subquery(subquery_sql),
4541 };
4542 self.annotations.push(annotation);
4543 self
4544 }
4545
4546 /// Perform an aggregation on the QuerySet
4547 ///
4548 /// Aggregations allow you to calculate summary statistics (COUNT, SUM, AVG, MAX, MIN)
4549 /// for the queryset. The aggregation result will be added to the SELECT clause.
4550 ///
4551 /// # Examples
4552 ///
4553 /// ```no_run
4554 /// # use reinhardt_db::orm::Model;
4555 /// # use serde::{Serialize, Deserialize};
4556 /// # #[derive(Serialize, Deserialize, Clone)]
4557 /// # struct User { id: Option<i64> }
4558 /// # #[derive(Clone)]
4559 /// # struct UserFields;
4560 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
4561 /// # fn with_alias(self, _alias: &str) -> Self { self }
4562 /// # }
4563 /// # impl Model for User {
4564 /// # type PrimaryKey = i64;
4565 /// # type Fields = UserFields;
4566 /// # fn table_name() -> &'static str { "users" }
4567 /// # fn new_fields() -> Self::Fields { UserFields }
4568 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4569 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4570 /// # }
4571 /// # #[derive(Serialize, Deserialize, Clone)]
4572 /// # struct Order { id: Option<i64> }
4573 /// # #[derive(Clone)]
4574 /// # struct OrderFields;
4575 /// # impl reinhardt_db::orm::model::FieldSelector for OrderFields {
4576 /// # fn with_alias(self, _alias: &str) -> Self { self }
4577 /// # }
4578 /// # impl Model for Order {
4579 /// # type PrimaryKey = i64;
4580 /// # type Fields = OrderFields;
4581 /// # fn table_name() -> &'static str { "orders" }
4582 /// # fn new_fields() -> Self::Fields { OrderFields }
4583 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4584 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4585 /// # }
4586 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
4587 /// use reinhardt_db::orm::aggregation::Aggregate;
4588 ///
4589 /// // Count all users
4590 /// let result = User::objects()
4591 /// .all()
4592 /// .aggregate(Aggregate::count_all().with_alias("total_users"))
4593 /// .all()
4594 /// .await?;
4595 ///
4596 /// // Sum order amounts
4597 /// let result = Order::objects()
4598 /// .all()
4599 /// .aggregate(Aggregate::sum("amount").with_alias("total_amount"))
4600 /// .all()
4601 /// .await?;
4602 /// # Ok(())
4603 /// # }
4604 /// ```
4605 pub fn aggregate(mut self, aggregate: super::aggregation::Aggregate) -> Self {
4606 // Convert Aggregate to Annotation and add to annotations list
4607 let alias = aggregate
4608 .alias
4609 .clone()
4610 .unwrap_or_else(|| aggregate.func.to_string().to_lowercase());
4611 let annotation = super::annotation::Annotation {
4612 alias,
4613 value: super::annotation::AnnotationValue::Aggregate(aggregate),
4614 };
4615 self.annotations.push(annotation);
4616 self
4617 }
4618
4619 /// Converts to sql.
4620 pub fn to_sql(&self) -> String {
4621 let mut stmt = if self.select_related_fields.is_empty() {
4622 // Simple SELECT without JOINs
4623 let mut stmt = Query::select();
4624
4625 // Apply FROM clause with optional alias
4626 if let Some(ref alias) = self.from_alias {
4627 stmt.from_as(Alias::new(T::table_name()), Alias::new(alias));
4628 } else {
4629 stmt.from(Alias::new(T::table_name()));
4630 }
4631
4632 // Apply DISTINCT if enabled
4633 if self.distinct_enabled {
4634 stmt.distinct();
4635 }
4636
4637 // Column selection considering selected_fields and deferred_fields
4638 if let Some(ref fields) = self.selected_fields {
4639 for field in fields {
4640 // Detect raw SQL expressions (like COUNT(*), AVG(price), etc.)
4641 if field.contains('(') && field.contains(')') {
4642 // Use expr() for raw SQL expressions - clone to satisfy lifetime
4643 stmt.expr(Expr::cust(field.clone()));
4644 } else {
4645 // Regular column reference
4646 let col_ref = parse_column_reference(field);
4647 stmt.column(col_ref);
4648 }
4649 }
4650 } else if !self.deferred_fields.is_empty() {
4651 let all_fields = T::field_metadata();
4652 for field in all_fields {
4653 if !self.deferred_fields.contains(&field.name) {
4654 let col_ref = parse_column_reference(&field.name);
4655 stmt.column(col_ref);
4656 }
4657 }
4658 } else {
4659 stmt.column(ColumnRef::Asterisk);
4660 }
4661
4662 // Apply JOINs
4663 for join in &self.joins {
4664 if join.on_condition.is_empty() {
4665 // CROSS JOIN (no ON condition)
4666 if let Some(ref alias) = join.target_alias {
4667 // CROSS JOIN with alias - reinhardt-query doesn't support this directly
4668 // Use regular join syntax instead
4669 stmt.cross_join((Alias::new(&join.target_table), Alias::new(alias)));
4670 } else {
4671 stmt.cross_join(Alias::new(&join.target_table));
4672 }
4673 } else {
4674 // Convert reinhardt JoinType to reinhardt-query JoinType
4675 let sea_join_type = match join.join_type {
4676 super::sqlalchemy_query::JoinType::Inner => SeaJoinType::InnerJoin,
4677 super::sqlalchemy_query::JoinType::Left => SeaJoinType::LeftJoin,
4678 super::sqlalchemy_query::JoinType::Right => SeaJoinType::RightJoin,
4679 super::sqlalchemy_query::JoinType::Full => SeaJoinType::FullOuterJoin,
4680 };
4681
4682 // Build the join with optional alias
4683 if let Some(ref alias) = join.target_alias {
4684 // JOIN with alias: (table, alias)
4685 stmt.join(
4686 sea_join_type,
4687 (Alias::new(&join.target_table), Alias::new(alias)),
4688 Expr::cust(join.on_condition.clone()),
4689 );
4690 } else {
4691 // JOIN without alias
4692 stmt.join(
4693 sea_join_type,
4694 Alias::new(&join.target_table),
4695 Expr::cust(join.on_condition.clone()),
4696 );
4697 }
4698 }
4699 }
4700
4701 // Apply WHERE conditions
4702 if let Some(cond) = self.build_where_condition() {
4703 stmt.cond_where(cond);
4704 }
4705
4706 // Apply GROUP BY
4707 for group_field in &self.group_by_fields {
4708 stmt.group_by_col(Alias::new(group_field));
4709 }
4710
4711 // Apply HAVING
4712 for having_cond in &self.having_conditions {
4713 match having_cond {
4714 HavingCondition::AggregateCompare {
4715 func,
4716 field,
4717 operator,
4718 value,
4719 } => {
4720 // Build aggregate function expression
4721 let agg_expr = match func {
4722 AggregateFunc::Avg => {
4723 Func::avg(Expr::col(Alias::new(field)).into_simple_expr())
4724 }
4725 AggregateFunc::Count => {
4726 if field == "*" {
4727 Func::count(Expr::asterisk().into_simple_expr())
4728 } else {
4729 Func::count(Expr::col(Alias::new(field)).into_simple_expr())
4730 }
4731 }
4732 AggregateFunc::Sum => {
4733 Func::sum(Expr::col(Alias::new(field)).into_simple_expr())
4734 }
4735 AggregateFunc::Min => {
4736 Func::min(Expr::col(Alias::new(field)).into_simple_expr())
4737 }
4738 AggregateFunc::Max => {
4739 Func::max(Expr::col(Alias::new(field)).into_simple_expr())
4740 }
4741 };
4742
4743 // Build comparison expression
4744 let having_expr = match operator {
4745 ComparisonOp::Eq => match value {
4746 AggregateValue::Int(v) => agg_expr.eq(*v),
4747 AggregateValue::Float(v) => agg_expr.eq(*v),
4748 },
4749 ComparisonOp::Ne => match value {
4750 AggregateValue::Int(v) => agg_expr.ne(*v),
4751 AggregateValue::Float(v) => agg_expr.ne(*v),
4752 },
4753 ComparisonOp::Gt => match value {
4754 AggregateValue::Int(v) => agg_expr.gt(*v),
4755 AggregateValue::Float(v) => agg_expr.gt(*v),
4756 },
4757 ComparisonOp::Gte => match value {
4758 AggregateValue::Int(v) => agg_expr.gte(*v),
4759 AggregateValue::Float(v) => agg_expr.gte(*v),
4760 },
4761 ComparisonOp::Lt => match value {
4762 AggregateValue::Int(v) => agg_expr.lt(*v),
4763 AggregateValue::Float(v) => agg_expr.lt(*v),
4764 },
4765 ComparisonOp::Lte => match value {
4766 AggregateValue::Int(v) => agg_expr.lte(*v),
4767 AggregateValue::Float(v) => agg_expr.lte(*v),
4768 },
4769 };
4770
4771 stmt.and_having(having_expr);
4772 }
4773 }
4774 }
4775
4776 // Apply ORDER BY
4777 for order_field in &self.order_by_fields {
4778 let (field, is_desc) = if let Some(stripped) = order_field.strip_prefix('-') {
4779 (stripped, true)
4780 } else {
4781 (order_field.as_str(), false)
4782 };
4783
4784 let col_ref = parse_column_reference(field);
4785 let expr = Expr::col(col_ref);
4786 if is_desc {
4787 stmt.order_by_expr(expr, Order::Desc);
4788 } else {
4789 stmt.order_by_expr(expr, Order::Asc);
4790 }
4791 }
4792
4793 // Apply LIMIT/OFFSET
4794 if let Some(limit) = self.limit {
4795 stmt.limit(limit as u64);
4796 }
4797 if let Some(offset) = self.offset {
4798 stmt.offset(offset as u64);
4799 }
4800
4801 stmt.to_owned()
4802 } else {
4803 // SELECT with JOINs for select_related
4804 self.select_related_query()
4805 };
4806
4807 // Add annotations to SELECT clause if any using reinhardt-query API
4808 // Collect annotation SQL strings first to handle lifetime issues
4809 // Note: Use to_sql_expr() to get expression without alias (reinhardt-query adds alias via expr_as)
4810 let annotation_exprs: Vec<_> = self
4811 .annotations
4812 .iter()
4813 .map(|a| (a.value.to_sql_expr(), a.alias.clone()))
4814 .collect();
4815
4816 for (value_sql, alias) in annotation_exprs {
4817 stmt.expr_as(Expr::cust(value_sql), Alias::new(alias));
4818 }
4819
4820 use reinhardt_query::prelude::PostgresQueryBuilder;
4821 let mut select_sql = stmt.to_string(PostgresQueryBuilder);
4822
4823 // Insert LATERAL JOIN clauses after FROM clause
4824 if !self.lateral_joins.is_empty() {
4825 let lateral_sql = self.lateral_joins.to_sql().join(" ");
4826
4827 // Find insertion point: after FROM clause, before WHERE/ORDER BY/LIMIT
4828 // Look for WHERE, ORDER BY, or end of string
4829 let insert_pos = select_sql
4830 .find(" WHERE ")
4831 .or_else(|| select_sql.find(" ORDER BY "))
4832 .or_else(|| select_sql.find(" LIMIT "))
4833 .unwrap_or(select_sql.len());
4834
4835 select_sql.insert_str(insert_pos, &format!(" {}", lateral_sql));
4836 }
4837
4838 // Replace FROM table with FROM subquery if from_subquery_sql is set
4839 if let Some(ref subquery_sql) = self.from_subquery_sql
4840 && let Some(ref alias) = self.from_alias
4841 {
4842 // Pattern: FROM "table_name" AS "alias" or FROM "table_name"
4843 let from_pattern_with_alias = format!("FROM \"{}\" AS \"{}\"", T::table_name(), alias);
4844 let from_pattern_simple = format!("FROM \"{}\"", T::table_name());
4845
4846 let from_replacement = format!("FROM {} AS \"{}\"", subquery_sql, alias);
4847
4848 // Try to replace with alias pattern first, then simple pattern
4849 if select_sql.contains(&from_pattern_with_alias) {
4850 select_sql = select_sql.replace(&from_pattern_with_alias, &from_replacement);
4851 } else if select_sql.contains(&from_pattern_simple) {
4852 select_sql = select_sql.replace(&from_pattern_simple, &from_replacement);
4853 }
4854 }
4855
4856 // Prepend CTE clause if any CTEs are defined
4857 if let Some(cte_sql) = self.ctes.to_sql() {
4858 format!("{} {}", cte_sql, select_sql)
4859 } else {
4860 select_sql
4861 }
4862 }
4863
4864 /// Select specific values from the QuerySet
4865 ///
4866 /// Returns only the specified fields instead of all columns.
4867 /// Useful for optimizing queries when you don't need all model fields.
4868 ///
4869 /// # Examples
4870 ///
4871 /// ```no_run
4872 /// # use reinhardt_db::orm::Model;
4873 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
4874 /// # use serde::{Serialize, Deserialize};
4875 /// # #[derive(Clone, Serialize, Deserialize)]
4876 /// # struct User { id: Option<i64> }
4877 /// # #[derive(Clone)]
4878 /// # struct UserFields;
4879 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
4880 /// # fn with_alias(self, _alias: &str) -> Self { self }
4881 /// # }
4882 /// # impl Model for User {
4883 /// # type PrimaryKey = i64;
4884 /// # type Fields = UserFields;
4885 /// # fn table_name() -> &'static str { "users" }
4886 /// # fn new_fields() -> Self::Fields { UserFields }
4887 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4888 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4889 /// # }
4890 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
4891 /// // Select only specific fields
4892 /// let users = User::objects()
4893 /// .values(&["id", "username", "email"])
4894 /// .all()
4895 /// .await?;
4896 /// // Generates: SELECT id, username, email FROM users
4897 ///
4898 /// // Combine with filters
4899 /// let active_user_names = User::objects()
4900 /// .filter("is_active", FilterOperator::Eq, FilterValue::Boolean(true))
4901 /// .values(&["username"])
4902 /// .all()
4903 /// .await?;
4904 /// # Ok(())
4905 /// # }
4906 /// ```
4907 pub fn values(mut self, fields: &[&str]) -> Self {
4908 self.selected_fields = Some(fields.iter().map(|s| s.to_string()).collect());
4909 self
4910 }
4911
4912 /// Select specific values as a list
4913 ///
4914 /// Alias for `values()` - returns tuple-like results with specified fields.
4915 /// In Django, this returns tuples instead of dictionaries, but in Rust
4916 /// the behavior is the same as `values()` due to type safety.
4917 ///
4918 /// # Examples
4919 ///
4920 /// ```no_run
4921 /// # use reinhardt_db::orm::Model;
4922 /// # use serde::{Serialize, Deserialize};
4923 /// # #[derive(Clone, Serialize, Deserialize)]
4924 /// # struct User { id: Option<i64> }
4925 /// # #[derive(Clone)]
4926 /// # struct UserFields;
4927 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
4928 /// # fn with_alias(self, _alias: &str) -> Self { self }
4929 /// # }
4930 /// # impl Model for User {
4931 /// # type PrimaryKey = i64;
4932 /// # type Fields = UserFields;
4933 /// # fn table_name() -> &'static str { "users" }
4934 /// # fn new_fields() -> Self::Fields { UserFields }
4935 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4936 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4937 /// # }
4938 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
4939 /// // Same as values()
4940 /// let user_data = User::objects()
4941 /// .values_list(&["id", "username"])
4942 /// .all()
4943 /// .await?;
4944 /// # Ok(())
4945 /// # }
4946 /// ```
4947 pub fn values_list(self, fields: &[&str]) -> Self {
4948 self.values(fields)
4949 }
4950
4951 /// Order the QuerySet by specified fields
4952 ///
4953 /// # Examples
4954 ///
4955 /// ```no_run
4956 /// # use reinhardt_db::orm::Model;
4957 /// # use serde::{Serialize, Deserialize};
4958 /// # #[derive(Clone, Serialize, Deserialize)]
4959 /// # struct User { id: Option<i64> }
4960 /// # #[derive(Clone)]
4961 /// # struct UserFields;
4962 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
4963 /// # fn with_alias(self, _alias: &str) -> Self { self }
4964 /// # }
4965 /// # impl Model for User {
4966 /// # type PrimaryKey = i64;
4967 /// # type Fields = UserFields;
4968 /// # fn table_name() -> &'static str { "users" }
4969 /// # fn new_fields() -> Self::Fields { UserFields }
4970 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
4971 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
4972 /// # }
4973 /// # fn example() {
4974 /// // Ascending order
4975 /// User::objects().order_by(&["name"]);
4976 ///
4977 /// // Descending order (prefix with '-')
4978 /// User::objects().order_by(&["-created_at"]);
4979 ///
4980 /// // Multiple fields
4981 /// User::objects().order_by(&["department", "-salary"]);
4982 /// # }
4983 /// ```
4984 pub fn order_by(mut self, fields: &[&str]) -> Self {
4985 self.order_by_fields = fields.iter().map(|s| s.to_string()).collect();
4986 self
4987 }
4988
4989 /// Return only distinct results
4990 pub fn distinct(mut self) -> Self {
4991 self.distinct_enabled = true;
4992 self
4993 }
4994
4995 /// Set LIMIT clause
4996 ///
4997 /// Limits the number of records returned by the query.
4998 /// Corresponds to Django's QuerySet slicing `[:limit]`.
4999 ///
5000 /// # Examples
5001 ///
5002 /// ```no_run
5003 /// # use reinhardt_db::orm::Model;
5004 /// # use serde::{Serialize, Deserialize};
5005 /// # #[derive(Clone, Serialize, Deserialize)]
5006 /// # struct User { id: Option<i64> }
5007 /// # #[derive(Clone)]
5008 /// # struct UserFields;
5009 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
5010 /// # fn with_alias(self, _alias: &str) -> Self { self }
5011 /// # }
5012 /// # impl Model for User {
5013 /// # type PrimaryKey = i64;
5014 /// # type Fields = UserFields;
5015 /// # fn table_name() -> &'static str { "users" }
5016 /// # fn new_fields() -> Self::Fields { UserFields }
5017 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5018 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5019 /// # }
5020 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5021 /// let users = User::objects()
5022 /// .limit(10)
5023 /// .all()
5024 /// .await?;
5025 /// # Ok(())
5026 /// # }
5027 /// ```
5028 pub fn limit(mut self, limit: usize) -> Self {
5029 self.limit = Some(limit);
5030 self
5031 }
5032
5033 /// Set OFFSET clause
5034 ///
5035 /// Skips the specified number of records before returning results.
5036 /// Corresponds to Django's QuerySet slicing `[offset:]`.
5037 ///
5038 /// # Examples
5039 ///
5040 /// ```no_run
5041 /// # use reinhardt_db::orm::Model;
5042 /// # use serde::{Serialize, Deserialize};
5043 /// # #[derive(Clone, Serialize, Deserialize)]
5044 /// # struct User { id: Option<i64> }
5045 /// # #[derive(Clone)]
5046 /// # struct UserFields;
5047 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
5048 /// # fn with_alias(self, _alias: &str) -> Self { self }
5049 /// # }
5050 /// # impl Model for User {
5051 /// # type PrimaryKey = i64;
5052 /// # type Fields = UserFields;
5053 /// # fn table_name() -> &'static str { "users" }
5054 /// # fn new_fields() -> Self::Fields { UserFields }
5055 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5056 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5057 /// # }
5058 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5059 /// let users = User::objects()
5060 /// .offset(20)
5061 /// .limit(10)
5062 /// .all()
5063 /// .await?;
5064 /// # Ok(())
5065 /// # }
5066 /// ```
5067 pub fn offset(mut self, offset: usize) -> Self {
5068 self.offset = Some(offset);
5069 self
5070 }
5071
5072 /// Paginate results using page number and page size
5073 ///
5074 /// Convenience method that calculates offset automatically.
5075 /// Corresponds to Django REST framework's PageNumberPagination.
5076 ///
5077 /// # Examples
5078 ///
5079 /// ```no_run
5080 /// # use reinhardt_db::orm::Model;
5081 /// # use serde::{Serialize, Deserialize};
5082 /// # #[derive(Clone, Serialize, Deserialize)]
5083 /// # struct User { id: Option<i64> }
5084 /// # #[derive(Clone)]
5085 /// # struct UserFields;
5086 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
5087 /// # fn with_alias(self, _alias: &str) -> Self { self }
5088 /// # }
5089 /// # impl Model for User {
5090 /// # type PrimaryKey = i64;
5091 /// # type Fields = UserFields;
5092 /// # fn table_name() -> &'static str { "users" }
5093 /// # fn new_fields() -> Self::Fields { UserFields }
5094 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5095 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5096 /// # }
5097 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5098 /// // Page 3, 10 items per page (offset=20, limit=10)
5099 /// let users = User::objects()
5100 /// .paginate(3, 10)
5101 /// .all()
5102 /// .await?;
5103 /// # Ok(())
5104 /// # }
5105 /// ```
5106 pub fn paginate(self, page: usize, page_size: usize) -> Self {
5107 let offset = page.saturating_sub(1) * page_size;
5108 self.offset(offset).limit(page_size)
5109 }
5110
5111 /// Convert QuerySet to a subquery
5112 ///
5113 /// Returns the QuerySet as a SQL subquery wrapped in parentheses,
5114 /// suitable for use in IN clauses, EXISTS clauses, or as a derived table.
5115 ///
5116 /// # Examples
5117 ///
5118 /// ```no_run
5119 /// # use reinhardt_db::orm::Model;
5120 /// # use reinhardt_db::orm::{Filter, FilterOperator, FilterValue};
5121 /// # use serde::{Serialize, Deserialize};
5122 /// # #[derive(Clone, Serialize, Deserialize)]
5123 /// # struct User { id: Option<i64> }
5124 /// # #[derive(Clone)]
5125 /// # struct UserFields;
5126 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
5127 /// # fn with_alias(self, _alias: &str) -> Self { self }
5128 /// # }
5129 /// # impl Model for User {
5130 /// # type PrimaryKey = i64;
5131 /// # type Fields = UserFields;
5132 /// # fn table_name() -> &'static str { "users" }
5133 /// # fn new_fields() -> Self::Fields { UserFields }
5134 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5135 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5136 /// # }
5137 /// # #[derive(Clone, Serialize, Deserialize)]
5138 /// # struct Post { id: Option<i64> }
5139 /// # #[derive(Clone)]
5140 /// # struct PostFields;
5141 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
5142 /// # fn with_alias(self, _alias: &str) -> Self { self }
5143 /// # }
5144 /// # impl Model for Post {
5145 /// # type PrimaryKey = i64;
5146 /// # type Fields = PostFields;
5147 /// # fn table_name() -> &'static str { "posts" }
5148 /// # fn new_fields() -> Self::Fields { PostFields }
5149 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5150 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5151 /// # }
5152 /// // Use in IN clause
5153 /// let active_user_ids = User::objects()
5154 /// .filter("is_active", FilterOperator::Eq, FilterValue::Bool(true))
5155 /// .values(&["id"])
5156 /// .as_subquery();
5157 /// // Generates: (SELECT id FROM users WHERE is_active = $1)
5158 ///
5159 /// // Use as derived table
5160 /// let subquery = Post::objects()
5161 /// .filter("published", FilterOperator::Eq, FilterValue::Bool(true))
5162 /// .as_subquery();
5163 /// // Generates: (SELECT * FROM posts WHERE published = $1)
5164 /// ```
5165 pub fn as_subquery(self) -> String {
5166 format!("({})", self.to_sql())
5167 }
5168
5169 /// Defer loading of specific fields
5170 ///
5171 /// Marks specific fields for deferred loading (lazy loading).
5172 /// The specified fields will be excluded from the initial query.
5173 ///
5174 /// # Examples
5175 ///
5176 /// ```no_run
5177 /// # use reinhardt_db::orm::Model;
5178 /// # use serde::{Serialize, Deserialize};
5179 /// # #[derive(Clone, Serialize, Deserialize)]
5180 /// # struct User { id: Option<i64>, username: String, email: String }
5181 /// # #[derive(Clone)]
5182 /// # struct UserFields;
5183 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
5184 /// # fn with_alias(self, _alias: &str) -> Self { self }
5185 /// # }
5186 /// # impl Model for User {
5187 /// # type PrimaryKey = i64;
5188 /// # type Fields = UserFields;
5189 /// # fn table_name() -> &'static str { "users" }
5190 /// # fn new_fields() -> Self::Fields { UserFields }
5191 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5192 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5193 /// # }
5194 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5195 /// // Defer large text fields
5196 /// let users = User::objects()
5197 /// .defer(&["bio", "profile_picture"])
5198 /// .all()
5199 /// .await?;
5200 /// // Generates: SELECT id, username, email FROM users (excluding bio, profile_picture)
5201 /// # Ok(())
5202 /// # }
5203 /// ```
5204 pub fn defer(mut self, fields: &[&str]) -> Self {
5205 self.deferred_fields = fields.iter().map(|s| s.to_string()).collect();
5206 self
5207 }
5208
5209 /// Load only specific fields
5210 ///
5211 /// Alias for `values()` - specifies which fields to load immediately.
5212 /// In Django, this is used for deferred loading optimization, but in Rust
5213 /// it behaves the same as `values()`.
5214 ///
5215 /// # Examples
5216 ///
5217 /// ```no_run
5218 /// # use reinhardt_db::orm::Model;
5219 /// # use serde::{Serialize, Deserialize};
5220 /// # #[derive(Clone, Serialize, Deserialize)]
5221 /// # struct User { id: Option<i64>, username: String }
5222 /// # #[derive(Clone)]
5223 /// # struct UserFields;
5224 /// # impl reinhardt_db::orm::model::FieldSelector for UserFields {
5225 /// # fn with_alias(self, _alias: &str) -> Self { self }
5226 /// # }
5227 /// # impl Model for User {
5228 /// # type PrimaryKey = i64;
5229 /// # type Fields = UserFields;
5230 /// # fn table_name() -> &'static str { "users" }
5231 /// # fn new_fields() -> Self::Fields { UserFields }
5232 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5233 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5234 /// # }
5235 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5236 /// // Load only specific fields
5237 /// let users = User::objects()
5238 /// .only(&["id", "username"])
5239 /// .all()
5240 /// .await?;
5241 /// // Generates: SELECT id, username FROM users
5242 /// # Ok(())
5243 /// # }
5244 /// ```
5245 pub fn only(self, fields: &[&str]) -> Self {
5246 self.values(fields)
5247 }
5248
5249 // ==================== PostgreSQL-specific convenience methods ====================
5250
5251 /// Filter by PostgreSQL full-text search
5252 ///
5253 /// This method adds a filter for full-text search using PostgreSQL's `@@` operator.
5254 /// The query is converted using `plainto_tsquery` for simple word matching.
5255 ///
5256 /// # Arguments
5257 ///
5258 /// * `field` - The tsvector field to search
5259 /// * `query` - The search query string
5260 ///
5261 /// # Examples
5262 ///
5263 /// ```no_run
5264 /// # use reinhardt_db::orm::Model;
5265 /// # use serde::{Serialize, Deserialize};
5266 /// # #[derive(Clone, Serialize, Deserialize)]
5267 /// # struct Article { id: Option<i64>, title: String }
5268 /// # #[derive(Clone)]
5269 /// # struct ArticleFields;
5270 /// # impl reinhardt_db::orm::model::FieldSelector for ArticleFields {
5271 /// # fn with_alias(self, _alias: &str) -> Self { self }
5272 /// # }
5273 /// # impl Model for Article {
5274 /// # type PrimaryKey = i64;
5275 /// # type Fields = ArticleFields;
5276 /// # fn table_name() -> &'static str { "articles" }
5277 /// # fn new_fields() -> Self::Fields { ArticleFields }
5278 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5279 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5280 /// # }
5281 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5282 /// // Search articles for "rust programming"
5283 /// let articles = Article::objects()
5284 /// .full_text_search("search_vector", "rust programming")
5285 /// .all()
5286 /// .await?;
5287 /// // Generates: WHERE search_vector @@ plainto_tsquery('english', 'rust programming')
5288 /// # Ok(())
5289 /// # }
5290 /// ```
5291 pub fn full_text_search(self, field: &str, query: &str) -> Self {
5292 self.filter(Filter::new(
5293 field,
5294 FilterOperator::FullTextMatch,
5295 FilterValue::String(query.to_string()),
5296 ))
5297 }
5298
5299 /// Filter by PostgreSQL array overlap
5300 ///
5301 /// Returns rows where the array field has at least one element in common with the given values.
5302 ///
5303 /// # Arguments
5304 ///
5305 /// * `field` - The array field name
5306 /// * `values` - Values to check for overlap
5307 ///
5308 /// # Examples
5309 ///
5310 /// ```no_run
5311 /// # use reinhardt_db::orm::Model;
5312 /// # use serde::{Serialize, Deserialize};
5313 /// # #[derive(Clone, Serialize, Deserialize)]
5314 /// # struct Post { id: Option<i64>, title: String }
5315 /// # #[derive(Clone)]
5316 /// # struct PostFields;
5317 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
5318 /// # fn with_alias(self, _alias: &str) -> Self { self }
5319 /// # }
5320 /// # impl Model for Post {
5321 /// # type PrimaryKey = i64;
5322 /// # type Fields = PostFields;
5323 /// # fn table_name() -> &'static str { "posts" }
5324 /// # fn new_fields() -> Self::Fields { PostFields }
5325 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5326 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5327 /// # }
5328 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5329 /// // Find posts with any of these tags
5330 /// let posts = Post::objects()
5331 /// .filter_array_overlap("tags", &["rust", "programming"])
5332 /// .all()
5333 /// .await?;
5334 /// // Generates: WHERE tags && ARRAY['rust', 'programming']
5335 /// # Ok(())
5336 /// # }
5337 /// ```
5338 pub fn filter_array_overlap(self, field: &str, values: &[&str]) -> Self {
5339 self.filter(Filter::new(
5340 field,
5341 FilterOperator::ArrayOverlap,
5342 FilterValue::Array(values.iter().map(|s| s.to_string()).collect()),
5343 ))
5344 }
5345
5346 /// Filter by PostgreSQL array containment
5347 ///
5348 /// Returns rows where the array field contains all the given values.
5349 ///
5350 /// # Arguments
5351 ///
5352 /// * `field` - The array field name
5353 /// * `values` - Values that must all be present in the array
5354 ///
5355 /// # Examples
5356 ///
5357 /// ```no_run
5358 /// # use reinhardt_db::orm::Model;
5359 /// # use serde::{Serialize, Deserialize};
5360 /// # #[derive(Clone, Serialize, Deserialize)]
5361 /// # struct Post { id: Option<i64>, title: String }
5362 /// # #[derive(Clone)]
5363 /// # struct PostFields;
5364 /// # impl reinhardt_db::orm::model::FieldSelector for PostFields {
5365 /// # fn with_alias(self, _alias: &str) -> Self { self }
5366 /// # }
5367 /// # impl Model for Post {
5368 /// # type PrimaryKey = i64;
5369 /// # type Fields = PostFields;
5370 /// # fn table_name() -> &'static str { "posts" }
5371 /// # fn new_fields() -> Self::Fields { PostFields }
5372 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5373 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5374 /// # }
5375 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5376 /// // Find posts that have both "rust" and "async" tags
5377 /// let posts = Post::objects()
5378 /// .filter_array_contains("tags", &["rust", "async"])
5379 /// .all()
5380 /// .await?;
5381 /// // Generates: WHERE tags @> ARRAY['rust', 'async']
5382 /// # Ok(())
5383 /// # }
5384 /// ```
5385 pub fn filter_array_contains(self, field: &str, values: &[&str]) -> Self {
5386 self.filter(Filter::new(
5387 field,
5388 FilterOperator::ArrayContains,
5389 FilterValue::Array(values.iter().map(|s| s.to_string()).collect()),
5390 ))
5391 }
5392
5393 /// Filter by PostgreSQL JSONB containment
5394 ///
5395 /// Returns rows where the JSONB field contains the given JSON object.
5396 ///
5397 /// # Arguments
5398 ///
5399 /// * `field` - The JSONB field name
5400 /// * `json` - JSON string to check for containment
5401 ///
5402 /// # Examples
5403 ///
5404 /// ```no_run
5405 /// # use reinhardt_db::orm::Model;
5406 /// # use serde::{Serialize, Deserialize};
5407 /// # #[derive(Clone, Serialize, Deserialize)]
5408 /// # struct Product { id: Option<i64>, name: String }
5409 /// # #[derive(Clone)]
5410 /// # struct ProductFields;
5411 /// # impl reinhardt_db::orm::model::FieldSelector for ProductFields {
5412 /// # fn with_alias(self, _alias: &str) -> Self { self }
5413 /// # }
5414 /// # impl Model for Product {
5415 /// # type PrimaryKey = i64;
5416 /// # type Fields = ProductFields;
5417 /// # fn table_name() -> &'static str { "products" }
5418 /// # fn new_fields() -> Self::Fields { ProductFields }
5419 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5420 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5421 /// # }
5422 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5423 /// // Find products with specific metadata
5424 /// let products = Product::objects()
5425 /// .filter_jsonb_contains("metadata", r#"{"active": true}"#)
5426 /// .all()
5427 /// .await?;
5428 /// // Generates: WHERE metadata @> '{"active": true}'::jsonb
5429 /// # Ok(())
5430 /// # }
5431 /// ```
5432 pub fn filter_jsonb_contains(self, field: &str, json: &str) -> Self {
5433 self.filter(Filter::new(
5434 field,
5435 FilterOperator::JsonbContains,
5436 FilterValue::String(json.to_string()),
5437 ))
5438 }
5439
5440 /// Filter by PostgreSQL JSONB key existence
5441 ///
5442 /// Returns rows where the JSONB field contains the given key.
5443 ///
5444 /// # Arguments
5445 ///
5446 /// * `field` - The JSONB field name
5447 /// * `key` - Key to check for existence
5448 ///
5449 /// # Examples
5450 ///
5451 /// ```no_run
5452 /// # use reinhardt_db::orm::Model;
5453 /// # use serde::{Serialize, Deserialize};
5454 /// # #[derive(Clone, Serialize, Deserialize)]
5455 /// # struct Product { id: Option<i64>, name: String }
5456 /// # #[derive(Clone)]
5457 /// # struct ProductFields;
5458 /// # impl reinhardt_db::orm::model::FieldSelector for ProductFields {
5459 /// # fn with_alias(self, _alias: &str) -> Self { self }
5460 /// # }
5461 /// # impl Model for Product {
5462 /// # type PrimaryKey = i64;
5463 /// # type Fields = ProductFields;
5464 /// # fn table_name() -> &'static str { "products" }
5465 /// # fn new_fields() -> Self::Fields { ProductFields }
5466 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5467 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5468 /// # }
5469 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5470 /// // Find products with "sale_price" in metadata
5471 /// let products = Product::objects()
5472 /// .filter_jsonb_key_exists("metadata", "sale_price")
5473 /// .all()
5474 /// .await?;
5475 /// // Generates: WHERE metadata ? 'sale_price'
5476 /// # Ok(())
5477 /// # }
5478 /// ```
5479 pub fn filter_jsonb_key_exists(self, field: &str, key: &str) -> Self {
5480 self.filter(Filter::new(
5481 field,
5482 FilterOperator::JsonbKeyExists,
5483 FilterValue::String(key.to_string()),
5484 ))
5485 }
5486
5487 /// Filter by PostgreSQL range containment
5488 ///
5489 /// Returns rows where the range field contains the given value.
5490 ///
5491 /// # Arguments
5492 ///
5493 /// * `field` - The range field name
5494 /// * `value` - Value to check for containment in the range
5495 ///
5496 /// # Examples
5497 ///
5498 /// ```no_run
5499 /// # use reinhardt_db::orm::Model;
5500 /// # use serde::{Serialize, Deserialize};
5501 /// # #[derive(Clone, Serialize, Deserialize)]
5502 /// # struct Event { id: Option<i64>, name: String }
5503 /// # #[derive(Clone)]
5504 /// # struct EventFields;
5505 /// # impl reinhardt_db::orm::model::FieldSelector for EventFields {
5506 /// # fn with_alias(self, _alias: &str) -> Self { self }
5507 /// # }
5508 /// # impl Model for Event {
5509 /// # type PrimaryKey = i64;
5510 /// # type Fields = EventFields;
5511 /// # fn table_name() -> &'static str { "events" }
5512 /// # fn new_fields() -> Self::Fields { EventFields }
5513 /// # fn primary_key(&self) -> Option<Self::PrimaryKey> { self.id }
5514 /// # fn set_primary_key(&mut self, value: Self::PrimaryKey) { self.id = Some(value); }
5515 /// # }
5516 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
5517 /// // Find events that include a specific date
5518 /// let events = Event::objects()
5519 /// .filter_range_contains("date_range", "2024-06-15")
5520 /// .all()
5521 /// .await?;
5522 /// // Generates: WHERE date_range @> '2024-06-15'
5523 /// # Ok(())
5524 /// # }
5525 /// ```
5526 pub fn filter_range_contains(self, field: &str, value: &str) -> Self {
5527 self.filter(Filter::new(
5528 field,
5529 FilterOperator::RangeContains,
5530 FilterValue::String(value.to_string()),
5531 ))
5532 }
5533}
5534
5535impl<T> Default for QuerySet<T>
5536where
5537 T: super::Model,
5538{
5539 fn default() -> Self {
5540 Self::new()
5541 }
5542}
5543
5544// Convenience conversions for FilterValue
5545impl FilterValue {
5546 /// Create a String variant from any value that can be converted to String
5547 ///
5548 /// Accepts any type that implements `ToString`, including:
5549 /// - String, &str
5550 /// - Uuid (via Display)
5551 /// - Numeric types (i64, u64, etc. via Display)
5552 pub fn string(value: impl ToString) -> Self {
5553 Self::String(value.to_string())
5554 }
5555}
5556
5557// ============================================================================
5558// Helper Functions
5559// ============================================================================
5560
5561/// Quote a SQL identifier to prevent injection via field names.
5562/// Uses PostgreSQL double-quote escaping (also valid for SQLite).
5563/// Handles dot-separated qualified names (e.g., "table.column" becomes "table"."column").
5564pub(crate) fn quote_identifier(field: &str) -> String {
5565 if field.contains('\0') {
5566 panic!("SQL identifier must not contain null bytes");
5567 }
5568
5569 fn quote_single(name: &str) -> String {
5570 format!("\"{}\"", name.replace('"', "\"\""))
5571 }
5572
5573 if field.contains('.') {
5574 field
5575 .split('.')
5576 .map(quote_single)
5577 .collect::<Vec<_>>()
5578 .join(".")
5579 } else {
5580 quote_single(field)
5581 }
5582}
5583
5584/// Parse field reference into reinhardt-query column expression
5585///
5586/// Handles both qualified (`table.column`) and unqualified (`column`) references.
5587///
5588/// # Examples
5589///
5590/// - `"id"` → `ColumnRef::Column("id")`
5591/// - `"users.id"` → `ColumnRef::Column("users.id")` (qualified name as-is)
5592///
5593/// Note: For reinhardt-query v1.0.0-rc.29+, we use the full qualified name as a column identifier.
5594/// This works for most databases that support qualified column references.
5595///
5596/// This function also detects raw SQL expressions (containing parentheses, like `COUNT(*)`,
5597/// `AVG(price)`) and returns them wrapped in `Expr::cust()` instead of as column references.
5598fn parse_column_reference(field: &str) -> reinhardt_query::prelude::ColumnRef {
5599 use reinhardt_query::prelude::ColumnRef;
5600
5601 // Detect raw SQL expressions by checking for parentheses
5602 // Examples: COUNT(*), AVG(price), SUM(amount), MAX(value)
5603 if field.contains('(') && field.contains(')') {
5604 // Use column reference with raw expression name
5605 ColumnRef::column(Alias::new(field))
5606 } else if field.contains('.') {
5607 // Qualified column reference (table.column format)
5608 let parts: Vec<&str> = field.split('.').collect();
5609 match parts.as_slice() {
5610 [table, column] => {
5611 // Produces: "table"."column" instead of "table.column"
5612 ColumnRef::table_column(Alias::new(*table), Alias::new(*column))
5613 }
5614 [schema, table, column] => {
5615 // Produces: "schema"."table"."column"
5616 ColumnRef::schema_table_column(
5617 Alias::new(*schema),
5618 Alias::new(*table),
5619 Alias::new(*column),
5620 )
5621 }
5622 _ => {
5623 // Fallback for unexpected formats (4+ parts)
5624 ColumnRef::column(Alias::new(field))
5625 }
5626 }
5627 } else {
5628 // Simple column reference
5629 ColumnRef::column(Alias::new(field))
5630 }
5631}
5632
5633#[cfg(test)]
5634mod tests {
5635 use crate::orm::query::UpdateValue;
5636 use crate::orm::{FilterOperator, FilterValue, Model, QuerySet, query::Filter};
5637 use rstest::rstest;
5638 use serde::{Deserialize, Serialize};
5639 use std::collections::HashMap;
5640
5641 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5642 struct TestUser {
5643 id: Option<i64>,
5644 username: String,
5645 email: String,
5646 }
5647
5648 impl TestUser {
5649 // Allow dead_code: test helper constructor for query tests
5650 #[allow(dead_code)]
5651 fn new(username: String, email: String) -> Self {
5652 Self {
5653 id: None,
5654 username,
5655 email,
5656 }
5657 }
5658 }
5659
5660 #[derive(Debug, Clone)]
5661 struct TestUserFields;
5662
5663 impl crate::orm::model::FieldSelector for TestUserFields {
5664 fn with_alias(self, _alias: &str) -> Self {
5665 self
5666 }
5667 }
5668
5669 impl Model for TestUser {
5670 type PrimaryKey = i64;
5671 type Fields = TestUserFields;
5672
5673 fn table_name() -> &'static str {
5674 "test_users"
5675 }
5676
5677 fn primary_key(&self) -> Option<Self::PrimaryKey> {
5678 self.id
5679 }
5680
5681 fn set_primary_key(&mut self, value: Self::PrimaryKey) {
5682 self.id = Some(value);
5683 }
5684
5685 fn primary_key_field() -> &'static str {
5686 "id"
5687 }
5688
5689 fn new_fields() -> Self::Fields {
5690 TestUserFields
5691 }
5692 }
5693
5694 #[tokio::test]
5695 async fn test_queryset_create_with_manager() {
5696 // Test QuerySet::create() with explicit manager
5697 let manager = std::sync::Arc::new(TestUser::objects());
5698 let queryset = QuerySet::with_manager(manager);
5699
5700 let user = TestUser {
5701 id: None,
5702 username: "testuser".to_string(),
5703 email: "test@example.com".to_string(),
5704 };
5705
5706 // Note: This will fail without a real database connection
5707 // In actual integration tests, we would set up a test database
5708 let result = queryset.create(user).await;
5709
5710 // In unit tests, we expect this to fail due to no database
5711 // In integration tests with TestContainers, this would succeed
5712 assert!(result.is_err() || result.is_ok());
5713 }
5714
5715 #[tokio::test]
5716 async fn test_queryset_create_without_manager() {
5717 // Test QuerySet::create() fallback without manager
5718 let queryset = QuerySet::<TestUser>::new();
5719
5720 let user = TestUser {
5721 id: None,
5722 username: "fallback_user".to_string(),
5723 email: "fallback@example.com".to_string(),
5724 };
5725
5726 // Note: This will fail without a real database connection
5727 let result = queryset.create(user).await;
5728
5729 // In unit tests, we expect this to fail due to no database
5730 assert!(result.is_err() || result.is_ok());
5731 }
5732
5733 #[test]
5734 fn test_queryset_with_manager() {
5735 let manager = std::sync::Arc::new(TestUser::objects());
5736 let queryset = QuerySet::with_manager(manager.clone());
5737
5738 // Verify manager is set
5739 assert!(queryset.manager.is_some());
5740 }
5741
5742 #[test]
5743 fn test_queryset_filter_preserves_manager() {
5744 let manager = std::sync::Arc::new(TestUser::objects());
5745 let queryset = QuerySet::with_manager(manager);
5746
5747 let filter = Filter::new(
5748 "username".to_string(),
5749 FilterOperator::Eq,
5750 FilterValue::String("alice".to_string()),
5751 );
5752
5753 let filtered = queryset.filter(filter);
5754
5755 // Verify manager is preserved after filter
5756 assert!(filtered.manager.is_some());
5757 }
5758
5759 #[test]
5760 fn test_queryset_select_related_preserves_manager() {
5761 let manager = std::sync::Arc::new(TestUser::objects());
5762 let queryset = QuerySet::with_manager(manager);
5763
5764 let selected = queryset.select_related(&["profile", "posts"]);
5765
5766 // Verify manager is preserved after select_related
5767 assert!(selected.manager.is_some());
5768 assert_eq!(selected.select_related_fields, vec!["profile", "posts"]);
5769 }
5770
5771 #[test]
5772 fn test_queryset_prefetch_related_preserves_manager() {
5773 let manager = std::sync::Arc::new(TestUser::objects());
5774 let queryset = QuerySet::with_manager(manager);
5775
5776 let prefetched = queryset.prefetch_related(&["comments", "likes"]);
5777
5778 // Verify manager is preserved after prefetch_related
5779 assert!(prefetched.manager.is_some());
5780 assert_eq!(
5781 prefetched.prefetch_related_fields,
5782 vec!["comments", "likes"]
5783 );
5784 }
5785
5786 #[tokio::test]
5787 async fn test_get_composite_validation_error() {
5788 use std::collections::HashMap;
5789
5790 let queryset = QuerySet::<TestUser>::new();
5791 let pk_values = HashMap::new(); // Empty HashMap - should fail validation
5792
5793 let result = queryset.get_composite(&pk_values).await;
5794
5795 // Expect error because TestUser doesn't have a composite primary key
5796 assert!(result.is_err());
5797 let err = result.unwrap_err();
5798 assert!(err.to_string().contains("composite primary key"));
5799 }
5800
5801 // SQL Generation Tests
5802
5803 #[test]
5804 fn test_update_sql_single_field_single_filter() {
5805 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
5806 "id".to_string(),
5807 FilterOperator::Eq,
5808 FilterValue::Integer(1),
5809 ));
5810
5811 let mut updates = HashMap::new();
5812 updates.insert(
5813 "username".to_string(),
5814 UpdateValue::String("alice".to_string()),
5815 );
5816 let (sql, params) = queryset.update_sql(&updates);
5817
5818 assert_eq!(
5819 sql,
5820 "UPDATE \"test_users\" SET \"username\" = $1 WHERE \"id\" = $2"
5821 );
5822 assert_eq!(params, vec!["alice", "1"]);
5823 }
5824
5825 #[test]
5826 fn test_update_sql_multiple_fields_multiple_filters() {
5827 let queryset = QuerySet::<TestUser>::new()
5828 .filter(Filter::new(
5829 "id".to_string(),
5830 FilterOperator::Gt,
5831 FilterValue::Integer(10),
5832 ))
5833 .filter(Filter::new(
5834 "email".to_string(),
5835 FilterOperator::Contains,
5836 FilterValue::String("example.com".to_string()),
5837 ));
5838
5839 let mut updates = HashMap::new();
5840 updates.insert(
5841 "username".to_string(),
5842 UpdateValue::String("bob".to_string()),
5843 );
5844 updates.insert(
5845 "email".to_string(),
5846 UpdateValue::String("bob@test.com".to_string()),
5847 );
5848 let (sql, params) = queryset.update_sql(&updates);
5849
5850 // HashMap iteration order is not guaranteed, so we check both possible orderings
5851 let valid_sql_1 = "UPDATE \"test_users\" SET \"username\" = $1, \"email\" = $2 WHERE (\"id\" > $3 AND \"email\" LIKE $4)";
5852 let valid_sql_2 = "UPDATE \"test_users\" SET \"email\" = $1, \"username\" = $2 WHERE (\"id\" > $3 AND \"email\" LIKE $4)";
5853 assert!(
5854 sql == valid_sql_1 || sql == valid_sql_2,
5855 "Generated SQL '{}' does not match either expected pattern",
5856 sql
5857 );
5858
5859 // Check that all expected values are present (order may vary for SET clause)
5860 assert!(
5861 params.contains(&"bob".to_string()) || params.contains(&"bob@test.com".to_string())
5862 );
5863 assert_eq!(params[2], "10");
5864 assert_eq!(params[3], "%example.com%");
5865 }
5866
5867 #[test]
5868 fn test_delete_sql_single_filter() {
5869 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
5870 "id".to_string(),
5871 FilterOperator::Eq,
5872 FilterValue::Integer(1),
5873 ));
5874
5875 let (sql, params) = queryset.delete_sql();
5876
5877 assert_eq!(sql, "DELETE FROM \"test_users\" WHERE \"id\" = $1");
5878 assert_eq!(params, vec!["1"]);
5879 }
5880
5881 #[test]
5882 fn test_delete_sql_multiple_filters() {
5883 let queryset = QuerySet::<TestUser>::new()
5884 .filter(Filter::new(
5885 "username".to_string(),
5886 FilterOperator::Eq,
5887 FilterValue::String("alice".to_string()),
5888 ))
5889 .filter(Filter::new(
5890 "email".to_string(),
5891 FilterOperator::StartsWith,
5892 FilterValue::String("alice@".to_string()),
5893 ));
5894
5895 let (sql, params) = queryset.delete_sql();
5896
5897 assert_eq!(
5898 sql,
5899 "DELETE FROM \"test_users\" WHERE (\"username\" = $1 AND \"email\" LIKE $2)"
5900 );
5901 assert_eq!(params, vec!["alice", "alice@%"]);
5902 }
5903
5904 #[test]
5905 #[should_panic(
5906 expected = "DELETE without WHERE clause is not allowed. Use .filter() to specify which rows to delete."
5907 )]
5908 fn test_delete_sql_without_filters_panics() {
5909 let queryset = QuerySet::<TestUser>::new();
5910 let _ = queryset.delete_sql();
5911 }
5912
5913 #[test]
5914 fn test_filter_operators() {
5915 let queryset = QuerySet::<TestUser>::new()
5916 .filter(Filter::new(
5917 "id".to_string(),
5918 FilterOperator::Gte,
5919 FilterValue::Integer(5),
5920 ))
5921 .filter(Filter::new(
5922 "username".to_string(),
5923 FilterOperator::Ne,
5924 FilterValue::String("admin".to_string()),
5925 ));
5926
5927 let (sql, params) = queryset.delete_sql();
5928
5929 assert_eq!(
5930 sql,
5931 "DELETE FROM \"test_users\" WHERE (\"id\" >= $1 AND \"username\" <> $2)"
5932 );
5933 assert_eq!(params, vec!["5", "admin"]);
5934 }
5935
5936 #[test]
5937 fn test_null_value_filter() {
5938 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
5939 "email".to_string(),
5940 FilterOperator::Eq,
5941 FilterValue::Null,
5942 ));
5943
5944 let (sql, params) = queryset.delete_sql();
5945
5946 assert_eq!(sql, "DELETE FROM \"test_users\" WHERE \"email\" IS NULL");
5947 assert_eq!(params, Vec::<String>::new());
5948 }
5949
5950 #[test]
5951 fn test_not_null_value_filter() {
5952 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
5953 "email".to_string(),
5954 FilterOperator::Ne,
5955 FilterValue::Null,
5956 ));
5957
5958 let (sql, params) = queryset.delete_sql();
5959
5960 assert_eq!(
5961 sql,
5962 "DELETE FROM \"test_users\" WHERE \"email\" IS NOT NULL"
5963 );
5964 assert_eq!(params, Vec::<String>::new());
5965 }
5966
5967 // Query Optimization Tests
5968
5969 #[test]
5970 fn test_select_related_query_generation() {
5971 // Test that select_related_query() generates SelectStatement correctly
5972 let queryset = QuerySet::<TestUser>::new().select_related(&["profile", "department"]);
5973
5974 let stmt = queryset.select_related_query();
5975
5976 // Convert to SQL to verify structure
5977 use reinhardt_query::prelude::{PostgresQueryBuilder, QueryStatementBuilder};
5978 let sql = stmt.build(PostgresQueryBuilder).0;
5979
5980 assert!(sql.contains("SELECT"));
5981 assert!(sql.contains("test_users"));
5982 assert!(sql.contains("LEFT JOIN"));
5983 }
5984
5985 #[test]
5986 fn test_prefetch_related_queries_generation() {
5987 // Test that prefetch_related_queries() generates correct queries
5988 let queryset = QuerySet::<TestUser>::new().prefetch_related(&["posts", "comments"]);
5989 let pk_values = vec![1, 2, 3];
5990
5991 let queries = queryset.prefetch_related_queries(&pk_values);
5992
5993 // Should generate 2 queries (one for each prefetch field)
5994 assert_eq!(queries.len(), 2);
5995
5996 // Each query should be a (field_name, SelectStatement) tuple
5997 assert_eq!(queries[0].0, "posts");
5998 assert_eq!(queries[1].0, "comments");
5999 }
6000
6001 #[test]
6002 fn test_prefetch_related_queries_empty_pk_values() {
6003 let queryset = QuerySet::<TestUser>::new().prefetch_related(&["posts", "comments"]);
6004 let pk_values = vec![];
6005
6006 let queries = queryset.prefetch_related_queries(&pk_values);
6007
6008 // Should return empty vector when no PK values provided
6009 assert_eq!(queries.len(), 0);
6010 }
6011
6012 #[test]
6013 fn test_select_related_and_prefetch_together() {
6014 // Test that both can be used together
6015 let queryset = QuerySet::<TestUser>::new()
6016 .select_related(&["profile"])
6017 .prefetch_related(&["posts", "comments"]);
6018
6019 // Check select_related generates query
6020 let select_stmt = queryset.select_related_query();
6021 use reinhardt_query::prelude::{PostgresQueryBuilder, QueryStatementBuilder};
6022 let select_sql = select_stmt.build(PostgresQueryBuilder).0;
6023 assert!(select_sql.contains("LEFT JOIN"));
6024
6025 // Check prefetch_related generates queries
6026 let pk_values = vec![1, 2, 3];
6027 let prefetch_queries = queryset.prefetch_related_queries(&pk_values);
6028 assert_eq!(prefetch_queries.len(), 2);
6029 }
6030
6031 // SmallVec Optimization Tests
6032
6033 #[test]
6034 fn test_smallvec_stack_allocation_within_capacity() {
6035 // Test with exactly 10 filters (at capacity)
6036 let mut queryset = QuerySet::<TestUser>::new();
6037
6038 for i in 0..10 {
6039 queryset = queryset.filter(Filter::new(
6040 format!("field{}", i),
6041 FilterOperator::Eq,
6042 FilterValue::Integer(i as i64),
6043 ));
6044 }
6045
6046 // Verify all filters are stored
6047 assert_eq!(queryset.filters.len(), 10);
6048
6049 // Generate SQL to ensure filters work correctly
6050 let (sql, params) = queryset.delete_sql();
6051 assert!(sql.contains("WHERE"));
6052 assert_eq!(params.len(), 10);
6053 }
6054
6055 #[test]
6056 fn test_smallvec_heap_fallback_over_capacity() {
6057 // Test with 15 filters (5 over capacity, should trigger heap allocation)
6058 let mut queryset = QuerySet::<TestUser>::new();
6059
6060 for i in 0..15 {
6061 queryset = queryset.filter(Filter::new(
6062 format!("field{}", i),
6063 FilterOperator::Eq,
6064 FilterValue::Integer(i as i64),
6065 ));
6066 }
6067
6068 // Verify all filters are stored (SmallVec automatically spills to heap)
6069 assert_eq!(queryset.filters.len(), 15);
6070
6071 // Generate SQL to ensure filters work correctly even after heap fallback
6072 let (sql, params) = queryset.delete_sql();
6073 assert!(sql.contains("WHERE"));
6074 assert_eq!(params.len(), 15);
6075 }
6076
6077 #[test]
6078 fn test_smallvec_typical_use_case_1_5_filters() {
6079 // Test typical use case: 1-5 filters (well within stack capacity)
6080 let queryset = QuerySet::<TestUser>::new()
6081 .filter(Filter::new(
6082 "username".to_string(),
6083 FilterOperator::StartsWith,
6084 FilterValue::String("admin".to_string()),
6085 ))
6086 .filter(Filter::new(
6087 "email".to_string(),
6088 FilterOperator::Contains,
6089 FilterValue::String("example.com".to_string()),
6090 ))
6091 .filter(Filter::new(
6092 "id".to_string(),
6093 FilterOperator::Gt,
6094 FilterValue::Integer(100),
6095 ));
6096
6097 // Verify filters stored correctly
6098 assert_eq!(queryset.filters.len(), 3);
6099
6100 // Generate SQL
6101 let (sql, params) = queryset.delete_sql();
6102 assert!(sql.contains("WHERE"));
6103 assert!(sql.contains("\"username\" LIKE"));
6104 assert!(sql.contains("\"email\" LIKE"));
6105 assert!(sql.contains("\"id\" >"));
6106 assert_eq!(params.len(), 3);
6107 }
6108
6109 #[test]
6110 fn test_smallvec_empty_initialization() {
6111 // Test that empty SmallVec is initialized correctly
6112 let queryset = QuerySet::<TestUser>::new();
6113
6114 assert_eq!(queryset.filters.len(), 0);
6115 assert!(queryset.filters.is_empty());
6116
6117 // Generate SQL with no filters should not include WHERE clause
6118 let (where_clause, params) = queryset.build_where_clause();
6119 assert!(where_clause.is_empty());
6120 assert!(params.is_empty());
6121 }
6122
6123 #[test]
6124 fn test_smallvec_single_filter() {
6125 // Test single filter (minimal usage)
6126 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6127 "id".to_string(),
6128 FilterOperator::Eq,
6129 FilterValue::Integer(1),
6130 ));
6131
6132 assert_eq!(queryset.filters.len(), 1);
6133
6134 let (sql, params) = queryset.delete_sql();
6135 assert_eq!(sql, "DELETE FROM \"test_users\" WHERE \"id\" = $1");
6136 assert_eq!(params, vec!["1"]);
6137 }
6138
6139 #[rstest]
6140 #[case("username", r#""username""#)]
6141 #[case("user_id", r#""user_id""#)]
6142 #[case(r#"a"b"#, r#""a""b""#)]
6143 #[case("field; DROP TABLE users", r#""field; DROP TABLE users""#)]
6144 #[case("", r#""""#)]
6145 #[case("authors.id", r#""authors"."id""#)]
6146 #[case("schema.table.column", r#""schema"."table"."column""#)]
6147 fn test_quote_identifier(#[case] input: &str, #[case] expected: &str) {
6148 // Arrange
6149 // input and expected provided by rstest cases
6150
6151 // Act
6152 let result = super::quote_identifier(input);
6153
6154 // Assert
6155 assert_eq!(result, expected);
6156 }
6157
6158 #[rstest]
6159 fn test_outerref_filter_uses_safe_quoting() {
6160 // Arrange
6161 use crate::orm::expressions::OuterRef;
6162 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6163 "author_id".to_string(),
6164 FilterOperator::Eq,
6165 FilterValue::OuterRef(OuterRef::new("id")),
6166 ));
6167
6168 // Act
6169 let sql = queryset.to_sql();
6170
6171 // Assert
6172 assert_eq!(
6173 sql,
6174 r#"SELECT * FROM "test_users" WHERE "author_id" = "id""#
6175 );
6176 }
6177
6178 #[rstest]
6179 fn test_array_contains_filter_quotes_field() {
6180 // Arrange
6181 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6182 "tags".to_string(),
6183 FilterOperator::ArrayContains,
6184 FilterValue::Array(vec!["rust".to_string(), "web".to_string()]),
6185 ));
6186
6187 // Act
6188 let sql = queryset.to_sql();
6189
6190 // Assert
6191 assert_eq!(
6192 sql,
6193 r#"SELECT * FROM "test_users" WHERE "tags" @> ARRAY['rust', 'web']"#
6194 );
6195 }
6196
6197 #[rstest]
6198 fn test_outerref_dot_separated_renders_qualified_column() {
6199 // Arrange
6200 use crate::orm::expressions::OuterRef;
6201 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6202 "author_id".to_string(),
6203 FilterOperator::Eq,
6204 FilterValue::OuterRef(OuterRef::new("authors.id")),
6205 ));
6206
6207 // Act
6208 let sql = queryset.to_sql();
6209
6210 // Assert
6211 assert_eq!(
6212 sql,
6213 r#"SELECT * FROM "test_users" WHERE "author_id" = "authors"."id""#
6214 );
6215 }
6216
6217 #[rstest]
6218 fn test_injection_attempt_in_field_name_is_quoted() {
6219 // Arrange
6220 // Attempt SQL injection via field name with double quote
6221 let malicious_field = r#"id" OR 1=1 --"#.to_string();
6222
6223 // Act
6224 let quoted = super::quote_identifier(&malicious_field);
6225
6226 // Assert
6227 // The double quote inside is escaped, preventing injection
6228 assert_eq!(quoted, r#""id"" OR 1=1 --""#);
6229 // Verify the quote is not broken out of
6230 assert!(quoted.starts_with('"'));
6231 assert!(quoted.ends_with('"'));
6232 }
6233
6234 #[rstest]
6235 #[should_panic(expected = "SQL identifier must not contain null bytes")]
6236 fn test_quote_identifier_rejects_null_bytes() {
6237 // Arrange
6238 let field_with_null = "field\0name";
6239
6240 // Act
6241 super::quote_identifier(field_with_null);
6242
6243 // Assert - should panic before reaching here
6244 }
6245
6246 #[rstest]
6247 #[case(FilterOperator::Ne, "<>")]
6248 #[case(FilterOperator::Gt, ">")]
6249 #[case(FilterOperator::Gte, ">=")]
6250 #[case(FilterOperator::Lt, "<")]
6251 #[case(FilterOperator::Lte, "<=")]
6252 fn test_outerref_comparison_operators(#[case] op: FilterOperator, #[case] sql_op: &str) {
6253 // Arrange
6254 use crate::orm::expressions::OuterRef;
6255 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6256 "author_id".to_string(),
6257 op,
6258 FilterValue::OuterRef(OuterRef::new("id")),
6259 ));
6260
6261 // Act
6262 let sql = queryset.to_sql();
6263
6264 // Assert
6265 let expected = format!(
6266 r#"SELECT * FROM "test_users" WHERE "author_id" {} "id""#,
6267 sql_op
6268 );
6269 assert_eq!(sql, expected);
6270 }
6271
6272 #[rstest]
6273 fn test_array_contained_by_filter_quotes_field() {
6274 // Arrange
6275 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6276 "tags".to_string(),
6277 FilterOperator::ArrayContainedBy,
6278 FilterValue::Array(vec!["rust".to_string()]),
6279 ));
6280
6281 // Act
6282 let sql = queryset.to_sql();
6283
6284 // Assert
6285 assert_eq!(
6286 sql,
6287 r#"SELECT * FROM "test_users" WHERE "tags" <@ ARRAY['rust']"#
6288 );
6289 }
6290
6291 #[rstest]
6292 fn test_array_overlap_filter_quotes_field() {
6293 // Arrange
6294 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6295 "tags".to_string(),
6296 FilterOperator::ArrayOverlap,
6297 FilterValue::Array(vec!["rust".to_string()]),
6298 ));
6299
6300 // Act
6301 let sql = queryset.to_sql();
6302
6303 // Assert
6304 assert_eq!(
6305 sql,
6306 r#"SELECT * FROM "test_users" WHERE "tags" && ARRAY['rust']"#
6307 );
6308 }
6309
6310 #[rstest]
6311 fn test_full_text_match_filter_quotes_field() {
6312 // Arrange
6313 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6314 "content".to_string(),
6315 FilterOperator::FullTextMatch,
6316 FilterValue::String("search term".to_string()),
6317 ));
6318
6319 // Act
6320 let sql = queryset.to_sql();
6321
6322 // Assert
6323 assert_eq!(
6324 sql,
6325 r#"SELECT * FROM "test_users" WHERE "content" @@ plainto_tsquery('english', 'search term')"#
6326 );
6327 }
6328
6329 #[rstest]
6330 fn test_jsonb_contains_filter_quotes_field() {
6331 // Arrange
6332 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6333 "metadata".to_string(),
6334 FilterOperator::JsonbContains,
6335 FilterValue::String(r#"{"key": "value"}"#.to_string()),
6336 ));
6337
6338 // Act
6339 let sql = queryset.to_sql();
6340
6341 // Assert
6342 assert_eq!(
6343 sql,
6344 r#"SELECT * FROM "test_users" WHERE "metadata" @> '{"key": "value"}'::jsonb"#
6345 );
6346 }
6347
6348 #[rstest]
6349 fn test_jsonb_contained_by_filter_quotes_field() {
6350 // Arrange
6351 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6352 "metadata".to_string(),
6353 FilterOperator::JsonbContainedBy,
6354 FilterValue::String(r#"{"key": "value"}"#.to_string()),
6355 ));
6356
6357 // Act
6358 let sql = queryset.to_sql();
6359
6360 // Assert
6361 assert_eq!(
6362 sql,
6363 r#"SELECT * FROM "test_users" WHERE "metadata" <@ '{"key": "value"}'::jsonb"#
6364 );
6365 }
6366
6367 #[rstest]
6368 fn test_jsonb_key_exists_filter_quotes_field() {
6369 // Arrange
6370 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6371 "metadata".to_string(),
6372 FilterOperator::JsonbKeyExists,
6373 FilterValue::String("key".to_string()),
6374 ));
6375
6376 // Act
6377 let sql = queryset.to_sql();
6378
6379 // Assert
6380 assert_eq!(
6381 sql,
6382 r#"SELECT * FROM "test_users" WHERE "metadata" ? 'key'"#
6383 );
6384 }
6385
6386 #[rstest]
6387 fn test_jsonb_any_key_exists_filter_quotes_field() {
6388 // Arrange
6389 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6390 "metadata".to_string(),
6391 FilterOperator::JsonbAnyKeyExists,
6392 FilterValue::Array(vec!["key1".to_string(), "key2".to_string()]),
6393 ));
6394
6395 // Act
6396 let sql = queryset.to_sql();
6397
6398 // Assert
6399 assert_eq!(
6400 sql,
6401 r#"SELECT * FROM "test_users" WHERE "metadata" ?| array['key1', 'key2']"#
6402 );
6403 }
6404
6405 #[rstest]
6406 fn test_jsonb_all_keys_exist_filter_quotes_field() {
6407 // Arrange
6408 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6409 "metadata".to_string(),
6410 FilterOperator::JsonbAllKeysExist,
6411 FilterValue::Array(vec!["key1".to_string(), "key2".to_string()]),
6412 ));
6413
6414 // Act
6415 let sql = queryset.to_sql();
6416
6417 // Assert
6418 assert_eq!(
6419 sql,
6420 r#"SELECT * FROM "test_users" WHERE "metadata" ?& array['key1', 'key2']"#
6421 );
6422 }
6423
6424 #[rstest]
6425 fn test_jsonb_path_exists_filter_quotes_field() {
6426 // Arrange
6427 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6428 "metadata".to_string(),
6429 FilterOperator::JsonbPathExists,
6430 FilterValue::String("$.key".to_string()),
6431 ));
6432
6433 // Act
6434 let sql = queryset.to_sql();
6435
6436 // Assert
6437 assert_eq!(
6438 sql,
6439 r#"SELECT * FROM "test_users" WHERE "metadata" @'$.key' "#
6440 );
6441 }
6442
6443 #[rstest]
6444 fn test_range_contains_filter_quotes_field() {
6445 // Arrange
6446 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6447 "age_range".to_string(),
6448 FilterOperator::RangeContains,
6449 FilterValue::String("25".to_string()),
6450 ));
6451
6452 // Act
6453 let sql = queryset.to_sql();
6454
6455 // Assert
6456 assert_eq!(
6457 sql,
6458 r#"SELECT * FROM "test_users" WHERE "age_range" @> '''25'''"#
6459 );
6460 }
6461
6462 #[rstest]
6463 fn test_range_contained_by_filter_quotes_field() {
6464 // Arrange
6465 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6466 "age_range".to_string(),
6467 FilterOperator::RangeContainedBy,
6468 FilterValue::String("[20, 30]".to_string()),
6469 ));
6470
6471 // Act
6472 let sql = queryset.to_sql();
6473
6474 // Assert
6475 assert_eq!(
6476 sql,
6477 r#"SELECT * FROM "test_users" WHERE "age_range" <@ '[20, 30]'"#
6478 );
6479 }
6480
6481 #[rstest]
6482 fn test_range_overlaps_filter_quotes_field() {
6483 // Arrange
6484 let queryset = QuerySet::<TestUser>::new().filter(Filter::new(
6485 "age_range".to_string(),
6486 FilterOperator::RangeOverlaps,
6487 FilterValue::String("[20, 30]".to_string()),
6488 ));
6489
6490 // Act
6491 let sql = queryset.to_sql();
6492
6493 // Assert
6494 assert_eq!(
6495 sql,
6496 r#"SELECT * FROM "test_users" WHERE "age_range" && '[20, 30]'"#
6497 );
6498 }
6499}