1use crate::backends::types::QueryValue;
8use crate::orm::Model;
9use reinhardt_query::prelude::{
10 Alias, ColumnRef, Expr, ExprTrait, Func, Query, QueryStatementBuilder, SelectStatement,
11};
12use rust_decimal::prelude::ToPrimitive;
13use std::marker::PhantomData;
14
15#[derive(Debug)]
17pub enum ExecutionResult<T> {
18 One(T),
20 OneOrNone(Option<T>),
22 All(Vec<T>),
24 Scalar(String),
26 None,
28}
29
30#[non_exhaustive]
32#[derive(Debug, thiserror::Error)]
33pub enum ExecutionError {
34 #[error("Database error: {0}")]
36 Database(#[from] crate::backends::DatabaseError),
37
38 #[error("No result found")]
40 NoResultFound,
41
42 #[error("Multiple results found (expected 1, got {0})")]
44 MultipleResultsFound(usize),
45
46 #[error("Failed to deserialize result: {0}")]
48 Deserialization(#[from] serde_json::Error),
49
50 #[error("Query building error: {0}")]
52 QueryBuild(String),
53
54 #[error("Generic error: {0}")]
56 Generic(#[from] anyhow::Error),
57}
58
59fn convert_value_to_query_value(value: reinhardt_query::value::Value) -> QueryValue {
61 use reinhardt_query::value::Value as SV;
62
63 match value {
64 SV::Bool(None)
66 | SV::TinyInt(None)
67 | SV::SmallInt(None)
68 | SV::Int(None)
69 | SV::BigInt(None)
70 | SV::TinyUnsigned(None)
71 | SV::SmallUnsigned(None)
72 | SV::Unsigned(None)
73 | SV::BigUnsigned(None)
74 | SV::Float(None)
75 | SV::Double(None)
76 | SV::String(None)
77 | SV::Char(None)
78 | SV::Bytes(None)
79 | SV::ChronoDateTimeUtc(None)
80 | SV::ChronoDateTimeLocal(None)
81 | SV::ChronoDateTimeWithTimeZone(None)
82 | SV::ChronoDate(None)
83 | SV::ChronoTime(None)
84 | SV::ChronoDateTime(None)
85 | SV::Json(None)
86 | SV::Decimal(None)
87 | SV::BigDecimal(None)
88 | SV::Uuid(None) => QueryValue::Null,
89
90 SV::Bool(Some(b)) => QueryValue::Bool(b),
92
93 SV::TinyInt(Some(v)) => QueryValue::Int(v as i64),
95 SV::SmallInt(Some(v)) => QueryValue::Int(v as i64),
96 SV::Int(Some(v)) => QueryValue::Int(v as i64),
97 SV::BigInt(Some(v)) => QueryValue::Int(v),
98
99 SV::TinyUnsigned(Some(v)) => QueryValue::Int(v as i64),
101 SV::SmallUnsigned(Some(v)) => QueryValue::Int(v as i64),
102 SV::Unsigned(Some(v)) => QueryValue::Int(v as i64),
103 SV::BigUnsigned(Some(v)) => QueryValue::Int(i64::try_from(v).unwrap_or_else(|_| {
104 tracing::warn!(
105 value = v,
106 "BigUnsigned value {} exceeds i64::MAX, clamping to i64::MAX",
107 v
108 );
109 i64::MAX
110 })),
111
112 SV::Float(Some(v)) => QueryValue::Float(v as f64),
114 SV::Double(Some(v)) => QueryValue::Float(v),
115
116 SV::String(Some(s)) => QueryValue::String(s.to_string()),
118 SV::Char(Some(c)) => QueryValue::String(c.to_string()),
119
120 SV::Bytes(Some(b)) => QueryValue::Bytes(b.to_vec()),
122
123 SV::ChronoDateTimeUtc(Some(dt)) => QueryValue::Timestamp(*dt),
125
126 SV::ChronoDateTimeLocal(Some(dt)) => {
128 QueryValue::Timestamp((*dt).with_timezone(&chrono::Utc))
129 }
130 SV::ChronoDateTimeWithTimeZone(Some(dt)) => {
131 QueryValue::Timestamp((*dt).with_timezone(&chrono::Utc))
132 }
133
134 SV::ChronoDate(_) | SV::ChronoTime(_) | SV::ChronoDateTime(_) => {
136 QueryValue::String(format!("{:?}", value))
138 }
139
140 SV::Json(_) => QueryValue::String(format!("{:?}", value)),
142
143 SV::Decimal(Some(d)) => {
145 let f = d.to_f64().unwrap_or_else(|| {
146 tracing::warn!(
147 decimal = %d,
148 "Decimal cannot be directly represented as f64, falling back to string parsing"
149 );
150 d.to_string().parse::<f64>().unwrap_or(0.0)
151 });
152 QueryValue::Float(f)
153 }
154 SV::BigDecimal(Some(d)) => {
155 let f = d.to_string().parse::<f64>().unwrap_or_else(|_| {
156 tracing::warn!(
157 big_decimal = %d,
158 "BigDecimal cannot be represented as f64"
159 );
160 0.0
161 });
162 QueryValue::Float(f)
163 }
164
165 SV::Uuid(Some(u)) => QueryValue::Uuid(*u),
167
168 SV::Array(_, arr) => QueryValue::String(format!("{:?}", arr)),
171 }
172}
173
174pub fn convert_values(values: reinhardt_query::prelude::Values) -> Vec<QueryValue> {
176 values
177 .0
178 .into_iter()
179 .map(convert_value_to_query_value)
180 .collect()
181}
182
183#[async_trait::async_trait]
185pub trait QueryExecution<T: Model>
186where
187 T: Send + Sync,
188 T::PrimaryKey: Send + Sync,
189{
190 async fn get_async(
193 &self,
194 db: &super::connection::DatabaseConnection,
195 pk: &T::PrimaryKey,
196 ) -> Result<T, ExecutionError>
197 where
198 T: for<'de> serde::Deserialize<'de>;
199
200 fn get(&self, pk: &T::PrimaryKey) -> SelectStatement;
203
204 async fn all_async(
207 &self,
208 db: &super::connection::DatabaseConnection,
209 ) -> Result<Vec<T>, ExecutionError>
210 where
211 T: for<'de> serde::Deserialize<'de>;
212
213 fn all(&self) -> SelectStatement;
216
217 async fn first_async(
220 &self,
221 db: &super::connection::DatabaseConnection,
222 ) -> Result<Option<T>, ExecutionError>
223 where
224 T: for<'de> serde::Deserialize<'de>;
225
226 fn first(&self) -> SelectStatement;
229
230 async fn one_async(
233 &self,
234 db: &super::connection::DatabaseConnection,
235 ) -> Result<T, ExecutionError>
236 where
237 T: for<'de> serde::Deserialize<'de>;
238
239 fn one(&self) -> SelectStatement;
242
243 async fn one_or_none_async(
246 &self,
247 db: &super::connection::DatabaseConnection,
248 ) -> Result<Option<T>, ExecutionError>
249 where
250 T: for<'de> serde::Deserialize<'de>;
251
252 fn one_or_none(&self) -> SelectStatement;
255
256 async fn scalar_async<S>(
259 &self,
260 db: &super::connection::DatabaseConnection,
261 ) -> Result<Option<S>, ExecutionError>
262 where
263 S: for<'de> serde::Deserialize<'de>;
264
265 fn scalar(&self) -> SelectStatement;
268
269 async fn count_async(
272 &self,
273 db: &super::connection::DatabaseConnection,
274 ) -> Result<i64, ExecutionError>;
275
276 fn count(&self) -> SelectStatement;
279
280 async fn exists_async(
283 &self,
284 db: &super::connection::DatabaseConnection,
285 ) -> Result<bool, ExecutionError>;
286
287 fn exists(&self) -> SelectStatement;
290}
291
292pub struct SelectExecution<T: Model> {
294 stmt: SelectStatement,
295 _phantom: PhantomData<T>,
296}
297
298impl<T: Model> SelectExecution<T> {
299 pub fn new(stmt: SelectStatement) -> Self {
337 Self {
338 stmt,
339 _phantom: PhantomData,
340 }
341 }
342 pub fn statement(&self) -> &SelectStatement {
383 &self.stmt
384 }
385}
386
387#[async_trait::async_trait]
388impl<T: Model> QueryExecution<T> for SelectExecution<T>
389where
390 T::PrimaryKey: Into<reinhardt_query::value::Value> + Clone + Send + Sync,
391 T: Send + Sync,
392{
393 fn get(&self, pk: &T::PrimaryKey) -> SelectStatement {
394 Query::select()
395 .from(Alias::new(T::table_name()))
396 .column(ColumnRef::Asterisk)
397 .and_where(
398 Expr::col(Alias::new(T::primary_key_field())).eq(Expr::val(pk.clone().into())),
399 )
400 .limit(1)
401 .to_owned()
402 }
403
404 fn all(&self) -> SelectStatement {
405 self.stmt.clone()
406 }
407
408 fn first(&self) -> SelectStatement {
409 let mut stmt = self.stmt.clone();
410 stmt.limit(1);
411 stmt
412 }
413
414 fn one(&self) -> SelectStatement {
415 let mut stmt = self.stmt.clone();
421 stmt.limit(2);
422 stmt
423 }
424
425 fn one_or_none(&self) -> SelectStatement {
426 let mut stmt = self.stmt.clone();
432 stmt.limit(2);
433 stmt
434 }
435
436 fn scalar(&self) -> SelectStatement {
437 let mut stmt = self.stmt.clone();
438 stmt.limit(1);
439 stmt
440 }
441
442 fn count(&self) -> SelectStatement {
443 Query::select()
446 .expr(Func::count(Expr::asterisk().into_simple_expr()))
447 .from_subquery(self.stmt.clone(), Alias::new("subquery"))
448 .to_owned()
449 }
450
451 fn exists(&self) -> SelectStatement {
452 Query::select()
453 .expr(Expr::exists(self.stmt.clone()))
454 .to_owned()
455 }
456
457 async fn get_async(
458 &self,
459 db: &super::connection::DatabaseConnection,
460 pk: &T::PrimaryKey,
461 ) -> Result<T, ExecutionError>
462 where
463 T: for<'de> serde::Deserialize<'de>,
464 {
465 let stmt = self.get(pk);
466 let (sql, values) = stmt.build_any(&reinhardt_query::prelude::PostgresQueryBuilder);
467
468 let query_values = convert_values(values);
469 let row = db.query_one(&sql, query_values).await?;
470 let json = serde_json::to_value(&row)?;
471 let result = serde_json::from_value(json)?;
472 Ok(result)
473 }
474
475 async fn all_async(
476 &self,
477 db: &super::connection::DatabaseConnection,
478 ) -> Result<Vec<T>, ExecutionError>
479 where
480 T: for<'de> serde::Deserialize<'de>,
481 {
482 let stmt = self.all();
483 let (sql, values) = stmt.build_any(&reinhardt_query::prelude::PostgresQueryBuilder);
484
485 let query_values = convert_values(values);
486 let rows = db.query(&sql, query_values).await?;
487 let mut results = Vec::with_capacity(rows.len());
488 for row in rows {
489 let json = serde_json::to_value(&row)?;
490 let result = serde_json::from_value(json)?;
491 results.push(result);
492 }
493 Ok(results)
494 }
495
496 async fn first_async(
497 &self,
498 db: &super::connection::DatabaseConnection,
499 ) -> Result<Option<T>, ExecutionError>
500 where
501 T: for<'de> serde::Deserialize<'de>,
502 {
503 let stmt = self.first();
504 let (sql, values) = stmt.build_any(&reinhardt_query::prelude::PostgresQueryBuilder);
505
506 let query_values = convert_values(values);
507 let rows = db.query(&sql, query_values).await?;
508 match rows.first() {
509 Some(row) => {
510 let json = serde_json::to_value(row)?;
511 let result = serde_json::from_value(json)?;
512 Ok(Some(result))
513 }
514 None => Ok(None),
515 }
516 }
517
518 async fn one_async(
519 &self,
520 db: &super::connection::DatabaseConnection,
521 ) -> Result<T, ExecutionError>
522 where
523 T: for<'de> serde::Deserialize<'de>,
524 {
525 let stmt = self.one();
526 let (sql, values) = stmt.build_any(&reinhardt_query::prelude::PostgresQueryBuilder);
527
528 let query_values = convert_values(values);
529 let rows = db.query(&sql, query_values).await?;
530 match rows.len() {
531 0 => Err(ExecutionError::NoResultFound),
532 1 => {
533 let json = serde_json::to_value(&rows[0])?;
534 let result = serde_json::from_value(json)?;
535 Ok(result)
536 }
537 n => Err(ExecutionError::MultipleResultsFound(n)),
538 }
539 }
540
541 async fn one_or_none_async(
542 &self,
543 db: &super::connection::DatabaseConnection,
544 ) -> Result<Option<T>, ExecutionError>
545 where
546 T: for<'de> serde::Deserialize<'de>,
547 {
548 let stmt = self.one_or_none();
549 let (sql, values) = stmt.build_any(&reinhardt_query::prelude::PostgresQueryBuilder);
550
551 let query_values = convert_values(values);
552 let rows = db.query(&sql, query_values).await?;
553 match rows.len() {
554 0 => Ok(None),
555 1 => {
556 let json = serde_json::to_value(&rows[0])?;
557 let result = serde_json::from_value(json)?;
558 Ok(Some(result))
559 }
560 n => Err(ExecutionError::MultipleResultsFound(n)),
561 }
562 }
563
564 async fn scalar_async<S>(
565 &self,
566 db: &super::connection::DatabaseConnection,
567 ) -> Result<Option<S>, ExecutionError>
568 where
569 S: for<'de> serde::Deserialize<'de>,
570 {
571 let stmt = self.scalar();
572 let (sql, values) = stmt.build_any(&reinhardt_query::prelude::PostgresQueryBuilder);
573
574 let query_values = convert_values(values);
575 let rows = db.query(&sql, query_values).await?;
576 match rows.first() {
577 Some(row) => {
578 let json = serde_json::to_value(row)?;
580 if let Some(obj) = json.as_object()
581 && let Some((_, value)) = obj.iter().next()
582 {
583 let result = serde_json::from_value(value.clone())?;
584 return Ok(Some(result));
585 }
586 Ok(None)
587 }
588 None => Ok(None),
589 }
590 }
591
592 async fn count_async(
593 &self,
594 db: &super::connection::DatabaseConnection,
595 ) -> Result<i64, ExecutionError> {
596 let stmt = self.count();
597 let (sql, values) = stmt.build_any(&reinhardt_query::prelude::PostgresQueryBuilder);
598
599 let query_values = convert_values(values);
600 let row = db.query_one(&sql, query_values).await?;
601 let json = serde_json::to_value(&row)?;
602
603 if let Some(obj) = json.as_object()
605 && let Some((_, value)) = obj.iter().next()
606 {
607 let count: i64 = serde_json::from_value(value.clone())?;
608 return Ok(count);
609 }
610
611 Err(ExecutionError::QueryBuild(
612 "Count query returned unexpected format".to_string(),
613 ))
614 }
615
616 async fn exists_async(
617 &self,
618 db: &super::connection::DatabaseConnection,
619 ) -> Result<bool, ExecutionError> {
620 let stmt = self.exists();
621 let (sql, values) = stmt.build_any(&reinhardt_query::prelude::PostgresQueryBuilder);
622
623 let query_values = convert_values(values);
624 let row = db.query_one(&sql, query_values).await?;
625 let json = serde_json::to_value(&row)?;
626
627 if let Some(obj) = json.as_object()
629 && let Some((_, value)) = obj.iter().next()
630 {
631 let exists: bool = serde_json::from_value(value.clone())?;
632 return Ok(exists);
633 }
634
635 Err(ExecutionError::QueryBuild(
636 "Exists query returned unexpected format".to_string(),
637 ))
638 }
639}
640
641#[derive(Debug, Clone)]
644pub enum LoadOption {
645 JoinedLoad(String),
648
649 SelectInLoad(String),
652
653 LazyLoad(String),
656
657 NoLoad(String),
660
661 RaiseLoad(String),
664
665 Defer(String),
668
669 Undefer(String),
672
673 LoadOnly(Vec<String>),
676}
677
678impl LoadOption {
679 pub fn to_sql_comment(&self) -> String {
696 match self {
697 LoadOption::JoinedLoad(rel) => format!("/* joinedload({}) */", rel),
698 LoadOption::SelectInLoad(rel) => format!("/* selectinload({}) */", rel),
699 LoadOption::LazyLoad(rel) => format!("/* lazyload({}) */", rel),
700 LoadOption::NoLoad(rel) => format!("/* noload({}) */", rel),
701 LoadOption::RaiseLoad(rel) => format!("/* raiseload({}) */", rel),
702 LoadOption::Defer(col) => format!("/* defer({}) */", col),
703 LoadOption::Undefer(col) => format!("/* undefer({}) */", col),
704 LoadOption::LoadOnly(cols) => format!("/* load_only({}) */", cols.join(", ")),
705 }
706 }
707}
708
709#[non_exhaustive]
711pub struct QueryOptions {
712 pub load_options: Vec<LoadOption>,
714}
715
716impl QueryOptions {
717 pub fn new() -> Self {
728 Self {
729 load_options: Vec::new(),
730 }
731 }
732 pub fn add_option(mut self, option: LoadOption) -> Self {
748 self.load_options.push(option);
749 self
750 }
751 pub fn to_sql_comments(&self) -> String {
764 if self.load_options.is_empty() {
765 String::new()
766 } else {
767 format!(
768 " {}",
769 self.load_options
770 .iter()
771 .map(|o| o.to_sql_comment())
772 .collect::<Vec<_>>()
773 .join(" ")
774 )
775 }
776 }
777}
778
779impl Default for QueryOptions {
780 fn default() -> Self {
781 Self::new()
782 }
783}
784
785#[cfg(test)]
786mod tests {
787 use super::*;
788 use crate::orm::Manager;
789 use reinhardt_core::validators::TableName;
790 use rstest::rstest;
791 use serde::{Deserialize, Serialize};
792
793 #[derive(Debug, Clone, Serialize, Deserialize)]
794 struct User {
795 id: Option<i64>,
796 name: String,
797 }
798
799 #[derive(Clone)]
800 struct UserFields;
801 impl crate::orm::model::FieldSelector for UserFields {
802 fn with_alias(self, _alias: &str) -> Self {
803 self
804 }
805 }
806
807 const USER_TABLE: TableName = TableName::new_const("users");
808
809 impl Model for User {
810 type PrimaryKey = i64;
811 type Fields = UserFields;
812 type Objects = Manager<Self>;
813
814 fn table_name() -> &'static str {
815 USER_TABLE.as_str()
816 }
817
818 fn new_fields() -> Self::Fields {
819 UserFields
820 }
821
822 fn primary_key(&self) -> Option<Self::PrimaryKey> {
823 self.id
824 }
825
826 fn set_primary_key(&mut self, value: Self::PrimaryKey) {
827 self.id = Some(value);
828 }
829 }
830
831 #[test]
832 fn test_execution_get() {
833 use reinhardt_query::prelude::{Alias, PostgresQueryBuilder, Query, QueryStatementBuilder};
834
835 let stmt = Query::select()
836 .from(Alias::new("users"))
837 .column(ColumnRef::Asterisk)
838 .to_owned();
839 let exec = SelectExecution::<User>::new(stmt);
840 let result_stmt = exec.get(&123);
841 let sql = result_stmt.to_string(PostgresQueryBuilder);
842 assert!(sql.contains("WHERE"));
843 assert!(sql.contains("LIMIT"));
844 }
845
846 #[test]
847 fn test_all() {
848 use reinhardt_query::prelude::{Alias, PostgresQueryBuilder, Query, QueryStatementBuilder};
849
850 let stmt = Query::select()
851 .from(Alias::new("users"))
852 .column(ColumnRef::Asterisk)
853 .to_owned();
854 let exec = SelectExecution::<User>::new(stmt);
855 let result_stmt = exec.all();
856 let sql = result_stmt.to_string(PostgresQueryBuilder);
857 assert!(sql.contains("SELECT"));
858 assert!(sql.contains("users"));
859 }
860
861 #[test]
862 fn test_first() {
863 use reinhardt_query::prelude::{
864 Alias, Expr, PostgresQueryBuilder, Query, QueryStatementBuilder,
865 };
866
867 let stmt = Query::select()
868 .from(Alias::new("users"))
869 .column(ColumnRef::Asterisk)
870 .and_where(Expr::col(Alias::new("active")).eq(true))
871 .to_owned();
872 let exec = SelectExecution::<User>::new(stmt);
873 let result_stmt = exec.first();
874 let sql = result_stmt.to_string(PostgresQueryBuilder);
875 assert!(sql.contains("LIMIT"));
876 }
877
878 #[test]
879 fn test_execution_count() {
880 use reinhardt_query::prelude::{
881 Alias, Expr, PostgresQueryBuilder, Query, QueryStatementBuilder,
882 };
883
884 let stmt = Query::select()
885 .from(Alias::new("users"))
886 .column(ColumnRef::Asterisk)
887 .and_where(Expr::col(Alias::new("active")).eq(true))
888 .to_owned();
889 let exec = SelectExecution::<User>::new(stmt);
890 let result_stmt = exec.count();
891 let sql = result_stmt.to_string(PostgresQueryBuilder);
892 assert!(sql.contains("COUNT"));
893 }
894
895 #[test]
896 fn test_execution_exists() {
897 use reinhardt_query::prelude::{
898 Alias, Expr, PostgresQueryBuilder, Query, QueryStatementBuilder,
899 };
900
901 let stmt = Query::select()
902 .from(Alias::new("users"))
903 .column(ColumnRef::Asterisk)
904 .and_where(Expr::col(Alias::new("name")).eq("Alice"))
905 .to_owned();
906 let exec = SelectExecution::<User>::new(stmt);
907 let result_stmt = exec.exists();
908 let sql = result_stmt.to_string(PostgresQueryBuilder);
909 assert!(sql.contains("EXISTS"));
910 }
911
912 #[test]
913 fn test_load_options() {
914 let options = QueryOptions::new()
915 .add_option(LoadOption::JoinedLoad("profile".to_string()))
916 .add_option(LoadOption::Defer("password".to_string()));
917
918 let comments = options.to_sql_comments();
919 assert!(comments.contains("joinedload(profile)"));
920 assert!(comments.contains("defer(password)"));
921 }
922
923 #[test]
924 fn test_load_only() {
925 let option = LoadOption::LoadOnly(vec!["id".to_string(), "name".to_string()]);
926 let comment = option.to_sql_comment();
927 assert!(comment.contains("load_only(id, name)"));
928 }
929
930 #[rstest]
931 #[case::zero(0u64, 0i64)]
932 #[case::one(1u64, 1i64)]
933 #[case::i64_max(i64::MAX as u64, i64::MAX)]
934 #[test]
935 fn test_big_unsigned_to_query_value_within_range(#[case] input: u64, #[case] expected: i64) {
936 let value = reinhardt_query::value::Value::BigUnsigned(Some(input));
938
939 let result = convert_value_to_query_value(value);
941
942 assert!(matches!(result, QueryValue::Int(v) if v == expected));
944 }
945
946 #[rstest]
947 #[case::i64_max_plus_one(i64::MAX as u64 + 1)]
948 #[case::u64_max(u64::MAX)]
949 #[test]
950 fn test_big_unsigned_overflow_clamps_to_i64_max(#[case] input: u64) {
951 let value = reinhardt_query::value::Value::BigUnsigned(Some(input));
953
954 let result = convert_value_to_query_value(value);
956
957 assert!(matches!(result, QueryValue::Int(v) if v == i64::MAX));
959 }
960
961 #[rstest]
962 #[test]
963 fn test_big_unsigned_none_converts_to_null() {
964 let value = reinhardt_query::value::Value::BigUnsigned(None);
966
967 let result = convert_value_to_query_value(value);
969
970 assert!(matches!(result, QueryValue::Null));
972 }
973}