1use std::marker::PhantomData;
10
11use crate::scope::{Concat, Cons, MaybeNull, Nil, Table, WrapNullable};
12
13mod sql_type {
14 pub trait Sealed {}
19}
20
21pub trait SqlType: 'static + sql_type::Sealed {
24 type Native;
25}
26
27#[derive(Debug, Clone)]
31pub(crate) enum ExprKind {
32 Column {
33 table: &'static str,
34 name: &'static str,
35 },
36 Value(Value),
37 BinOp {
38 op: BinOp,
39 lhs: Box<ExprKind>,
40 rhs: Box<ExprKind>,
41 },
42 And(Box<ExprKind>, Box<ExprKind>),
43 Or(Box<ExprKind>, Box<ExprKind>),
44 Always(bool),
47 Not(Box<ExprKind>),
48 IsNull {
51 expr: Box<ExprKind>,
52 negated: bool,
53 },
54 InList {
57 expr: Box<ExprKind>,
58 values: Vec<ExprKind>,
59 },
60 Exists {
64 body: Box<crate::select::SelectBody>,
65 selection: Vec<crate::render::SelectItem>,
66 negated: bool,
67 },
68 Template {
73 head: String,
74 rest: Vec<(ExprKind, String)>,
75 },
76 Cast {
80 expr: Box<ExprKind>,
81 target: CastTarget,
82 },
83 Func {
89 name: &'static str,
90 arg: Option<Box<ExprKind>>,
91 },
92 Window {
98 func: &'static str,
99 partition_by: Vec<ExprKind>,
100 order_by: Vec<(ExprKind, SortDir)>,
101 },
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub enum SortDir {
109 Asc,
110 Desc,
111}
112
113#[doc(hidden)]
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub enum CastTarget {
118 BigInt,
119 Double,
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123pub enum BinOp {
124 Eq,
125 Ne,
126 Lt,
127 Lte,
128 Gt,
129 Gte,
130 Like,
131}
132
133#[derive(Debug, Clone, PartialEq)]
138pub enum Value {
139 I32(i32),
140 I64(i64),
141 F64(f64),
142 Text(String),
143 Bool(bool),
144 Bytes(Vec<u8>),
145 NullI32,
151 NullI64,
152 NullF64,
153 NullText,
154 NullBool,
155 NullBytes,
156 #[cfg(feature = "chrono")]
157 Timestamptz(chrono::DateTime<chrono::Utc>),
158 #[cfg(feature = "chrono")]
159 NullTimestamptz,
160 #[cfg(feature = "chrono")]
161 Date(chrono::NaiveDate),
162 #[cfg(feature = "chrono")]
163 NullDate,
164 #[cfg(feature = "uuid")]
165 Uuid(uuid::Uuid),
166 #[cfg(feature = "uuid")]
167 NullUuid,
168 #[cfg(feature = "decimal")]
169 Numeric(rust_decimal::Decimal),
170 #[cfg(feature = "decimal")]
171 NullNumeric,
172 Placeholder(&'static str),
178}
179
180impl Value {
181 pub fn type_name(&self) -> &'static str {
185 match self {
186 Value::I32(_) | Value::NullI32 => "Integer",
187 Value::I64(_) | Value::NullI64 => "BigInt",
188 Value::F64(_) | Value::NullF64 => "Real",
189 Value::Text(_) | Value::NullText => "Text",
190 Value::Bool(_) | Value::NullBool => "Bool",
191 Value::Bytes(_) | Value::NullBytes => "Bytes",
192 Value::Placeholder(_) => "placeholder",
193 #[cfg(feature = "chrono")]
194 Value::Timestamptz(_) | Value::NullTimestamptz => "Timestamptz",
195 #[cfg(feature = "chrono")]
196 Value::Date(_) | Value::NullDate => "Date",
197 #[cfg(feature = "uuid")]
198 Value::Uuid(_) | Value::NullUuid => "Uuid",
199 #[cfg(feature = "decimal")]
200 Value::Numeric(_) | Value::NullNumeric => "Numeric",
201 }
202 }
203}
204
205macro_rules! value_from {
206 ($ty:ty, $variant:ident) => {
207 impl From<$ty> for Value {
208 fn from(v: $ty) -> Self {
209 Value::$variant(v)
210 }
211 }
212 };
213}
214value_from!(i32, I32);
215value_from!(i64, I64);
216value_from!(f64, F64);
217value_from!(String, Text);
218value_from!(bool, Bool);
219value_from!(Vec<u8>, Bytes);
220#[cfg(feature = "chrono")]
221value_from!(chrono::DateTime<chrono::Utc>, Timestamptz);
222#[cfg(feature = "chrono")]
223value_from!(chrono::NaiveDate, Date);
224#[cfg(feature = "uuid")]
225value_from!(uuid::Uuid, Uuid);
226#[cfg(feature = "decimal")]
227value_from!(rust_decimal::Decimal, Numeric);
228
229impl From<&str> for Value {
230 fn from(v: &str) -> Self {
231 Value::Text(v.to_string())
232 }
233}
234
235pub struct Expr<Req, S: SqlType> {
242 pub(crate) kind: ExprKind,
243 _marker: PhantomData<fn() -> (Req, S)>,
244}
245
246impl<Req, S: SqlType> Expr<Req, S> {
247 pub(crate) fn from_kind(kind: ExprKind) -> Self {
248 Expr {
249 kind,
250 _marker: PhantomData,
251 }
252 }
253}
254
255impl<Req, S: SqlType> Clone for Expr<Req, S> {
258 fn clone(&self) -> Self {
259 Expr::from_kind(self.kind.clone())
260 }
261}
262
263#[diagnostic::on_unimplemented(
267 message = "`{Self}` isn't a SQL expression",
268 label = "a column, a literal, an aggregate, or a `sql!{{}}` fragment is; a `label!` name is not",
269 note = "an `Option` isn't one either: asking about NULL is `.is_null()`, and assigning it is `null::<Text>()` — `= NULL` is never true in SQL"
270)]
271pub trait IntoExpr {
272 type Sql: SqlType;
278 type Req;
279 fn into_expr(self) -> Expr<Self::Req, Self::Sql>;
280}
281
282impl<Req, S: SqlType> IntoExpr for Expr<Req, S> {
283 type Sql = S;
284 type Req = Req;
285 fn into_expr(self) -> Expr<Req, S> {
286 self
287 }
288}
289
290#[diagnostic::on_unimplemented(
296 message = "a `{Self}` expression can't be assigned to a `{Column}` column",
297 label = "the value has to fit the column: the same type, a narrower number, or a non-null value for a nullable column"
298)]
299pub trait AssignsTo<Column: SqlType>: SqlType {}
300
301impl<T: SqlType> AssignsTo<T> for T {}
302impl<T: SqlType> AssignsTo<crate::scope::Nullable<T>> for T {}
303
304mod writable {
305 pub trait Sealed {}
309}
310
311#[doc(hidden)]
312pub use writable::Sealed as WritableSealed;
313
314#[diagnostic::on_unimplemented(
318 message = "`{Self}` isn't a column a statement can assign to",
319 label = "a primary-key or generated column is the database's to write, which is why `*Update` leaves it out too"
320)]
321pub trait Writable: WritableSealed {}
322
323pub trait ColumnKey: crate::row::Spelled + Copy + 'static {
329 type Table: Table;
330 type Sql: SqlType;
331}
332
333pub struct Column<C: ColumnKey>(PhantomData<C>);
340
341impl<C: ColumnKey> Column<C> {
342 pub const fn new() -> Self {
343 Column(PhantomData)
344 }
345}
346
347impl<C: ColumnKey> Default for Column<C> {
348 fn default() -> Self {
349 Self::new()
350 }
351}
352
353impl<C: ColumnKey> Clone for Column<C> {
354 fn clone(&self) -> Self {
355 *self
356 }
357}
358impl<C: ColumnKey> Copy for Column<C> {}
359
360impl<C: ColumnKey> IntoExpr for Column<C> {
361 type Sql = C::Sql;
362 type Req = Cons<C::Table, Nil>;
363 fn into_expr(self) -> Expr<Self::Req, C::Sql> {
364 Expr::from_kind(ExprKind::Column {
365 table: <C::Table as Table>::NAME,
366 name: <C as crate::row::Named>::NAME,
367 })
368 }
369}
370
371pub struct Keyed<K, Req, S: SqlType> {
376 pub(crate) kind: ExprKind,
377 _marker: PhantomData<fn() -> (K, Req, S)>,
378}
379
380impl<K, Req, S: SqlType> Keyed<K, Req, S> {
381 pub(crate) fn from_kind(kind: ExprKind) -> Self {
382 Keyed {
383 kind,
384 _marker: PhantomData,
385 }
386 }
387}
388
389impl<K, Req, S: SqlType> Clone for Keyed<K, Req, S> {
390 fn clone(&self) -> Self {
391 Keyed::from_kind(self.kind.clone())
392 }
393}
394
395impl<K, Req, S: SqlType> IntoExpr for Keyed<K, Req, S> {
396 type Sql = S;
397 type Req = Req;
398 fn into_expr(self) -> Expr<Req, S> {
399 Expr::from_kind(self.kind)
400 }
401}
402
403#[diagnostic::on_unimplemented(
411 message = "`{Self}` and `{Other}` aren't comparable",
412 label = "both sides of a comparison must be the same SQL type, or two numeric ones",
413 note = "nullability doesn't matter here: a `Nullable<T>` compares with a `T`"
414)]
415pub trait Comparable<Other: SqlType>: SqlType {}
416
417impl<T: SqlType> Comparable<T> for T {}
418impl<T: SqlType> Comparable<crate::scope::Nullable<T>> for T {}
419impl<T: SqlType> Comparable<T> for crate::scope::Nullable<T> {}
420
421macro_rules! comparable_across {
424 ($($a:ty => $b:ty),+ $(,)?) => {
425 $(
426 impl Comparable<$b> for $a {}
427 impl Comparable<$b> for crate::scope::Nullable<$a> {}
428 impl Comparable<crate::scope::Nullable<$b>> for $a {}
429 impl Comparable<crate::scope::Nullable<$b>> for crate::scope::Nullable<$a> {}
430 )+
431 };
432}
433comparable_across!(
434 Integer => BigInt,
435 BigInt => Integer,
436 Integer => Real,
437 Real => Integer,
438 BigInt => Real,
439 Real => BigInt,
440);
441
442macro_rules! assigns_across {
443 ($($from:ty => $to:ty),+ $(,)?) => {
444 $(
445 impl AssignsTo<$to> for $from {}
446 impl AssignsTo<crate::scope::Nullable<$to>> for $from {}
447 impl AssignsTo<crate::scope::Nullable<$to>> for crate::scope::Nullable<$from> {}
448 )+
449 };
450}
451assigns_across!(
454 Integer => BigInt,
455 Integer => Real,
456 BigInt => Real,
457);
458
459#[cfg(feature = "decimal")]
460assigns_across!(
461 Integer => Numeric,
462 BigInt => Numeric,
463 Real => Numeric,
464);
465
466#[cfg(feature = "decimal")]
470comparable_across!(
471 Numeric => Integer,
472 Integer => Numeric,
473 Numeric => BigInt,
474 BigInt => Numeric,
475 Numeric => Real,
476 Real => Numeric,
477);
478
479pub trait LabelKey: crate::row::Spelled + Copy + 'static {}
483
484pub type Declared<Req, S> = Keyed<crate::row::Anon, Req, S>;
489
490impl<Req, S: SqlType> Expr<Req, S> {
491 pub fn decodes_as<S2: SqlType>(self) -> Declared<Req, S2> {
495 Keyed {
496 kind: self.kind,
497 _marker: PhantomData,
498 }
499 }
500}
501
502pub struct Labeled<K, Inner> {
505 pub(crate) inner: Inner,
506 _key: PhantomData<fn() -> K>,
507}
508
509impl<K, Inner: Clone> Clone for Labeled<K, Inner> {
510 fn clone(&self) -> Self {
511 Labeled::new(self.inner.clone())
512 }
513}
514
515impl<K, Inner> Labeled<K, Inner> {
516 pub(crate) fn new(inner: Inner) -> Self {
517 Labeled {
518 inner,
519 _key: PhantomData,
520 }
521 }
522}
523
524pub trait LabelExt: Sized {
528 fn label<K: LabelKey>(self, _key: K) -> Labeled<K, Self> {
529 Labeled::new(self)
530 }
531}
532impl<C: ColumnKey> LabelExt for Column<C> {}
533impl<K, Req, S: SqlType> LabelExt for Keyed<K, Req, S> {}
534impl<Req, S: SqlType> LabelExt for Expr<Req, S> {}
539
540#[allow(clippy::wrong_self_convention)]
547pub trait ExprMethods: IntoExpr + Sized {
548 fn eq<Rhs: IntoExpr>(self, rhs: Rhs) -> Expr<<Self::Req as Concat<Rhs::Req>>::Output, Bool>
549 where
550 Self::Sql: Comparable<Rhs::Sql>,
551 Self::Req: Concat<Rhs::Req>,
552 {
553 bin_op(BinOp::Eq, self, rhs)
554 }
555
556 fn ne<Rhs: IntoExpr>(self, rhs: Rhs) -> Expr<<Self::Req as Concat<Rhs::Req>>::Output, Bool>
557 where
558 Self::Sql: Comparable<Rhs::Sql>,
559 Self::Req: Concat<Rhs::Req>,
560 {
561 bin_op(BinOp::Ne, self, rhs)
562 }
563
564 fn lt<Rhs: IntoExpr>(self, rhs: Rhs) -> Expr<<Self::Req as Concat<Rhs::Req>>::Output, Bool>
565 where
566 Self::Sql: Comparable<Rhs::Sql>,
567 Self::Req: Concat<Rhs::Req>,
568 {
569 bin_op(BinOp::Lt, self, rhs)
570 }
571
572 fn lte<Rhs: IntoExpr>(self, rhs: Rhs) -> Expr<<Self::Req as Concat<Rhs::Req>>::Output, Bool>
573 where
574 Self::Sql: Comparable<Rhs::Sql>,
575 Self::Req: Concat<Rhs::Req>,
576 {
577 bin_op(BinOp::Lte, self, rhs)
578 }
579
580 fn gt<Rhs: IntoExpr>(self, rhs: Rhs) -> Expr<<Self::Req as Concat<Rhs::Req>>::Output, Bool>
581 where
582 Self::Sql: Comparable<Rhs::Sql>,
583 Self::Req: Concat<Rhs::Req>,
584 {
585 bin_op(BinOp::Gt, self, rhs)
586 }
587
588 fn gte<Rhs: IntoExpr>(self, rhs: Rhs) -> Expr<<Self::Req as Concat<Rhs::Req>>::Output, Bool>
589 where
590 Self::Sql: Comparable<Rhs::Sql>,
591 Self::Req: Concat<Rhs::Req>,
592 {
593 bin_op(BinOp::Gte, self, rhs)
594 }
595
596 fn is_null(self) -> Expr<Self::Req, Bool> {
599 Expr::from_kind(ExprKind::IsNull {
600 expr: Box::new(self.into_expr().kind),
601 negated: false,
602 })
603 }
604
605 fn is_not_null(self) -> Expr<Self::Req, Bool> {
606 Expr::from_kind(ExprKind::IsNull {
607 expr: Box::new(self.into_expr().kind),
608 negated: true,
609 })
610 }
611
612 fn and<Rhs: IntoExpr>(self, rhs: Rhs) -> Expr<<Self::Req as Concat<Rhs::Req>>::Output, Bool>
617 where
618 Self::Sql: BoolLike,
619 Rhs::Sql: BoolLike,
620 Self::Req: Concat<Rhs::Req>,
621 {
622 Expr::from_kind(ExprKind::And(
623 Box::new(self.into_expr().kind),
624 Box::new(rhs.into_expr().kind),
625 ))
626 }
627
628 fn or<Rhs: IntoExpr>(self, rhs: Rhs) -> Expr<<Self::Req as Concat<Rhs::Req>>::Output, Bool>
630 where
631 Self::Sql: BoolLike,
632 Rhs::Sql: BoolLike,
633 Self::Req: Concat<Rhs::Req>,
634 {
635 Expr::from_kind(ExprKind::Or(
636 Box::new(self.into_expr().kind),
637 Box::new(rhs.into_expr().kind),
638 ))
639 }
640
641 fn like<Rhs: IntoExpr>(self, rhs: Rhs) -> Expr<<Self::Req as Concat<Rhs::Req>>::Output, Bool>
645 where
646 Self::Sql: TextLike,
647 Rhs::Sql: TextLike,
648 Self::Req: Concat<Rhs::Req>,
649 {
650 bin_op(BinOp::Like, self, rhs)
651 }
652
653 fn is_in<I>(self, values: I) -> Expr<Self::Req, Bool>
660 where
661 I: IntoIterator,
662 I::Item: IntoExpr<Req = Nil>,
663 Self::Sql: Comparable<<I::Item as IntoExpr>::Sql>,
664 {
665 let values: Vec<ExprKind> = values.into_iter().map(|v| v.into_expr().kind).collect();
666 if values.is_empty() {
668 return Expr::from_kind(ExprKind::Always(false));
669 }
670 Expr::from_kind(ExprKind::InList {
671 expr: Box::new(self.into_expr().kind),
672 values,
673 })
674 }
675}
676
677pub fn any_of<Req, C: IntoExpr<Req = Req>>(conds: impl IntoIterator<Item = C>) -> Expr<Req, Bool>
683where
684 C::Sql: BoolLike,
685{
686 combine(conds, false)
687}
688
689pub fn all_of<Req, C: IntoExpr<Req = Req>>(conds: impl IntoIterator<Item = C>) -> Expr<Req, Bool>
692where
693 C::Sql: BoolLike,
694{
695 combine(conds, true)
696}
697
698fn combine<Req, C: IntoExpr<Req = Req>>(
699 conds: impl IntoIterator<Item = C>,
700 all: bool,
701) -> Expr<Req, Bool>
702where
703 C::Sql: BoolLike,
704{
705 Expr::from_kind(fold_conditions(
706 conds.into_iter().map(|c| c.into_expr().kind),
707 all,
708 ))
709}
710
711pub(crate) fn fold_conditions(kinds: impl IntoIterator<Item = ExprKind>, all: bool) -> ExprKind {
716 let mut folded: Option<ExprKind> = None;
717 for kind in kinds {
718 folded = Some(match folded {
719 None => kind,
720 Some(acc) if all => ExprKind::And(Box::new(acc), Box::new(kind)),
721 Some(acc) => ExprKind::Or(Box::new(acc), Box::new(kind)),
722 });
723 }
724 folded.unwrap_or(ExprKind::Always(all))
725}
726
727impl<T: IntoExpr> ExprMethods for T {}
728
729fn bin_op<Lhs, Rhs>(
730 op: BinOp,
731 lhs: Lhs,
732 rhs: Rhs,
733) -> Expr<<Lhs::Req as Concat<Rhs::Req>>::Output, Bool>
734where
735 Lhs: IntoExpr,
736 Rhs: IntoExpr,
737 Lhs::Req: Concat<Rhs::Req>,
738{
739 Expr::from_kind(ExprKind::BinOp {
740 op,
741 lhs: Box::new(lhs.into_expr().kind),
742 rhs: Box::new(rhs.into_expr().kind),
743 })
744}
745
746#[diagnostic::on_unimplemented(
750 message = "`LIKE` needs a text expression, and `{Self}` isn't one",
751 label = "only `Text` and `Nullable<Text>` columns and expressions accept `.like(..)`"
752)]
753pub trait TextLike: SqlType {}
754
755impl TextLike for Text {}
756impl TextLike for crate::scope::Nullable<Text> {}
757
758#[diagnostic::on_unimplemented(
762 message = "a condition has to be a boolean expression, and `{Self}` isn't one",
763 label = "expected `Bool` or `Nullable<Bool>`"
764)]
765pub trait BoolLike: SqlType {}
766
767impl BoolLike for Bool {}
768impl BoolLike for crate::scope::Nullable<Bool> {}
769
770impl<Req, S: BoolLike> std::ops::Not for Expr<Req, S> {
776 type Output = Expr<Req, S>;
777 fn not(self) -> Self::Output {
778 Expr::from_kind(ExprKind::Not(Box::new(self.kind)))
779 }
780}
781
782impl<K, Req, S: BoolLike> std::ops::Not for Keyed<K, Req, S> {
783 type Output = Keyed<K, Req, S>;
784 fn not(self) -> Self::Output {
785 Keyed::from_kind(ExprKind::Not(Box::new(self.kind)))
786 }
787}
788
789impl<C: ColumnKey> std::ops::Not for Column<C>
790where
791 C::Sql: BoolLike,
792{
793 type Output = Expr<Cons<C::Table, Nil>, C::Sql>;
794 fn not(self) -> Self::Output {
795 Expr::from_kind(ExprKind::Not(Box::new(self.into_expr().kind)))
796 }
797}
798
799pub trait NullValue: SqlType {
802 const NULL_VALUE: Value;
803}
804
805pub fn null<S: NullValue>() -> Expr<Nil, crate::scope::Nullable<S>> {
810 Expr::from_kind(ExprKind::Value(S::NULL_VALUE))
811}
812
813mod raw_arg {
819 pub trait Sealed {}
824 impl<T: super::IntoExpr> Sealed for T {}
825}
826
827macro_rules! sql_leaf_type {
828 ($name:ident, $native:ty, $null_variant:ident) => {
829 pub struct $name;
830
831 impl sql_type::Sealed for $name {}
832
833 impl SqlType for $name {
834 type Native = $native;
835 }
836
837 impl crate::scope::wrap::Sealed<MaybeNull> for $name {}
838
839 impl crate::scope::WrapNullable<MaybeNull> for $name {
840 type Output = crate::scope::Nullable<$name>;
841 }
842
843 impl IntoExpr for $native {
844 type Sql = $name;
845 type Req = Nil;
846 fn into_expr(self) -> Expr<Nil, $name> {
847 Expr::from_kind(ExprKind::Value(Value::from(self)))
848 }
849 }
850
851 impl crate::select::SingleColumn for $native {}
852 impl crate::select::SingleColumn for ::std::option::Option<$native> {}
853
854 impl NullValue for $name {
855 const NULL_VALUE: Value = Value::$null_variant;
856 }
857
858 impl crate::insert::IntoColumnValue<$native> for $native {
859 fn into_column_value(self) -> $native {
860 self
861 }
862 }
863
864 impl crate::insert::IntoColumnValue<::std::option::Option<$native>> for $native {
865 fn into_column_value(self) -> ::std::option::Option<$native> {
866 ::std::option::Option::Some(self)
867 }
868 }
869
870 impl crate::insert::IntoColumnValue<::std::option::Option<$native>>
871 for ::std::option::Option<$native>
872 {
873 fn into_column_value(self) -> ::std::option::Option<$native> {
874 self
875 }
876 }
877
878 impl crate::insert::IntoColumnValue<crate::insert::Defaultable<$native>> for $native {
879 fn into_column_value(self) -> crate::insert::Defaultable<$native> {
880 crate::insert::Defaultable::Value(self)
881 }
882 }
883
884 impl crate::insert::IntoColumnValue<crate::insert::Defaultable<$native>>
885 for ::std::option::Option<$native>
886 {
887 fn into_column_value(self) -> crate::insert::Defaultable<$native> {
888 match self {
889 ::std::option::Option::Some(v) => crate::insert::Defaultable::Value(v),
890 ::std::option::Option::None => crate::insert::Defaultable::Default,
891 }
892 }
893 }
894
895 impl
896 crate::insert::IntoColumnValue<
897 crate::insert::Defaultable<::std::option::Option<$native>>,
898 > for $native
899 {
900 fn into_column_value(
901 self,
902 ) -> crate::insert::Defaultable<::std::option::Option<$native>> {
903 crate::insert::Defaultable::Value(::std::option::Option::Some(self))
904 }
905 }
906
907 impl
908 crate::insert::IntoColumnValue<
909 crate::insert::Defaultable<::std::option::Option<$native>>,
910 > for ::std::option::Option<$native>
911 {
912 fn into_column_value(
913 self,
914 ) -> crate::insert::Defaultable<::std::option::Option<$native>> {
915 match self {
916 ::std::option::Option::Some(v) => {
917 crate::insert::Defaultable::Value(::std::option::Option::Some(v))
918 }
919 ::std::option::Option::None => crate::insert::Defaultable::Default,
920 }
921 }
922 }
923
924 impl raw_arg::Sealed for ::std::option::Option<$native> {}
925
926 impl RawArg for ::std::option::Option<$native> {
927 type Req = Nil;
928 fn into_raw_arg(self) -> RawSlot {
929 RawSlot(ExprKind::Value(Value::from(self)))
930 }
931 }
932
933 impl crate::row::SameShape<$native> for $native {}
934 impl crate::row::SameShape<::std::option::Option<$native>>
935 for ::std::option::Option<$native>
936 {
937 }
938
939 impl From<::std::option::Option<$native>> for Value {
942 fn from(v: ::std::option::Option<$native>) -> Self {
943 match v {
944 ::std::option::Option::Some(x) => Value::from(x),
945 ::std::option::Option::None => Value::$null_variant,
946 }
947 }
948 }
949 };
950}
951
952sql_leaf_type!(Integer, i32, NullI32);
953sql_leaf_type!(BigInt, i64, NullI64);
954sql_leaf_type!(Real, f64, NullF64);
955sql_leaf_type!(Text, String, NullText);
956sql_leaf_type!(Bool, bool, NullBool);
957sql_leaf_type!(Bytes, Vec<u8>, NullBytes);
958
959#[cfg(feature = "chrono")]
963sql_leaf_type!(Timestamptz, chrono::DateTime<chrono::Utc>, NullTimestamptz);
964#[cfg(feature = "chrono")]
965sql_leaf_type!(Date, chrono::NaiveDate, NullDate);
966#[cfg(feature = "uuid")]
967sql_leaf_type!(Uuid, uuid::Uuid, NullUuid);
968#[cfg(feature = "decimal")]
969sql_leaf_type!(Numeric, rust_decimal::Decimal, NullNumeric);
970
971impl IntoExpr for &String {
974 type Sql = Text;
975 type Req = Nil;
976 fn into_expr(self) -> Expr<Nil, Text> {
977 Expr::from_kind(ExprKind::Value(Value::Text(self.clone())))
978 }
979}
980
981impl IntoExpr for &str {
982 type Sql = Text;
983 type Req = Nil;
984 fn into_expr(self) -> Expr<Nil, Text> {
985 Expr::from_kind(ExprKind::Value(Value::Text(self.to_string())))
986 }
987}
988
989macro_rules! text_column_value {
992 ($borrowed:ty) => {
993 impl crate::insert::IntoColumnValue<String> for $borrowed {
994 fn into_column_value(self) -> String {
995 self.to_string()
996 }
997 }
998
999 impl crate::insert::IntoColumnValue<Option<String>> for $borrowed {
1000 fn into_column_value(self) -> Option<String> {
1001 Some(self.to_string())
1002 }
1003 }
1004
1005 impl crate::insert::IntoColumnValue<crate::insert::Defaultable<String>> for $borrowed {
1006 fn into_column_value(self) -> crate::insert::Defaultable<String> {
1007 crate::insert::Defaultable::Value(self.to_string())
1008 }
1009 }
1010
1011 impl crate::insert::IntoColumnValue<crate::insert::Defaultable<Option<String>>>
1012 for $borrowed
1013 {
1014 fn into_column_value(self) -> crate::insert::Defaultable<Option<String>> {
1015 crate::insert::Defaultable::Value(Some(self.to_string()))
1016 }
1017 }
1018 };
1019}
1020
1021text_column_value!(&str);
1022text_column_value!(&String);
1023
1024impl<S: SqlType> sql_type::Sealed for crate::scope::Nullable<S> {}
1025
1026impl<S: SqlType> SqlType for crate::scope::Nullable<S> {
1027 type Native = Option<S::Native>;
1028}
1029
1030crate::row::expr_key!(
1031 Count,
1032 HasCount,
1033 count,
1034 "The identity a selected `count(*)` is filed under in a row.",
1035 'c',
1036 'o',
1037 'u',
1038 'n',
1039 't'
1040);
1041
1042pub(crate) fn count_item() -> crate::render::SelectItem {
1044 crate::render::SelectItem::bare(count_star())
1045}
1046
1047fn count_star() -> ExprKind {
1048 ExprKind::Func {
1049 name: "count",
1050 arg: None,
1051 }
1052}
1053
1054pub fn count() -> Keyed<Count, Nil, BigInt> {
1057 Keyed::from_kind(count_star())
1058}
1059
1060#[diagnostic::on_unimplemented(
1068 message = "`min`/`max` need an ordered expression, and `{Self}` isn't one",
1069 label = "numbers, text, and dates/timestamps are ordered; booleans, bytes and UUIDs are not — `bool_or`/`bool_and` are the aggregate a flag wants, and aren't built yet"
1070)]
1071pub trait Ordered: SqlType + WrapNullable<MaybeNull> {}
1072
1073impl Ordered for Integer {}
1074impl Ordered for BigInt {}
1075impl Ordered for Real {}
1076impl Ordered for Text {}
1077#[cfg(feature = "decimal")]
1078impl Ordered for Numeric {}
1079#[cfg(feature = "chrono")]
1080impl Ordered for Timestamptz {}
1081#[cfg(feature = "chrono")]
1082impl Ordered for Date {}
1083
1084impl<S: Ordered> Ordered for crate::scope::Nullable<S> {}
1087
1088#[diagnostic::on_unimplemented(
1094 message = "`sum`/`avg` need a numeric expression, and `{Self}` isn't one",
1095 label = "only `Integer`, `BigInt` and `Real` columns and expressions (and `Numeric`, with the `decimal` feature) can be summed or averaged"
1096)]
1097pub trait Summable: SqlType {
1098 type Sum: SqlType;
1099 const SUM_CAST: Option<CastTarget>;
1100 const AVG_CAST: Option<CastTarget> = Some(CastTarget::Double);
1101}
1102impl Summable for Integer {
1103 type Sum = crate::scope::Nullable<BigInt>;
1104 const SUM_CAST: Option<CastTarget> = None;
1105}
1106impl Summable for BigInt {
1107 type Sum = crate::scope::Nullable<BigInt>;
1108 const SUM_CAST: Option<CastTarget> = Some(CastTarget::BigInt);
1109}
1110impl Summable for Real {
1111 type Sum = crate::scope::Nullable<Real>;
1112 const SUM_CAST: Option<CastTarget> = None;
1113 const AVG_CAST: Option<CastTarget> = None;
1114}
1115#[cfg(feature = "decimal")]
1118impl Summable for Numeric {
1119 type Sum = crate::scope::Nullable<Numeric>;
1120 const SUM_CAST: Option<CastTarget> = None;
1121 const AVG_CAST: Option<CastTarget> = Some(CastTarget::Double);
1122}
1123impl<T: Summable> Summable for crate::scope::Nullable<T> {
1124 type Sum = T::Sum;
1125 const SUM_CAST: Option<CastTarget> = T::SUM_CAST;
1126 const AVG_CAST: Option<CastTarget> = T::AVG_CAST;
1127}
1128
1129pub struct Agg<Op, C>(PhantomData<fn() -> (Op, C)>);
1133
1134#[doc(hidden)]
1141impl<Op, C: crate::row::Named> crate::row::NamedSealed for Agg<Op, C> {}
1142
1143#[doc(hidden)]
1144impl<Op: 'static, C: crate::row::Named + 'static> crate::row::Named for Agg<Op, C> {
1145 type Name = <C as crate::row::Named>::Name;
1146 const NAME: &'static str = <C as crate::row::Named>::NAME;
1147}
1148
1149#[doc(hidden)]
1150impl<Op: 'static, C: crate::row::Spelled + 'static> crate::row::Spelled for Agg<Op, C> {}
1151
1152fn aggregate_kind<C: ColumnKey>(name: &'static str, cast: Option<CastTarget>) -> ExprKind {
1153 let call = ExprKind::Func {
1154 name,
1155 arg: Some(Box::new(ExprKind::Column {
1156 table: <C::Table as Table>::NAME,
1157 name: <C as crate::row::Named>::NAME,
1158 })),
1159 };
1160 match cast {
1161 Some(target) => ExprKind::Cast {
1162 expr: Box::new(call),
1163 target,
1164 },
1165 None => call,
1166 }
1167}
1168
1169macro_rules! aggregate {
1170 ($op:ident, $func:ident, $sql:literal, $out:ty, $bound:path, $cast:expr, $doc:literal) => {
1171 #[doc = $doc]
1172 pub struct $op;
1173
1174 #[doc = $doc]
1175 pub fn $func<C: ColumnKey>(
1176 _column: Column<C>,
1177 ) -> Keyed<Agg<$op, C>, Cons<C::Table, Nil>, $out>
1178 where
1179 C::Sql: $bound,
1180 $out: SqlType,
1181 {
1182 Keyed::from_kind(aggregate_kind::<C>($sql, $cast))
1183 }
1184 };
1185}
1186
1187aggregate!(
1188 Sum,
1189 sum,
1190 "sum",
1191 <C::Sql as Summable>::Sum,
1192 Summable,
1193 <C::Sql as Summable>::SUM_CAST,
1194 "`sum(column)`. NULL over zero rows, so the result is always nullable."
1195);
1196aggregate!(
1197 Min,
1198 min,
1199 "min",
1200 <C::Sql as WrapNullable<MaybeNull>>::Output,
1201 Ordered,
1202 None,
1203 "`min(column)`. NULL over zero rows."
1204);
1205aggregate!(
1206 Max,
1207 max,
1208 "max",
1209 <C::Sql as WrapNullable<MaybeNull>>::Output,
1210 Ordered,
1211 None,
1212 "`max(column)`. NULL over zero rows."
1213);
1214aggregate!(
1215 Avg,
1216 avg,
1217 "avg",
1218 crate::scope::Nullable<Real>,
1219 Summable,
1220 <C::Sql as Summable>::AVG_CAST,
1221 "`avg(column)`. NULL over zero rows."
1222);
1223aggregate!(
1224 CountOf,
1225 count_of,
1226 "count",
1227 BigInt,
1228 SqlType,
1229 None,
1230 "`count(column)` — non-NULL values, unlike `count()`'s `count(*)` rows."
1231);
1232
1233pub trait RawArg: raw_arg::Sealed {
1238 type Req;
1239 #[doc(hidden)]
1240 fn into_raw_arg(self) -> RawSlot;
1241}
1242
1243impl<T: IntoExpr> RawArg for T {
1244 type Req = T::Req;
1245 fn into_raw_arg(self) -> RawSlot {
1246 RawSlot(self.into_expr().kind)
1247 }
1248}
1249
1250pub struct RawSlot(ExprKind);
1254
1255impl RawSlot {
1256 fn into_kind(self) -> ExprKind {
1257 self.0
1258 }
1259}
1260
1261#[diagnostic::on_unimplemented(
1264 message = "a `sql!` fragment takes at most 8 `?` slots",
1265 label = "split the fragment, or fold part of it into the builder"
1266)]
1267pub trait RawArgs {
1268 type Req;
1269 #[doc(hidden)]
1270 fn into_raw_args(self) -> Vec<RawSlot>;
1271}
1272
1273impl RawArgs for () {
1274 type Req = Nil;
1275 fn into_raw_args(self) -> Vec<RawSlot> {
1276 Vec::new()
1277 }
1278}
1279
1280macro_rules! raw_args_tuple {
1281 ($head:ident $(, $rest:ident)*) => {
1282 #[allow(non_snake_case)]
1283 impl<$head: RawArg $(, $rest: RawArg)*> RawArgs for ($head, $($rest,)*)
1284 where
1285 ($($rest,)*): RawArgs,
1286 $head::Req: Concat<<($($rest,)*) as RawArgs>::Req>,
1287 {
1288 type Req = <$head::Req as Concat<<($($rest,)*) as RawArgs>::Req>>::Output;
1289 fn into_raw_args(self) -> Vec<RawSlot> {
1290 let ($head, $($rest,)*) = self;
1291 let mut kinds = ::std::vec![RawArg::into_raw_arg($head)];
1292 kinds.extend(RawArgs::into_raw_args(($($rest,)*)));
1293 kinds
1294 }
1295 }
1296 };
1297}
1298raw_args_tuple!(A);
1299raw_args_tuple!(A, B);
1300raw_args_tuple!(A, B, C);
1301raw_args_tuple!(A, B, C, D);
1302raw_args_tuple!(A, B, C, D, E);
1303raw_args_tuple!(A, B, C, D, E, F);
1304raw_args_tuple!(A, B, C, D, E, F, G);
1305raw_args_tuple!(A, B, C, D, E, F, G, H);
1306
1307#[doc(hidden)]
1311pub const fn placeholder_count(sql: &str) -> usize {
1312 let bytes = sql.as_bytes();
1313 let mut i = 0;
1314 let mut count = 0;
1315 while i < bytes.len() {
1316 if bytes[i] == b'?' {
1317 count += 1;
1318 }
1319 i += 1;
1320 }
1321 count
1322}
1323
1324#[doc(hidden)]
1329pub fn raw_expr<S: SqlType, Args: RawArgs>(
1330 sql: &'static str,
1331 args: Args,
1332) -> Declared<Args::Req, S> {
1333 Keyed::from_kind(template(sql, args.into_raw_args()))
1334}
1335
1336fn template(sql: &'static str, args: Vec<RawSlot>) -> ExprKind {
1340 let mut pieces: Vec<String> = vec![String::new()];
1341 for c in sql.chars() {
1342 match c {
1343 '?' => pieces.push(String::new()),
1344 c => pieces.last_mut().expect("one piece to start").push(c),
1345 }
1346 }
1347
1348 let mut pieces = pieces.into_iter();
1349 let head = pieces.next().unwrap_or_default();
1350 let rest = args
1351 .into_iter()
1352 .map(RawSlot::into_kind)
1353 .zip(pieces)
1354 .collect();
1355 ExprKind::Template { head, rest }
1356}
1357
1358#[doc(hidden)]
1365pub fn placeholder<S: SqlType>(name: &'static str) -> Expr<Nil, S> {
1366 Expr::from_kind(ExprKind::Value(Value::Placeholder(name)))
1367}