Skip to main content

drizzle_core/row/
mod.rs

1//! Type-safe row inference for query builders.
2//!
3//! Provides type-level machinery to infer the Rust return type from a query's
4//! selected columns, table, and joins — so `.all()` and `.get()` return the
5//! correct type without turbofish annotations.
6//!
7//! # Architecture
8//!
9//! ```rust
10//! # let _ = r####"
11//! .select(cols)    → Marker  (SelectStar | SelectCols<C> | SelectExpr)
12//! .from(table)     → R       (Marker + Table → row type via ResolveRow)
13//! .join(t2)        → R'      (Marker + R + JoinedTable → new R via AfterJoin)
14//! .all()           → Vec<R>  (R: FromDrizzleRow)
15//! # "####;
16//! ```
17
18// Driver-specific leaf FromDrizzleRow implementations
19#[cfg(feature = "libsql")]
20mod libsql;
21#[cfg(any(feature = "tokio-postgres", feature = "postgres-sync"))]
22mod postgres;
23#[cfg(feature = "rusqlite")]
24mod rusqlite;
25// Shared blanket impls for SQLite-flavored drivers whose cells are tagged
26// unions (rusqlite, libsql, turso). The driver-specific files above just
27// impl `SqliteValueRow` for their row type; everything else lives here.
28#[cfg(any(feature = "rusqlite", feature = "libsql", feature = "turso"))]
29pub(crate) mod sqlite_value;
30#[cfg(feature = "turso")]
31mod turso;
32
33use core::marker::PhantomData;
34
35use crate::error::DrizzleError;
36use crate::prelude::{String, Vec};
37use crate::{Cons, Nil};
38
39// =============================================================================
40// Select Target Markers
41// =============================================================================
42
43/// Marker: `SELECT *` — R inferred from the table, grows with joins.
44#[derive(Debug, Clone, Copy, Default)]
45pub struct SelectStar;
46
47/// Marker: explicit columns — R inferred from column value types, stable across joins.
48#[derive(Debug, Clone, Copy, Default)]
49pub struct SelectCols<Cols>(PhantomData<Cols>);
50
51/// Marker: raw SQL or untyped expression — R must be user-specified.
52#[derive(Debug, Clone, Copy, Default)]
53pub struct SelectExpr;
54
55/// Marker: explicit row model target chosen by user.
56#[derive(Debug, Clone, Copy, Default)]
57pub struct SelectAs<R>(PhantomData<R>);
58
59/// Marker wrapper that carries in-scope tables.
60#[derive(Debug, Clone, Copy, Default)]
61pub struct Scoped<Marker, Scope>(PhantomData<(Marker, Scope)>);
62
63/// Declares the set of tables a custom row model requires.
64pub trait SelectRequiredTables {
65    type RequiredTables;
66}
67
68/// Type-level witness that a table exists at the head of a scope list.
69#[derive(Debug, Clone, Copy, Default)]
70pub struct ScopeHere;
71
72/// Type-level witness that a table exists deeper in a scope list.
73#[derive(Debug, Clone, Copy, Default)]
74pub struct ScopeThere<Prev>(PhantomData<Prev>);
75
76/// Type-level table membership in a scope list.
77pub trait ScopeContains<Table, Witness> {}
78
79impl<Head, Tail> ScopeContains<Head, ScopeHere> for Cons<Head, Tail> {}
80
81impl<Head, Tail, Table, Witness> ScopeContains<Table, ScopeThere<Witness>> for Cons<Head, Tail> where
82    Tail: ScopeContains<Table, Witness>
83{
84}
85
86/// Required-table list satisfaction.
87pub trait ScopeSatisfies<Required, Proof> {}
88
89impl<Scope> ScopeSatisfies<Nil, ()> for Scope {}
90
91impl<Scope, Head, Tail, HeadProof, TailProof>
92    ScopeSatisfies<Cons<Head, Tail>, (HeadProof, TailProof)> for Scope
93where
94    Scope: ScopeContains<Head, HeadProof> + ScopeSatisfies<Tail, TailProof>,
95{
96}
97
98/// Marker-level required-table extraction.
99pub trait MarkerRequiredTables {
100    type RequiredTables;
101}
102
103impl MarkerRequiredTables for SelectStar {
104    type RequiredTables = Nil;
105}
106
107impl<Cols> MarkerRequiredTables for SelectCols<Cols> {
108    type RequiredTables = Nil;
109}
110
111impl MarkerRequiredTables for SelectExpr {
112    type RequiredTables = Nil;
113}
114
115impl<R> MarkerRequiredTables for SelectAs<R>
116where
117    R: SelectRequiredTables,
118{
119    type RequiredTables = R::RequiredTables;
120}
121
122/// Marker validation for a specific scope-satisfaction proof.
123#[diagnostic::on_unimplemented(
124    message = "selected row requires tables not present in the current query scope",
125    label = "add .join(...) entries for every table referenced by this selector",
126    note = "for aliased selectors, use the same alias type in #[from(...)] and .from(...)"
127)]
128///
129/// ```
130/// use drizzle_core::{Cons, Nil, Scoped, SelectAs, SelectRequiredTables};
131/// use drizzle_core::row::{MarkerScopeValidFor, ScopeHere};
132///
133/// struct Users;
134/// struct Model;
135///
136/// impl SelectRequiredTables for Model {
137///     type RequiredTables = Cons<Users, Nil>;
138/// }
139///
140/// type Good = Scoped<SelectAs<Model>, Cons<Users, Nil>>;
141///
142/// fn needs_valid<M: MarkerScopeValidFor<(ScopeHere, ())>>() {}
143///
144/// fn main() {
145///     needs_valid::<Good>();
146/// }
147/// ```
148///
149/// ```compile_fail
150/// use drizzle_core::{Cons, Nil, Scoped, SelectAs, SelectRequiredTables};
151/// use drizzle_core::row::{MarkerScopeValidFor, ScopeHere};
152///
153/// struct Users;
154/// struct Posts;
155/// struct Model;
156///
157/// impl SelectRequiredTables for Model {
158///     type RequiredTables = Cons<Users, Nil>;
159/// }
160///
161/// type Bad = Scoped<SelectAs<Model>, Cons<Posts, Nil>>;
162///
163/// fn needs_valid<M: MarkerScopeValidFor<(ScopeHere, ())>>() {}
164///
165/// fn main() {
166///     needs_valid::<Bad>();
167/// }
168/// ```
169pub trait MarkerScopeValidFor<Proof> {}
170
171impl<M, Scope, Proof> MarkerScopeValidFor<Proof> for Scoped<M, Scope>
172where
173    M: MarkerRequiredTables,
174    Scope: ScopeSatisfies<M::RequiredTables, Proof>,
175{
176}
177
178/// Proof marker for a column found in a SELECT source scope.
179#[doc(hidden)]
180pub struct ColumnScope<Table, Witness>(PhantomData<(Table, Witness)>);
181
182/// Proof marker for a typed expression whose source columns are opaque.
183#[doc(hidden)]
184pub struct OpaqueScope;
185
186/// Proof marker for a binary expression's operands.
187#[doc(hidden)]
188pub struct BinaryScope<Left, Right>(PhantomData<(Left, Right)>);
189
190/// Proof marker for an expression wrapper.
191#[doc(hidden)]
192pub struct WrappedScope<Proof>(PhantomData<Proof>);
193
194/// Validates one explicit SELECT expression against its source scope.
195#[doc(hidden)]
196pub trait ProjectionInScope<Scope, Proof> {}
197
198impl<Lhs, Rhs, Op, D, T, N, Scope, LeftProof, RightProof>
199    ProjectionInScope<Scope, BinaryScope<LeftProof, RightProof>>
200    for crate::expr::ColumnBinOp<Lhs, Rhs, Op, D, T, N>
201where
202    Lhs: ProjectionInScope<Scope, LeftProof>,
203    Rhs: ProjectionInScope<Scope, RightProof>,
204{
205}
206
207impl<T, D, SQLType, Nullable, Scope, Proof> ProjectionInScope<Scope, WrappedScope<Proof>>
208    for crate::expr::ColumnNeg<T, D, SQLType, Nullable>
209where
210    T: ProjectionInScope<Scope, Proof>,
211{
212}
213
214impl<E, Scope, Proof> ProjectionInScope<Scope, WrappedScope<Proof>> for crate::expr::AliasedExpr<E> where
215    E: ProjectionInScope<Scope, Proof>
216{
217}
218
219impl<E, Name, Scope, Proof> ProjectionInScope<Scope, WrappedScope<Proof>>
220    for crate::expr::NamedExpr<E, Name>
221where
222    E: ProjectionInScope<Scope, Proof>,
223{
224}
225
226macro_rules! impl_scope_opaque {
227    ($($ty:ty),+ $(,)?) => {
228        $(impl<Scope> ProjectionInScope<Scope, OpaqueScope> for $ty {})+
229    };
230}
231
232impl_scope_opaque!(
233    bool,
234    i8,
235    i16,
236    i32,
237    i64,
238    i128,
239    isize,
240    u8,
241    u16,
242    u32,
243    u64,
244    u128,
245    usize,
246    f32,
247    f64,
248    String,
249    &str,
250    Vec<u8>,
251    &[u8]
252);
253
254impl<T, Scope, Proof> ProjectionInScope<Scope, WrappedScope<Proof>> for Option<T> where
255    T: ProjectionInScope<Scope, Proof>
256{
257}
258
259impl<T, Scope, Proof> ProjectionInScope<Scope, WrappedScope<Proof>> for &T where
260    T: ProjectionInScope<Scope, Proof>
261{
262}
263
264/// Validates every expression in an explicit SELECT projection.
265#[doc(hidden)]
266pub trait ProjectionsInScope<Scope, Proof> {}
267
268impl<Scope> ProjectionsInScope<Scope, ()> for Nil {}
269
270impl<Head, Tail, Scope, HeadProof, TailProof> ProjectionsInScope<Scope, (HeadProof, TailProof)>
271    for Cons<Head, Tail>
272where
273    Head: ProjectionInScope<Scope, HeadProof>,
274    Tail: ProjectionsInScope<Scope, TailProof>,
275{
276}
277
278// =============================================================================
279// Aggregate status validation for SELECT lists
280// =============================================================================
281
282/// Fold the aggregate statuses of a tuple of expressions.
283///
284/// For a 1-tuple, returns the single element's status.
285/// For N-tuples, folds pairwise using `CombineAggStatus`.
286pub trait AggStatus {
287    type Status;
288}
289
290// 1-tuple base case
291impl<E: crate::expr::HasAggStatus> AggStatus for (E,) {
292    type Status = E::Status;
293}
294
295// Generate AggStatus for 2..N tuples.
296// The `with_col_sizes_*` macros call this incrementally as:
297//   callback!(T0; 0)  callback!(T0, T1; 0, 1)  callback!(T0, T1, T2; 0, 1, 2)  ...
298// We skip the 1-tuple (handled above) and implement 2+ tuples.
299macro_rules! impl_tuple_agg_status {
300    // 1-tuple: skip (already implemented above)
301    ($E0:ident; $i0:tt) => {};
302    // 2-tuple
303    ($E0:ident, $E1:ident; $i0:tt, $i1:tt) => {
304        impl<$E0, $E1> AggStatus for ($E0, $E1)
305        where
306            $E0: crate::expr::HasAggStatus,
307            $E1: crate::expr::HasAggStatus,
308            <$E0 as crate::expr::HasAggStatus>::Status:
309                crate::expr::CombineAggStatus<<$E1 as crate::expr::HasAggStatus>::Status>,
310        {
311            type Status = <<$E0 as crate::expr::HasAggStatus>::Status as
312                crate::expr::CombineAggStatus<<$E1 as crate::expr::HasAggStatus>::Status>>::Output;
313        }
314    };
315    // 3+ tuples: fold head element's status with the rest-tuple's status
316    ($E0:ident, $E1:ident, $($rest:ident),+; $i0:tt, $i1:tt, $($ri:tt),+) => {
317        impl<$E0, $E1, $($rest),+> AggStatus for ($E0, $E1, $($rest),+)
318        where
319            $E0: crate::expr::HasAggStatus,
320            ($E1, $($rest),+): AggStatus,
321            <$E0 as crate::expr::HasAggStatus>::Status:
322                crate::expr::CombineAggStatus<<($E1, $($rest),+) as AggStatus>::Status>,
323        {
324            type Status = <<$E0 as crate::expr::HasAggStatus>::Status as
325                crate::expr::CombineAggStatus<<($E1, $($rest),+) as AggStatus>::Status>>::Output;
326        }
327    };
328}
329
330with_col_sizes_8!(impl_tuple_agg_status);
331
332#[cfg(any(
333    feature = "col16",
334    feature = "col32",
335    feature = "col64",
336    feature = "col128",
337    feature = "col200"
338))]
339with_col_sizes_16!(impl_tuple_agg_status);
340
341#[cfg(any(
342    feature = "col32",
343    feature = "col64",
344    feature = "col128",
345    feature = "col200"
346))]
347with_col_sizes_32!(impl_tuple_agg_status);
348
349#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
350with_col_sizes_64!(impl_tuple_agg_status);
351
352#[cfg(any(feature = "col128", feature = "col200"))]
353with_col_sizes_128!(impl_tuple_agg_status);
354
355#[cfg(feature = "col200")]
356with_col_sizes_200!(impl_tuple_agg_status);
357
358// =============================================================================
359// GROUP BY column tracking
360// =============================================================================
361
362/// Trait for types that can be passed to `.group_by()`.
363///
364/// Single columns and tuples of columns implement this.
365/// The `Columns` associated type is a `Cons<...>` list of column types
366/// for compile-time validation.
367pub trait IntoGroupBy<'a, V: crate::SQLParam + 'a>: crate::ToSQL<'a, V> {
368    /// Type-level list of grouped columns (e.g., `Cons<Col1, Cons<Col2, Nil>>`).
369    type Columns;
370}
371
372// Single column → Cons<Self, Nil>
373// (Implemented by proc macros for each column ZST)
374
375/// Type-level GROUP BY marker: the group key is table `Table`'s single-column
376/// primary key.
377///
378/// Produced by the proc-macro `IntoGroupBy` impl when `.group_by(col)` is
379/// called with a table's sole primary-key column. Every other column of that
380/// table is functionally dependent on its primary key (SQL:1999), so any
381/// scalar column of `Table` may appear in SELECT without being listed in
382/// GROUP BY. Columns of *other* tables (e.g. joined tables) still must be
383/// aggregated.
384///
385/// Beyond correctness, grouping by the bare primary key lets the database
386/// stream groups off the PK order instead of sorting the full result through
387/// a temp structure, which matters for `GROUP BY ... ORDER BY pk LIMIT n`.
388#[derive(Debug, Clone, Copy, Default)]
389pub struct PkGroup<Table>(PhantomData<Table>);
390
391// Tuple impls: (Col1, Col2) → Cons<Col1, Cons<Col2, Nil>>
392macro_rules! impl_into_group_by_tuple {
393    // 1-tuple: skip (single column uses direct impl)
394    ($T0:ident; $i0:tt) => {};
395    // 2-tuple
396    ($T0:ident, $T1:ident; $i0:tt, $i1:tt) => {
397        impl<'a, V: crate::SQLParam + 'a, $T0, $T1> IntoGroupBy<'a, V> for ($T0, $T1)
398        where
399            $T0: crate::ToSQL<'a, V>,
400            $T1: crate::ToSQL<'a, V>,
401        {
402            type Columns = Cons<$T0, Cons<$T1, Nil>>;
403        }
404    };
405    // 3+ tuples
406    ($T0:ident, $T1:ident, $($rest:ident),+; $i0:tt, $i1:tt, $($ri:tt),+) => {
407        impl<'a, V: crate::SQLParam + 'a, $T0, $T1, $($rest),+> IntoGroupBy<'a, V> for ($T0, $T1, $($rest),+)
408        where
409            $T0: crate::ToSQL<'a, V>,
410            $T1: crate::ToSQL<'a, V>,
411            $($rest: crate::ToSQL<'a, V>,)+
412        {
413            type Columns = impl_into_group_by_tuple!(@cons $T0, $T1, $($rest),+);
414        }
415    };
416    // Helper: build nested Cons type
417    (@cons $T:ident) => { Cons<$T, Nil> };
418    (@cons $T:ident, $($rest:ident),+) => { Cons<$T, impl_into_group_by_tuple!(@cons $($rest),+)> };
419}
420
421with_col_sizes_8!(impl_into_group_by_tuple);
422
423#[cfg(any(
424    feature = "col16",
425    feature = "col32",
426    feature = "col64",
427    feature = "col128",
428    feature = "col200"
429))]
430with_col_sizes_16!(impl_into_group_by_tuple);
431
432// =============================================================================
433// Scalar column validation against grouped columns
434// =============================================================================
435
436/// Checks that every scalar column in a `SelectCols` tuple is present in
437/// the Grouped column list. Aggregate columns are skipped.
438///
439/// `Proof` is a witness type inferred by the compiler (like `ScopeContains`).
440#[diagnostic::on_unimplemented(
441    message = "non-aggregate column in SELECT is not in GROUP BY",
442    label = "this column must appear in .group_by(...) or be wrapped in an aggregate function",
443    note = "when using GROUP BY, every non-aggregate column in SELECT must be listed in GROUP BY, \
444            unless the group key is the column's table primary key (`.group_by(table.pk)`)"
445)]
446pub trait ScalarColumnsIn<Grouped, Proof> {}
447
448/// Aggregate expressions always pass (they don't need to be in GROUP BY).
449pub struct AggSkip;
450
451/// Scalar expressions need a `ScopeContains` witness.
452pub struct ScalarCheck<W>(core::marker::PhantomData<W>);
453
454/// Witness: scalar column allowed because the group key is its table's
455/// primary key (functional dependency).
456pub struct PkDependent;
457
458// 1-tuple
459impl<E, Grouped, Proof> ScalarColumnsIn<Grouped, (Proof,)> for (E,) where
460    E: SingleColGroupCheck<Grouped, Proof>
461{
462}
463
464/// Per-element check: either skip (Agg) or verify (Scalar).
465pub trait SingleColGroupCheck<Grouped, Proof> {}
466
467/// Extracts the "base column" identity from an expression for GROUP BY matching.
468///
469/// `AliasedExpr<Col>` → `Col`, bare column → `Self`.
470pub trait GroupByIdentity {
471    type Identity;
472}
473
474// Default: identity is self (bare column ZSTs)
475// (implemented by proc macros for each column ZST)
476
477// AliasedExpr unwraps to inner
478impl<E: GroupByIdentity> GroupByIdentity for crate::expr::AliasedExpr<E> {
479    type Identity = E::Identity;
480}
481
482// SQLExpr: identity is self (for aggregate expressions, this won't be checked anyway)
483impl<V: crate::SQLParam, T, N, A> GroupByIdentity for crate::expr::SQLExpr<'_, V, T, N, A>
484where
485    T: crate::types::DataType,
486    N: crate::expr::Nullability,
487    A: crate::expr::AggregateKind,
488{
489    type Identity = Self;
490}
491
492// ColumnBinOp: identity is self (complex expressions won't match GROUP BY)
493impl<Lhs, Rhs, Op, D, SQLType, Nullable> GroupByIdentity
494    for crate::expr::ColumnBinOp<Lhs, Rhs, Op, D, SQLType, Nullable>
495{
496    type Identity = Self;
497}
498
499// ColumnNeg: identity is self
500impl<T, D, SQLType, Nullable> GroupByIdentity for crate::expr::ColumnNeg<T, D, SQLType, Nullable> {
501    type Identity = Self;
502}
503
504// Aggregate expressions → always OK
505impl<E, Grouped> SingleColGroupCheck<Grouped, AggSkip> for E where
506    E: crate::expr::HasAggStatus<Status = crate::expr::AllAgg>
507{
508}
509
510// Scalar expressions → base column identity must be in Grouped list
511impl<E, Grouped, W> SingleColGroupCheck<Grouped, ScalarCheck<W>> for E
512where
513    E: crate::expr::HasAggStatus<Status = crate::expr::AllScalar> + GroupByIdentity,
514    Grouped: ScopeContains<E::Identity, W>,
515{
516}
517
518// Scalar expressions under a primary-key group → the whole row of that table
519// is functionally dependent on the group key, so any column of the grouped
520// table passes. No overlap with the `ScalarCheck` impl above: `PkGroup<T>`
521// never implements `ScopeContains`.
522impl<E, T> SingleColGroupCheck<PkGroup<T>, PkDependent> for E
523where
524    E: crate::expr::HasAggStatus<Status = crate::expr::AllScalar> + GroupByIdentity,
525    E::Identity: crate::traits::ColumnOf<T>,
526{
527}
528
529// N-tuple: check head element, recurse on tail
530// Uses (HeadProof, TailProof) witness structure, like ScopeSatisfies.
531
532// 2-tuple
533impl<T0, T1, Grouped, P0, P1> ScalarColumnsIn<Grouped, (P0, P1)> for (T0, T1)
534where
535    T0: SingleColGroupCheck<Grouped, P0>,
536    T1: SingleColGroupCheck<Grouped, P1>,
537{
538}
539
540// 3+ tuples: check head, recurse on (T1, T2, ...)
541macro_rules! impl_scalar_columns_in {
542    // 1-tuple: skip (handled above directly)
543    ($T0:ident; $i0:tt) => {};
544    // 2-tuple: skip (handled above directly)
545    ($T0:ident, $T1:ident; $i0:tt, $i1:tt) => {};
546    // 3+ tuples: head + tail recursion
547    ($T0:ident, $($rest:ident),+; $i0:tt, $($ri:tt),+) => {
548        impl<$T0, $($rest),+, Grouped, HeadProof, TailProof>
549            ScalarColumnsIn<Grouped, (HeadProof, TailProof)>
550            for ($T0, $($rest),+)
551        where
552            $T0: SingleColGroupCheck<Grouped, HeadProof>,
553            ($($rest,)+): ScalarColumnsIn<Grouped, TailProof>,
554        {}
555    };
556}
557
558with_col_sizes_8!(impl_scalar_columns_in);
559
560#[cfg(any(
561    feature = "col16",
562    feature = "col32",
563    feature = "col64",
564    feature = "col128",
565    feature = "col200"
566))]
567with_col_sizes_16!(impl_scalar_columns_in);
568
569// =============================================================================
570// MarkerAggValidFor — top-level bound on terminal methods
571// =============================================================================
572
573/// Validates that the SELECT list is legal given the grouped column set.
574///
575/// - `Grouped = ()` (no GROUP BY): everything is valid
576/// - `Grouped = Cons<...>` (has GROUP BY): scalar columns must be in the list
577#[diagnostic::on_unimplemented(
578    message = "non-aggregate column in SELECT is not in GROUP BY",
579    label = "add this column to .group_by(...) or wrap it in an aggregate function"
580)]
581pub trait MarkerAggValidFor<Grouped, Proof = ()> {}
582
583// No GROUP BY (Grouped = ()) → always valid, any mix is fine
584impl<Mk> MarkerAggValidFor<()> for Mk {}
585
586// SelectStar with GROUP BY: can't check at compile time, always passes
587impl<Scope, Head, Tail> MarkerAggValidFor<Cons<Head, Tail>> for Scoped<SelectStar, Scope> {}
588
589// SelectExpr with GROUP BY: can't check, always passes
590impl<Scope, Head, Tail> MarkerAggValidFor<Cons<Head, Tail>> for Scoped<SelectExpr, Scope> {}
591
592// SelectAs with GROUP BY: user-specified type, always passes
593impl<Scope, R, Head, Tail> MarkerAggValidFor<Cons<Head, Tail>> for Scoped<SelectAs<R>, Scope> {}
594
595// SelectCols with GROUP BY: check each scalar column is in the Grouped list
596impl<Scope, Cols, Head, Tail, Proof> MarkerAggValidFor<Cons<Head, Tail>, Proof>
597    for Scoped<SelectCols<Cols>, Scope>
598where
599    Cols: ScalarColumnsIn<Cons<Head, Tail>, Proof>,
600{
601}
602
603// GROUP BY a table's primary key (`Grouped = PkGroup<T>`): same shape as the
604// Cons impls above, but scalar columns are checked for membership in the
605// grouped table instead of the grouped column list.
606impl<Scope, T> MarkerAggValidFor<PkGroup<T>> for Scoped<SelectStar, Scope> {}
607
608impl<Scope, T> MarkerAggValidFor<PkGroup<T>> for Scoped<SelectExpr, Scope> {}
609
610impl<Scope, R, T> MarkerAggValidFor<PkGroup<T>> for Scoped<SelectAs<R>, Scope> {}
611
612impl<Scope, Cols, T, Proof> MarkerAggValidFor<PkGroup<T>, Proof> for Scoped<SelectCols<Cols>, Scope> where
613    Cols: ScalarColumnsIn<PkGroup<T>, Proof>
614{
615}
616
617// =============================================================================
618// Marker column-count validation for strict decode paths
619// =============================================================================
620
621/// Type-level column-list representation for a row decode target.
622///
623/// Each consumed column is represented by a `Cons<T, ...>` node where `T`
624/// is the decoded Rust type for that column.
625pub trait RowColumnList<Row: ?Sized> {
626    type Columns: crate::TypeSet;
627}
628
629/// Type-level column-list representation for selected column tuples.
630pub trait SelectedColumnList {
631    type Columns: crate::TypeSet;
632}
633
634/// Type-level expression list for an explicit SELECT projection.
635///
636/// Unlike [`SelectedColumnList`], this preserves each expression type so a
637/// dialect can validate SQL types and nullability at a later boundary such as
638/// `INSERT ... SELECT`.
639#[doc(hidden)]
640pub trait SelectedExpressionList {
641    type Expressions: crate::TypeSet;
642}
643
644trait SameType<T> {}
645impl<T> SameType<T> for T {}
646
647trait ColumnTypeCompatible<Row: ?Sized, Expected, Actual> {}
648
649impl<Row: ?Sized, T> ColumnTypeCompatible<Row, T, T> for () {}
650
651trait TypeListCompatible<Row: ?Sized, ActualList> {}
652
653impl<Row: ?Sized> TypeListCompatible<Row, Self> for crate::Nil {}
654
655impl<Row: ?Sized, EH, ET, AH, AT> TypeListCompatible<Row, crate::Cons<AH, AT>>
656    for crate::Cons<EH, ET>
657where
658    (): ColumnTypeCompatible<Row, EH, AH>,
659    ET: TypeListCompatible<Row, AT>,
660{
661}
662
663trait SqliteDecodeRow {}
664
665#[cfg(feature = "rusqlite")]
666impl SqliteDecodeRow for ::rusqlite::Row<'_> {}
667
668#[cfg(feature = "libsql")]
669impl SqliteDecodeRow for ::libsql::Row {}
670
671#[cfg(feature = "turso")]
672impl SqliteDecodeRow for ::turso::Row {}
673
674macro_rules! impl_sqlite_integer_decode_compat {
675    ($expected:ty => $($actual:ty),+ $(,)?) => {
676        $(
677            impl<Row> ColumnTypeCompatible<Row, $expected, $actual> for ()
678            where
679                Row: SqliteDecodeRow,
680            {
681            }
682        )+
683    };
684}
685
686impl_sqlite_integer_decode_compat!(
687    i64 => i8, i16, i32, isize, u8, u16, u32, u64, usize, bool
688);
689
690impl_sqlite_integer_decode_compat!(
691    Option<i64> =>
692        Option<i8>,
693        Option<i16>,
694        Option<i32>,
695        Option<isize>,
696        Option<u8>,
697        Option<u16>,
698        Option<u32>,
699        Option<u64>,
700        Option<usize>,
701        Option<bool>
702);
703
704macro_rules! impl_row_column_list_one {
705    ($($ty:ty),+ $(,)?) => {
706        $(
707            impl<Row: ?Sized> RowColumnList<Row> for $ty {
708                type Columns = crate::Cons<$ty, crate::Nil>;
709            }
710        )+
711    };
712}
713
714impl_row_column_list_one!(
715    i8,
716    i16,
717    i32,
718    i64,
719    isize,
720    u8,
721    u16,
722    u32,
723    u64,
724    usize,
725    f32,
726    f64,
727    bool,
728    crate::prelude::String,
729    crate::prelude::Vec<u8>
730);
731
732impl<Row: ?Sized> RowColumnList<Row> for () {
733    type Columns = crate::Cons<(), crate::Nil>;
734}
735
736#[cfg(feature = "uuid")]
737impl<Row: ?Sized> RowColumnList<Row> for uuid::Uuid {
738    type Columns = crate::Cons<Self, crate::Nil>;
739}
740
741#[cfg(feature = "chrono")]
742impl<Row: ?Sized> RowColumnList<Row> for chrono::NaiveDate {
743    type Columns = crate::Cons<Self, crate::Nil>;
744}
745
746#[cfg(feature = "chrono")]
747impl<Row: ?Sized> RowColumnList<Row> for chrono::NaiveTime {
748    type Columns = crate::Cons<Self, crate::Nil>;
749}
750
751#[cfg(feature = "chrono")]
752impl<Row: ?Sized> RowColumnList<Row> for chrono::NaiveDateTime {
753    type Columns = crate::Cons<Self, crate::Nil>;
754}
755
756#[cfg(feature = "chrono")]
757impl<Row: ?Sized> RowColumnList<Row> for chrono::DateTime<chrono::Utc> {
758    type Columns = crate::Cons<Self, crate::Nil>;
759}
760
761#[cfg(feature = "serde")]
762impl<Row: ?Sized> RowColumnList<Row> for serde_json::Value {
763    type Columns = crate::Cons<Self, crate::Nil>;
764}
765
766#[cfg(feature = "rust-decimal")]
767impl<Row: ?Sized> RowColumnList<Row> for rust_decimal::Decimal {
768    type Columns = crate::Cons<Self, crate::Nil>;
769}
770
771#[cfg(feature = "chrono")]
772impl<Row: ?Sized> RowColumnList<Row> for chrono::Duration {
773    type Columns = crate::Cons<Self, crate::Nil>;
774}
775
776#[cfg(feature = "time")]
777impl<Row: ?Sized> RowColumnList<Row> for time::Date {
778    type Columns = crate::Cons<Self, crate::Nil>;
779}
780
781#[cfg(feature = "time")]
782impl<Row: ?Sized> RowColumnList<Row> for time::Time {
783    type Columns = crate::Cons<Self, crate::Nil>;
784}
785
786#[cfg(feature = "time")]
787impl<Row: ?Sized> RowColumnList<Row> for time::PrimitiveDateTime {
788    type Columns = crate::Cons<Self, crate::Nil>;
789}
790
791#[cfg(feature = "time")]
792impl<Row: ?Sized> RowColumnList<Row> for time::OffsetDateTime {
793    type Columns = crate::Cons<Self, crate::Nil>;
794}
795
796#[cfg(feature = "time")]
797impl<Row: ?Sized> RowColumnList<Row> for time::Duration {
798    type Columns = crate::Cons<Self, crate::Nil>;
799}
800
801#[cfg(feature = "jiff")]
802impl<Row: ?Sized> RowColumnList<Row> for jiff::civil::Date {
803    type Columns = crate::Cons<Self, crate::Nil>;
804}
805
806#[cfg(feature = "jiff")]
807impl<Row: ?Sized> RowColumnList<Row> for jiff::civil::Time {
808    type Columns = crate::Cons<Self, crate::Nil>;
809}
810
811#[cfg(feature = "jiff")]
812impl<Row: ?Sized> RowColumnList<Row> for jiff::civil::DateTime {
813    type Columns = crate::Cons<Self, crate::Nil>;
814}
815
816#[cfg(feature = "jiff")]
817impl<Row: ?Sized> RowColumnList<Row> for jiff::Timestamp {
818    type Columns = crate::Cons<Self, crate::Nil>;
819}
820
821#[cfg(feature = "cidr")]
822impl<Row: ?Sized> RowColumnList<Row> for cidr::IpInet {
823    type Columns = crate::Cons<Self, crate::Nil>;
824}
825
826#[cfg(feature = "cidr")]
827impl<Row: ?Sized> RowColumnList<Row> for cidr::IpCidr {
828    type Columns = crate::Cons<Self, crate::Nil>;
829}
830
831#[cfg(feature = "geo-types")]
832impl<Row: ?Sized> RowColumnList<Row> for geo_types::Point<f64> {
833    type Columns = crate::Cons<Self, crate::Nil>;
834}
835
836#[cfg(feature = "geo-types")]
837impl<Row: ?Sized> RowColumnList<Row> for geo_types::LineString<f64> {
838    type Columns = crate::Cons<Self, crate::Nil>;
839}
840
841#[cfg(feature = "geo-types")]
842impl<Row: ?Sized> RowColumnList<Row> for geo_types::Rect<f64> {
843    type Columns = crate::Cons<Self, crate::Nil>;
844}
845
846#[cfg(feature = "bit-vec")]
847impl<Row: ?Sized> RowColumnList<Row> for bit_vec::BitVec {
848    type Columns = crate::Cons<Self, crate::Nil>;
849}
850
851#[cfg(feature = "arrayvec")]
852impl<Row: ?Sized, const N: usize> RowColumnList<Row> for arrayvec::ArrayString<N> {
853    type Columns = crate::Cons<Self, crate::Nil>;
854}
855
856#[cfg(feature = "arrayvec")]
857impl<Row: ?Sized, T, const N: usize> RowColumnList<Row> for arrayvec::ArrayVec<T, N> {
858    type Columns = crate::Cons<Self, crate::Nil>;
859}
860
861impl<Row: ?Sized> RowColumnList<Row> for compact_str::CompactString {
862    type Columns = crate::Cons<Self, crate::Nil>;
863}
864
865#[cfg(feature = "bytes")]
866impl<Row: ?Sized> RowColumnList<Row> for bytes::Bytes {
867    type Columns = crate::Cons<Self, crate::Nil>;
868}
869
870#[cfg(feature = "bytes")]
871impl<Row: ?Sized> RowColumnList<Row> for bytes::BytesMut {
872    type Columns = crate::Cons<Self, crate::Nil>;
873}
874
875impl<Row: ?Sized, A: smallvec::Array> RowColumnList<Row> for smallvec::SmallVec<A> {
876    type Columns = crate::Cons<Self, crate::Nil>;
877}
878
879impl<Row: ?Sized, T> RowColumnList<Row> for Option<T> {
880    type Columns = crate::Cons<Self, crate::Nil>;
881}
882
883/// Helper: split last element from a type list and generate `RowColumnList` impl.
884/// Called by `impl_rcl_tuple` after separating first from rest.
885macro_rules! impl_rcl_body {
886    // 1-tuple: just delegate
887    ([$A:ident] []) => {
888        impl<Row: ?Sized, $A: RowColumnList<Row>> RowColumnList<Row> for ($A,) {
889            type Columns = <$A as RowColumnList<Row>>::Columns;
890        }
891    };
892    // N-tuple: delegate to (N-1)-tuple, concat last
893    ([$($all:ident),+] [$($prev:ident),+; $last:ident]) => {
894        impl<Row: ?Sized, $($all),+> RowColumnList<Row> for ($($all,)+)
895        where
896            $last: RowColumnList<Row>,
897            ($($prev,)+): RowColumnList<Row>,
898            <($($prev,)+) as RowColumnList<Row>>::Columns:
899                crate::Concat<<$last as RowColumnList<Row>>::Columns>,
900        {
901            type Columns = <<($($prev,)+) as RowColumnList<Row>>::Columns as crate::Concat<
902                <$last as RowColumnList<Row>>::Columns,
903            >>::Output;
904        }
905    };
906}
907
908/// Callback for `with_type_sizes_*!`: receives all types, separates last via
909/// recursive accumulator, then delegates to `impl_rcl_body`.
910macro_rules! impl_rcl_tuple {
911    ($($T:ident),+) => {
912        impl_rcl_split!([$($T),+] [] $($T),+);
913    };
914}
915
916/// Recursive accumulator to split `[all] [prev...] remaining...`
917macro_rules! impl_rcl_split {
918    // 1-tuple: no prev, single element
919    ([$A:ident] [] $only:ident) => {
920        impl_rcl_body!([$A] []);
921    };
922    // Base: one element left in remaining = it's the last
923    ([$($all:ident),+] [$($prev:ident),+] $last:ident) => {
924        impl_rcl_body!([$($all),+] [$($prev),+; $last]);
925    };
926    // Recurse from empty prev
927    ([$($all:ident),+] [] $head:ident, $($rest:ident),+) => {
928        impl_rcl_split!([$($all),+] [$head] $($rest),+);
929    };
930    // Recurse with non-empty prev
931    ([$($all:ident),+] [$($prev:ident),+] $head:ident, $($rest:ident),+) => {
932        impl_rcl_split!([$($all),+] [$($prev),+, $head] $($rest),+);
933    };
934}
935
936with_type_sizes_8!(impl_rcl_tuple);
937
938#[cfg(any(
939    feature = "col16",
940    feature = "col32",
941    feature = "col64",
942    feature = "col128",
943    feature = "col200"
944))]
945with_type_sizes_16!(impl_rcl_tuple);
946
947#[cfg(any(
948    feature = "col32",
949    feature = "col64",
950    feature = "col128",
951    feature = "col200"
952))]
953with_type_sizes_32!(impl_rcl_tuple);
954
955/// Marker-level column-count compatibility check used by strict `.all()` / `.get()`.
956///
957/// Currently enforced for `SelectCols<_>` where selected shape is explicit.
958#[diagnostic::on_unimplemented(
959    message = "selected shape does not match decode target `{Actual}`",
960    label = "this decode target is not type-compatible with .select(...) output",
961    note = "use typed expressions or derive FromRow for explicit remapping when selecting custom expressions"
962)]
963pub trait MarkerColumnCountValid<Row: ?Sized, Inferred, Actual> {}
964
965/// Marker-level guard for strict decode entry points.
966///
967/// Raw `SelectExpr` (`select(sql!(...))`) is intentionally excluded so strict
968/// decode requires either typed expressions (`raw_non_null`, `sql!(.., Type)`) or
969/// explicit remapping via typed expressions or `FromRow` derive.
970#[diagnostic::on_unimplemented(
971    message = "raw select expressions require explicit typing in strict decode",
972    label = "`select(sql!(...)).all()/get()` is not allowed in strict mode",
973    note = "use typed wrappers like `raw_non_null`/`raw_nullable` or derive FromRow"
974)]
975pub trait StrictDecodeMarker {}
976
977impl StrictDecodeMarker for SelectStar {}
978impl<Cols> StrictDecodeMarker for SelectCols<Cols> {}
979impl<R> StrictDecodeMarker for SelectAs<R> {}
980impl<M, Scope> StrictDecodeMarker for Scoped<M, Scope> where M: StrictDecodeMarker {}
981
982impl<Row: ?Sized, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual> for SelectStar {}
983
984impl<Row: ?Sized, Cols, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual>
985    for SelectCols<Cols>
986where
987    Cols: SelectedColumnList,
988    Actual: RowColumnList<Row>,
989    <Cols as SelectedColumnList>::Columns:
990        TypeListCompatible<Row, <Actual as RowColumnList<Row>>::Columns>,
991{
992}
993
994impl<Row: ?Sized, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual> for SelectExpr where
995    Inferred: SameType<Actual>
996{
997}
998
999impl<Row: ?Sized, R, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual>
1000    for SelectAs<R>
1001{
1002}
1003
1004impl<M, Scope, Row: ?Sized, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual>
1005    for Scoped<M, Scope>
1006where
1007    M: MarkerColumnCountValid<Row, Inferred, Actual>,
1008{
1009}
1010
1011/// Pushes a joined table into the marker scope.
1012pub trait ScopePush<Joined> {
1013    type Out;
1014}
1015
1016impl<M, Scope, Joined> ScopePush<Joined> for Scoped<M, Scope> {
1017    type Out = Scoped<M, Cons<Joined, Scope>>;
1018}
1019
1020/// Marker-directed row decoding for `.all()`/`.get()`.
1021pub trait DecodeSelectedRef<RowRef, R> {
1022    /// Decode the row into `R` according to the marker.
1023    ///
1024    /// # Errors
1025    ///
1026    /// Returns an error if the row cannot be decoded into the expected type
1027    /// (missing columns, type mismatch, or downstream conversion failure).
1028    fn decode(row: RowRef) -> Result<R, DrizzleError>;
1029}
1030
1031impl<RowRef, R> DecodeSelectedRef<RowRef, R> for SelectAs<R>
1032where
1033    R: TryFrom<RowRef>,
1034    <R as TryFrom<RowRef>>::Error: Into<DrizzleError>,
1035{
1036    fn decode(row: RowRef) -> Result<R, DrizzleError> {
1037        R::try_from(row).map_err(Into::into)
1038    }
1039}
1040
1041impl<RowRef, R, M, Scope> DecodeSelectedRef<RowRef, R> for Scoped<M, Scope>
1042where
1043    M: DecodeSelectedRef<RowRef, R>,
1044{
1045    fn decode(row: RowRef) -> Result<R, DrizzleError> {
1046        M::decode(row)
1047    }
1048}
1049
1050impl<RowRef, Row: ?Sized, R> DecodeSelectedRef<RowRef, R> for SelectStar
1051where
1052    RowRef: core::ops::Deref<Target = Row>,
1053    R: FromDrizzleRow<Row>,
1054{
1055    fn decode(row: RowRef) -> Result<R, DrizzleError> {
1056        R::from_row(&*row)
1057    }
1058}
1059
1060impl<RowRef, Row: ?Sized, Cols, R> DecodeSelectedRef<RowRef, R> for SelectCols<Cols>
1061where
1062    RowRef: core::ops::Deref<Target = Row>,
1063    R: FromDrizzleRow<Row>,
1064{
1065    fn decode(row: RowRef) -> Result<R, DrizzleError> {
1066        R::from_row(&*row)
1067    }
1068}
1069
1070impl<RowRef, Row: ?Sized, R> DecodeSelectedRef<RowRef, R> for SelectExpr
1071where
1072    RowRef: core::ops::Deref<Target = Row>,
1073    R: FromDrizzleRow<Row>,
1074{
1075    fn decode(row: RowRef) -> Result<R, DrizzleError> {
1076        R::from_row(&*row)
1077    }
1078}
1079
1080// =============================================================================
1081// FromDrizzleRow — offset-based row extraction
1082// =============================================================================
1083
1084/// Extracts a Rust value from a database row at a given column offset.
1085///
1086/// Unlike `TryFrom<Row>`, supports offset-based reading so joined results
1087/// can split a single row across multiple model types.
1088///
1089/// Tuple impls compose: `(A, B)` reads A at `offset`, then B at
1090/// `offset + A::COLUMN_COUNT`.
1091#[diagnostic::on_unimplemented(
1092    message = "cannot deserialize `{Self}` from a database row",
1093    label = "this type does not implement FromDrizzleRow",
1094    note = "derive #[SQLiteFromRow], #[PostgresFromRow], or #[MySQLFromRow]"
1095)]
1096pub trait FromDrizzleRow<Row: ?Sized>: Sized {
1097    /// Number of columns this type reads from the row.
1098    const COLUMN_COUNT: usize;
1099
1100    /// Read this type from `row` starting at column `offset`.
1101    ///
1102    /// # Errors
1103    ///
1104    /// Returns an error if any column from `offset` through
1105    /// `offset + COLUMN_COUNT - 1` cannot be read or converted.
1106    fn from_row_at(row: &Row, offset: usize) -> Result<Self, DrizzleError>;
1107
1108    /// Read from offset 0.
1109    ///
1110    /// # Errors
1111    ///
1112    /// Returns an error if the row cannot be decoded — see [`Self::from_row_at`].
1113    fn from_row(row: &Row) -> Result<Self, DrizzleError> {
1114        Self::from_row_at(row, 0)
1115    }
1116}
1117
1118/// Trait for composite (multi-column) row types that support NULL probing.
1119///
1120/// Implementing this trait enables `Option<T>` to work as a `FromDrizzleRow`
1121/// target, used for LEFT JOIN results where the joined table may be absent
1122/// (all columns NULL).
1123///
1124/// Proc macros generate this for each `SelectModel`. Leaf types (i32, String,
1125/// etc.) use concrete `Option<T>` impls instead.
1126pub trait NullProbeRow<Row: ?Sized>: FromDrizzleRow<Row> {
1127    /// Returns `true` if the first column at `offset` is NULL.
1128    ///
1129    /// # Errors
1130    ///
1131    /// Returns an error if the row cannot be inspected at `offset` (e.g. the
1132    /// driver reports an out-of-range index or conversion failure).
1133    fn is_null_at(row: &Row, offset: usize) -> Result<bool, DrizzleError>;
1134}
1135
1136// -- Tuple impls: generic over Row, composing inner impls --
1137
1138macro_rules! impl_from_drizzle_row_tuple {
1139    ($($T:ident),+; $($idx:tt),+) => {
1140        impl<__Row: ?Sized, $($T: FromDrizzleRow<__Row>),+> FromDrizzleRow<__Row> for ($($T,)+) {
1141            const COLUMN_COUNT: usize = 0 $(+ <$T as FromDrizzleRow<__Row>>::COLUMN_COUNT)+;
1142
1143            #[allow(non_snake_case)]
1144            fn from_row_at(
1145                row: &__Row,
1146                offset: usize,
1147            ) -> Result<Self, DrizzleError> {
1148                let mut __off = offset;
1149                $(
1150                    let $T = <$T as FromDrizzleRow<__Row>>::from_row_at(row, __off)?;
1151                    __off += <$T as FromDrizzleRow<__Row>>::COLUMN_COUNT;
1152                )+
1153                Ok(($($T,)+))
1154            }
1155        }
1156    };
1157}
1158
1159with_col_sizes_8!(impl_from_drizzle_row_tuple);
1160
1161#[cfg(any(
1162    feature = "col16",
1163    feature = "col32",
1164    feature = "col64",
1165    feature = "col128",
1166    feature = "col200"
1167))]
1168with_col_sizes_16!(impl_from_drizzle_row_tuple);
1169
1170#[cfg(any(
1171    feature = "col32",
1172    feature = "col64",
1173    feature = "col128",
1174    feature = "col200"
1175))]
1176with_col_sizes_32!(impl_from_drizzle_row_tuple);
1177
1178#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
1179with_col_sizes_64!(impl_from_drizzle_row_tuple);
1180
1181#[cfg(any(feature = "col128", feature = "col200"))]
1182with_col_sizes_128!(impl_from_drizzle_row_tuple);
1183
1184#[cfg(feature = "col200")]
1185with_col_sizes_200!(impl_from_drizzle_row_tuple);
1186
1187// =============================================================================
1188// SQLTypeToRust — SQL type marker × dialect → canonical Rust type
1189// =============================================================================
1190
1191/// Maps a dialect-native SQL type marker to its canonical Rust type.
1192///
1193/// Parameterized by `D` (a dialect marker such as
1194/// [`SQLiteDialect`] or [`PostgresDialect`]) so that
1195/// type mappings can differ per database.
1196///
1197/// Each dialect's native type markers (e.g., `sqlite::types::Integer`,
1198/// `postgres::types::Int4`) implement this trait for their respective dialect.
1199///
1200/// Feature-gated types (`chrono`, `uuid`, `serde`) provide mappings when
1201/// the feature is enabled. Without the feature there is **no impl**,
1202/// producing a compile error that guides the user.
1203#[diagnostic::on_unimplemented(
1204    message = "SQL type `{Self}` has no default Rust mapping for dialect `{D}`",
1205    label = "this SQL type has no default Rust mapping for this dialect",
1206    note = "enable `chrono` for Date/Time/Timestamp/TimestampTz, `uuid` for Uuid, or `serde` for Json/Jsonb"
1207)]
1208pub trait SQLTypeToRust<D> {
1209    type RustType;
1210}
1211
1212// -- Dialect-native mappings ---------------------------------------------------
1213
1214use crate::dialect::{MySQLDialect, PostgresDialect, SQLiteDialect};
1215
1216impl<D, T> SQLTypeToRust<D> for crate::types::Array<T>
1217where
1218    T: crate::types::DataType + SQLTypeToRust<D>,
1219{
1220    type RustType = crate::prelude::Vec<<T as SQLTypeToRust<D>>::RustType>;
1221}
1222
1223impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Integer {
1224    type RustType = i64;
1225}
1226
1227impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Text {
1228    type RustType = crate::prelude::String;
1229}
1230
1231impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Real {
1232    type RustType = f64;
1233}
1234
1235impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Blob {
1236    type RustType = crate::prelude::Vec<u8>;
1237}
1238
1239impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Numeric {
1240    type RustType = f64;
1241}
1242
1243impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Any {
1244    type RustType = crate::prelude::String;
1245}
1246
1247macro_rules! impl_mysql_sql_type_to_rust {
1248    ($rust:ty => $($sql:ty),+ $(,)?) => {
1249        $(
1250            impl SQLTypeToRust<MySQLDialect> for $sql {
1251                type RustType = $rust;
1252            }
1253        )+
1254    };
1255}
1256
1257impl_mysql_sql_type_to_rust!(i8 => drizzle_types::mysql::types::TinyInt);
1258impl_mysql_sql_type_to_rust!(u8 => drizzle_types::mysql::types::TinyIntUnsigned);
1259impl_mysql_sql_type_to_rust!(i16 => drizzle_types::mysql::types::SmallInt);
1260impl_mysql_sql_type_to_rust!(u16 => drizzle_types::mysql::types::SmallIntUnsigned);
1261impl_mysql_sql_type_to_rust!(i32 =>
1262    drizzle_types::mysql::types::MediumInt,
1263    drizzle_types::mysql::types::Int,
1264);
1265impl_mysql_sql_type_to_rust!(u32 =>
1266    drizzle_types::mysql::types::MediumIntUnsigned,
1267    drizzle_types::mysql::types::IntUnsigned,
1268);
1269impl_mysql_sql_type_to_rust!(i64 => drizzle_types::mysql::types::BigInt);
1270impl_mysql_sql_type_to_rust!(u64 => drizzle_types::mysql::types::BigIntUnsigned);
1271impl_mysql_sql_type_to_rust!(f32 => drizzle_types::mysql::types::Float);
1272impl_mysql_sql_type_to_rust!(f64 => drizzle_types::mysql::types::Double);
1273impl_mysql_sql_type_to_rust!(bool => drizzle_types::mysql::types::Boolean);
1274impl_mysql_sql_type_to_rust!(crate::prelude::String =>
1275    drizzle_types::mysql::types::Char,
1276    drizzle_types::mysql::types::Varchar,
1277    drizzle_types::mysql::types::TinyText,
1278    drizzle_types::mysql::types::Text,
1279    drizzle_types::mysql::types::MediumText,
1280    drizzle_types::mysql::types::LongText,
1281    drizzle_types::mysql::types::Enum,
1282    drizzle_types::mysql::types::Set,
1283    drizzle_types::mysql::types::Any,
1284);
1285impl_mysql_sql_type_to_rust!(crate::prelude::Vec<u8> =>
1286    drizzle_types::mysql::types::Binary,
1287    drizzle_types::mysql::types::Varbinary,
1288    drizzle_types::mysql::types::TinyBlob,
1289    drizzle_types::mysql::types::Blob,
1290    drizzle_types::mysql::types::MediumBlob,
1291    drizzle_types::mysql::types::LongBlob,
1292    drizzle_types::mysql::types::Bit,
1293);
1294impl_mysql_sql_type_to_rust!(u16 => drizzle_types::mysql::types::Year);
1295
1296#[cfg(feature = "rust-decimal")]
1297impl_mysql_sql_type_to_rust!(rust_decimal::Decimal => drizzle_types::mysql::types::Decimal);
1298#[cfg(not(feature = "rust-decimal"))]
1299impl_mysql_sql_type_to_rust!(crate::prelude::String => drizzle_types::mysql::types::Decimal);
1300
1301#[cfg(feature = "serde")]
1302impl_mysql_sql_type_to_rust!(serde_json::Value => drizzle_types::mysql::types::Json);
1303#[cfg(not(feature = "serde"))]
1304impl_mysql_sql_type_to_rust!(crate::prelude::String => drizzle_types::mysql::types::Json);
1305
1306#[cfg(feature = "chrono")]
1307impl_mysql_sql_type_to_rust!(chrono::NaiveDate => drizzle_types::mysql::types::Date);
1308#[cfg(all(not(feature = "chrono"), feature = "time"))]
1309impl_mysql_sql_type_to_rust!(time::Date => drizzle_types::mysql::types::Date);
1310#[cfg(all(not(any(feature = "chrono", feature = "time")), feature = "jiff"))]
1311impl_mysql_sql_type_to_rust!(jiff::civil::Date => drizzle_types::mysql::types::Date);
1312#[cfg(not(any(feature = "chrono", feature = "time", feature = "jiff")))]
1313impl_mysql_sql_type_to_rust!(crate::prelude::String => drizzle_types::mysql::types::Date);
1314
1315// Unlike SQL TIME in SQLite/PostgreSQL, MySQL TIME is a signed duration that
1316// can exceed 24 hours (up to 838:59:59). Clock-only chrono/time values cannot
1317// represent its full domain, so the canonical selected value remains text,
1318// matching Drizzle ORM's MySQL TIME mapping.
1319impl_mysql_sql_type_to_rust!(crate::prelude::String => drizzle_types::mysql::types::Time);
1320
1321#[cfg(feature = "chrono")]
1322impl_mysql_sql_type_to_rust!(chrono::NaiveDateTime => drizzle_types::mysql::types::DateTime);
1323// MySQL TIMESTAMP is session-time-zone aware. Wire adapters must establish a
1324// UTC session before executing typed queries, so the public value is an
1325// explicit UTC instant rather than an ambiguous naive datetime.
1326#[cfg(feature = "chrono")]
1327impl_mysql_sql_type_to_rust!(chrono::DateTime<chrono::Utc> => drizzle_types::mysql::types::Timestamp);
1328#[cfg(all(not(feature = "chrono"), feature = "time"))]
1329impl_mysql_sql_type_to_rust!(time::PrimitiveDateTime => drizzle_types::mysql::types::DateTime);
1330#[cfg(all(not(feature = "chrono"), feature = "time"))]
1331impl_mysql_sql_type_to_rust!(time::OffsetDateTime => drizzle_types::mysql::types::Timestamp);
1332#[cfg(all(not(any(feature = "chrono", feature = "time")), feature = "jiff"))]
1333impl_mysql_sql_type_to_rust!(jiff::civil::DateTime => drizzle_types::mysql::types::DateTime);
1334#[cfg(all(not(any(feature = "chrono", feature = "time")), feature = "jiff"))]
1335impl_mysql_sql_type_to_rust!(jiff::Timestamp => drizzle_types::mysql::types::Timestamp);
1336#[cfg(not(any(feature = "chrono", feature = "time", feature = "jiff")))]
1337impl_mysql_sql_type_to_rust!(crate::prelude::String =>
1338    drizzle_types::mysql::types::DateTime,
1339    drizzle_types::mysql::types::Timestamp,
1340);
1341
1342impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Int2 {
1343    type RustType = i16;
1344}
1345
1346impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Int4 {
1347    type RustType = i32;
1348}
1349
1350impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Int8 {
1351    type RustType = i64;
1352}
1353
1354impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Float4 {
1355    type RustType = f32;
1356}
1357
1358impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Float8 {
1359    type RustType = f64;
1360}
1361
1362impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Varchar {
1363    type RustType = crate::prelude::String;
1364}
1365
1366impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Text {
1367    type RustType = crate::prelude::String;
1368}
1369
1370impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Char {
1371    type RustType = crate::prelude::String;
1372}
1373
1374impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Bytea {
1375    type RustType = crate::prelude::Vec<u8>;
1376}
1377
1378impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Boolean {
1379    type RustType = bool;
1380}
1381
1382#[cfg(feature = "rust-decimal")]
1383impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Numeric {
1384    type RustType = rust_decimal::Decimal;
1385}
1386
1387#[cfg(not(feature = "rust-decimal"))]
1388impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Numeric {
1389    type RustType = crate::prelude::String;
1390}
1391
1392impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Any {
1393    type RustType = crate::prelude::String;
1394}
1395
1396#[cfg(feature = "chrono")]
1397impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timestamptz {
1398    type RustType = chrono::DateTime<chrono::Utc>;
1399}
1400
1401#[cfg(feature = "chrono")]
1402impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timestamp {
1403    type RustType = chrono::NaiveDateTime;
1404}
1405
1406#[cfg(feature = "chrono")]
1407impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Date {
1408    type RustType = chrono::NaiveDate;
1409}
1410
1411#[cfg(feature = "chrono")]
1412impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Time {
1413    type RustType = chrono::NaiveTime;
1414}
1415
1416#[cfg(feature = "chrono")]
1417impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timetz {
1418    type RustType = chrono::NaiveTime;
1419}
1420
1421#[cfg(all(not(feature = "chrono"), feature = "time"))]
1422impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timestamptz {
1423    type RustType = time::OffsetDateTime;
1424}
1425
1426#[cfg(all(not(feature = "chrono"), feature = "time"))]
1427impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timestamp {
1428    type RustType = time::PrimitiveDateTime;
1429}
1430
1431#[cfg(all(not(feature = "chrono"), feature = "time"))]
1432impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Date {
1433    type RustType = time::Date;
1434}
1435
1436#[cfg(all(not(feature = "chrono"), feature = "time"))]
1437impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Time {
1438    type RustType = time::Time;
1439}
1440
1441#[cfg(all(not(any(feature = "chrono", feature = "time")), feature = "jiff"))]
1442impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timestamptz {
1443    type RustType = jiff::Timestamp;
1444}
1445
1446#[cfg(all(not(any(feature = "chrono", feature = "time")), feature = "jiff"))]
1447impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timestamp {
1448    type RustType = jiff::civil::DateTime;
1449}
1450
1451#[cfg(all(not(any(feature = "chrono", feature = "time")), feature = "jiff"))]
1452impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Date {
1453    type RustType = jiff::civil::Date;
1454}
1455
1456#[cfg(all(not(any(feature = "chrono", feature = "time")), feature = "jiff"))]
1457impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Time {
1458    type RustType = jiff::civil::Time;
1459}
1460
1461#[cfg(feature = "uuid")]
1462impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Uuid {
1463    type RustType = uuid::Uuid;
1464}
1465
1466#[cfg(feature = "serde")]
1467impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Json {
1468    type RustType = serde_json::Value;
1469}
1470
1471#[cfg(feature = "serde")]
1472impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Jsonb {
1473    type RustType = serde_json::Value;
1474}
1475
1476// -- Feature-gated type marker mappings --
1477
1478#[cfg(feature = "chrono")]
1479impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Interval {
1480    type RustType = chrono::Duration;
1481}
1482
1483#[cfg(not(feature = "chrono"))]
1484impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Interval {
1485    type RustType = crate::prelude::String;
1486}
1487
1488#[cfg(feature = "cidr")]
1489impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Inet {
1490    type RustType = cidr::IpInet;
1491}
1492
1493#[cfg(not(feature = "cidr"))]
1494impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Inet {
1495    type RustType = crate::prelude::String;
1496}
1497
1498#[cfg(feature = "cidr")]
1499impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Cidr {
1500    type RustType = cidr::IpCidr;
1501}
1502
1503#[cfg(not(feature = "cidr"))]
1504impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Cidr {
1505    type RustType = crate::prelude::String;
1506}
1507
1508impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::MacAddr {
1509    type RustType = crate::prelude::String;
1510}
1511
1512impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::MacAddr8 {
1513    type RustType = crate::prelude::String;
1514}
1515
1516#[cfg(feature = "geo-types")]
1517impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Point {
1518    type RustType = geo_types::Point<f64>;
1519}
1520
1521#[cfg(not(feature = "geo-types"))]
1522impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Point {
1523    type RustType = crate::prelude::String;
1524}
1525
1526#[cfg(feature = "geo-types")]
1527impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::LineString {
1528    type RustType = geo_types::LineString<f64>;
1529}
1530
1531#[cfg(not(feature = "geo-types"))]
1532impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::LineString {
1533    type RustType = crate::prelude::String;
1534}
1535
1536#[cfg(feature = "geo-types")]
1537impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Rect {
1538    type RustType = geo_types::Rect<f64>;
1539}
1540
1541#[cfg(not(feature = "geo-types"))]
1542impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Rect {
1543    type RustType = crate::prelude::String;
1544}
1545
1546#[cfg(feature = "bit-vec")]
1547impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::BitString {
1548    type RustType = bit_vec::BitVec;
1549}
1550
1551#[cfg(not(feature = "bit-vec"))]
1552impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::BitString {
1553    type RustType = crate::prelude::String;
1554}
1555
1556impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Line {
1557    type RustType = crate::prelude::String;
1558}
1559
1560impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::LineSegment {
1561    type RustType = crate::prelude::String;
1562}
1563
1564impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Polygon {
1565    type RustType = crate::prelude::String;
1566}
1567
1568impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Circle {
1569    type RustType = crate::prelude::String;
1570}
1571
1572impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Enum {
1573    type RustType = crate::prelude::String;
1574}
1575
1576// =============================================================================
1577// WrapNullable — Option<T> wrapping based on nullability
1578// =============================================================================
1579
1580/// Wraps a Rust type in `Option<T>` when nullable.
1581pub trait WrapNullable<T> {
1582    type Output;
1583}
1584
1585impl<T> WrapNullable<T> for crate::expr::NonNull {
1586    type Output = T;
1587}
1588
1589impl<T> WrapNullable<T> for crate::expr::Null {
1590    type Output = Option<T>;
1591}
1592
1593// =============================================================================
1594// ExprValueType — "what Rust type does this expression produce?"
1595// =============================================================================
1596
1597/// Resolves the Rust value type for a column or typed expression.
1598///
1599/// Implemented for:
1600/// - Column ZSTs (proc macro generates alongside `ColumnValueType`)
1601/// - `SQLExpr<T, N, A>` where `T: SQLTypeToRust<D>` and `N: WrapNullable`
1602///
1603/// For `SQL<'a, V>` (raw SQL), `ValueType = ()` — the user must specify
1604/// the concrete row type via turbofish (`.all::<T>()`).
1605#[diagnostic::on_unimplemented(
1606    message = "cannot infer Rust type for expression `{Self}`",
1607    label = "use typed expressions or derive FromRow to specify the Rust type",
1608    note = "raw SQL and JSON expressions require explicit type annotation"
1609)]
1610pub trait ExprValueType {
1611    type ValueType;
1612}
1613
1614impl<T: ExprValueType + ?Sized> ExprValueType for &T {
1615    type ValueType = T::ValueType;
1616}
1617
1618impl<V: crate::SQLParam, T, N, A> ExprValueType for crate::expr::SQLExpr<'_, V, T, N, A>
1619where
1620    T: crate::types::DataType + SQLTypeToRust<V::DialectMarker>,
1621    N: crate::expr::Nullability + WrapNullable<<T as SQLTypeToRust<V::DialectMarker>>::RustType>,
1622    A: crate::expr::AggregateKind,
1623{
1624    type ValueType = <N as WrapNullable<<T as SQLTypeToRust<V::DialectMarker>>::RustType>>::Output;
1625}
1626
1627/// Raw SQL fallback — value type is `()`, user must specify the concrete type.
1628impl<V: crate::SQLParam> ExprValueType for crate::sql::SQL<'_, V> {
1629    type ValueType = ();
1630}
1631
1632// =============================================================================
1633// HasSelectModel — table → Select model (lifetime-free)
1634// =============================================================================
1635
1636/// Associates a table with its Select model type and column count.
1637///
1638/// Generated by `#[SQLiteTable]`, `#[PostgresTable]`, and `#[MySQLTable]`
1639/// alongside `SQLTable`.
1640#[diagnostic::on_unimplemented(
1641    message = "`{Self}` is not a drizzle table",
1642    label = "ensure this type was derived with #[SQLiteTable], #[PostgresTable], or #[MySQLTable]"
1643)]
1644pub trait HasSelectModel {
1645    type SelectModel;
1646    const COLUMN_COUNT: usize;
1647}
1648
1649impl<T: HasSelectModel + ?Sized> HasSelectModel for &T {
1650    type SelectModel = T::SelectModel;
1651
1652    const COLUMN_COUNT: usize = T::COLUMN_COUNT;
1653}
1654
1655// =============================================================================
1656// ResolveRow — Marker + Table → default row type R
1657// =============================================================================
1658
1659/// Given a select marker and a table, determines the default row type R.
1660/// Evaluated at `.from(table)` time.
1661#[diagnostic::on_unimplemented(
1662    message = "cannot resolve return type for this query",
1663    label = "the selected columns and table do not produce a known row type"
1664)]
1665pub trait ResolveRow<Table> {
1666    type Row;
1667}
1668
1669impl<T: HasSelectModel> ResolveRow<T> for SelectStar {
1670    type Row = T::SelectModel;
1671}
1672
1673impl<T> ResolveRow<T> for SelectExpr {
1674    type Row = ();
1675}
1676
1677impl<R, T> ResolveRow<T> for SelectAs<R>
1678where
1679    R: SelectAsFrom<T>,
1680{
1681    type Row = R;
1682}
1683
1684impl<M, Scope, T> ResolveRow<T> for Scoped<M, Scope>
1685where
1686    M: ResolveRow<T>,
1687{
1688    type Row = M::Row;
1689}
1690
1691/// Compile-time constraint for `.select(MyRow::Select).from(table)` base table matching.
1692///
1693/// `#[from(Table)]` on `*FromRow` structs emits `impl SelectAsFrom<Table> for MyRow`.
1694/// Structs without `#[from(...)]` may opt into any table.
1695#[diagnostic::on_unimplemented(
1696    message = "row selector `{Self}` cannot be used with table `{Table}`",
1697    label = "the #[from(...)] table does not match .from(...)",
1698    note = "set #[from(TheTable)] to the same table passed to .from(...)"
1699)]
1700pub trait SelectAsFrom<Table> {}
1701
1702// -- SelectCols: column value types → row tuple --
1703
1704macro_rules! impl_resolve_row_cols {
1705    ($($T:ident),+; $($idx:tt),+) => {
1706        impl<__Table, $($T: ExprValueType),+> ResolveRow<__Table> for SelectCols<($($T,)+)> {
1707            type Row = ($(<$T as ExprValueType>::ValueType,)+);
1708        }
1709    };
1710}
1711
1712with_col_sizes_8!(impl_resolve_row_cols);
1713
1714#[cfg(any(
1715    feature = "col16",
1716    feature = "col32",
1717    feature = "col64",
1718    feature = "col128",
1719    feature = "col200"
1720))]
1721with_col_sizes_16!(impl_resolve_row_cols);
1722
1723#[cfg(any(
1724    feature = "col32",
1725    feature = "col64",
1726    feature = "col128",
1727    feature = "col200"
1728))]
1729with_col_sizes_32!(impl_resolve_row_cols);
1730
1731#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
1732with_col_sizes_64!(impl_resolve_row_cols);
1733
1734#[cfg(any(feature = "col128", feature = "col200"))]
1735with_col_sizes_128!(impl_resolve_row_cols);
1736
1737#[cfg(feature = "col200")]
1738with_col_sizes_200!(impl_resolve_row_cols);
1739
1740macro_rules! selected_columns_cons {
1741    () => {
1742        crate::Nil
1743    };
1744    ($head:ident $(, $tail:ident)*) => {
1745        crate::Cons<<$head as ExprValueType>::ValueType, selected_columns_cons!($($tail),*)>
1746    };
1747}
1748
1749macro_rules! selected_expressions_cons {
1750    () => {
1751        crate::Nil
1752    };
1753    ($head:ident $(, $tail:ident)*) => {
1754        crate::Cons<$head, selected_expressions_cons!($($tail),*)>
1755    };
1756}
1757
1758macro_rules! impl_selected_column_list_tuple {
1759    ($($T:ident),+; $($idx:tt),+) => {
1760        impl<$($T: ExprValueType),+> SelectedColumnList for ($($T,)+) {
1761            type Columns = selected_columns_cons!($($T),+);
1762        }
1763    };
1764}
1765
1766macro_rules! impl_selected_expression_list_tuple {
1767    ($($T:ident),+; $($idx:tt),+) => {
1768        impl<$($T),+> SelectedExpressionList for ($($T,)+) {
1769            type Expressions = selected_expressions_cons!($($T),+);
1770        }
1771    };
1772}
1773
1774with_col_sizes_8!(impl_selected_column_list_tuple);
1775with_col_sizes_8!(impl_selected_expression_list_tuple);
1776
1777#[cfg(any(
1778    feature = "col16",
1779    feature = "col32",
1780    feature = "col64",
1781    feature = "col128",
1782    feature = "col200"
1783))]
1784with_col_sizes_16!(impl_selected_column_list_tuple);
1785#[cfg(any(
1786    feature = "col16",
1787    feature = "col32",
1788    feature = "col64",
1789    feature = "col128",
1790    feature = "col200"
1791))]
1792with_col_sizes_16!(impl_selected_expression_list_tuple);
1793
1794#[cfg(any(
1795    feature = "col32",
1796    feature = "col64",
1797    feature = "col128",
1798    feature = "col200"
1799))]
1800with_col_sizes_32!(impl_selected_column_list_tuple);
1801#[cfg(any(
1802    feature = "col32",
1803    feature = "col64",
1804    feature = "col128",
1805    feature = "col200"
1806))]
1807with_col_sizes_32!(impl_selected_expression_list_tuple);
1808
1809#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
1810with_col_sizes_64!(impl_selected_column_list_tuple);
1811#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
1812with_col_sizes_64!(impl_selected_expression_list_tuple);
1813
1814#[cfg(any(feature = "col128", feature = "col200"))]
1815with_col_sizes_128!(impl_selected_column_list_tuple);
1816#[cfg(any(feature = "col128", feature = "col200"))]
1817with_col_sizes_128!(impl_selected_expression_list_tuple);
1818
1819#[cfg(feature = "col200")]
1820with_col_sizes_200!(impl_selected_column_list_tuple);
1821#[cfg(feature = "col200")]
1822with_col_sizes_200!(impl_selected_expression_list_tuple);
1823
1824// =============================================================================
1825// AfterJoin — how joins transform the row type
1826// =============================================================================
1827
1828/// Determines the new row type after a JOIN.
1829pub trait AfterJoin<CurrentRow, JoinedTable> {
1830    type NewRow;
1831}
1832
1833/// Determines the new row type after a LEFT JOIN.
1834pub trait AfterLeftJoin<CurrentRow, JoinedTable> {
1835    type NewRow;
1836}
1837
1838/// Select projections whose row type can represent an unmatched lateral row.
1839///
1840/// `SELECT *` decodes the joined source as `Option<JoinedTable::SelectModel>`.
1841/// Explicit columns are accepted only when they all belong to the current
1842/// left-hand scope. A projection that reads the lateral source is rejected
1843/// because its output would need to become nullable on unmatched rows.
1844#[doc(hidden)]
1845pub trait LeftLateralSelection<Proof = ()>: left_lateral_private::Sealed {}
1846
1847#[doc(hidden)]
1848pub trait ColumnInScope<Scope, Proof> {}
1849
1850impl<Column, Scope, Table, Witness> ColumnInScope<Scope, (Table, Witness)> for Column
1851where
1852    Column: crate::traits::ColumnOf<Table>,
1853    Scope: ScopeContains<Table, Witness>,
1854{
1855}
1856
1857#[doc(hidden)]
1858pub trait ColumnsInScope<Scope, Proof> {}
1859
1860impl<Scope> ColumnsInScope<Scope, ()> for Nil {}
1861
1862impl<Head, Tail, Scope, HeadProof, TailProof> ColumnsInScope<Scope, (HeadProof, TailProof)>
1863    for Cons<Head, Tail>
1864where
1865    Head: ColumnInScope<Scope, HeadProof>,
1866    Tail: ColumnsInScope<Scope, TailProof>,
1867{
1868}
1869
1870mod left_lateral_private {
1871    pub trait Sealed {}
1872
1873    impl Sealed for super::SelectStar {}
1874    impl<Scope> Sealed for super::Scoped<super::SelectStar, Scope> {}
1875    impl<Columns, Scope> Sealed for super::Scoped<super::SelectCols<Columns>, Scope> {}
1876    impl<Row, Scope> Sealed for super::Scoped<super::SelectAs<Row>, Scope> {}
1877}
1878
1879impl LeftLateralSelection for SelectStar {}
1880impl<Scope> LeftLateralSelection for Scoped<SelectStar, Scope> {}
1881
1882impl<Columns, Scope, Proof> LeftLateralSelection<Proof> for Scoped<SelectCols<Columns>, Scope>
1883where
1884    Columns: SelectedExpressionList,
1885    Columns::Expressions: ColumnsInScope<Scope, Proof>,
1886{
1887}
1888
1889impl<Row, Scope, Proof> LeftLateralSelection<Proof> for Scoped<SelectAs<Row>, Scope> where
1890    Self: MarkerScopeValidFor<Proof>
1891{
1892}
1893
1894/// Determines the new row type after a RIGHT JOIN.
1895pub trait AfterRightJoin<CurrentRow, JoinedTable> {
1896    type NewRow;
1897}
1898
1899/// Determines the new row type after a FULL JOIN.
1900pub trait AfterFullJoin<CurrentRow, JoinedTable> {
1901    type NewRow;
1902}
1903
1904/// `SELECT *` + JOIN → `(CurrentRow, JoinedTable::SelectModel)`.
1905impl<R, T: HasSelectModel> AfterJoin<R, T> for SelectStar {
1906    type NewRow = (R, T::SelectModel);
1907}
1908
1909/// `SELECT *` + LEFT JOIN → `(CurrentRow, Option<JoinedTable::SelectModel>)`.
1910impl<R, T: HasSelectModel> AfterLeftJoin<R, T> for SelectStar {
1911    type NewRow = (R, Option<T::SelectModel>);
1912}
1913
1914/// `SELECT *` + RIGHT JOIN → `(Option<CurrentRow>, JoinedTable::SelectModel)`.
1915impl<R, T: HasSelectModel> AfterRightJoin<R, T> for SelectStar {
1916    type NewRow = (Option<R>, T::SelectModel);
1917}
1918
1919/// `SELECT *` + FULL JOIN → `(Option<CurrentRow>, Option<JoinedTable::SelectModel>)`.
1920impl<R, T: HasSelectModel> AfterFullJoin<R, T> for SelectStar {
1921    type NewRow = (Option<R>, Option<T::SelectModel>);
1922}
1923
1924/// Explicit columns + JOIN → R unchanged.
1925impl<Cols, R, T> AfterJoin<R, T> for SelectCols<Cols> {
1926    type NewRow = R;
1927}
1928
1929impl<Cols, R, T> AfterLeftJoin<R, T> for SelectCols<Cols> {
1930    type NewRow = R;
1931}
1932
1933impl<Cols, R, T> AfterRightJoin<R, T> for SelectCols<Cols> {
1934    type NewRow = R;
1935}
1936
1937impl<Cols, R, T> AfterFullJoin<R, T> for SelectCols<Cols> {
1938    type NewRow = R;
1939}
1940
1941/// Raw/untyped + JOIN → R unchanged.
1942impl<R, T> AfterJoin<R, T> for SelectExpr {
1943    type NewRow = R;
1944}
1945
1946impl<R, T> AfterLeftJoin<R, T> for SelectExpr {
1947    type NewRow = R;
1948}
1949
1950impl<R, T> AfterRightJoin<R, T> for SelectExpr {
1951    type NewRow = R;
1952}
1953
1954impl<R, T> AfterFullJoin<R, T> for SelectExpr {
1955    type NewRow = R;
1956}
1957
1958/// Explicit model + JOIN → R unchanged.
1959impl<Row, R, T> AfterJoin<R, T> for SelectAs<Row> {
1960    type NewRow = R;
1961}
1962
1963impl<Row, R, T> AfterLeftJoin<R, T> for SelectAs<Row> {
1964    type NewRow = R;
1965}
1966
1967impl<Row, R, T> AfterRightJoin<R, T> for SelectAs<Row> {
1968    type NewRow = R;
1969}
1970
1971impl<Row, R, T> AfterFullJoin<R, T> for SelectAs<Row> {
1972    type NewRow = R;
1973}
1974
1975impl<M, Scope, R, T> AfterJoin<R, T> for Scoped<M, Scope>
1976where
1977    M: AfterJoin<R, T>,
1978{
1979    type NewRow = M::NewRow;
1980}
1981
1982impl<M, Scope, R, T> AfterLeftJoin<R, T> for Scoped<M, Scope>
1983where
1984    M: AfterLeftJoin<R, T>,
1985{
1986    type NewRow = M::NewRow;
1987}
1988
1989impl<M, Scope, R, T> AfterRightJoin<R, T> for Scoped<M, Scope>
1990where
1991    M: AfterRightJoin<R, T>,
1992{
1993    type NewRow = M::NewRow;
1994}
1995
1996impl<M, Scope, R, T> AfterFullJoin<R, T> for Scoped<M, Scope>
1997where
1998    M: AfterFullJoin<R, T>,
1999{
2000    type NewRow = M::NewRow;
2001}
2002
2003// =============================================================================
2004// IntoSelectTarget — select arguments → Marker type
2005// =============================================================================
2006
2007/// Determines the select marker from what was passed to `.select()`.
2008///
2009/// The marker controls how row types are inferred:
2010/// - `SelectStar` — infer R from the table's Select model
2011/// - `SelectCols<C>` — infer R from the column value types
2012/// - `SelectExpr` — R must be specified by the user
2013///
2014/// Implemented automatically for:
2015/// - `()` → `SelectStar`
2016/// - `SQL<'a, V>` → `SelectExpr`
2017/// - `SQLExpr<'a, V, T, N, A>` → `SelectCols<(Self,)>`
2018/// - Tuples `(A, B, ...)` → `SelectCols<(A, B, ...)>`
2019/// - Column ZSTs (proc macro generated)
2020/// - Table structs (proc macro generated) → `SelectStar`
2021#[diagnostic::on_unimplemented(
2022    message = "`{Self}` cannot be used as a select target",
2023    label = "this type does not implement IntoSelectTarget",
2024    note = "implement IntoSelectTarget or use a column, table, or typed expression"
2025)]
2026pub trait IntoSelectTarget {
2027    type Marker;
2028}
2029
2030impl<T: IntoSelectTarget + ?Sized> IntoSelectTarget for &T {
2031    type Marker = T::Marker;
2032}
2033
2034/// `select(())` → `SelectStar` — infer row type from the table.
2035impl IntoSelectTarget for () {
2036    type Marker = SelectStar;
2037}
2038
2039/// `select(sql!(...))` → `SelectExpr` — user must specify row type.
2040impl<V: crate::SQLParam> IntoSelectTarget for crate::sql::SQL<'_, V> {
2041    type Marker = SelectExpr;
2042}
2043
2044/// `select(typed_expr)` → `SelectCols<(Expr,)>` — single typed expression.
2045impl<V: crate::SQLParam, T, N, A> IntoSelectTarget for crate::expr::SQLExpr<'_, V, T, N, A>
2046where
2047    T: crate::types::DataType,
2048    N: crate::expr::Nullability,
2049    A: crate::expr::AggregateKind,
2050{
2051    type Marker = SelectCols<(Self,)>;
2052}
2053
2054/// Tuples of select targets → `SelectCols<(A, B, ...)>`.
2055macro_rules! impl_into_select_target_tuple {
2056    ($($T:ident),+; $($idx:tt),+) => {
2057        impl<$($T),+> IntoSelectTarget for ($($T,)+) {
2058            type Marker = SelectCols<($($T,)+)>;
2059        }
2060    };
2061}
2062
2063with_col_sizes_8!(impl_into_select_target_tuple);
2064
2065#[cfg(any(
2066    feature = "col16",
2067    feature = "col32",
2068    feature = "col64",
2069    feature = "col128",
2070    feature = "col200"
2071))]
2072with_col_sizes_16!(impl_into_select_target_tuple);
2073
2074#[cfg(any(
2075    feature = "col32",
2076    feature = "col64",
2077    feature = "col128",
2078    feature = "col200"
2079))]
2080with_col_sizes_32!(impl_into_select_target_tuple);
2081
2082#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
2083with_col_sizes_64!(impl_into_select_target_tuple);
2084
2085#[cfg(any(feature = "col128", feature = "col200"))]
2086with_col_sizes_128!(impl_into_select_target_tuple);
2087
2088#[cfg(feature = "col200")]
2089with_col_sizes_200!(impl_into_select_target_tuple);