1use super::Numeric;
4
5#[doc(hidden)]
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
9pub struct ArithmeticOp;
10
11#[doc(hidden)]
13#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
14pub struct AddOp;
15
16#[doc(hidden)]
18#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
19pub struct SubOp;
20
21#[doc(hidden)]
23#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
24pub struct MulOp;
25
26#[doc(hidden)]
28#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
29pub struct DivOp;
30
31#[doc(hidden)]
33#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
34pub struct RemOp;
35
36#[doc(hidden)]
38#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
39pub struct PropagateNullability;
40
41#[doc(hidden)]
43#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
44pub struct AlwaysNullable;
45
46#[doc(hidden)]
48pub trait ArithmeticNullability: super::private::Sealed + Copy + 'static {}
49
50impl super::private::Sealed for PropagateNullability {}
51impl super::private::Sealed for AlwaysNullable {}
52impl ArithmeticNullability for PropagateNullability {}
53impl ArithmeticNullability for AlwaysNullable {}
54
55#[diagnostic::on_unimplemented(
95 message = "arithmetic between `{Self}` and `{Rhs}` is not supported",
96 label = "both operands must be Numeric (Int, BigInt, Float, Double, etc.)"
97)]
98pub trait ArithmeticOutput<Rhs: Numeric = Self, Op = ArithmeticOp>: Numeric {
99 type Output: Numeric;
101
102 type Nullability: ArithmeticNullability;
108}
109
110#[diagnostic::on_unimplemented(
115 message = "unary negation of `{Self}` is not supported",
116 label = "the dialect has no numeric result mapping for this operand"
117)]
118pub trait NegOutput: Numeric {
119 type Output: Numeric;
121}
122
123macro_rules! neg_output {
124 ($input:ty => $out:ty) => {
125 impl NegOutput for $input {
126 type Output = $out;
127 }
128 };
129}
130
131macro_rules! arithmetic_output {
134 ($lhs:ty, $rhs:ty => $out:ty) => {
135 arithmetic_output!($lhs, $rhs => $out; zero_divisor: PropagateNullability);
136 };
137 ($lhs:ty, $rhs:ty => $out:ty; zero_divisor: $zero_divisor:ty) => {
138 impl ArithmeticOutput<$rhs> for $lhs {
139 type Output = $out;
140 type Nullability = PropagateNullability;
141 }
142
143 impl ArithmeticOutput<$rhs, AddOp> for $lhs {
144 type Output = $out;
145 type Nullability = PropagateNullability;
146 }
147
148 impl ArithmeticOutput<$rhs, SubOp> for $lhs {
149 type Output = $out;
150 type Nullability = PropagateNullability;
151 }
152
153 impl ArithmeticOutput<$rhs, MulOp> for $lhs {
154 type Output = $out;
155 type Nullability = PropagateNullability;
156 }
157
158 impl ArithmeticOutput<$rhs, DivOp> for $lhs {
159 type Output = $out;
160 type Nullability = $zero_divisor;
161 }
162
163 impl ArithmeticOutput<$rhs, RemOp> for $lhs {
164 type Output = $out;
165 type Nullability = $zero_divisor;
166 }
167 };
168}
169
170use crate::sqlite::types::{Integer, Numeric as SqliteNumeric, Real};
178
179arithmetic_output!(Integer, Integer => Integer; zero_divisor: AlwaysNullable);
181arithmetic_output!(Integer, Real => Real; zero_divisor: AlwaysNullable);
183arithmetic_output!(Integer, SqliteNumeric => SqliteNumeric; zero_divisor: AlwaysNullable);
185
186arithmetic_output!(Real, Integer => Real; zero_divisor: AlwaysNullable);
188arithmetic_output!(Real, Real => Real; zero_divisor: AlwaysNullable);
190arithmetic_output!(Real, SqliteNumeric => Real; zero_divisor: AlwaysNullable);
192
193arithmetic_output!(SqliteNumeric, Integer => SqliteNumeric; zero_divisor: AlwaysNullable);
195arithmetic_output!(SqliteNumeric, Real => Real; zero_divisor: AlwaysNullable);
197arithmetic_output!(SqliteNumeric, SqliteNumeric => SqliteNumeric; zero_divisor: AlwaysNullable);
199
200use crate::sqlite::types::Any as SqliteAny;
202
203arithmetic_output!(SqliteAny, SqliteAny => SqliteAny);
204arithmetic_output!(SqliteAny, Integer => SqliteAny);
205arithmetic_output!(SqliteAny, Real => SqliteAny);
206arithmetic_output!(SqliteAny, SqliteNumeric => SqliteAny);
207arithmetic_output!(Integer, SqliteAny => SqliteAny);
208arithmetic_output!(Real, SqliteAny => SqliteAny);
209arithmetic_output!(SqliteNumeric, SqliteAny => SqliteAny);
210
211neg_output!(Integer => Integer);
212neg_output!(Real => Real);
213neg_output!(SqliteNumeric => SqliteNumeric);
214neg_output!(SqliteAny => SqliteAny);
215
216use crate::postgres::types::{Float4, Float8, Int2, Int4, Int8, Numeric as PgNumeric};
228
229arithmetic_output!(Int2, Int2 => Int2);
231arithmetic_output!(Int2, Int4 => Int4); arithmetic_output!(Int2, Int8 => Int8); arithmetic_output!(Int2, Float4 => Float4); arithmetic_output!(Int2, Float8 => Float8); arithmetic_output!(Int2, PgNumeric => PgNumeric);
236
237arithmetic_output!(Int4, Int2 => Int4); arithmetic_output!(Int4, Int4 => Int4);
240arithmetic_output!(Int4, Int8 => Int8); arithmetic_output!(Int4, Float4 => Float8); arithmetic_output!(Int4, Float8 => Float8); arithmetic_output!(Int4, PgNumeric => PgNumeric);
244
245arithmetic_output!(Int8, Int2 => Int8); arithmetic_output!(Int8, Int4 => Int8); arithmetic_output!(Int8, Int8 => Int8);
249arithmetic_output!(Int8, Float4 => Float8); arithmetic_output!(Int8, Float8 => Float8); arithmetic_output!(Int8, PgNumeric => PgNumeric);
252
253arithmetic_output!(Float4, Int2 => Float4); arithmetic_output!(Float4, Int4 => Float8); arithmetic_output!(Float4, Int8 => Float8); arithmetic_output!(Float4, Float4 => Float4);
258arithmetic_output!(Float4, Float8 => Float8); arithmetic_output!(Float4, PgNumeric => Float8);
260
261arithmetic_output!(Float8, Int2 => Float8);
263arithmetic_output!(Float8, Int4 => Float8);
264arithmetic_output!(Float8, Int8 => Float8);
265arithmetic_output!(Float8, Float4 => Float8); arithmetic_output!(Float8, Float8 => Float8);
267arithmetic_output!(Float8, PgNumeric => Float8);
268
269arithmetic_output!(PgNumeric, Int2 => PgNumeric);
271arithmetic_output!(PgNumeric, Int4 => PgNumeric);
272arithmetic_output!(PgNumeric, Int8 => PgNumeric);
273arithmetic_output!(PgNumeric, Float4 => Float8); arithmetic_output!(PgNumeric, Float8 => Float8);
275arithmetic_output!(PgNumeric, PgNumeric => PgNumeric);
276
277neg_output!(Int2 => Int2);
278neg_output!(Int4 => Int4);
279neg_output!(Int8 => Int8);
280neg_output!(Float4 => Float4);
281neg_output!(Float8 => Float8);
282neg_output!(PgNumeric => PgNumeric);
283
284use crate::mysql::types::{
294 BigInt as MyBigInt, BigIntUnsigned as MyBigIntUnsigned, Decimal as MyDecimal,
295 Double as MyDouble,
296};
297
298macro_rules! mysql_arithmetic {
299 (
300 signed: [$($signed:ty),+ $(,)?],
301 unsigned: [$($unsigned:ty),+ $(,)?],
302 decimal: $decimal:ty,
303 approximate: [$($approximate:ty),+ $(,)?],
304 ) => {
305 mysql_arithmetic!(@matrix [AddOp, SubOp, MulOp], PropagateNullability;
306 [$($signed),+], [$($signed),+] => MyBigInt);
307 mysql_arithmetic!(@matrix [AddOp, SubOp, MulOp], PropagateNullability;
308 [$($signed),+], [$($unsigned),+] => MyBigIntUnsigned);
309 mysql_arithmetic!(@matrix [AddOp, SubOp, MulOp], PropagateNullability;
310 [$($unsigned),+], [$($signed),+, $($unsigned),+] => MyBigIntUnsigned);
311
312 mysql_arithmetic!(@matrix [AddOp, SubOp, MulOp], PropagateNullability;
313 [$($signed),+, $($unsigned),+], [$decimal] => MyDecimal);
314 mysql_arithmetic!(@matrix [AddOp, SubOp, MulOp], PropagateNullability;
315 [$decimal], [$($signed),+, $($unsigned),+, $decimal] => MyDecimal);
316
317 mysql_arithmetic!(@matrix [AddOp, SubOp, MulOp], PropagateNullability;
318 [$($signed),+, $($unsigned),+, $decimal], [$($approximate),+] => MyDouble);
319 mysql_arithmetic!(@matrix [AddOp, SubOp, MulOp], PropagateNullability;
320 [$($approximate),+],
321 [$($signed),+, $($unsigned),+, $decimal, $($approximate),+] => MyDouble);
322
323 mysql_arithmetic!(@matrix [RemOp], AlwaysNullable;
324 [$($signed),+], [$($signed),+, $($unsigned),+] => MyBigInt);
325 mysql_arithmetic!(@matrix [RemOp], AlwaysNullable;
326 [$($unsigned),+], [$($signed),+, $($unsigned),+] => MyBigIntUnsigned);
327 mysql_arithmetic!(@matrix [RemOp], AlwaysNullable;
328 [$($signed),+, $($unsigned),+], [$decimal] => MyDecimal);
329 mysql_arithmetic!(@matrix [RemOp], AlwaysNullable;
330 [$decimal], [$($signed),+, $($unsigned),+, $decimal] => MyDecimal);
331 mysql_arithmetic!(@matrix [RemOp], AlwaysNullable;
332 [$($signed),+, $($unsigned),+, $decimal], [$($approximate),+] => MyDouble);
333 mysql_arithmetic!(@matrix [RemOp], AlwaysNullable;
334 [$($approximate),+],
335 [$($signed),+, $($unsigned),+, $decimal, $($approximate),+] => MyDouble);
336
337 mysql_arithmetic!(@matrix [DivOp], AlwaysNullable;
338 [$($signed),+, $($unsigned),+, $decimal],
339 [$($signed),+, $($unsigned),+, $decimal] => MyDecimal);
340 mysql_arithmetic!(@matrix [DivOp], AlwaysNullable;
341 [$($signed),+, $($unsigned),+, $decimal], [$($approximate),+] => MyDouble);
342 mysql_arithmetic!(@matrix [DivOp], AlwaysNullable;
343 [$($approximate),+],
344 [$($signed),+, $($unsigned),+, $decimal, $($approximate),+] => MyDouble);
345
346 $(neg_output!($signed => MyBigInt);)+
347 $(neg_output!($unsigned => MyBigInt);)+
348 neg_output!($decimal => MyDecimal);
349 $(neg_output!($approximate => MyDouble);)+
350 };
351 (@matrix $ops:tt, $nullability:ty;
352 [$($lhs:ty),+], $rhs:tt => $out:ty
353 ) => {
354 $(mysql_arithmetic!(@row $ops, $nullability; $lhs, $rhs => $out);)+
355 };
356 (@row [$op:ty $(, $remaining:ty)*], $nullability:ty;
357 $lhs:ty, [$($rhs:ty),+] => $out:ty
358 ) => {
359 $(
360 impl ArithmeticOutput<$rhs, $op> for $lhs {
361 type Output = $out;
362 type Nullability = $nullability;
363 }
364 )+
365 mysql_arithmetic!(@row [$($remaining),*], $nullability;
366 $lhs, [$($rhs),+] => $out);
367 };
368 (@row [], $nullability:ty; $lhs:ty, $rhs:tt => $out:ty) => {};
369}
370
371mysql_arithmetic! {
372 signed: [
373 crate::mysql::types::TinyInt,
374 crate::mysql::types::SmallInt,
375 crate::mysql::types::MediumInt,
376 crate::mysql::types::Int,
377 crate::mysql::types::BigInt,
378 ],
379 unsigned: [
380 crate::mysql::types::TinyIntUnsigned,
381 crate::mysql::types::SmallIntUnsigned,
382 crate::mysql::types::MediumIntUnsigned,
383 crate::mysql::types::IntUnsigned,
384 crate::mysql::types::BigIntUnsigned,
385 crate::mysql::types::Year,
386 ],
387 decimal: crate::mysql::types::Decimal,
388 approximate: [crate::mysql::types::Float, crate::mysql::types::Double],
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394 use crate::mysql::types as my;
395 use crate::postgres::types as pg;
396 use crate::sqlite::types as sqlite;
397
398 trait Same<T> {}
399 impl<T> Same<T> for T {}
400
401 fn assert_output<Lhs, Rhs, Op, Output, Nullability>()
402 where
403 Lhs: ArithmeticOutput<Rhs, Op, Output = Output>,
404 Rhs: Numeric,
405 Output: Numeric,
406 <Lhs as ArithmeticOutput<Rhs, Op>>::Nullability: Same<Nullability>,
407 {
408 }
409
410 fn assert_neg_output<Input, Output>()
411 where
412 Input: NegOutput<Output = Output>,
413 Output: Numeric,
414 {
415 }
416
417 #[test]
418 fn mysql_operator_result_types_follow_server_categories() {
419 assert_output::<my::Int, my::SmallInt, AddOp, my::BigInt, PropagateNullability>();
420 assert_output::<my::Int, my::IntUnsigned, SubOp, my::BigIntUnsigned, PropagateNullability>(
421 );
422 assert_output::<my::BigIntUnsigned, my::Int, MulOp, my::BigIntUnsigned, PropagateNullability>(
423 );
424 assert_output::<my::Int, my::Int, DivOp, my::Decimal, AlwaysNullable>();
425 assert_output::<my::Int, my::IntUnsigned, RemOp, my::BigInt, AlwaysNullable>();
426 assert_output::<my::IntUnsigned, my::Int, RemOp, my::BigIntUnsigned, AlwaysNullable>();
427 assert_output::<my::Decimal, my::Int, AddOp, my::Decimal, PropagateNullability>();
428 assert_output::<my::Float, my::Int, AddOp, my::Double, PropagateNullability>();
429 assert_output::<my::Int, my::Double, DivOp, my::Double, AlwaysNullable>();
430 }
431
432 #[test]
433 fn every_mysql_numeric_marker_has_operator_and_negation_policy() {
434 macro_rules! assert_numeric_policy {
435 ($($ty:ty),+ $(,)?) => {
436 $(
437 assert_output::<$ty, $ty, AddOp, _, PropagateNullability>();
438 assert_output::<$ty, $ty, DivOp, _, AlwaysNullable>();
439 assert_output::<$ty, $ty, RemOp, _, AlwaysNullable>();
440 assert_neg_output::<$ty, _>();
441 )+
442 };
443 }
444
445 assert_numeric_policy!(
446 my::TinyInt,
447 my::TinyIntUnsigned,
448 my::SmallInt,
449 my::SmallIntUnsigned,
450 my::MediumInt,
451 my::MediumIntUnsigned,
452 my::Int,
453 my::IntUnsigned,
454 my::BigInt,
455 my::BigIntUnsigned,
456 my::Year,
457 my::Decimal,
458 my::Float,
459 my::Double,
460 );
461 }
462
463 #[test]
464 fn legacy_operator_independent_projection_remains_available() {
465 fn assert_legacy<Lhs, Rhs, Output>()
466 where
467 Lhs: ArithmeticOutput<Rhs, Output = Output>,
468 Rhs: Numeric,
469 Output: Numeric,
470 {
471 }
472
473 assert_legacy::<sqlite::Integer, sqlite::Real, sqlite::Real>();
474 assert_legacy::<pg::Int4, pg::Float8, pg::Float8>();
475 }
476
477 #[test]
478 fn sqlite_zero_divisor_operators_are_nullable() {
479 assert_output::<sqlite::Integer, sqlite::Integer, DivOp, sqlite::Integer, AlwaysNullable>();
480 assert_output::<sqlite::Integer, sqlite::Integer, RemOp, sqlite::Integer, AlwaysNullable>();
481 assert_output::<sqlite::Real, sqlite::Integer, DivOp, sqlite::Real, AlwaysNullable>();
482 }
483
484 #[test]
485 fn mysql_unary_negation_widens_to_a_signed_result() {
486 assert_neg_output::<my::TinyInt, my::BigInt>();
487 assert_neg_output::<my::BigIntUnsigned, my::BigInt>();
488 assert_neg_output::<my::Decimal, my::Decimal>();
489 assert_neg_output::<my::Float, my::Double>();
490 }
491}