Skip to main content

reifydb_type/value/type/
promote.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::cmp::min;
5
6use Type::*;
7
8use crate::value::r#type::Type;
9
10impl Type {
11	pub fn promote(left: Type, right: Type) -> Type {
12		if matches!(left, Any) || matches!(right, Any) {
13			return Any;
14		}
15
16		if matches!(left, Option(_)) || matches!(right, Option(_)) {
17			return left;
18		}
19
20		if left == Utf8 || right == Utf8 {
21			return Utf8;
22		}
23
24		if left == Boolean || right == Boolean {
25			return Boolean;
26		}
27
28		if left == Float8 || right == Float8 {
29			return Float8;
30		}
31
32		if left == Float4 || right == Float4 {
33			return Float8;
34		}
35
36		let signed_order = [Int1, Int2, Int4, Int8, Int16];
37		let unsigned_order = [Uint1, Uint2, Uint4, Uint8, Uint16];
38
39		let is_signed = |k: &Type| signed_order.contains(k);
40		let is_unsigned = |k: &Type| unsigned_order.contains(k);
41
42		let rank = |k: &Type| match k {
43			Int1 | Uint1 => 0,
44			Int2 | Uint2 => 1,
45			Int4 | Uint4 => 2,
46			Int8 | Uint8 => 3,
47			Int16 | Uint16 => 4,
48			_ => usize::MAX,
49		};
50
51		if is_signed(&left) && is_signed(&right) {
52			return signed_order[min(rank(&left).max(rank(&right)), 3) + 1].clone();
53		}
54
55		if is_unsigned(&left) && is_unsigned(&right) {
56			return unsigned_order[min(rank(&left).max(rank(&right)), 3) + 1].clone();
57		}
58
59		if (is_signed(&left) && is_unsigned(&right)) || (is_unsigned(&left) && is_signed(&right)) {
60			return match rank(&left).max(rank(&right)) + 1 {
61				0 => Int1,
62				1 => Int2,
63				2 => Int4,
64				3 => Int8,
65				4 => Int16,
66				_ => Int16,
67			};
68		}
69
70		left
71	}
72}
73
74#[cfg(test)]
75pub mod tests {
76	use Type::*;
77
78	use crate::value::r#type::Type;
79
80	#[test]
81	fn test_promote_bool() {
82		let cases = [
83			(Boolean, Boolean, Boolean),
84			(Boolean, Float4, Boolean),
85			(Boolean, Float8, Boolean),
86			(Boolean, Int1, Boolean),
87			(Boolean, Int2, Boolean),
88			(Boolean, Int4, Boolean),
89			(Boolean, Int8, Boolean),
90			(Boolean, Int16, Boolean),
91			(Boolean, Utf8, Utf8),
92			(Boolean, Uint1, Boolean),
93			(Boolean, Uint2, Boolean),
94			(Boolean, Uint4, Boolean),
95			(Boolean, Uint8, Boolean),
96			(Boolean, Uint16, Boolean),
97		];
98		for (left, right, expected) in cases {
99			assert_eq!(Type::promote(left, right), expected);
100		}
101	}
102
103	#[test]
104	fn test_promote_float4() {
105		let cases = [
106			(Float4, Boolean, Boolean),
107			(Float4, Float4, Float8),
108			(Float4, Float8, Float8),
109			(Float4, Int1, Float8),
110			(Float4, Int2, Float8),
111			(Float4, Int4, Float8),
112			(Float4, Int8, Float8),
113			(Float4, Int16, Float8),
114			(Float4, Utf8, Utf8),
115			(Float4, Uint1, Float8),
116			(Float4, Uint2, Float8),
117			(Float4, Uint4, Float8),
118			(Float4, Uint8, Float8),
119			(Float4, Uint16, Float8),
120		];
121		for (left, right, expected) in cases {
122			assert_eq!(Type::promote(left, right), expected);
123		}
124	}
125
126	#[test]
127	fn test_promote_float8() {
128		let cases = [
129			(Float8, Boolean, Boolean),
130			(Float8, Float4, Float8),
131			(Float8, Float8, Float8),
132			(Float8, Int1, Float8),
133			(Float8, Int2, Float8),
134			(Float8, Int4, Float8),
135			(Float8, Int8, Float8),
136			(Float8, Int16, Float8),
137			(Float8, Utf8, Utf8),
138			(Float8, Uint1, Float8),
139			(Float8, Uint2, Float8),
140			(Float8, Uint4, Float8),
141			(Float8, Uint8, Float8),
142			(Float8, Uint16, Float8),
143		];
144		for (left, right, expected) in cases {
145			assert_eq!(Type::promote(left, right), expected);
146		}
147	}
148
149	#[test]
150	fn test_promote_int1() {
151		let cases = [
152			(Int1, Boolean, Boolean),
153			(Int1, Float4, Float8),
154			(Int1, Float8, Float8),
155			(Int1, Int1, Int2),
156			(Int1, Int2, Int4),
157			(Int1, Int4, Int8),
158			(Int1, Int8, Int16),
159			(Int1, Int16, Int16),
160			(Int1, Utf8, Utf8),
161			(Int1, Uint1, Int2),
162			(Int1, Uint2, Int4),
163			(Int1, Uint4, Int8),
164			(Int1, Uint8, Int16),
165			(Int1, Uint16, Int16),
166		];
167		for (left, right, expected) in cases {
168			assert_eq!(Type::promote(left, right), expected);
169		}
170	}
171
172	#[test]
173	fn test_promote_int2() {
174		let cases = [
175			(Int2, Boolean, Boolean),
176			(Int2, Float4, Float8),
177			(Int2, Float8, Float8),
178			(Int2, Int1, Int4),
179			(Int2, Int2, Int4),
180			(Int2, Int4, Int8),
181			(Int2, Int8, Int16),
182			(Int2, Int16, Int16),
183			(Int2, Utf8, Utf8),
184			(Int2, Uint1, Int4),
185			(Int2, Uint2, Int4),
186			(Int2, Uint4, Int8),
187			(Int2, Uint8, Int16),
188			(Int2, Uint16, Int16),
189		];
190		for (left, right, expected) in cases {
191			assert_eq!(Type::promote(left, right), expected);
192		}
193	}
194
195	#[test]
196	fn test_promote_int4() {
197		let cases = [
198			(Int4, Boolean, Boolean),
199			(Int4, Float4, Float8),
200			(Int4, Float8, Float8),
201			(Int4, Int1, Int8),
202			(Int4, Int2, Int8),
203			(Int4, Int4, Int8),
204			(Int4, Int8, Int16),
205			(Int4, Int16, Int16),
206			(Int4, Utf8, Utf8),
207			(Int4, Uint1, Int8),
208			(Int4, Uint2, Int8),
209			(Int4, Uint4, Int8),
210			(Int4, Uint8, Int16),
211			(Int4, Uint16, Int16),
212		];
213		for (left, right, expected) in cases {
214			assert_eq!(Type::promote(left, right), expected);
215		}
216	}
217
218	#[test]
219	fn test_promote_int8() {
220		let cases = [
221			(Int8, Boolean, Boolean),
222			(Int8, Float4, Float8),
223			(Int8, Float8, Float8),
224			(Int8, Int1, Int16),
225			(Int8, Int2, Int16),
226			(Int8, Int4, Int16),
227			(Int8, Int8, Int16),
228			(Int8, Int16, Int16),
229			(Int8, Utf8, Utf8),
230			(Int8, Uint1, Int16),
231			(Int8, Uint2, Int16),
232			(Int8, Uint4, Int16),
233			(Int8, Uint8, Int16),
234			(Int8, Uint16, Int16),
235		];
236		for (left, right, expected) in cases {
237			assert_eq!(Type::promote(left, right), expected);
238		}
239	}
240
241	#[test]
242	fn test_promote_int16() {
243		let cases = [
244			(Int16, Boolean, Boolean),
245			(Int16, Float4, Float8),
246			(Int16, Float8, Float8),
247			(Int16, Int1, Int16),
248			(Int16, Int2, Int16),
249			(Int16, Int4, Int16),
250			(Int16, Int8, Int16),
251			(Int16, Int16, Int16),
252			(Int16, Utf8, Utf8),
253			(Int16, Uint1, Int16),
254			(Int16, Uint2, Int16),
255			(Int16, Uint4, Int16),
256			(Int16, Uint8, Int16),
257			(Int16, Uint16, Int16),
258		];
259		for (left, right, expected) in cases {
260			assert_eq!(Type::promote(left, right), expected);
261		}
262	}
263
264	#[test]
265	fn test_promote_string() {
266		let kinds = [
267			Boolean, Float4, Float8, Int1, Int2, Int4, Int8, Int16, Utf8, Uint1, Uint2, Uint4, Uint8,
268			Uint16,
269		];
270		for ty in kinds {
271			assert_eq!(Type::promote(Utf8, ty), Utf8);
272		}
273	}
274
275	#[test]
276	fn test_promote_uint1() {
277		let cases = [
278			(Uint1, Boolean, Boolean),
279			(Uint1, Float4, Float8),
280			(Uint1, Float8, Float8),
281			(Uint1, Int1, Int2),
282			(Uint1, Int2, Int4),
283			(Uint1, Int4, Int8),
284			(Uint1, Int8, Int16),
285			(Uint1, Int16, Int16),
286			(Uint1, Utf8, Utf8),
287			(Uint1, Uint1, Uint2),
288			(Uint1, Uint2, Uint4),
289			(Uint1, Uint4, Uint8),
290			(Uint1, Uint8, Uint16),
291			(Uint1, Uint16, Uint16),
292		];
293		for (left, right, expected) in cases {
294			assert_eq!(Type::promote(left, right), expected);
295		}
296	}
297
298	#[test]
299	fn test_promote_uint2() {
300		let cases = [
301			(Uint2, Boolean, Boolean),
302			(Uint2, Float4, Float8),
303			(Uint2, Float8, Float8),
304			(Uint2, Int1, Int4),
305			(Uint2, Int2, Int4),
306			(Uint2, Int4, Int8),
307			(Uint2, Int8, Int16),
308			(Uint2, Int16, Int16),
309			(Uint2, Utf8, Utf8),
310			(Uint2, Uint1, Uint4),
311			(Uint2, Uint2, Uint4),
312			(Uint2, Uint4, Uint8),
313			(Uint2, Uint8, Uint16),
314			(Uint2, Uint16, Uint16),
315		];
316		for (left, right, expected) in cases {
317			assert_eq!(Type::promote(left, right), expected);
318		}
319	}
320
321	#[test]
322	fn test_promote_uint4() {
323		let cases = [
324			(Uint4, Boolean, Boolean),
325			(Uint4, Float4, Float8),
326			(Uint4, Float8, Float8),
327			(Uint4, Int1, Int8),
328			(Uint4, Int2, Int8),
329			(Uint4, Int4, Int8),
330			(Uint4, Int8, Int16),
331			(Uint4, Int16, Int16),
332			(Uint4, Utf8, Utf8),
333			(Uint4, Uint1, Uint8),
334			(Uint4, Uint2, Uint8),
335			(Uint4, Uint4, Uint8),
336			(Uint4, Uint8, Uint16),
337			(Uint4, Uint16, Uint16),
338		];
339		for (left, right, expected) in cases {
340			assert_eq!(Type::promote(left, right), expected);
341		}
342	}
343
344	#[test]
345	fn test_promote_uint8() {
346		let cases = [
347			(Uint8, Boolean, Boolean),
348			(Uint8, Float4, Float8),
349			(Uint8, Float8, Float8),
350			(Uint8, Int1, Int16),
351			(Uint8, Int2, Int16),
352			(Uint8, Int4, Int16),
353			(Uint8, Int8, Int16),
354			(Uint8, Int16, Int16),
355			(Uint8, Utf8, Utf8),
356			(Uint8, Uint1, Uint16),
357			(Uint8, Uint2, Uint16),
358			(Uint8, Uint4, Uint16),
359			(Uint8, Uint8, Uint16),
360			(Uint8, Uint16, Uint16),
361		];
362		for (left, right, expected) in cases {
363			assert_eq!(Type::promote(left, right), expected);
364		}
365	}
366
367	#[test]
368	fn test_promote_any_is_absorbing() {
369		let kinds = [
370			Boolean,
371			Float4,
372			Float8,
373			Int1,
374			Int2,
375			Int4,
376			Int8,
377			Int16,
378			Uint1,
379			Uint2,
380			Uint4,
381			Uint8,
382			Uint16,
383			Utf8,
384			Date,
385			DateTime,
386			Time,
387			Duration,
388			Uuid4,
389			Uuid7,
390			Blob,
391			IdentityId,
392			DictionaryId,
393			Int,
394			Uint,
395			Decimal,
396			Any,
397		];
398		for ty in kinds {
399			assert_eq!(Type::promote(Any, ty.clone()), Any, "Any on left with {:?}", ty);
400			assert_eq!(Type::promote(ty.clone(), Any), Any, "Any on right with {:?}", ty);
401		}
402		assert_eq!(Type::promote(Any, Option(Box::new(Int4))), Any);
403		assert_eq!(Type::promote(Option(Box::new(Int4)), Any), Any);
404	}
405
406	#[test]
407	fn test_promote_uint16() {
408		let cases = [
409			(Uint16, Boolean, Boolean),
410			(Uint16, Float4, Float8),
411			(Uint16, Float8, Float8),
412			(Uint16, Int1, Int16),
413			(Uint16, Int2, Int16),
414			(Uint16, Int4, Int16),
415			(Uint16, Int8, Int16),
416			(Uint16, Int16, Int16),
417			(Uint16, Utf8, Utf8),
418			(Uint16, Uint1, Uint16),
419			(Uint16, Uint2, Uint16),
420			(Uint16, Uint4, Uint16),
421			(Uint16, Uint8, Uint16),
422			(Uint16, Uint16, Uint16),
423		];
424		for (left, right, expected) in cases {
425			assert_eq!(Type::promote(left, right), expected);
426		}
427	}
428}