Skip to main content

drizzle_types/sql/
coerce.rs

1//! Comparison ([`Compatible`]) and assignment ([`Assignable`]) rules between
2//! SQL type markers.
3
4use super::DataType;
5
6/// SQL types that can be compared with `Rhs`: `Self` on the left of `=`, `<`,
7/// `IN`, `LIKE` and similar operators, `Rhs` on the right.
8///
9/// Comparison is loose: it allows any pair the database can compare without
10/// an explicit cast. The rules, per dialect:
11///
12/// - **Every dialect**: a type is compatible with itself; [`Placeholder`](crate::Placeholder)
13///   and the dialect's `Any` (untyped SQL) are compatible with every marker
14///   of that dialect, both ways; tuples are compatible element by element.
15/// - **SQLite**: `Integer`, `Real` and `Numeric` with each other; `Text` with `Blob`.
16/// - **PostgreSQL**: all of `Int2`, `Int4`, `Int8`, `Float4`, `Float8` and
17///   `Numeric` with each other; `Text`, `Varchar`, `Char` and `Enum` with each
18///   other; `Text` with `Date`, `Time`, `Timestamp` and `Timestamptz`; `Timestamp`
19///   with `Timestamptz`; `Time` with `Timetz`; `Json` with `Jsonb`; `Inet`
20///   with `Cidr`; `MacAddr` with `MacAddr8`.
21/// - **MySQL**: signed integers with each other, and unsigned integers with
22///   each other, but not signed with unsigned (cast explicitly); `Year`,
23///   `Float`, `Double` and `Decimal` with every integer, and `Float`,
24///   `Double` and `Decimal` with each other; text types with each
25///   other and with `Enum` and `Set` (but `Enum` not with `Set`); binary types
26///   and `Bit` with each other; `DateTime` with `Timestamp`.
27///
28/// Everything else is rejected, for example a number with text, or a
29/// `boolean` with an integer. An [`Array<T>`](crate::Array) is compatible only
30/// with the same `Array<T>`.
31///
32/// For storing a value in a column, the stricter [`Assignable`] applies.
33///
34/// # Examples
35///
36/// ```
37/// use drizzle_types::{Compatible, DataType};
38/// use drizzle_types::postgres::types::{Float8, Int4, Jsonb, Json, Text, Varchar};
39///
40/// fn compatible<L: Compatible<R>, R: DataType>() {}
41///
42/// compatible::<Int4, Float8>(); // int4 = float8
43/// compatible::<Varchar, Text>(); // varchar = text
44/// compatible::<Jsonb, Json>(); // jsonb = json
45/// ```
46///
47/// # Type safety
48///
49/// ```compile_fail
50/// use drizzle_types::{Compatible, DataType};
51/// use drizzle_types::postgres::types::{Int4, Text};
52///
53/// fn compatible<L: Compatible<R>, R: DataType>() {}
54///
55/// compatible::<Int4, Text>(); // an integer cannot be compared with text
56/// ```
57#[diagnostic::on_unimplemented(
58    message = "SQL type `{Self}` is not compatible with `{Rhs}`",
59    label = "these SQL types cannot be compared or coerced",
60    note = "compatible types include: integers with integers/floats, text with text/varchar, and any type with itself"
61)]
62pub trait Compatible<Rhs: DataType = Self>: DataType {}
63
64/// SQL types that accept a value of type `Rhs`: `Self` is the target (a
65/// column or typed placeholder), `Rhs` the type of the value stored in it.
66///
67/// Used when binding Rust values to typed columns and placeholders, and for
68/// `INSERT ... SELECT`. Assignment is stricter than [`Compatible`] and
69/// one-directional: a target accepts only values it can hold without loss.
70///
71/// - **Every dialect**: a type accepts itself; the dialect's `Any` accepts
72///   every marker of that dialect; tuples are checked element by element.
73/// - **SQLite**: `Real` accepts `Integer`; `Numeric` accepts `Integer` and `Real`.
74/// - **PostgreSQL**: wider integers accept narrower ones (`Int8` accepts
75///   `Int4` and `Int2`); `Float4`, `Float8` and `Numeric` accept every
76///   integer, and `Float8` and `Numeric` also accept `Float4` (`Numeric` also
77///   `Float8`); `Text`, `Varchar` and `Char` accept each other and `Enum`;
78///   `Timestamptz` accepts `Timestamp`; `Timetz` accepts `Time`; `Jsonb`
79///   accepts `Json`; `Cidr` accepts `Inet`.
80/// - **MySQL**: integers widen within range (an unsigned source fits a signed
81///   target only when its whole range does), except that `MediumInt` and
82///   `MediumIntUnsigned` also accept `Int` and `IntUnsigned` (Rust has no
83///   24-bit integer; the server checks the range); `Float`, `Double` and `Decimal`
84///   accept integers, `Double` accepts `Float`, and `Decimal` accepts both;
85///   `Year` accepts `SmallIntUnsigned`; text types accept each other, `Enum`
86///   and `Set`, and `Enum` and `Set` accept text; `Decimal`, `Json` and the
87///   date/time types accept `Text`; binary types and `Bit` accept each other,
88///   and `Bit` also accepts `Boolean` and unsigned integers.
89///
90/// Narrowing is rejected: an `Int8` value cannot go into an `Int4` column,
91/// and a `Text` value cannot go into an `Enum` column. [`Placeholder`](crate::Placeholder)
92/// is not assignable; give it a type first.
93///
94/// # Examples
95///
96/// ```
97/// use drizzle_types::{Assignable, DataType};
98/// use drizzle_types::postgres::types::{Float8, Int2, Int8, Jsonb, Json};
99///
100/// fn assignable<Target: Assignable<Value>, Value: DataType>() {}
101///
102/// assignable::<Int8, Int2>(); // an i16 fits in a bigint column
103/// assignable::<Float8, Int8>(); // an integer fits in a double column
104/// assignable::<Jsonb, Json>(); // a json value goes into a jsonb column
105/// ```
106///
107/// # Type safety
108///
109/// ```compile_fail
110/// use drizzle_types::{Assignable, DataType};
111/// use drizzle_types::postgres::types::{Int4, Int8};
112///
113/// fn assignable<Target: Assignable<Value>, Value: DataType>() {}
114///
115/// assignable::<Int4, Int8>(); // an i64 does not fit in an integer column
116/// ```
117#[diagnostic::on_unimplemented(
118    message = "SQL type `{Self}` is not assignable from `{Rhs}`",
119    label = "this value type cannot be bound to the placeholder/column type",
120    note = "assignment is stricter than comparison compatibility; use a value matching the target SQL marker"
121)]
122pub trait Assignable<Rhs: DataType = Self>: DataType {}
123
124// =============================================================================
125// Core macros
126// =============================================================================
127
128macro_rules! impl_reflexive_compat {
129    ($($ty:ty),+ $(,)?) => {
130        $(impl Compatible<$ty> for $ty {})+
131    };
132}
133
134macro_rules! impl_reflexive_assign {
135    ($($ty:ty),+ $(,)?) => {
136        $(impl Assignable<$ty> for $ty {})+
137    };
138}
139
140/// Generate mutual (bidirectional) `Compatible` impls for all unique cross-pairs in a group.
141/// Does NOT generate reflexive impls (those come from `impl_reflexive_compat!`).
142///
143/// `mutual_compat!(A, B, C)` expands to:
144///   Compatible<B> for A, Compatible<A> for B,
145///   Compatible<C> for A, Compatible<A> for C,
146///   Compatible<C> for B, Compatible<B> for C
147macro_rules! mutual_compat {
148    // Entry: dispatch to pair generation
149    ($($ty:ty),+ $(,)?) => {
150        mutual_compat!(@pairs [] $($ty),+);
151    };
152    // Base case: no more types to process
153    (@pairs [$($done:ty),*]) => {};
154    // Recursive case: take first type, pair it with all remaining
155    (@pairs [$($done:ty),*] $first:ty $(, $rest:ty)*) => {
156        $(
157            impl Compatible<$rest> for $first {}
158            impl Compatible<$first> for $rest {}
159        )*
160        mutual_compat!(@pairs [$($done,)* $first] $($rest),*);
161    };
162}
163
164/// Generate bidirectional `Compatible` impls between an "any" type and a list of types.
165macro_rules! any_compat {
166    ($any:ty; $($ty:ty),+ $(,)?) => {
167        $(
168            impl Compatible<$ty> for $any {}
169            impl Compatible<$any> for $ty {}
170        )+
171    };
172}
173
174macro_rules! impl_placeholder_compat {
175    ($($ty:ty),+ $(,)?) => {
176        $(
177            impl Compatible<crate::Placeholder> for $ty {}
178            impl Compatible<$ty> for crate::Placeholder {}
179        )+
180    };
181}
182
183/// Generate directional `Assignable` impls: each source can be assigned to the target.
184/// `assign_to!(Target; Src1, Src2)` → `Assignable<Src1> for Target`, `Assignable<Src2> for Target`
185macro_rules! assign_to {
186    ($target:ty; $($src:ty),+ $(,)?) => {
187        $(impl Assignable<$src> for $target {})+
188    };
189}
190
191/// Generate bidirectional `Assignable` for all cross-pairs in a group (mutual assignment).
192/// Does NOT generate reflexive impls (those come from `impl_reflexive_assign!`).
193macro_rules! mutual_assign {
194    ($($ty:ty),+ $(,)?) => {
195        mutual_assign!(@pairs [] $($ty),+);
196    };
197    (@pairs [$($done:ty),*]) => {};
198    (@pairs [$($done:ty),*] $first:ty $(, $rest:ty)*) => {
199        $(
200            impl Assignable<$rest> for $first {}
201            impl Assignable<$first> for $rest {}
202        )*
203        mutual_assign!(@pairs [$($done,)* $first] $($rest),*);
204    };
205}
206
207/// Generate `Assignable` from every source to a single "any" target.
208macro_rules! any_assign {
209    ($any:ty; $($ty:ty),+ $(,)?) => {
210        $(impl Assignable<$ty> for $any {})+
211    };
212}
213
214// =============================================================================
215// Generic impls
216// =============================================================================
217
218impl<T: DataType> Compatible<Self> for crate::Array<T> {}
219impl Compatible<Self> for crate::Placeholder {}
220impl<T: DataType> Assignable<Self> for crate::Array<T> {}
221
222// Reflexive compat & assign for all concrete types
223impl_reflexive_compat!(
224    crate::sqlite::types::Integer,
225    crate::sqlite::types::Text,
226    crate::sqlite::types::Real,
227    crate::sqlite::types::Blob,
228    crate::sqlite::types::Numeric,
229    crate::sqlite::types::Any,
230    crate::postgres::types::Int2,
231    crate::postgres::types::Int4,
232    crate::postgres::types::Int8,
233    crate::postgres::types::Float4,
234    crate::postgres::types::Float8,
235    crate::postgres::types::Varchar,
236    crate::postgres::types::Text,
237    crate::postgres::types::Char,
238    crate::postgres::types::Bytea,
239    crate::postgres::types::Boolean,
240    crate::postgres::types::Timestamptz,
241    crate::postgres::types::Timestamp,
242    crate::postgres::types::Date,
243    crate::postgres::types::Time,
244    crate::postgres::types::Timetz,
245    crate::postgres::types::Numeric,
246    crate::postgres::types::Uuid,
247    crate::postgres::types::Json,
248    crate::postgres::types::Jsonb,
249    crate::postgres::types::Any,
250    crate::postgres::types::Interval,
251    crate::postgres::types::Inet,
252    crate::postgres::types::Cidr,
253    crate::postgres::types::MacAddr,
254    crate::postgres::types::MacAddr8,
255    crate::postgres::types::Point,
256    crate::postgres::types::LineString,
257    crate::postgres::types::Rect,
258    crate::postgres::types::BitString,
259    crate::postgres::types::Line,
260    crate::postgres::types::LineSegment,
261    crate::postgres::types::Polygon,
262    crate::postgres::types::Circle,
263    crate::postgres::types::Enum,
264    crate::mysql::types::TinyInt,
265    crate::mysql::types::TinyIntUnsigned,
266    crate::mysql::types::SmallInt,
267    crate::mysql::types::SmallIntUnsigned,
268    crate::mysql::types::MediumInt,
269    crate::mysql::types::MediumIntUnsigned,
270    crate::mysql::types::Int,
271    crate::mysql::types::IntUnsigned,
272    crate::mysql::types::BigInt,
273    crate::mysql::types::BigIntUnsigned,
274    crate::mysql::types::Float,
275    crate::mysql::types::Double,
276    crate::mysql::types::Decimal,
277    crate::mysql::types::Boolean,
278    crate::mysql::types::Char,
279    crate::mysql::types::Varchar,
280    crate::mysql::types::TinyText,
281    crate::mysql::types::Text,
282    crate::mysql::types::MediumText,
283    crate::mysql::types::LongText,
284    crate::mysql::types::Binary,
285    crate::mysql::types::Varbinary,
286    crate::mysql::types::TinyBlob,
287    crate::mysql::types::Blob,
288    crate::mysql::types::MediumBlob,
289    crate::mysql::types::LongBlob,
290    crate::mysql::types::Json,
291    crate::mysql::types::Date,
292    crate::mysql::types::Time,
293    crate::mysql::types::DateTime,
294    crate::mysql::types::Timestamp,
295    crate::mysql::types::Year,
296    crate::mysql::types::Enum,
297    crate::mysql::types::Set,
298    crate::mysql::types::Bit,
299    crate::mysql::types::Any,
300);
301
302impl_reflexive_assign!(
303    crate::sqlite::types::Integer,
304    crate::sqlite::types::Text,
305    crate::sqlite::types::Real,
306    crate::sqlite::types::Blob,
307    crate::sqlite::types::Numeric,
308    crate::sqlite::types::Any,
309    crate::postgres::types::Int2,
310    crate::postgres::types::Int4,
311    crate::postgres::types::Int8,
312    crate::postgres::types::Float4,
313    crate::postgres::types::Float8,
314    crate::postgres::types::Varchar,
315    crate::postgres::types::Text,
316    crate::postgres::types::Char,
317    crate::postgres::types::Bytea,
318    crate::postgres::types::Boolean,
319    crate::postgres::types::Timestamptz,
320    crate::postgres::types::Timestamp,
321    crate::postgres::types::Date,
322    crate::postgres::types::Time,
323    crate::postgres::types::Timetz,
324    crate::postgres::types::Numeric,
325    crate::postgres::types::Uuid,
326    crate::postgres::types::Json,
327    crate::postgres::types::Jsonb,
328    crate::postgres::types::Any,
329    crate::postgres::types::Interval,
330    crate::postgres::types::Inet,
331    crate::postgres::types::Cidr,
332    crate::postgres::types::MacAddr,
333    crate::postgres::types::MacAddr8,
334    crate::postgres::types::Point,
335    crate::postgres::types::LineString,
336    crate::postgres::types::Rect,
337    crate::postgres::types::BitString,
338    crate::postgres::types::Line,
339    crate::postgres::types::LineSegment,
340    crate::postgres::types::Polygon,
341    crate::postgres::types::Circle,
342    crate::postgres::types::Enum,
343    crate::mysql::types::TinyInt,
344    crate::mysql::types::TinyIntUnsigned,
345    crate::mysql::types::SmallInt,
346    crate::mysql::types::SmallIntUnsigned,
347    crate::mysql::types::MediumInt,
348    crate::mysql::types::MediumIntUnsigned,
349    crate::mysql::types::Int,
350    crate::mysql::types::IntUnsigned,
351    crate::mysql::types::BigInt,
352    crate::mysql::types::BigIntUnsigned,
353    crate::mysql::types::Float,
354    crate::mysql::types::Double,
355    crate::mysql::types::Decimal,
356    crate::mysql::types::Boolean,
357    crate::mysql::types::Char,
358    crate::mysql::types::Varchar,
359    crate::mysql::types::TinyText,
360    crate::mysql::types::Text,
361    crate::mysql::types::MediumText,
362    crate::mysql::types::LongText,
363    crate::mysql::types::Binary,
364    crate::mysql::types::Varbinary,
365    crate::mysql::types::TinyBlob,
366    crate::mysql::types::Blob,
367    crate::mysql::types::MediumBlob,
368    crate::mysql::types::LongBlob,
369    crate::mysql::types::Json,
370    crate::mysql::types::Date,
371    crate::mysql::types::Time,
372    crate::mysql::types::DateTime,
373    crate::mysql::types::Timestamp,
374    crate::mysql::types::Year,
375    crate::mysql::types::Enum,
376    crate::mysql::types::Set,
377    crate::mysql::types::Bit,
378    crate::mysql::types::Any,
379);
380
381// =============================================================================
382// SQLite compatibility (category-based)
383// =============================================================================
384
385// SQLite numeric family: Integer ↔ Real ↔ Numeric (all mutually compatible)
386mutual_compat!(
387    crate::sqlite::types::Integer,
388    crate::sqlite::types::Real,
389    crate::sqlite::types::Numeric
390);
391
392// Blob ↔ Text (SQLite stores UUIDs, etc. as either)
393mutual_compat!(crate::sqlite::types::Text, crate::sqlite::types::Blob);
394
395// Any ↔ all SQLite types
396any_compat!(crate::sqlite::types::Any;
397    crate::sqlite::types::Integer,
398    crate::sqlite::types::Text,
399    crate::sqlite::types::Real,
400    crate::sqlite::types::Blob,
401    crate::sqlite::types::Numeric
402);
403
404impl_placeholder_compat!(
405    crate::sqlite::types::Integer,
406    crate::sqlite::types::Text,
407    crate::sqlite::types::Real,
408    crate::sqlite::types::Blob,
409    crate::sqlite::types::Numeric,
410    crate::sqlite::types::Any
411);
412
413// =============================================================================
414// PostgreSQL compatibility (category-based)
415// =============================================================================
416
417// Integer family: Int2 ↔ Int4 ↔ Int8 (all mutually compatible)
418mutual_compat!(
419    crate::postgres::types::Int2,
420    crate::postgres::types::Int4,
421    crate::postgres::types::Int8
422);
423
424// Float family: Float4 ↔ Float8
425mutual_compat!(
426    crate::postgres::types::Float4,
427    crate::postgres::types::Float8
428);
429
430// Cross-numeric: all integers ↔ all floats
431// (Int2, Int4, Int8) × (Float4, Float8)
432/// Generate cross-product `Compatible` impls between two groups of types.
433/// Every type in group A becomes compatible with every type in group B (bidirectional).
434macro_rules! cross_compat {
435    ([$first_a:ty $(, $rest_a:ty)* $(,)?], [$($b:ty),+ $(,)?]) => {
436        // Expand first_a × all B
437        $(
438            impl Compatible<$b> for $first_a {}
439            impl Compatible<$first_a> for $b {}
440        )+
441        // Recurse for remaining A types
442        cross_compat!([$($rest_a),*], [$($b),+]);
443    };
444    // Base case: empty A list
445    ([], [$($b:ty),+ $(,)?]) => {};
446}
447
448cross_compat!(
449    [
450        crate::postgres::types::Int2,
451        crate::postgres::types::Int4,
452        crate::postgres::types::Int8
453    ],
454    [
455        crate::postgres::types::Float4,
456        crate::postgres::types::Float8
457    ]
458);
459
460// Numeric ↔ all numeric types (Int2, Int4, Int8, Float4, Float8)
461any_compat!(crate::postgres::types::Numeric;
462    crate::postgres::types::Int2,
463    crate::postgres::types::Int4,
464    crate::postgres::types::Int8,
465    crate::postgres::types::Float4,
466    crate::postgres::types::Float8
467);
468
469// Text family: Text ↔ Varchar ↔ Char (all mutually compatible)
470mutual_compat!(
471    crate::postgres::types::Text,
472    crate::postgres::types::Varchar,
473    crate::postgres::types::Char
474);
475
476// Temporal cross-compatibility pairs
477mutual_compat!(
478    crate::postgres::types::Timestamptz,
479    crate::postgres::types::Timestamp
480);
481mutual_compat!(crate::postgres::types::Time, crate::postgres::types::Timetz);
482
483// JSON cross-compatibility
484mutual_compat!(crate::postgres::types::Json, crate::postgres::types::Jsonb);
485
486// Text ↔ Temporal (string comparisons with timestamps)
487cross_compat!(
488    [crate::postgres::types::Text],
489    [
490        crate::postgres::types::Timestamptz,
491        crate::postgres::types::Timestamp,
492        crate::postgres::types::Date,
493        crate::postgres::types::Time
494    ]
495);
496
497// Network types: Inet ↔ Cidr
498mutual_compat!(crate::postgres::types::Inet, crate::postgres::types::Cidr);
499
500// MAC address types: MacAddr ↔ MacAddr8
501mutual_compat!(
502    crate::postgres::types::MacAddr,
503    crate::postgres::types::MacAddr8
504);
505
506// Enum ↔ Text family
507cross_compat!(
508    [crate::postgres::types::Enum],
509    [
510        crate::postgres::types::Text,
511        crate::postgres::types::Varchar,
512        crate::postgres::types::Char
513    ]
514);
515
516// Any ↔ all PostgreSQL types
517any_compat!(crate::postgres::types::Any;
518    crate::postgres::types::Int2,
519    crate::postgres::types::Int4,
520    crate::postgres::types::Int8,
521    crate::postgres::types::Float4,
522    crate::postgres::types::Float8,
523    crate::postgres::types::Varchar,
524    crate::postgres::types::Text,
525    crate::postgres::types::Char,
526    crate::postgres::types::Bytea,
527    crate::postgres::types::Boolean,
528    crate::postgres::types::Timestamptz,
529    crate::postgres::types::Timestamp,
530    crate::postgres::types::Date,
531    crate::postgres::types::Time,
532    crate::postgres::types::Timetz,
533    crate::postgres::types::Numeric,
534    crate::postgres::types::Uuid,
535    crate::postgres::types::Json,
536    crate::postgres::types::Jsonb,
537    crate::postgres::types::Interval,
538    crate::postgres::types::Inet,
539    crate::postgres::types::Cidr,
540    crate::postgres::types::MacAddr,
541    crate::postgres::types::MacAddr8,
542    crate::postgres::types::Point,
543    crate::postgres::types::LineString,
544    crate::postgres::types::Rect,
545    crate::postgres::types::BitString,
546    crate::postgres::types::Line,
547    crate::postgres::types::LineSegment,
548    crate::postgres::types::Polygon,
549    crate::postgres::types::Circle,
550    crate::postgres::types::Enum
551);
552
553impl_placeholder_compat!(
554    crate::postgres::types::Int2,
555    crate::postgres::types::Int4,
556    crate::postgres::types::Int8,
557    crate::postgres::types::Float4,
558    crate::postgres::types::Float8,
559    crate::postgres::types::Varchar,
560    crate::postgres::types::Text,
561    crate::postgres::types::Char,
562    crate::postgres::types::Bytea,
563    crate::postgres::types::Boolean,
564    crate::postgres::types::Timestamptz,
565    crate::postgres::types::Timestamp,
566    crate::postgres::types::Date,
567    crate::postgres::types::Time,
568    crate::postgres::types::Timetz,
569    crate::postgres::types::Numeric,
570    crate::postgres::types::Uuid,
571    crate::postgres::types::Json,
572    crate::postgres::types::Jsonb,
573    crate::postgres::types::Any,
574    crate::postgres::types::Interval,
575    crate::postgres::types::Inet,
576    crate::postgres::types::Cidr,
577    crate::postgres::types::MacAddr,
578    crate::postgres::types::MacAddr8,
579    crate::postgres::types::Point,
580    crate::postgres::types::LineString,
581    crate::postgres::types::Rect,
582    crate::postgres::types::BitString,
583    crate::postgres::types::Line,
584    crate::postgres::types::LineSegment,
585    crate::postgres::types::Polygon,
586    crate::postgres::types::Circle,
587    crate::postgres::types::Enum
588);
589
590// =============================================================================
591// MySQL compatibility (category-based)
592// =============================================================================
593
594// Keep signed and unsigned integer families distinct. MySQL can coerce between
595// them, but negative signed values have surprising comparison semantics with
596// unsigned values; callers can opt into that behavior with an explicit cast.
597mutual_compat!(
598    crate::mysql::types::TinyInt,
599    crate::mysql::types::SmallInt,
600    crate::mysql::types::MediumInt,
601    crate::mysql::types::Int,
602    crate::mysql::types::BigInt
603);
604mutual_compat!(
605    crate::mysql::types::TinyIntUnsigned,
606    crate::mysql::types::SmallIntUnsigned,
607    crate::mysql::types::MediumIntUnsigned,
608    crate::mysql::types::IntUnsigned,
609    crate::mysql::types::BigIntUnsigned
610);
611
612// YEAR is numeric and has a restricted domain, so it may be compared with
613// either integer family without implying compatibility between those families.
614cross_compat!(
615    [crate::mysql::types::Year],
616    [
617        crate::mysql::types::TinyInt,
618        crate::mysql::types::SmallInt,
619        crate::mysql::types::MediumInt,
620        crate::mysql::types::Int,
621        crate::mysql::types::BigInt,
622        crate::mysql::types::TinyIntUnsigned,
623        crate::mysql::types::SmallIntUnsigned,
624        crate::mysql::types::MediumIntUnsigned,
625        crate::mysql::types::IntUnsigned,
626        crate::mysql::types::BigIntUnsigned
627    ]
628);
629
630// Exact and approximate numeric types can be compared with every integer
631// marker; FLOAT, DOUBLE, and DECIMAL are also mutually compatible.
632cross_compat!(
633    [
634        crate::mysql::types::TinyInt,
635        crate::mysql::types::SmallInt,
636        crate::mysql::types::MediumInt,
637        crate::mysql::types::Int,
638        crate::mysql::types::BigInt,
639        crate::mysql::types::TinyIntUnsigned,
640        crate::mysql::types::SmallIntUnsigned,
641        crate::mysql::types::MediumIntUnsigned,
642        crate::mysql::types::IntUnsigned,
643        crate::mysql::types::BigIntUnsigned,
644        crate::mysql::types::Year
645    ],
646    [
647        crate::mysql::types::Decimal,
648        crate::mysql::types::Float,
649        crate::mysql::types::Double
650    ]
651);
652mutual_compat!(
653    crate::mysql::types::Decimal,
654    crate::mysql::types::Float,
655    crate::mysql::types::Double
656);
657
658// Text families are mutually comparable. ENUM and SET are textual values, but
659// remain distinct from each other because SET's comma-separated representation
660// has different semantics.
661mutual_compat!(
662    crate::mysql::types::Char,
663    crate::mysql::types::Varchar,
664    crate::mysql::types::TinyText,
665    crate::mysql::types::Text,
666    crate::mysql::types::MediumText,
667    crate::mysql::types::LongText
668);
669cross_compat!(
670    [crate::mysql::types::Enum],
671    [
672        crate::mysql::types::Char,
673        crate::mysql::types::Varchar,
674        crate::mysql::types::TinyText,
675        crate::mysql::types::Text,
676        crate::mysql::types::MediumText,
677        crate::mysql::types::LongText
678    ]
679);
680cross_compat!(
681    [crate::mysql::types::Set],
682    [
683        crate::mysql::types::Char,
684        crate::mysql::types::Varchar,
685        crate::mysql::types::TinyText,
686        crate::mysql::types::Text,
687        crate::mysql::types::MediumText,
688        crate::mysql::types::LongText
689    ]
690);
691
692// All binary families, including BIT's packed bytes, are mutually comparable.
693mutual_compat!(
694    crate::mysql::types::Binary,
695    crate::mysql::types::Varbinary,
696    crate::mysql::types::TinyBlob,
697    crate::mysql::types::Blob,
698    crate::mysql::types::MediumBlob,
699    crate::mysql::types::LongBlob,
700    crate::mysql::types::Bit
701);
702
703// DATETIME and TIMESTAMP represent the same wall-clock shape at the SQL type
704// level, while preserving their distinct storage semantics elsewhere.
705mutual_compat!(
706    crate::mysql::types::DateTime,
707    crate::mysql::types::Timestamp
708);
709
710// Any ↔ all MySQL types.
711any_compat!(crate::mysql::types::Any;
712    crate::mysql::types::TinyInt,
713    crate::mysql::types::TinyIntUnsigned,
714    crate::mysql::types::SmallInt,
715    crate::mysql::types::SmallIntUnsigned,
716    crate::mysql::types::MediumInt,
717    crate::mysql::types::MediumIntUnsigned,
718    crate::mysql::types::Int,
719    crate::mysql::types::IntUnsigned,
720    crate::mysql::types::BigInt,
721    crate::mysql::types::BigIntUnsigned,
722    crate::mysql::types::Float,
723    crate::mysql::types::Double,
724    crate::mysql::types::Decimal,
725    crate::mysql::types::Boolean,
726    crate::mysql::types::Char,
727    crate::mysql::types::Varchar,
728    crate::mysql::types::TinyText,
729    crate::mysql::types::Text,
730    crate::mysql::types::MediumText,
731    crate::mysql::types::LongText,
732    crate::mysql::types::Binary,
733    crate::mysql::types::Varbinary,
734    crate::mysql::types::TinyBlob,
735    crate::mysql::types::Blob,
736    crate::mysql::types::MediumBlob,
737    crate::mysql::types::LongBlob,
738    crate::mysql::types::Json,
739    crate::mysql::types::Date,
740    crate::mysql::types::Time,
741    crate::mysql::types::DateTime,
742    crate::mysql::types::Timestamp,
743    crate::mysql::types::Year,
744    crate::mysql::types::Enum,
745    crate::mysql::types::Set,
746    crate::mysql::types::Bit
747);
748
749impl_placeholder_compat!(
750    crate::mysql::types::TinyInt,
751    crate::mysql::types::TinyIntUnsigned,
752    crate::mysql::types::SmallInt,
753    crate::mysql::types::SmallIntUnsigned,
754    crate::mysql::types::MediumInt,
755    crate::mysql::types::MediumIntUnsigned,
756    crate::mysql::types::Int,
757    crate::mysql::types::IntUnsigned,
758    crate::mysql::types::BigInt,
759    crate::mysql::types::BigIntUnsigned,
760    crate::mysql::types::Float,
761    crate::mysql::types::Double,
762    crate::mysql::types::Decimal,
763    crate::mysql::types::Boolean,
764    crate::mysql::types::Char,
765    crate::mysql::types::Varchar,
766    crate::mysql::types::TinyText,
767    crate::mysql::types::Text,
768    crate::mysql::types::MediumText,
769    crate::mysql::types::LongText,
770    crate::mysql::types::Binary,
771    crate::mysql::types::Varbinary,
772    crate::mysql::types::TinyBlob,
773    crate::mysql::types::Blob,
774    crate::mysql::types::MediumBlob,
775    crate::mysql::types::LongBlob,
776    crate::mysql::types::Json,
777    crate::mysql::types::Date,
778    crate::mysql::types::Time,
779    crate::mysql::types::DateTime,
780    crate::mysql::types::Timestamp,
781    crate::mysql::types::Year,
782    crate::mysql::types::Enum,
783    crate::mysql::types::Set,
784    crate::mysql::types::Bit,
785    crate::mysql::types::Any
786);
787
788// =============================================================================
789// Assignment compatibility (bind-time)
790// =============================================================================
791// Assignment is stricter than comparison: only wider types accept narrower values.
792
793// SQLite assignment
794assign_to!(crate::sqlite::types::Real; crate::sqlite::types::Integer);
795assign_to!(crate::sqlite::types::Numeric; crate::sqlite::types::Integer, crate::sqlite::types::Real);
796any_assign!(crate::sqlite::types::Any;
797    crate::sqlite::types::Integer,
798    crate::sqlite::types::Text,
799    crate::sqlite::types::Real,
800    crate::sqlite::types::Blob,
801    crate::sqlite::types::Numeric
802);
803
804// PostgreSQL integer widening: Int2 → Int4 → Int8
805assign_to!(crate::postgres::types::Int4; crate::postgres::types::Int2);
806assign_to!(crate::postgres::types::Int8; crate::postgres::types::Int2, crate::postgres::types::Int4);
807
808// Int → Float widening
809assign_to!(crate::postgres::types::Float4;
810    crate::postgres::types::Int2,
811    crate::postgres::types::Int4,
812    crate::postgres::types::Int8
813);
814assign_to!(crate::postgres::types::Float8;
815    crate::postgres::types::Int2,
816    crate::postgres::types::Int4,
817    crate::postgres::types::Int8,
818    crate::postgres::types::Float4
819);
820
821// Numeric accepts all numeric types
822assign_to!(crate::postgres::types::Numeric;
823    crate::postgres::types::Int2,
824    crate::postgres::types::Int4,
825    crate::postgres::types::Int8,
826    crate::postgres::types::Float4,
827    crate::postgres::types::Float8
828);
829
830// Text family: all mutually assignable
831mutual_assign!(
832    crate::postgres::types::Text,
833    crate::postgres::types::Varchar,
834    crate::postgres::types::Char
835);
836
837// Enum → Text family
838assign_to!(crate::postgres::types::Varchar; crate::postgres::types::Enum);
839assign_to!(crate::postgres::types::Text; crate::postgres::types::Enum);
840assign_to!(crate::postgres::types::Char; crate::postgres::types::Enum);
841
842// Temporal and JSON assignment
843assign_to!(crate::postgres::types::Timestamptz; crate::postgres::types::Timestamp);
844assign_to!(crate::postgres::types::Timetz; crate::postgres::types::Time);
845assign_to!(crate::postgres::types::Jsonb; crate::postgres::types::Json);
846
847// Network assignment
848assign_to!(crate::postgres::types::Cidr; crate::postgres::types::Inet);
849
850// Any accepts all concrete PostgreSQL markers
851any_assign!(crate::postgres::types::Any;
852    crate::postgres::types::Int2,
853    crate::postgres::types::Int4,
854    crate::postgres::types::Int8,
855    crate::postgres::types::Float4,
856    crate::postgres::types::Float8,
857    crate::postgres::types::Varchar,
858    crate::postgres::types::Text,
859    crate::postgres::types::Char,
860    crate::postgres::types::Bytea,
861    crate::postgres::types::Boolean,
862    crate::postgres::types::Timestamptz,
863    crate::postgres::types::Timestamp,
864    crate::postgres::types::Date,
865    crate::postgres::types::Time,
866    crate::postgres::types::Timetz,
867    crate::postgres::types::Numeric,
868    crate::postgres::types::Uuid,
869    crate::postgres::types::Json,
870    crate::postgres::types::Jsonb,
871    crate::postgres::types::Interval,
872    crate::postgres::types::Inet,
873    crate::postgres::types::Cidr,
874    crate::postgres::types::MacAddr,
875    crate::postgres::types::MacAddr8,
876    crate::postgres::types::Point,
877    crate::postgres::types::LineString,
878    crate::postgres::types::Rect,
879    crate::postgres::types::BitString,
880    crate::postgres::types::Line,
881    crate::postgres::types::LineSegment,
882    crate::postgres::types::Polygon,
883    crate::postgres::types::Circle,
884    crate::postgres::types::Enum
885);
886
887// MySQL integer widening. Unsigned sources are accepted by a signed target
888// only when the entire source range fits in that target. The reverse direction
889// is deliberately absent because negative values cannot be represented.
890assign_to!(crate::mysql::types::SmallInt;
891    crate::mysql::types::TinyInt,
892    crate::mysql::types::TinyIntUnsigned
893);
894assign_to!(crate::mysql::types::MediumInt;
895    crate::mysql::types::TinyInt,
896    crate::mysql::types::SmallInt,
897    // Rust has no 24-bit integer. Selected MEDIUMINT values use i32, whose
898    // dialect marker is INT, and the server validates the column range.
899    crate::mysql::types::Int,
900    crate::mysql::types::TinyIntUnsigned,
901    crate::mysql::types::SmallIntUnsigned,
902    crate::mysql::types::Year
903);
904assign_to!(crate::mysql::types::Int;
905    crate::mysql::types::TinyInt,
906    crate::mysql::types::SmallInt,
907    crate::mysql::types::MediumInt,
908    crate::mysql::types::TinyIntUnsigned,
909    crate::mysql::types::SmallIntUnsigned,
910    crate::mysql::types::MediumIntUnsigned,
911    crate::mysql::types::Year
912);
913assign_to!(crate::mysql::types::BigInt;
914    crate::mysql::types::TinyInt,
915    crate::mysql::types::SmallInt,
916    crate::mysql::types::MediumInt,
917    crate::mysql::types::Int,
918    crate::mysql::types::TinyIntUnsigned,
919    crate::mysql::types::SmallIntUnsigned,
920    crate::mysql::types::MediumIntUnsigned,
921    crate::mysql::types::IntUnsigned,
922    crate::mysql::types::Year
923);
924assign_to!(crate::mysql::types::SmallIntUnsigned;
925    crate::mysql::types::TinyIntUnsigned,
926    crate::mysql::types::Year
927);
928assign_to!(crate::mysql::types::MediumIntUnsigned;
929    crate::mysql::types::TinyIntUnsigned,
930    crate::mysql::types::SmallIntUnsigned,
931    // Rust has no 24-bit unsigned integer; see the signed case above.
932    crate::mysql::types::IntUnsigned,
933    crate::mysql::types::Year
934);
935assign_to!(crate::mysql::types::IntUnsigned;
936    crate::mysql::types::TinyIntUnsigned,
937    crate::mysql::types::SmallIntUnsigned,
938    crate::mysql::types::MediumIntUnsigned,
939    crate::mysql::types::Year
940);
941assign_to!(crate::mysql::types::BigIntUnsigned;
942    crate::mysql::types::TinyIntUnsigned,
943    crate::mysql::types::SmallIntUnsigned,
944    crate::mysql::types::MediumIntUnsigned,
945    crate::mysql::types::IntUnsigned,
946    crate::mysql::types::Year
947);
948// Rust represents YEAR as u16, whose dialect value marker is
949// SmallIntUnsigned. The server remains responsible for YEAR's narrower
950// calendar domain (the same way it validates VARCHAR lengths and DECIMAL
951// precision).
952assign_to!(crate::mysql::types::Year; crate::mysql::types::SmallIntUnsigned);
953
954// Numeric targets follow the existing PostgreSQL policy: exact integers may be
955// bound to floating and decimal columns, and FLOAT widens to DOUBLE. DECIMAL
956// also accepts approximate values because MySQL performs that conversion.
957assign_to!(crate::mysql::types::Float;
958    crate::mysql::types::TinyInt,
959    crate::mysql::types::SmallInt,
960    crate::mysql::types::MediumInt,
961    crate::mysql::types::Int,
962    crate::mysql::types::BigInt,
963    crate::mysql::types::TinyIntUnsigned,
964    crate::mysql::types::SmallIntUnsigned,
965    crate::mysql::types::MediumIntUnsigned,
966    crate::mysql::types::IntUnsigned,
967    crate::mysql::types::BigIntUnsigned,
968    crate::mysql::types::Year
969);
970assign_to!(crate::mysql::types::Double;
971    crate::mysql::types::TinyInt,
972    crate::mysql::types::SmallInt,
973    crate::mysql::types::MediumInt,
974    crate::mysql::types::Int,
975    crate::mysql::types::BigInt,
976    crate::mysql::types::TinyIntUnsigned,
977    crate::mysql::types::SmallIntUnsigned,
978    crate::mysql::types::MediumIntUnsigned,
979    crate::mysql::types::IntUnsigned,
980    crate::mysql::types::BigIntUnsigned,
981    crate::mysql::types::Year,
982    crate::mysql::types::Float
983);
984assign_to!(crate::mysql::types::Decimal;
985    crate::mysql::types::TinyInt,
986    crate::mysql::types::SmallInt,
987    crate::mysql::types::MediumInt,
988    crate::mysql::types::Int,
989    crate::mysql::types::BigInt,
990    crate::mysql::types::TinyIntUnsigned,
991    crate::mysql::types::SmallIntUnsigned,
992    crate::mysql::types::MediumIntUnsigned,
993    crate::mysql::types::IntUnsigned,
994    crate::mysql::types::BigIntUnsigned,
995    crate::mysql::types::Year,
996    crate::mysql::types::Float,
997    crate::mysql::types::Double
998);
999
1000// When optional domain crates are disabled, rows decode these values as
1001// strings. Accepting Text closes that intentional feature-off round trip; the
1002// MySQL server remains the parser and validator at the database boundary.
1003assign_to!(crate::mysql::types::Decimal; crate::mysql::types::Text);
1004assign_to!(crate::mysql::types::Json; crate::mysql::types::Text);
1005assign_to!(crate::mysql::types::Date; crate::mysql::types::Text);
1006assign_to!(crate::mysql::types::Time; crate::mysql::types::Text);
1007assign_to!(crate::mysql::types::DateTime; crate::mysql::types::Text);
1008assign_to!(crate::mysql::types::Timestamp; crate::mysql::types::Text);
1009
1010// Text and binary storage families are interchangeable at bind time. MySQL
1011// validates inline ENUM and SET membership at the column boundary, so their
1012// generated Rust string values must be accepted as text inputs here.
1013mutual_assign!(
1014    crate::mysql::types::Char,
1015    crate::mysql::types::Varchar,
1016    crate::mysql::types::TinyText,
1017    crate::mysql::types::Text,
1018    crate::mysql::types::MediumText,
1019    crate::mysql::types::LongText
1020);
1021assign_to!(crate::mysql::types::Char;
1022    crate::mysql::types::Enum,
1023    crate::mysql::types::Set
1024);
1025assign_to!(crate::mysql::types::Varchar;
1026    crate::mysql::types::Enum,
1027    crate::mysql::types::Set
1028);
1029assign_to!(crate::mysql::types::TinyText;
1030    crate::mysql::types::Enum,
1031    crate::mysql::types::Set
1032);
1033assign_to!(crate::mysql::types::Text;
1034    crate::mysql::types::Enum,
1035    crate::mysql::types::Set
1036);
1037assign_to!(crate::mysql::types::MediumText;
1038    crate::mysql::types::Enum,
1039    crate::mysql::types::Set
1040);
1041assign_to!(crate::mysql::types::LongText;
1042    crate::mysql::types::Enum,
1043    crate::mysql::types::Set
1044);
1045assign_to!(crate::mysql::types::Enum;
1046    crate::mysql::types::Char,
1047    crate::mysql::types::Varchar,
1048    crate::mysql::types::TinyText,
1049    crate::mysql::types::Text,
1050    crate::mysql::types::MediumText,
1051    crate::mysql::types::LongText
1052);
1053assign_to!(crate::mysql::types::Set;
1054    crate::mysql::types::Char,
1055    crate::mysql::types::Varchar,
1056    crate::mysql::types::TinyText,
1057    crate::mysql::types::Text,
1058    crate::mysql::types::MediumText,
1059    crate::mysql::types::LongText
1060);
1061mutual_assign!(
1062    crate::mysql::types::Binary,
1063    crate::mysql::types::Varbinary,
1064    crate::mysql::types::TinyBlob,
1065    crate::mysql::types::Blob,
1066    crate::mysql::types::MediumBlob,
1067    crate::mysql::types::LongBlob,
1068    crate::mysql::types::Bit
1069);
1070// BIT may retain its packed bytes through the binary family above or decode as
1071// an unsigned integer (and as bool for BIT(1)). MySQL validates the declared
1072// bit width at the column boundary.
1073assign_to!(crate::mysql::types::Bit;
1074    crate::mysql::types::Boolean,
1075    crate::mysql::types::TinyIntUnsigned,
1076    crate::mysql::types::SmallIntUnsigned,
1077    crate::mysql::types::MediumIntUnsigned,
1078    crate::mysql::types::IntUnsigned,
1079    crate::mysql::types::BigIntUnsigned
1080);
1081
1082// Any accepts all concrete MySQL markers.
1083any_assign!(crate::mysql::types::Any;
1084    crate::mysql::types::TinyInt,
1085    crate::mysql::types::TinyIntUnsigned,
1086    crate::mysql::types::SmallInt,
1087    crate::mysql::types::SmallIntUnsigned,
1088    crate::mysql::types::MediumInt,
1089    crate::mysql::types::MediumIntUnsigned,
1090    crate::mysql::types::Int,
1091    crate::mysql::types::IntUnsigned,
1092    crate::mysql::types::BigInt,
1093    crate::mysql::types::BigIntUnsigned,
1094    crate::mysql::types::Float,
1095    crate::mysql::types::Double,
1096    crate::mysql::types::Decimal,
1097    crate::mysql::types::Boolean,
1098    crate::mysql::types::Char,
1099    crate::mysql::types::Varchar,
1100    crate::mysql::types::TinyText,
1101    crate::mysql::types::Text,
1102    crate::mysql::types::MediumText,
1103    crate::mysql::types::LongText,
1104    crate::mysql::types::Binary,
1105    crate::mysql::types::Varbinary,
1106    crate::mysql::types::TinyBlob,
1107    crate::mysql::types::Blob,
1108    crate::mysql::types::MediumBlob,
1109    crate::mysql::types::LongBlob,
1110    crate::mysql::types::Json,
1111    crate::mysql::types::Date,
1112    crate::mysql::types::Time,
1113    crate::mysql::types::DateTime,
1114    crate::mysql::types::Timestamp,
1115    crate::mysql::types::Year,
1116    crate::mysql::types::Enum,
1117    crate::mysql::types::Set,
1118    crate::mysql::types::Bit
1119);
1120
1121// =============================================================================
1122// Tuple compatibility
1123// =============================================================================
1124
1125macro_rules! seq_dual {
1126    (@acc $callback:ident [$($sa:ident),*] [$($da:ident),*]) => {};
1127    (@acc $callback:ident [$($sa:ident),*] [$($da:ident),*] ($s:ident, $d:ident) $($rest:tt)*) => {
1128        $callback!($($sa,)* $s; $($da,)* $d);
1129        seq_dual!(@acc $callback [$($sa,)* $s] [$($da,)* $d] $($rest)*);
1130    };
1131    ($callback:ident; $($pairs:tt)+) => {
1132        seq_dual!(@acc $callback [] [] $($pairs)+);
1133    };
1134    (@from $callback:ident [$($sa:ident),*] [$($da:ident),*]; $($pairs:tt)+) => {
1135        seq_dual!(@acc $callback [$($sa),*] [$($da),*] $($pairs)+);
1136    };
1137}
1138
1139macro_rules! with_dual_col_sizes_8 {
1140    ($callback:ident) => {
1141        seq_dual!($callback;
1142            (T0,U0) (T1,U1) (T2,U2) (T3,U3)
1143            (T4,U4) (T5,U5) (T6,U6) (T7,U7)
1144        );
1145    };
1146}
1147
1148#[allow(unused_macros)]
1149macro_rules! with_dual_col_sizes_16 {
1150    ($callback:ident) => {
1151        seq_dual!(@from $callback
1152            [T0,T1,T2,T3,T4,T5,T6,T7]
1153            [U0,U1,U2,U3,U4,U5,U6,U7];
1154            (T8,U8) (T9,U9) (T10,U10) (T11,U11)
1155            (T12,U12) (T13,U13) (T14,U14) (T15,U15)
1156        );
1157    };
1158}
1159
1160#[allow(unused_macros)]
1161macro_rules! with_dual_col_sizes_32 {
1162    ($callback:ident) => {
1163        seq_dual!(@from $callback
1164            [T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15]
1165            [U0,U1,U2,U3,U4,U5,U6,U7,U8,U9,U10,U11,U12,U13,U14,U15];
1166            (T16,U16) (T17,U17) (T18,U18) (T19,U19)
1167            (T20,U20) (T21,U21) (T22,U22) (T23,U23)
1168            (T24,U24) (T25,U25) (T26,U26) (T27,U27)
1169            (T28,U28) (T29,U29) (T30,U30) (T31,U31)
1170        );
1171    };
1172}
1173
1174#[allow(unused_macros)]
1175macro_rules! with_dual_col_sizes_64 {
1176    ($callback:ident) => {
1177        seq_dual!(@from $callback
1178            [T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,
1179             T16,T17,T18,T19,T20,T21,T22,T23,T24,T25,T26,T27,T28,T29,T30,T31]
1180            [U0,U1,U2,U3,U4,U5,U6,U7,U8,U9,U10,U11,U12,U13,U14,U15,
1181             U16,U17,U18,U19,U20,U21,U22,U23,U24,U25,U26,U27,U28,U29,U30,U31];
1182            (T32,U32) (T33,U33) (T34,U34) (T35,U35)
1183            (T36,U36) (T37,U37) (T38,U38) (T39,U39)
1184            (T40,U40) (T41,U41) (T42,U42) (T43,U43)
1185            (T44,U44) (T45,U45) (T46,U46) (T47,U47)
1186            (T48,U48) (T49,U49) (T50,U50) (T51,U51)
1187            (T52,U52) (T53,U53) (T54,U54) (T55,U55)
1188            (T56,U56) (T57,U57) (T58,U58) (T59,U59)
1189            (T60,U60) (T61,U61) (T62,U62) (T63,U63)
1190        );
1191    };
1192}
1193
1194#[allow(unused_macros)]
1195macro_rules! with_dual_col_sizes_128 {
1196    ($callback:ident) => {
1197        seq_dual!(@from $callback
1198            [T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,
1199             T16,T17,T18,T19,T20,T21,T22,T23,T24,T25,T26,T27,T28,T29,T30,T31,
1200             T32,T33,T34,T35,T36,T37,T38,T39,T40,T41,T42,T43,T44,T45,T46,T47,
1201             T48,T49,T50,T51,T52,T53,T54,T55,T56,T57,T58,T59,T60,T61,T62,T63]
1202            [U0,U1,U2,U3,U4,U5,U6,U7,U8,U9,U10,U11,U12,U13,U14,U15,
1203             U16,U17,U18,U19,U20,U21,U22,U23,U24,U25,U26,U27,U28,U29,U30,U31,
1204             U32,U33,U34,U35,U36,U37,U38,U39,U40,U41,U42,U43,U44,U45,U46,U47,
1205             U48,U49,U50,U51,U52,U53,U54,U55,U56,U57,U58,U59,U60,U61,U62,U63];
1206            (T64,U64) (T65,U65) (T66,U66) (T67,U67)
1207            (T68,U68) (T69,U69) (T70,U70) (T71,U71)
1208            (T72,U72) (T73,U73) (T74,U74) (T75,U75)
1209            (T76,U76) (T77,U77) (T78,U78) (T79,U79)
1210            (T80,U80) (T81,U81) (T82,U82) (T83,U83)
1211            (T84,U84) (T85,U85) (T86,U86) (T87,U87)
1212            (T88,U88) (T89,U89) (T90,U90) (T91,U91)
1213            (T92,U92) (T93,U93) (T94,U94) (T95,U95)
1214            (T96,U96) (T97,U97) (T98,U98) (T99,U99)
1215            (T100,U100) (T101,U101) (T102,U102) (T103,U103)
1216            (T104,U104) (T105,U105) (T106,U106) (T107,U107)
1217            (T108,U108) (T109,U109) (T110,U110) (T111,U111)
1218            (T112,U112) (T113,U113) (T114,U114) (T115,U115)
1219            (T116,U116) (T117,U117) (T118,U118) (T119,U119)
1220            (T120,U120) (T121,U121) (T122,U122) (T123,U123)
1221            (T124,U124) (T125,U125) (T126,U126) (T127,U127)
1222        );
1223    };
1224}
1225
1226#[allow(unused_macros)]
1227macro_rules! with_dual_col_sizes_200 {
1228    ($callback:ident) => {
1229        seq_dual!(@from $callback
1230            [T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,
1231             T16,T17,T18,T19,T20,T21,T22,T23,T24,T25,T26,T27,T28,T29,T30,T31,
1232             T32,T33,T34,T35,T36,T37,T38,T39,T40,T41,T42,T43,T44,T45,T46,T47,
1233             T48,T49,T50,T51,T52,T53,T54,T55,T56,T57,T58,T59,T60,T61,T62,T63,
1234             T64,T65,T66,T67,T68,T69,T70,T71,T72,T73,T74,T75,T76,T77,T78,T79,
1235             T80,T81,T82,T83,T84,T85,T86,T87,T88,T89,T90,T91,T92,T93,T94,T95,
1236             T96,T97,T98,T99,T100,T101,T102,T103,T104,T105,T106,T107,T108,T109,T110,T111,
1237             T112,T113,T114,T115,T116,T117,T118,T119,T120,T121,T122,T123,T124,T125,T126,T127]
1238            [U0,U1,U2,U3,U4,U5,U6,U7,U8,U9,U10,U11,U12,U13,U14,U15,
1239             U16,U17,U18,U19,U20,U21,U22,U23,U24,U25,U26,U27,U28,U29,U30,U31,
1240             U32,U33,U34,U35,U36,U37,U38,U39,U40,U41,U42,U43,U44,U45,U46,U47,
1241             U48,U49,U50,U51,U52,U53,U54,U55,U56,U57,U58,U59,U60,U61,U62,U63,
1242             U64,U65,U66,U67,U68,U69,U70,U71,U72,U73,U74,U75,U76,U77,U78,U79,
1243             U80,U81,U82,U83,U84,U85,U86,U87,U88,U89,U90,U91,U92,U93,U94,U95,
1244             U96,U97,U98,U99,U100,U101,U102,U103,U104,U105,U106,U107,U108,U109,U110,U111,
1245             U112,U113,U114,U115,U116,U117,U118,U119,U120,U121,U122,U123,U124,U125,U126,U127];
1246            (T128,U128) (T129,U129) (T130,U130) (T131,U131)
1247            (T132,U132) (T133,U133) (T134,U134) (T135,U135)
1248            (T136,U136) (T137,U137) (T138,U138) (T139,U139)
1249            (T140,U140) (T141,U141) (T142,U142) (T143,U143)
1250            (T144,U144) (T145,U145) (T146,U146) (T147,U147)
1251            (T148,U148) (T149,U149) (T150,U150) (T151,U151)
1252            (T152,U152) (T153,U153) (T154,U154) (T155,U155)
1253            (T156,U156) (T157,U157) (T158,U158) (T159,U159)
1254            (T160,U160) (T161,U161) (T162,U162) (T163,U163)
1255            (T164,U164) (T165,U165) (T166,U166) (T167,U167)
1256            (T168,U168) (T169,U169) (T170,U170) (T171,U171)
1257            (T172,U172) (T173,U173) (T174,U174) (T175,U175)
1258            (T176,U176) (T177,U177) (T178,U178) (T179,U179)
1259            (T180,U180) (T181,U181) (T182,U182) (T183,U183)
1260            (T184,U184) (T185,U185) (T186,U186) (T187,U187)
1261            (T188,U188) (T189,U189) (T190,U190) (T191,U191)
1262            (T192,U192) (T193,U193) (T194,U194) (T195,U195)
1263            (T196,U196) (T197,U197) (T198,U198) (T199,U199)
1264        );
1265    };
1266}
1267
1268macro_rules! impl_tuple_compatible {
1269    ($($T:ident),+; $($U:ident),+) => {
1270        impl<$($T, $U),+> Compatible<($($U,)+)> for ($($T,)+)
1271        where
1272            $($T: DataType + Compatible<$U>, $U: DataType,)+
1273        {}
1274    };
1275}
1276
1277macro_rules! impl_tuple_assignable {
1278    ($($S:ident),+; $($D:ident),+) => {
1279        impl<$($S, $D),+> Assignable<($($S,)+)> for ($($D,)+)
1280        where
1281            $($S: DataType, $D: DataType + Assignable<$S>,)+
1282        {}
1283    };
1284}
1285
1286with_dual_col_sizes_8!(impl_tuple_compatible);
1287
1288#[cfg(any(
1289    feature = "col16",
1290    feature = "col32",
1291    feature = "col64",
1292    feature = "col128",
1293    feature = "col200"
1294))]
1295with_dual_col_sizes_16!(impl_tuple_compatible);
1296
1297#[cfg(any(
1298    feature = "col32",
1299    feature = "col64",
1300    feature = "col128",
1301    feature = "col200"
1302))]
1303with_dual_col_sizes_32!(impl_tuple_compatible);
1304
1305#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
1306with_dual_col_sizes_64!(impl_tuple_compatible);
1307
1308#[cfg(any(feature = "col128", feature = "col200"))]
1309with_dual_col_sizes_128!(impl_tuple_compatible);
1310
1311#[cfg(feature = "col200")]
1312with_dual_col_sizes_200!(impl_tuple_compatible);
1313
1314with_dual_col_sizes_8!(impl_tuple_assignable);
1315
1316#[cfg(any(
1317    feature = "col16",
1318    feature = "col32",
1319    feature = "col64",
1320    feature = "col128",
1321    feature = "col200"
1322))]
1323with_dual_col_sizes_16!(impl_tuple_assignable);
1324
1325#[cfg(any(
1326    feature = "col32",
1327    feature = "col64",
1328    feature = "col128",
1329    feature = "col200"
1330))]
1331with_dual_col_sizes_32!(impl_tuple_assignable);
1332
1333#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
1334with_dual_col_sizes_64!(impl_tuple_assignable);
1335
1336#[cfg(any(feature = "col128", feature = "col200"))]
1337with_dual_col_sizes_128!(impl_tuple_assignable);
1338
1339#[cfg(feature = "col200")]
1340with_dual_col_sizes_200!(impl_tuple_assignable);
1341
1342#[cfg(test)]
1343mod tests {
1344    use super::{Assignable, Compatible, DataType};
1345
1346    fn assert_compatible<L, R>()
1347    where
1348        L: Compatible<R>,
1349        R: DataType,
1350    {
1351    }
1352
1353    fn assert_assignable<Target, Source>()
1354    where
1355        Target: Assignable<Source>,
1356        Source: DataType,
1357    {
1358    }
1359
1360    #[test]
1361    fn mysql_compatibility_covers_the_intended_type_families() {
1362        assert_compatible::<crate::mysql::types::TinyInt, crate::mysql::types::BigInt>();
1363        assert_compatible::<
1364            crate::mysql::types::TinyIntUnsigned,
1365            crate::mysql::types::BigIntUnsigned,
1366        >();
1367        assert_compatible::<crate::mysql::types::Year, crate::mysql::types::Int>();
1368        assert_compatible::<crate::mysql::types::Decimal, crate::mysql::types::Double>();
1369        assert_compatible::<crate::mysql::types::Enum, crate::mysql::types::Text>();
1370        assert_compatible::<crate::mysql::types::Set, crate::mysql::types::Varchar>();
1371        assert_compatible::<crate::mysql::types::Bit, crate::mysql::types::Blob>();
1372        assert_compatible::<crate::mysql::types::DateTime, crate::mysql::types::Timestamp>();
1373        assert_compatible::<crate::mysql::types::Any, crate::mysql::types::Json>();
1374        assert_compatible::<crate::Placeholder, crate::mysql::types::Json>();
1375    }
1376
1377    #[test]
1378    fn mysql_assignment_allows_widening_and_storage_families() {
1379        assert_assignable::<crate::mysql::types::SmallInt, crate::mysql::types::TinyIntUnsigned>();
1380        assert_assignable::<crate::mysql::types::MediumInt, crate::mysql::types::Int>();
1381        assert_assignable::<crate::mysql::types::MediumIntUnsigned, crate::mysql::types::IntUnsigned>(
1382        );
1383        assert_assignable::<crate::mysql::types::BigInt, crate::mysql::types::IntUnsigned>();
1384        assert_assignable::<crate::mysql::types::BigIntUnsigned, crate::mysql::types::IntUnsigned>(
1385        );
1386        assert_assignable::<crate::mysql::types::Decimal, crate::mysql::types::BigIntUnsigned>();
1387        assert_assignable::<crate::mysql::types::Text, crate::mysql::types::Enum>();
1388        assert_assignable::<crate::mysql::types::Enum, crate::mysql::types::Text>();
1389        assert_assignable::<crate::mysql::types::Set, crate::mysql::types::Text>();
1390        assert_assignable::<crate::mysql::types::Year, crate::mysql::types::SmallIntUnsigned>();
1391        assert_assignable::<crate::mysql::types::Bit, crate::mysql::types::Blob>();
1392        assert_assignable::<crate::mysql::types::Bit, crate::mysql::types::Boolean>();
1393        assert_assignable::<crate::mysql::types::Bit, crate::mysql::types::BigIntUnsigned>();
1394        assert_assignable::<crate::mysql::types::Json, crate::mysql::types::Text>();
1395        assert_assignable::<crate::mysql::types::Time, crate::mysql::types::Text>();
1396        assert_assignable::<crate::mysql::types::Any, crate::mysql::types::Json>();
1397    }
1398}