Skip to main content

reifydb_engine/expression/cast/
number.rs

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