Skip to main content

drizzle_types/sql/
ops.rs

1use super::Numeric;
2
3/// Maps the left-hand numeric type to the result type of an arithmetic
4/// operation (`+`, `-`, `*`, `/`, `%`).
5///
6/// The output follows SQL's type promotion rules: narrower types widen to
7/// wider types (e.g. `Int2 + Int8 → Int8`, `Int4 + Float8 → Float8`).
8/// Within the same width, the left operand's type is preserved.
9#[diagnostic::on_unimplemented(
10    message = "arithmetic between `{Self}` and `{Rhs}` is not supported",
11    label = "both operands must be Numeric (Int, BigInt, Float, Double, etc.)"
12)]
13pub trait ArithmeticOutput<Rhs: Numeric = Self>: Numeric {
14    /// The resulting SQL type of the arithmetic expression.
15    type Output: Numeric;
16}
17
18// =============================================================================
19// SQLite arithmetic output
20// =============================================================================
21//
22// SQLite has only 3 numeric storage classes: Integer, Real, Numeric.
23// Integer + Integer → Integer, Real + anything → Real, etc.
24
25use crate::sqlite::types::{Integer, Numeric as SqliteNumeric, Real};
26
27// Integer op Integer → Integer
28impl ArithmeticOutput<Self> for Integer {
29    type Output = Self;
30}
31// Integer op Real → Real (widens to float)
32impl ArithmeticOutput<Real> for Integer {
33    type Output = Real;
34}
35// Integer op Numeric → Numeric
36impl ArithmeticOutput<SqliteNumeric> for Integer {
37    type Output = SqliteNumeric;
38}
39
40// Real op Integer → Real
41impl ArithmeticOutput<Integer> for Real {
42    type Output = Self;
43}
44// Real op Real → Real
45impl ArithmeticOutput<Self> for Real {
46    type Output = Self;
47}
48// Real op Numeric → Real
49impl ArithmeticOutput<SqliteNumeric> for Real {
50    type Output = Self;
51}
52
53// Numeric op Integer → Numeric
54impl ArithmeticOutput<Integer> for SqliteNumeric {
55    type Output = Self;
56}
57// Numeric op Real → Real (widens to float)
58impl ArithmeticOutput<Real> for SqliteNumeric {
59    type Output = Real;
60}
61// Numeric op Numeric → Numeric
62impl ArithmeticOutput<Self> for SqliteNumeric {
63    type Output = Self;
64}
65
66// SQLite Any ↔ all SQLite numeric types
67use crate::sqlite::types::Any as SqliteAny;
68
69impl ArithmeticOutput<Self> for SqliteAny {
70    type Output = Self;
71}
72impl ArithmeticOutput<Integer> for SqliteAny {
73    type Output = Self;
74}
75impl ArithmeticOutput<Real> for SqliteAny {
76    type Output = Self;
77}
78impl ArithmeticOutput<SqliteNumeric> for SqliteAny {
79    type Output = Self;
80}
81impl ArithmeticOutput<SqliteAny> for Integer {
82    type Output = SqliteAny;
83}
84impl ArithmeticOutput<SqliteAny> for Real {
85    type Output = SqliteAny;
86}
87impl ArithmeticOutput<SqliteAny> for SqliteNumeric {
88    type Output = SqliteAny;
89}
90
91// =============================================================================
92// PostgreSQL arithmetic output
93// =============================================================================
94//
95// PostgreSQL type promotion lattice:
96//   Int2 < Int4 < Int8 < Numeric
97//   Float4 < Float8
98//   Int + Float → Float (cross-family always widens to float)
99//   Any integer + Numeric → Numeric
100//   Any float + Numeric → Numeric (Float8)
101
102use crate::postgres::types::{Float4, Float8, Int2, Int4, Int8, Numeric as PgNumeric};
103
104/// Helper macro: generates `ArithmeticOutput` impls for a pair of PG types.
105/// `wider!(A, B => W)` means A op B → W.
106macro_rules! pg_arith {
107    ($lhs:ty, $rhs:ty => $out:ty) => {
108        impl ArithmeticOutput<$rhs> for $lhs {
109            type Output = $out;
110        }
111    };
112}
113
114// --- Int2 (SMALLINT) ---
115pg_arith!(Int2, Int2 => Int2);
116pg_arith!(Int2, Int4 => Int4); // widens to Int4
117pg_arith!(Int2, Int8 => Int8); // widens to Int8
118pg_arith!(Int2, Float4 => Float4); // cross-family → float
119pg_arith!(Int2, Float8 => Float8); // cross-family → float
120pg_arith!(Int2, PgNumeric => PgNumeric);
121
122// --- Int4 (INTEGER) ---
123pg_arith!(Int4, Int2 => Int4); // Int4 is wider
124pg_arith!(Int4, Int4 => Int4);
125pg_arith!(Int4, Int8 => Int8); // widens to Int8
126pg_arith!(Int4, Float4 => Float8); // cross-family → Float8 (PG rule)
127pg_arith!(Int4, Float8 => Float8); // cross-family → Float8
128pg_arith!(Int4, PgNumeric => PgNumeric);
129
130// --- Int8 (BIGINT) ---
131pg_arith!(Int8, Int2 => Int8); // Int8 is wider
132pg_arith!(Int8, Int4 => Int8); // Int8 is wider
133pg_arith!(Int8, Int8 => Int8);
134pg_arith!(Int8, Float4 => Float8); // cross-family → Float8
135pg_arith!(Int8, Float8 => Float8); // cross-family → Float8
136pg_arith!(Int8, PgNumeric => PgNumeric);
137
138// --- Float4 (REAL) ---
139pg_arith!(Float4, Int2 => Float4); // float absorbs int
140pg_arith!(Float4, Int4 => Float8); // PG: float4 + int4 → float8
141pg_arith!(Float4, Int8 => Float8); // PG: float4 + int8 → float8
142pg_arith!(Float4, Float4 => Float4);
143pg_arith!(Float4, Float8 => Float8); // widens to Float8
144pg_arith!(Float4, PgNumeric => Float8);
145
146// --- Float8 (DOUBLE PRECISION) ---
147pg_arith!(Float8, Int2 => Float8);
148pg_arith!(Float8, Int4 => Float8);
149pg_arith!(Float8, Int8 => Float8);
150pg_arith!(Float8, Float4 => Float8); // Float8 is wider
151pg_arith!(Float8, Float8 => Float8);
152pg_arith!(Float8, PgNumeric => Float8);
153
154// --- Numeric (NUMERIC/DECIMAL) ---
155pg_arith!(PgNumeric, Int2 => PgNumeric);
156pg_arith!(PgNumeric, Int4 => PgNumeric);
157pg_arith!(PgNumeric, Int8 => PgNumeric);
158pg_arith!(PgNumeric, Float4 => Float8); // PG casts numeric+float → float8
159pg_arith!(PgNumeric, Float8 => Float8);
160pg_arith!(PgNumeric, PgNumeric => PgNumeric);