Skip to main content

reifydb_engine/expression/cast/
number.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::value::column::buffer::ColumnBuffer;
5use reifydb_type::{
6	error::{Error, TypeError},
7	fragment::{Fragment, LazyFragment},
8	value::{
9		container::number::NumberContainer,
10		decimal::{Decimal, parse::parse_decimal},
11		int::Int,
12		is::IsNumber,
13		number::{
14			parse::{parse_float, parse_primitive_int, parse_primitive_uint},
15			safe::convert::SafeConvert,
16		},
17		r#type::{Type, get::GetType},
18		uint::Uint,
19	},
20};
21
22use crate::{Result, error::CastError, expression::convert::Convert};
23
24pub fn to_number(
25	ctx: impl Convert,
26	data: &ColumnBuffer,
27	target: Type,
28	lazy_fragment: impl LazyFragment,
29) -> Result<ColumnBuffer> {
30	if !target.is_number() {
31		let from = data.get_type();
32		return Err(TypeError::UnsupportedCast {
33			from,
34			to: target,
35			fragment: lazy_fragment.fragment(),
36		}
37		.into());
38	}
39
40	if data.get_type().is_number() {
41		return number_to_number(data, target, ctx, lazy_fragment);
42	}
43
44	if data.is_bool() {
45		return boolean_to_number(data, target, lazy_fragment);
46	}
47
48	if data.is_utf8() {
49		return match &target {
50			Type::Float4 | Type::Float8 => text_to_float(data, target, lazy_fragment),
51			Type::Decimal => text_to_decimal(data, target, lazy_fragment),
52			_ => text_to_integer(data, target, lazy_fragment),
53		};
54	}
55
56	if data.is_float() {
57		return float_to_integer(data, target, lazy_fragment);
58	}
59
60	let from = data.get_type();
61	Err(TypeError::UnsupportedCast {
62		from,
63		to: target,
64		fragment: lazy_fragment.fragment(),
65	}
66	.into())
67}
68
69fn boolean_to_number(data: &ColumnBuffer, target: Type, lazy_fragment: impl LazyFragment) -> Result<ColumnBuffer> {
70	macro_rules! boolean_to_number {
71		($target_ty:ty, $true_val:expr, $false_val:expr) => {{
72			|out: &mut ColumnBuffer, val: bool| {
73				out.push::<$target_ty>(if val {
74					$true_val
75				} else {
76					$false_val
77				})
78			}
79		}};
80	}
81
82	match data {
83		ColumnBuffer::Bool(container) => {
84			let converter = match &target {
85				Type::Int1 => boolean_to_number!(i8, 1i8, 0i8),
86				Type::Int2 => {
87					boolean_to_number!(i16, 1i16, 0i16)
88				}
89				Type::Int4 => {
90					boolean_to_number!(i32, 1i32, 0i32)
91				}
92				Type::Int8 => {
93					boolean_to_number!(i64, 1i64, 0i64)
94				}
95				Type::Int16 => {
96					boolean_to_number!(i128, 1i128, 0i128)
97				}
98				Type::Uint1 => boolean_to_number!(u8, 1u8, 0u8),
99				Type::Uint2 => {
100					boolean_to_number!(u16, 1u16, 0u16)
101				}
102				Type::Uint4 => {
103					boolean_to_number!(u32, 1u32, 0u32)
104				}
105				Type::Uint8 => {
106					boolean_to_number!(u64, 1u64, 0u64)
107				}
108				Type::Uint16 => {
109					boolean_to_number!(u128, 1u128, 0u128)
110				}
111				Type::Float4 => {
112					boolean_to_number!(f32, 1.0f32, 0.0f32)
113				}
114				Type::Float8 => {
115					boolean_to_number!(f64, 1.0f64, 0.0f64)
116				}
117				Type::Int => |out: &mut ColumnBuffer, val: bool| {
118					out.push::<Int>(if val {
119						Int::from_i64(1)
120					} else {
121						Int::from_i64(0)
122					})
123				},
124				Type::Uint => |out: &mut ColumnBuffer, val: bool| {
125					out.push::<Uint>(if val {
126						Uint::from_u64(1)
127					} else {
128						Uint::from_u64(0)
129					})
130				},
131				Type::Decimal => |out: &mut ColumnBuffer, val: bool| {
132					let decimal = if val {
133						Decimal::from_i64(1)
134					} else {
135						Decimal::from_i64(0)
136					};
137					out.push::<Decimal>(decimal)
138				},
139				_ => {
140					let from = data.get_type();
141					return Err(TypeError::UnsupportedCast {
142						from,
143						to: target,
144						fragment: lazy_fragment.fragment(),
145					}
146					.into());
147				}
148			};
149
150			let mut out = ColumnBuffer::with_capacity(target, container.len());
151			for idx in 0..container.len() {
152				if container.is_defined(idx) {
153					let val = container.data().get(idx);
154					converter(&mut out, val);
155				} else {
156					out.push_none();
157				}
158			}
159			Ok(out)
160		}
161		_ => {
162			let from = data.get_type();
163			Err(TypeError::UnsupportedCast {
164				from,
165				to: target,
166				fragment: lazy_fragment.fragment(),
167			}
168			.into())
169		}
170	}
171}
172
173fn float_to_integer(data: &ColumnBuffer, target: Type, lazy_fragment: impl LazyFragment) -> Result<ColumnBuffer> {
174	match data {
175		ColumnBuffer::Float4(container) => match &target {
176			Type::Int1 => f32_to_i8_vec(container),
177			Type::Int2 => f32_to_i16_vec(container),
178			Type::Int4 => f32_to_i32_vec(container),
179			Type::Int8 => f32_to_i64_vec(container),
180			Type::Int16 => f32_to_i128_vec(container),
181			Type::Uint1 => f32_to_u8_vec(container),
182			Type::Uint2 => f32_to_u16_vec(container),
183			Type::Uint4 => f32_to_u32_vec(container),
184			Type::Uint8 => f32_to_u64_vec(container),
185			Type::Uint16 => f32_to_u128_vec(container),
186			Type::Int => f32_to_int_vec(container),
187			Type::Uint => f32_to_uint_vec(container),
188			Type::Decimal => f32_to_decimal_vec(container, target),
189			_ => {
190				let from = data.get_type();
191				Err(TypeError::UnsupportedCast {
192					from,
193					to: target,
194					fragment: lazy_fragment.fragment(),
195				}
196				.into())
197			}
198		},
199		ColumnBuffer::Float8(container) => match &target {
200			Type::Int1 => f64_to_i8_vec(container),
201			Type::Int2 => f64_to_i16_vec(container),
202			Type::Int4 => f64_to_i32_vec(container),
203			Type::Int8 => f64_to_i64_vec(container),
204			Type::Int16 => f64_to_i128_vec(container),
205			Type::Uint1 => f64_to_u8_vec(container),
206			Type::Uint2 => f64_to_u16_vec(container),
207			Type::Uint4 => f64_to_u32_vec(container),
208			Type::Uint8 => f64_to_u64_vec(container),
209			Type::Uint16 => f64_to_u128_vec(container),
210			Type::Int => f64_to_int_vec(container),
211			Type::Uint => f64_to_uint_vec(container),
212			Type::Decimal => f64_to_decimal_vec(container, target),
213			_ => {
214				let from = data.get_type();
215				Err(TypeError::UnsupportedCast {
216					from,
217					to: target,
218					fragment: lazy_fragment.fragment(),
219				}
220				.into())
221			}
222		},
223		_ => {
224			let from = data.get_type();
225			Err(TypeError::UnsupportedCast {
226				from,
227				to: target,
228				fragment: lazy_fragment.fragment(),
229			}
230			.into())
231		}
232	}
233}
234
235macro_rules! parse_and_push {
236	(parse_int, $ty:ty, $target_type:expr, $out:expr, $temp_fragment:expr, $base_fragment:expr) => {{
237		let result = parse_primitive_int::<$ty>($temp_fragment.clone()).map_err(|mut e| {
238			e.0.with_fragment($base_fragment.clone());
239			Error::from(CastError::InvalidNumber {
240				fragment: $base_fragment.clone(),
241				target: $target_type,
242				cause: e.diagnostic(),
243			})
244		})?;
245		$out.push::<$ty>(result);
246	}};
247	(parse_uint, $ty:ty, $target_type:expr, $out:expr, $temp_fragment:expr, $base_fragment:expr) => {{
248		let result = parse_primitive_uint::<$ty>($temp_fragment.clone()).map_err(|mut e| {
249			e.0.with_fragment($base_fragment.clone());
250			Error::from(CastError::InvalidNumber {
251				fragment: $base_fragment.clone(),
252				target: $target_type,
253				cause: e.diagnostic(),
254			})
255		})?;
256		$out.push::<$ty>(result);
257	}};
258}
259
260fn text_to_integer(data: &ColumnBuffer, target: Type, lazy_fragment: impl LazyFragment) -> Result<ColumnBuffer> {
261	match data {
262		ColumnBuffer::Utf8 {
263			container,
264			..
265		} => {
266			let base_fragment = lazy_fragment.fragment();
267			let mut out = ColumnBuffer::with_capacity(target.clone(), container.len());
268			for idx in 0..container.len() {
269				if container.is_defined(idx) {
270					let val = container.get(idx).unwrap();
271					let temp_fragment = Fragment::internal(val);
272
273					match target.clone() {
274						Type::Int1 => {
275							parse_and_push!(
276								parse_int,
277								i8,
278								Type::Int1,
279								out,
280								temp_fragment,
281								base_fragment
282							)
283						}
284						Type::Int2 => {
285							parse_and_push!(
286								parse_int,
287								i16,
288								Type::Int2,
289								out,
290								temp_fragment,
291								base_fragment
292							)
293						}
294						Type::Int4 => {
295							parse_and_push!(
296								parse_int,
297								i32,
298								Type::Int4,
299								out,
300								temp_fragment,
301								base_fragment
302							)
303						}
304						Type::Int8 => {
305							parse_and_push!(
306								parse_int,
307								i64,
308								Type::Int8,
309								out,
310								temp_fragment,
311								base_fragment
312							)
313						}
314						Type::Int16 => {
315							parse_and_push!(
316								parse_int,
317								i128,
318								Type::Int16,
319								out,
320								temp_fragment,
321								base_fragment
322							)
323						}
324						Type::Uint1 => {
325							parse_and_push!(
326								parse_uint,
327								u8,
328								Type::Uint1,
329								out,
330								temp_fragment,
331								base_fragment
332							)
333						}
334						Type::Uint2 => {
335							parse_and_push!(
336								parse_uint,
337								u16,
338								Type::Uint2,
339								out,
340								temp_fragment,
341								base_fragment
342							)
343						}
344						Type::Uint4 => {
345							parse_and_push!(
346								parse_uint,
347								u32,
348								Type::Uint4,
349								out,
350								temp_fragment,
351								base_fragment
352							)
353						}
354						Type::Uint8 => {
355							parse_and_push!(
356								parse_uint,
357								u64,
358								Type::Uint8,
359								out,
360								temp_fragment,
361								base_fragment
362							)
363						}
364						Type::Uint16 => {
365							parse_and_push!(
366								parse_uint,
367								u128,
368								Type::Uint16,
369								out,
370								temp_fragment,
371								base_fragment
372							)
373						}
374						Type::Int => {
375							let result = parse_primitive_int(temp_fragment.clone())
376								.map_err(|mut e| {
377									e.0.with_fragment(base_fragment.clone());
378									Error::from(CastError::InvalidNumber {
379										fragment: base_fragment.clone(),
380										target: Type::Int,
381										cause: e.diagnostic(),
382									})
383								})?;
384							out.push::<Int>(result);
385						}
386						Type::Uint => {
387							let result = parse_primitive_uint(temp_fragment.clone())
388								.map_err(|mut e| {
389									e.0.with_fragment(base_fragment.clone());
390									Error::from(CastError::InvalidNumber {
391										fragment: base_fragment.clone(),
392										target: Type::Uint,
393										cause: e.diagnostic(),
394									})
395								})?;
396							out.push::<Uint>(result);
397						}
398						Type::Decimal => {
399							let target_clone = target.clone();
400							let result = parse_decimal(temp_fragment.clone()).map_err(
401								|mut e| {
402									e.0.with_fragment(base_fragment.clone());
403									Error::from(CastError::InvalidNumber {
404										fragment: base_fragment.clone(),
405										target: target_clone,
406										cause: e.diagnostic(),
407									})
408								},
409							)?;
410							out.push::<Decimal>(result);
411						}
412						_ => {
413							let from = data.get_type();
414							return Err(TypeError::UnsupportedCast {
415								from,
416								to: target,
417								fragment: base_fragment.clone(),
418							}
419							.into());
420						}
421					}
422				} else {
423					out.push_none();
424				}
425			}
426			Ok(out)
427		}
428		_ => {
429			let from = data.get_type();
430			Err(TypeError::UnsupportedCast {
431				from,
432				to: target,
433				fragment: lazy_fragment.fragment(),
434			}
435			.into())
436		}
437	}
438}
439
440fn text_to_float(column_data: &ColumnBuffer, target: Type, lazy_fragment: impl LazyFragment) -> Result<ColumnBuffer> {
441	if let ColumnBuffer::Utf8 {
442		container,
443		..
444	} = column_data
445	{
446		// Create base fragment once for efficiency
447		let base_fragment = lazy_fragment.fragment();
448		let mut out = ColumnBuffer::with_capacity(target.clone(), container.len());
449		for idx in 0..container.len() {
450			if container.is_defined(idx) {
451				let val = container.get(idx).unwrap();
452				// Create efficient borrowed fragment for
453				// parsing
454				let temp_fragment = Fragment::internal(val);
455
456				match target.clone() {
457					Type::Float4 => out.push::<f32>(
458						parse_float::<f32>(temp_fragment.clone()).map_err(|mut e| {
459							e.0.with_fragment(base_fragment.clone());
460							Error::from(CastError::InvalidNumber {
461								fragment: base_fragment.clone(),
462								target: Type::Float4,
463								cause: e.diagnostic(),
464							})
465						})?,
466					),
467
468					Type::Float8 => out.push::<f64>(parse_float::<f64>(temp_fragment).map_err(
469						|mut e| {
470							e.0.with_fragment(base_fragment.clone());
471							Error::from(CastError::InvalidNumber {
472								fragment: base_fragment.clone(),
473								target: Type::Float8,
474								cause: e.diagnostic(),
475							})
476						},
477					)?),
478					_ => {
479						let from = column_data.get_type();
480						return Err(TypeError::UnsupportedCast {
481							from,
482							to: target,
483							fragment: base_fragment.clone(),
484						}
485						.into());
486					}
487				}
488			} else {
489				out.push_none();
490			}
491		}
492		Ok(out)
493	} else {
494		let from = column_data.get_type();
495		Err(TypeError::UnsupportedCast {
496			from,
497			to: target,
498			fragment: lazy_fragment.fragment(),
499		}
500		.into())
501	}
502}
503
504fn text_to_decimal(column_data: &ColumnBuffer, target: Type, lazy_fragment: impl LazyFragment) -> Result<ColumnBuffer> {
505	if let ColumnBuffer::Utf8 {
506		container,
507		..
508	} = column_data
509	{
510		let base_fragment = lazy_fragment.fragment();
511		let mut out = ColumnBuffer::with_capacity(target.clone(), container.len());
512		for idx in 0..container.len() {
513			if container.is_defined(idx) {
514				let val = container.get(idx).unwrap();
515				let temp_fragment = Fragment::internal(val);
516
517				let result = parse_decimal(temp_fragment.clone()).map_err(|mut e| {
518					e.0.with_fragment(base_fragment.clone());
519					Error::from(CastError::InvalidNumber {
520						fragment: base_fragment.clone(),
521						target: target.clone(),
522						cause: e.diagnostic(),
523					})
524				})?;
525				out.push::<Decimal>(result);
526			} else {
527				out.push_none();
528			}
529		}
530		Ok(out)
531	} else {
532		let from = column_data.get_type();
533		Err(TypeError::UnsupportedCast {
534			from,
535			to: target,
536			fragment: lazy_fragment.fragment(),
537		}
538		.into())
539	}
540}
541
542macro_rules! float_to_int_vec {
543	($fn_name:ident, $float_ty:ty, $int_ty:ty, $target_type:expr, $min_val:expr, $max_val:expr) => {
544		fn $fn_name(container: &NumberContainer<$float_ty>) -> Result<ColumnBuffer>
545		where
546			$float_ty: Copy + IsNumber,
547		{
548			let mut out = ColumnBuffer::with_capacity($target_type, container.len());
549			for idx in 0..container.len() {
550				if container.is_defined(idx) {
551					let val = container[idx];
552					let truncated = val.trunc();
553					if truncated >= $min_val && truncated <= $max_val {
554						out.push::<$int_ty>(truncated as $int_ty);
555					} else {
556						out.push_none();
557					}
558				} else {
559					out.push_none();
560				}
561			}
562			Ok(out)
563		}
564	};
565}
566
567float_to_int_vec!(f32_to_i8_vec, f32, i8, Type::Int1, i8::MIN as f32, i8::MAX as f32);
568float_to_int_vec!(f32_to_i16_vec, f32, i16, Type::Int2, i16::MIN as f32, i16::MAX as f32);
569float_to_int_vec!(f32_to_i32_vec, f32, i32, Type::Int4, i32::MIN as f32, i32::MAX as f32);
570float_to_int_vec!(f32_to_i64_vec, f32, i64, Type::Int8, i64::MIN as f32, i64::MAX as f32);
571float_to_int_vec!(f32_to_i128_vec, f32, i128, Type::Int16, i128::MIN as f32, i128::MAX as f32);
572float_to_int_vec!(f32_to_u8_vec, f32, u8, Type::Uint1, 0.0, u8::MAX as f32);
573float_to_int_vec!(f32_to_u16_vec, f32, u16, Type::Uint2, 0.0, u16::MAX as f32);
574float_to_int_vec!(f32_to_u32_vec, f32, u32, Type::Uint4, 0.0, u32::MAX as f32);
575float_to_int_vec!(f32_to_u64_vec, f32, u64, Type::Uint8, 0.0, u64::MAX as f32);
576float_to_int_vec!(f32_to_u128_vec, f32, u128, Type::Uint16, 0.0, u128::MAX as f32);
577
578float_to_int_vec!(f64_to_i8_vec, f64, i8, Type::Int1, i8::MIN as f64, i8::MAX as f64);
579float_to_int_vec!(f64_to_i16_vec, f64, i16, Type::Int2, i16::MIN as f64, i16::MAX as f64);
580float_to_int_vec!(f64_to_i32_vec, f64, i32, Type::Int4, i32::MIN as f64, i32::MAX as f64);
581float_to_int_vec!(f64_to_i64_vec, f64, i64, Type::Int8, i64::MIN as f64, i64::MAX as f64);
582float_to_int_vec!(f64_to_i128_vec, f64, i128, Type::Int16, i128::MIN as f64, i128::MAX as f64);
583float_to_int_vec!(f64_to_u8_vec, f64, u8, Type::Uint1, 0.0, u8::MAX as f64);
584float_to_int_vec!(f64_to_u16_vec, f64, u16, Type::Uint2, 0.0, u16::MAX as f64);
585float_to_int_vec!(f64_to_u32_vec, f64, u32, Type::Uint4, 0.0, u32::MAX as f64);
586float_to_int_vec!(f64_to_u64_vec, f64, u64, Type::Uint8, 0.0, u64::MAX as f64);
587float_to_int_vec!(f64_to_u128_vec, f64, u128, Type::Uint16, 0.0, u128::MAX as f64);
588
589// Float to Int conversion
590fn f32_to_int_vec(container: &NumberContainer<f32>) -> Result<ColumnBuffer> {
591	let mut out = ColumnBuffer::with_capacity(Type::Int, container.len());
592	for idx in 0..container.len() {
593		if container.is_defined(idx) {
594			let val = container[idx];
595			let truncated = val.trunc();
596			let int = Int::from_i64(truncated as i64);
597			out.push::<Int>(int);
598		} else {
599			out.push_none();
600		}
601	}
602	Ok(out)
603}
604
605fn f64_to_int_vec(container: &NumberContainer<f64>) -> Result<ColumnBuffer> {
606	let mut out = ColumnBuffer::with_capacity(Type::Int, container.len());
607	for idx in 0..container.len() {
608		if container.is_defined(idx) {
609			let val = container[idx];
610			let truncated = val.trunc();
611			let int = Int::from_i64(truncated as i64);
612			out.push::<Int>(int);
613		} else {
614			out.push_none();
615		}
616	}
617	Ok(out)
618}
619
620// Float to Uint conversion
621fn f32_to_uint_vec(container: &NumberContainer<f32>) -> Result<ColumnBuffer> {
622	let mut out = ColumnBuffer::with_capacity(Type::Uint, container.len());
623	for idx in 0..container.len() {
624		if container.is_defined(idx) {
625			let val = container[idx];
626			let truncated = val.trunc();
627			if truncated >= 0.0 {
628				let uint = Uint::from_u64(truncated as u64);
629				out.push::<Uint>(uint);
630			} else {
631				out.push_none();
632			}
633		} else {
634			out.push_none();
635		}
636	}
637	Ok(out)
638}
639
640fn f64_to_uint_vec(container: &NumberContainer<f64>) -> Result<ColumnBuffer> {
641	let mut out = ColumnBuffer::with_capacity(Type::Uint, container.len());
642	for idx in 0..container.len() {
643		if container.is_defined(idx) {
644			let val = container[idx];
645			let truncated = val.trunc();
646			if truncated >= 0.0 {
647				let uint = Uint::from_u64(truncated as u64);
648				out.push::<Uint>(uint);
649			} else {
650				out.push_none();
651			}
652		} else {
653			out.push_none();
654		}
655	}
656	Ok(out)
657}
658
659// Float to Decimal conversion
660fn f32_to_decimal_vec(container: &NumberContainer<f32>, target: Type) -> Result<ColumnBuffer> {
661	let mut out = ColumnBuffer::with_capacity(target, container.len());
662	for idx in 0..container.len() {
663		if container.is_defined(idx) {
664			let val = container[idx];
665			// Convert float to decimal with default precision/scale
666			let decimal = Decimal::from_i64(val.trunc() as i64);
667			out.push::<Decimal>(decimal);
668		} else {
669			out.push_none();
670		}
671	}
672	Ok(out)
673}
674
675fn f64_to_decimal_vec(container: &NumberContainer<f64>, target: Type) -> Result<ColumnBuffer> {
676	let mut out = ColumnBuffer::with_capacity(target, container.len());
677	for idx in 0..container.len() {
678		if container.is_defined(idx) {
679			let val = container[idx];
680			// Convert float to decimal with default precision/scale
681			let decimal = Decimal::from_i64(val.trunc() as i64);
682			out.push::<Decimal>(decimal);
683		} else {
684			out.push_none();
685		}
686	}
687	Ok(out)
688}
689
690fn number_to_number(
691	data: &ColumnBuffer,
692	target: Type,
693	ctx: impl Convert,
694	lazy_fragment: impl LazyFragment,
695) -> Result<ColumnBuffer> {
696	if !target.is_number() {
697		return Err(TypeError::UnsupportedCast {
698			from: data.get_type(),
699			to: target,
700			fragment: lazy_fragment.fragment(),
701		}
702		.into());
703	}
704
705	macro_rules! cast {
706            (
707                $src_variant:ident, $src_ty:ty,
708                to => [ $( ($dst_variant:ident, $dst_ty:ty) ),* ]
709                $(, to_struct => [ $( ($struct_variant:ident, $struct_ty:ty) ),* ])?
710            ) => {
711            if let ColumnBuffer::$src_variant(container) = data {
712                    match target {
713                        $(
714                        Type::$dst_variant => return convert_vec::<$src_ty, $dst_ty>(
715                            &container,
716                                ctx,
717                                lazy_fragment,
718                                Type::$dst_variant,
719                                ColumnBuffer::push::<$dst_ty>,
720                            ),
721                        )*
722                        $($(
723                        Type::$struct_variant { .. } => return convert_vec::<$src_ty, $struct_ty>(
724                            &container,
725                                ctx,
726                                lazy_fragment,
727                                target,
728                                ColumnBuffer::push::<$struct_ty>,
729                            ),
730                        )*)?
731                        _ => {}
732                    }
733                }
734            }
735        }
736
737	cast!(Float4, f32,
738	    to => [(Float8, f64), (Int1, i8), (Int2, i16), (Int4, i32), (Int8, i64), (Int16, i128), (Uint1, u8), (Uint2, u16), (Uint4, u32), (Uint8, u64), (Uint16, u128), (Int, Int), (Uint, Uint)],
739	    to_struct => [(Decimal, Decimal)]
740	);
741
742	cast!(Float8, f64,
743	    to => [(Float4, f32), (Int1, i8), (Int2, i16), (Int4, i32), (Int8, i64), (Int16, i128), (Uint1, u8), (Uint2, u16), (Uint4, u32), (Uint8, u64), (Uint16, u128), (Int, Int), (Uint, Uint)],
744	    to_struct => [(Decimal, Decimal)]
745	);
746
747	cast!(Int1, i8,
748	    to => [(Int2, i16), (Int4, i32), (Int8, i64), (Int16, i128), (Float4, f32), (Float8, f64), (Uint1, u8), (Uint2, u16), (Uint4, u32), (Uint8, u64), (Uint16, u128), (Int, Int), (Uint, Uint)],
749	    to_struct => [(Decimal, Decimal)]
750	);
751
752	cast!(Int2, i16,
753	    to => [(Int1, i8), (Int4, i32), (Int8, i64), (Int16, i128), (Float4, f32), (Float8, f64), (Uint1, u8), (Uint2, u16), (Uint4, u32), (Uint8, u64), (Uint16, u128), (Int, Int), (Uint, Uint)],
754	    to_struct => [(Decimal, Decimal)]
755	);
756
757	cast!(Int4, i32,
758	    to => [(Int1, i8), (Int2, i16), (Int8, i64), (Int16, i128), (Float4, f32), (Float8, f64), (Uint1, u8), (Uint2, u16), (Uint4, u32), (Uint8, u64), (Uint16, u128), (Int, Int), (Uint, Uint)],
759	    to_struct => [(Decimal, Decimal)]
760	);
761
762	cast!(Int8, i64,
763	    to => [(Int1, i8), (Int2, i16), (Int4, i32), (Int16, i128), (Float4, f32), (Float8, f64), (Uint1, u8), (Uint2, u16), (Uint4, u32), (Uint8, u64), (Uint16, u128), (Int, Int), (Uint, Uint)],
764	    to_struct => [(Decimal, Decimal)]
765	);
766
767	cast!(Int16, i128,
768	    to => [(Int1, i8), (Int2, i16), (Int4, i32), (Int8, i64), (Float4, f32), (Float8, f64), (Uint1, u8), (Uint2, u16), (Uint4, u32), (Uint8, u64), (Uint16, u128), (Int, Int), (Uint, Uint)],
769	    to_struct => [(Decimal, Decimal)]
770	);
771
772	cast!(Uint1, u8,
773	    to => [(Uint2, u16), (Uint4, u32), (Uint8, u64), (Uint16, u128), (Float4, f32), (Float8, f64), (Int1, i8), (Int2, i16), (Int4, i32), (Int8, i64), (Int16, i128), (Int, Int), (Uint, Uint)],
774	    to_struct => [(Decimal, Decimal)]
775	);
776
777	cast!(Uint2, u16,
778	    to => [(Uint1, u8), (Uint4, u32), (Uint8, u64), (Uint16, u128), (Float4, f32), (Float8, f64), (Int1, i8), (Int2, i16), (Int4, i32), (Int8, i64), (Int16, i128), (Int, Int), (Uint, Uint)],
779	    to_struct => [(Decimal, Decimal)]
780	);
781
782	cast!(Uint4, u32,
783	    to => [(Uint1, u8), (Uint2, u16), (Uint8, u64), (Uint16, u128), (Float4, f32), (Float8, f64), (Int1, i8), (Int2, i16), (Int4, i32), (Int8, i64), (Int16, i128), (Int, Int), (Uint, Uint)],
784	    to_struct => [(Decimal, Decimal)]
785	);
786
787	cast!(Uint8, u64,
788	    to => [(Uint1, u8), (Uint2, u16), (Uint4, u32), (Uint16, u128), (Float4, f32), (Float8, f64), (Int1, i8), (Int2, i16), (Int4, i32), (Int8, i64), (Int16, i128), (Int, Int), (Uint, Uint)],
789	    to_struct => [(Decimal, Decimal)]
790	);
791
792	cast!(Uint16, u128,
793	    to => [(Uint1, u8), (Uint2, u16), (Uint4, u32), (Uint8, u64), (Float4, f32), (Float8, f64), (Int1, i8), (Int2, i16), (Int4, i32), (Int8, i64), (Int16, i128), (Int, Int), (Uint, Uint)],
794	    to_struct => [(Decimal, Decimal)]
795	);
796
797	// Special handling for Int (uses Clone instead of Copy)
798	if let ColumnBuffer::Int {
799		container,
800		..
801	} = data
802	{
803		match target {
804			Type::Int1 => {
805				return convert_vec_clone::<Int, i8>(
806					container,
807					ctx,
808					lazy_fragment,
809					Type::Int1,
810					ColumnBuffer::push::<i8>,
811				);
812			}
813			Type::Int2 => {
814				return convert_vec_clone::<Int, i16>(
815					container,
816					ctx,
817					lazy_fragment,
818					Type::Int2,
819					ColumnBuffer::push::<i16>,
820				);
821			}
822			Type::Int4 => {
823				return convert_vec_clone::<Int, i32>(
824					container,
825					ctx,
826					lazy_fragment,
827					Type::Int4,
828					ColumnBuffer::push::<i32>,
829				);
830			}
831			Type::Int8 => {
832				return convert_vec_clone::<Int, i64>(
833					container,
834					ctx,
835					lazy_fragment,
836					Type::Int8,
837					ColumnBuffer::push::<i64>,
838				);
839			}
840			Type::Int16 => {
841				return convert_vec_clone::<Int, i128>(
842					container,
843					ctx,
844					lazy_fragment,
845					Type::Int16,
846					ColumnBuffer::push::<i128>,
847				);
848			}
849			Type::Uint1 => {
850				return convert_vec_clone::<Int, u8>(
851					container,
852					ctx,
853					lazy_fragment,
854					Type::Uint1,
855					ColumnBuffer::push::<u8>,
856				);
857			}
858			Type::Uint2 => {
859				return convert_vec_clone::<Int, u16>(
860					container,
861					ctx,
862					lazy_fragment,
863					Type::Uint2,
864					ColumnBuffer::push::<u16>,
865				);
866			}
867			Type::Uint4 => {
868				return convert_vec_clone::<Int, u32>(
869					container,
870					ctx,
871					lazy_fragment,
872					Type::Uint4,
873					ColumnBuffer::push::<u32>,
874				);
875			}
876			Type::Uint8 => {
877				return convert_vec_clone::<Int, u64>(
878					container,
879					ctx,
880					lazy_fragment,
881					Type::Uint8,
882					ColumnBuffer::push::<u64>,
883				);
884			}
885			Type::Uint16 => {
886				return convert_vec_clone::<Int, u128>(
887					container,
888					ctx,
889					lazy_fragment,
890					Type::Uint16,
891					ColumnBuffer::push::<u128>,
892				);
893			}
894			Type::Float4 => {
895				return convert_vec_clone::<Int, f32>(
896					container,
897					ctx,
898					lazy_fragment,
899					Type::Float4,
900					ColumnBuffer::push::<f32>,
901				);
902			}
903			Type::Float8 => {
904				return convert_vec_clone::<Int, f64>(
905					container,
906					ctx,
907					lazy_fragment,
908					Type::Float8,
909					ColumnBuffer::push::<f64>,
910				);
911			}
912			Type::Uint => {
913				return convert_vec_clone::<Int, Uint>(
914					container,
915					ctx,
916					lazy_fragment,
917					Type::Uint,
918					ColumnBuffer::push::<Uint>,
919				);
920			}
921			Type::Decimal => {
922				return convert_vec_clone::<Int, Decimal>(
923					container,
924					ctx,
925					lazy_fragment,
926					target,
927					ColumnBuffer::push::<Decimal>,
928				);
929			}
930			_ => {}
931		}
932	}
933
934	// Special handling for Uint (uses Clone instead of Copy)
935	if let ColumnBuffer::Uint {
936		container,
937		..
938	} = data
939	{
940		match target {
941			Type::Uint1 => {
942				return convert_vec_clone::<Uint, u8>(
943					container,
944					ctx,
945					lazy_fragment,
946					Type::Uint1,
947					ColumnBuffer::push::<u8>,
948				);
949			}
950			Type::Uint2 => {
951				return convert_vec_clone::<Uint, u16>(
952					container,
953					ctx,
954					lazy_fragment,
955					Type::Uint2,
956					ColumnBuffer::push::<u16>,
957				);
958			}
959			Type::Uint4 => {
960				return convert_vec_clone::<Uint, u32>(
961					container,
962					ctx,
963					lazy_fragment,
964					Type::Uint4,
965					ColumnBuffer::push::<u32>,
966				);
967			}
968			Type::Uint8 => {
969				return convert_vec_clone::<Uint, u64>(
970					container,
971					ctx,
972					lazy_fragment,
973					Type::Uint8,
974					ColumnBuffer::push::<u64>,
975				);
976			}
977			Type::Uint16 => {
978				return convert_vec_clone::<Uint, u128>(
979					container,
980					ctx,
981					lazy_fragment,
982					Type::Uint16,
983					ColumnBuffer::push::<u128>,
984				);
985			}
986			Type::Int1 => {
987				return convert_vec_clone::<Uint, i8>(
988					container,
989					ctx,
990					lazy_fragment,
991					Type::Int1,
992					ColumnBuffer::push::<i8>,
993				);
994			}
995			Type::Int2 => {
996				return convert_vec_clone::<Uint, i16>(
997					container,
998					ctx,
999					lazy_fragment,
1000					Type::Int2,
1001					ColumnBuffer::push::<i16>,
1002				);
1003			}
1004			Type::Int4 => {
1005				return convert_vec_clone::<Uint, i32>(
1006					container,
1007					ctx,
1008					lazy_fragment,
1009					Type::Int4,
1010					ColumnBuffer::push::<i32>,
1011				);
1012			}
1013			Type::Int8 => {
1014				return convert_vec_clone::<Uint, i64>(
1015					container,
1016					ctx,
1017					lazy_fragment,
1018					Type::Int8,
1019					ColumnBuffer::push::<i64>,
1020				);
1021			}
1022			Type::Int16 => {
1023				return convert_vec_clone::<Uint, i128>(
1024					container,
1025					ctx,
1026					lazy_fragment,
1027					Type::Int16,
1028					ColumnBuffer::push::<i128>,
1029				);
1030			}
1031			Type::Float4 => {
1032				return convert_vec_clone::<Uint, f32>(
1033					container,
1034					ctx,
1035					lazy_fragment,
1036					Type::Float4,
1037					ColumnBuffer::push::<f32>,
1038				);
1039			}
1040			Type::Float8 => {
1041				return convert_vec_clone::<Uint, f64>(
1042					container,
1043					ctx,
1044					lazy_fragment,
1045					Type::Float8,
1046					ColumnBuffer::push::<f64>,
1047				);
1048			}
1049			Type::Int => {
1050				return convert_vec_clone::<Uint, Int>(
1051					container,
1052					ctx,
1053					lazy_fragment,
1054					Type::Int,
1055					ColumnBuffer::push::<Int>,
1056				);
1057			}
1058			Type::Decimal => {
1059				return convert_vec_clone::<Uint, Decimal>(
1060					container,
1061					ctx,
1062					lazy_fragment,
1063					target,
1064					ColumnBuffer::push::<Decimal>,
1065				);
1066			}
1067			_ => {}
1068		}
1069	}
1070
1071	// Special handling for Decimal source (which is a struct variant in
1072	// ColumnBuffer)
1073	if let ColumnBuffer::Decimal {
1074		container,
1075		..
1076	} = data
1077	{
1078		match target {
1079			Type::Int1 => {
1080				return convert_vec_clone::<Decimal, i8>(
1081					container,
1082					ctx,
1083					lazy_fragment,
1084					Type::Int1,
1085					ColumnBuffer::push::<i8>,
1086				);
1087			}
1088			Type::Int2 => {
1089				return convert_vec_clone::<Decimal, i16>(
1090					container,
1091					ctx,
1092					lazy_fragment,
1093					Type::Int2,
1094					ColumnBuffer::push::<i16>,
1095				);
1096			}
1097			Type::Int4 => {
1098				return convert_vec_clone::<Decimal, i32>(
1099					container,
1100					ctx,
1101					lazy_fragment,
1102					Type::Int4,
1103					ColumnBuffer::push::<i32>,
1104				);
1105			}
1106			Type::Int8 => {
1107				return convert_vec_clone::<Decimal, i64>(
1108					container,
1109					ctx,
1110					lazy_fragment,
1111					Type::Int8,
1112					ColumnBuffer::push::<i64>,
1113				);
1114			}
1115			Type::Int16 => {
1116				return convert_vec_clone::<Decimal, i128>(
1117					container,
1118					ctx,
1119					lazy_fragment,
1120					Type::Int16,
1121					ColumnBuffer::push::<i128>,
1122				);
1123			}
1124			Type::Uint1 => {
1125				return convert_vec_clone::<Decimal, u8>(
1126					container,
1127					ctx,
1128					lazy_fragment,
1129					Type::Uint1,
1130					ColumnBuffer::push::<u8>,
1131				);
1132			}
1133			Type::Uint2 => {
1134				return convert_vec_clone::<Decimal, u16>(
1135					container,
1136					ctx,
1137					lazy_fragment,
1138					Type::Uint2,
1139					ColumnBuffer::push::<u16>,
1140				);
1141			}
1142			Type::Uint4 => {
1143				return convert_vec_clone::<Decimal, u32>(
1144					container,
1145					ctx,
1146					lazy_fragment,
1147					Type::Uint4,
1148					ColumnBuffer::push::<u32>,
1149				);
1150			}
1151			Type::Uint8 => {
1152				return convert_vec_clone::<Decimal, u64>(
1153					container,
1154					ctx,
1155					lazy_fragment,
1156					Type::Uint8,
1157					ColumnBuffer::push::<u64>,
1158				);
1159			}
1160			Type::Uint16 => {
1161				return convert_vec_clone::<Decimal, u128>(
1162					container,
1163					ctx,
1164					lazy_fragment,
1165					Type::Uint16,
1166					ColumnBuffer::push::<u128>,
1167				);
1168			}
1169			Type::Float4 => {
1170				return convert_vec_clone::<Decimal, f32>(
1171					container,
1172					ctx,
1173					lazy_fragment,
1174					Type::Float4,
1175					ColumnBuffer::push::<f32>,
1176				);
1177			}
1178			Type::Float8 => {
1179				return convert_vec_clone::<Decimal, f64>(
1180					container,
1181					ctx,
1182					lazy_fragment,
1183					Type::Float8,
1184					ColumnBuffer::push::<f64>,
1185				);
1186			}
1187			Type::Int => {
1188				return convert_vec_clone::<Decimal, Int>(
1189					container,
1190					ctx,
1191					lazy_fragment,
1192					Type::Int,
1193					ColumnBuffer::push::<Int>,
1194				);
1195			}
1196			Type::Uint => {
1197				return convert_vec_clone::<Decimal, Uint>(
1198					container,
1199					ctx,
1200					lazy_fragment,
1201					Type::Uint,
1202					ColumnBuffer::push::<Uint>,
1203				);
1204			}
1205			Type::Decimal => {
1206				return convert_vec_clone::<Decimal, Decimal>(
1207					container,
1208					ctx,
1209					lazy_fragment,
1210					target,
1211					ColumnBuffer::push::<Decimal>,
1212				);
1213			}
1214			_ => {}
1215		}
1216	}
1217
1218	let from = data.get_type();
1219	Err(TypeError::UnsupportedCast {
1220		from,
1221		to: target,
1222		fragment: lazy_fragment.fragment(),
1223	}
1224	.into())
1225}
1226
1227pub(crate) fn convert_vec<From, To>(
1228	container: &NumberContainer<From>,
1229	ctx: impl Convert,
1230	lazy_fragment: impl LazyFragment,
1231	target_kind: Type,
1232	mut push: impl FnMut(&mut ColumnBuffer, To),
1233) -> Result<ColumnBuffer>
1234where
1235	From: Copy + SafeConvert<To> + GetType + IsNumber + Default,
1236	To: GetType,
1237{
1238	let mut out = ColumnBuffer::with_capacity(target_kind, container.len());
1239	for idx in 0..container.len() {
1240		if container.is_defined(idx) {
1241			let val = container[idx];
1242			let fragment = lazy_fragment.fragment();
1243			match ctx.convert::<From, To>(val, fragment)? {
1244				Some(v) => push(&mut out, v),
1245				None => out.push_none(),
1246			}
1247		} else {
1248			out.push_none();
1249		}
1250	}
1251	Ok(out)
1252}
1253
1254pub(crate) fn convert_vec_clone<From, To>(
1255	container: &NumberContainer<From>,
1256	ctx: impl Convert,
1257	lazy_fragment: impl LazyFragment,
1258	target_kind: Type,
1259	mut push: impl FnMut(&mut ColumnBuffer, To),
1260) -> Result<ColumnBuffer>
1261where
1262	From: Clone + SafeConvert<To> + GetType + IsNumber + Default,
1263	To: GetType,
1264{
1265	let mut out = ColumnBuffer::with_capacity(target_kind, container.len());
1266	for idx in 0..container.len() {
1267		if container.is_defined(idx) {
1268			let val = container[idx].clone();
1269			let fragment = lazy_fragment.fragment();
1270			match ctx.convert::<From, To>(val, fragment)? {
1271				Some(v) => push(&mut out, v),
1272				None => out.push_none(),
1273			}
1274		} else {
1275			out.push_none();
1276		}
1277	}
1278	Ok(out)
1279}
1280
1281#[cfg(test)]
1282pub mod tests {
1283	mod convert {
1284		use std::mem;
1285
1286		use reifydb_type::{
1287			Result,
1288			fragment::Fragment,
1289			value::{
1290				container::number::NumberContainer,
1291				number::safe::convert::SafeConvert,
1292				r#type::{Type, get::GetType},
1293			},
1294		};
1295
1296		use crate::expression::{cast::number::convert_vec, convert::Convert};
1297
1298		#[test]
1299		fn test_promote_ok() {
1300			let data = [1i8, 2i8];
1301			let ctx = TestCtx::new();
1302
1303			let container = NumberContainer::new(data.to_vec());
1304			let result = convert_vec::<i8, i16>(
1305				&container,
1306				&ctx,
1307				|| Fragment::testing_empty(),
1308				Type::Int2,
1309				|col, v| col.push::<i16>(v),
1310			)
1311			.unwrap();
1312
1313			let slice: &[i16] = result.as_slice();
1314			assert_eq!(slice, &[1i16, 2i16]);
1315		}
1316
1317		#[test]
1318		fn test_promote_none_maps_to_undefined() {
1319			// 42 mapped to None
1320			let data = [42i8];
1321			let ctx = TestCtx::new();
1322
1323			let container = NumberContainer::new(data.to_vec());
1324			let result = convert_vec::<i8, i16>(
1325				&container,
1326				&ctx,
1327				|| Fragment::testing_empty(),
1328				Type::Int2,
1329				|col, v| col.push::<i16>(v),
1330			)
1331			.unwrap();
1332
1333			assert!(!result.is_defined(0));
1334		}
1335
1336		#[test]
1337		fn test_promote_valid_input_is_defined() {
1338			// With the Option-based nullability model, containers without an
1339			// Option wrapper are fully defined.  Value 1 converts successfully,
1340			// so the result must be defined.
1341			let data = [1i8];
1342			let ctx = TestCtx::new();
1343
1344			let container = NumberContainer::new(data.to_vec());
1345			let result = convert_vec::<i8, i16>(
1346				&container,
1347				&ctx,
1348				|| Fragment::testing_empty(),
1349				Type::Int2,
1350				|col, v| col.push::<i16>(v),
1351			)
1352			.unwrap();
1353
1354			assert!(result.is_defined(0));
1355			let slice = result.as_slice::<i16>();
1356			assert_eq!(slice, &[1i16]);
1357		}
1358
1359		#[test]
1360		fn test_promote_conversion_failure_is_undefined() {
1361			// Only value 42 triggers a conversion failure (ctx returns None).
1362			// Value 3 is fully defined in the input and converts successfully.
1363			let data = [1i8, 42i8, 3i8, 4i8];
1364			let ctx = TestCtx::new();
1365
1366			let container = NumberContainer::new(data.to_vec());
1367			let result = convert_vec::<i8, i16>(
1368				&container,
1369				&ctx,
1370				|| Fragment::testing_empty(),
1371				Type::Int2,
1372				|col, v| col.push::<i16>(v),
1373			)
1374			.unwrap();
1375
1376			let slice = result.as_slice::<i16>();
1377			assert_eq!(slice, &[1i16, 0, 3i16, 4i16]);
1378			assert!(result.is_defined(0));
1379			assert!(!result.is_defined(1));
1380			assert!(result.is_defined(2));
1381			assert!(result.is_defined(3));
1382		}
1383
1384		struct TestCtx;
1385
1386		impl TestCtx {
1387			fn new() -> Self {
1388				Self
1389			}
1390		}
1391
1392		impl Convert for &TestCtx {
1393			/// Can only used with i8
1394			fn convert<From, To>(&self, val: From, _fragment: impl Into<Fragment>) -> Result<Option<To>>
1395			where
1396				From: SafeConvert<To> + GetType,
1397				To: GetType,
1398			{
1399				// Only simulate conversion failure for i8 == 42
1400				// or i16 == 42
1401				if mem::size_of::<From>() == 1 {
1402					let raw: i8 = unsafe { mem::transmute_copy(&val) };
1403					if raw == 42 {
1404						return Ok(None);
1405					}
1406				} else if mem::size_of::<From>() == 2 {
1407					let raw: i16 = unsafe { mem::transmute_copy(&val) };
1408					if raw == 42 {
1409						return Ok(None);
1410					}
1411				}
1412				Ok(Some(val.checked_convert().unwrap()))
1413			}
1414		}
1415
1416		#[test]
1417		fn test_demote_ok() {
1418			let data = [1i16, 2i16];
1419			let ctx = TestCtx::new();
1420
1421			let container = NumberContainer::new(data.to_vec());
1422			let result = convert_vec::<i16, i8>(
1423				&container,
1424				&ctx,
1425				|| Fragment::testing_empty(),
1426				Type::Int1,
1427				|col, v| col.push::<i8>(v),
1428			)
1429			.unwrap();
1430
1431			let slice: &[i8] = result.as_slice();
1432			assert_eq!(slice, &[1i8, 2i8]);
1433			assert!(result.is_defined(0));
1434			assert!(result.is_defined(1));
1435		}
1436
1437		#[test]
1438		fn test_demote_none_maps_to_undefined() {
1439			let data = [42i16];
1440			let ctx = TestCtx::new();
1441
1442			let container = NumberContainer::new(data.to_vec());
1443			let result = convert_vec::<i16, i8>(
1444				&container,
1445				&ctx,
1446				|| Fragment::testing_empty(),
1447				Type::Int1,
1448				|col, v| col.push::<i8>(v),
1449			)
1450			.unwrap();
1451
1452			assert!(!result.is_defined(0));
1453		}
1454
1455		#[test]
1456		fn test_demote_valid_input_is_defined() {
1457			// With the Option-based nullability model, containers without an
1458			// Option wrapper are fully defined.  Value 1 converts successfully,
1459			// so the result must be defined.
1460			let data = [1i16];
1461			let ctx = TestCtx::new();
1462
1463			let container = NumberContainer::new(data.to_vec());
1464			let result = convert_vec::<i16, i8>(
1465				&container,
1466				&ctx,
1467				|| Fragment::testing_empty(),
1468				Type::Int1,
1469				|col, v| col.push::<i8>(v),
1470			)
1471			.unwrap();
1472
1473			assert!(result.is_defined(0));
1474			let slice: &[i8] = result.as_slice();
1475			assert_eq!(slice, &[1i8]);
1476		}
1477
1478		#[test]
1479		fn test_demote_conversion_failure_is_undefined() {
1480			// Only value 42 triggers a conversion failure (ctx returns None).
1481			// Value 3 is fully defined in the input and converts successfully.
1482			let data = [1i16, 42i16, 3i16, 4i16];
1483			let ctx = TestCtx::new();
1484
1485			let container = NumberContainer::new(data.to_vec());
1486			let result = convert_vec::<i16, i8>(
1487				&container,
1488				&ctx,
1489				|| Fragment::testing_empty(),
1490				Type::Int1,
1491				|col, v| col.push::<i8>(v),
1492			)
1493			.unwrap();
1494
1495			let slice: &[i8] = result.as_slice();
1496			assert_eq!(slice, &[1i8, 0, 3i8, 4i8]);
1497			assert!(result.is_defined(0));
1498			assert!(!result.is_defined(1));
1499			assert!(result.is_defined(2));
1500			assert!(result.is_defined(3));
1501		}
1502	}
1503}