1pub mod any;
5pub mod blob;
6pub mod boolean;
7pub mod number;
8pub mod temporal;
9pub mod text;
10pub mod uuid;
11
12use reifydb_core::value::column::data::ColumnData;
13use reifydb_type::{
14 error::TypeError, fragment::LazyFragment, storage::DataBitVec, util::bitvec::BitVec, value::r#type::Type,
15};
16
17use crate::{
18 Result,
19 expression::{cast::uuid::to_uuid, context::EvalContext},
20};
21
22pub fn cast_column_data(
23 ctx: &EvalContext,
24 data: &ColumnData,
25 target: Type,
26 lazy_fragment: impl LazyFragment + Clone,
27) -> Result<ColumnData> {
28 if let ColumnData::Option {
30 inner,
31 bitvec,
32 } = data
33 {
34 let inner_target = match &target {
35 Type::Option(t) => t.as_ref().clone(),
36 other => other.clone(),
37 };
38 let total_len = inner.len();
39 let defined_count = DataBitVec::count_ones(bitvec);
40
41 if defined_count == 0 {
42 return Ok(ColumnData::none_typed(inner_target, total_len));
43 }
44
45 if defined_count < total_len {
46 let mut compacted = inner.as_ref().clone();
48 compacted.filter(bitvec)?;
49
50 let mut cast_compacted = cast_column_data(ctx, &compacted, inner_target, lazy_fragment)?;
52
53 let sentinel = defined_count;
56 let mut expand_indices = Vec::with_capacity(total_len);
57 let mut src_idx = 0usize;
58 for i in 0..total_len {
59 if DataBitVec::get(bitvec, i) {
60 expand_indices.push(src_idx);
61 src_idx += 1;
62 } else {
63 expand_indices.push(sentinel);
64 }
65 }
66 cast_compacted.reorder(&expand_indices);
67
68 return Ok(match cast_compacted {
69 already @ ColumnData::Option {
70 ..
71 } => already,
72 other => ColumnData::Option {
73 inner: Box::new(other),
74 bitvec: bitvec.clone(),
75 },
76 });
77 }
78
79 let cast_inner = cast_column_data(ctx, inner, inner_target, lazy_fragment)?;
81 return Ok(match cast_inner {
82 already @ ColumnData::Option {
83 ..
84 } => already,
85 other => ColumnData::Option {
86 inner: Box::new(other),
87 bitvec: bitvec.clone(),
88 },
89 });
90 }
91 if let Type::Option(inner_target) = &target {
93 let cast_inner = cast_column_data(ctx, data, *inner_target.clone(), lazy_fragment)?;
94 return Ok(match cast_inner {
95 already @ ColumnData::Option {
96 ..
97 } => already,
98 other => {
99 let bitvec = BitVec::repeat(other.len(), true);
100 ColumnData::Option {
101 inner: Box::new(other),
102 bitvec,
103 }
104 }
105 });
106 }
107
108 let source_type = data.get_type();
109 if target == source_type {
110 return Ok(data.clone());
111 }
112 match (&source_type, &target) {
113 (Type::Any, _) => any::from_any(ctx, data, target, lazy_fragment),
114 (_, t) if t.is_number() => number::to_number(ctx, data, target, lazy_fragment),
115 (_, t) if t.is_blob() => blob::to_blob(data, lazy_fragment),
116 (_, t) if t.is_bool() => boolean::to_boolean(data, lazy_fragment),
117 (_, t) if t.is_utf8() => text::to_text(data, lazy_fragment),
118 (_, t) if t.is_temporal() => temporal::to_temporal(data, target, lazy_fragment),
119 (_, t) if t.is_uuid() => to_uuid(data, target, lazy_fragment),
120 (source, t) if source.is_uuid() || t.is_uuid() => to_uuid(data, target, lazy_fragment),
121 _ => Err(TypeError::UnsupportedCast {
122 from: source_type,
123 to: target,
124 fragment: lazy_fragment.fragment(),
125 }
126 .into()),
127 }
128}
129
130#[cfg(test)]
131pub mod tests {
132 use reifydb_core::value::column::data::ColumnData;
133 use reifydb_function::registry::Functions;
134 use reifydb_rql::expression::{
135 CastExpression, ConstantExpression,
136 ConstantExpression::Number,
137 Expression::{Cast, Constant, Prefix},
138 PrefixExpression, PrefixOperator, TypeExpression,
139 };
140 use reifydb_runtime::clock::Clock;
141 use reifydb_type::{fragment::Fragment, value::r#type::Type};
142
143 use crate::expression::{context::EvalContext, eval::evaluate};
144
145 #[test]
146 fn test_cast_integer() {
147 let mut ctx = EvalContext::testing();
148 let result = evaluate(
149 &mut ctx,
150 &Cast(CastExpression {
151 fragment: Fragment::testing_empty(),
152 expression: Box::new(Constant(Number {
153 fragment: Fragment::internal("42"),
154 })),
155 to: TypeExpression {
156 fragment: Fragment::testing_empty(),
157 ty: Type::Int4,
158 },
159 }),
160 &Functions::empty(),
161 &Clock::default(),
162 )
163 .unwrap();
164
165 assert_eq!(*result.data(), ColumnData::int4([42]));
166 }
167
168 #[test]
169 fn test_cast_negative_integer() {
170 let mut ctx = EvalContext::testing();
171 let result = evaluate(
172 &mut ctx,
173 &Cast(CastExpression {
174 fragment: Fragment::testing_empty(),
175 expression: Box::new(Prefix(PrefixExpression {
176 operator: PrefixOperator::Minus(Fragment::testing_empty()),
177 expression: Box::new(Constant(Number {
178 fragment: Fragment::internal("42"),
179 })),
180 fragment: Fragment::testing_empty(),
181 })),
182 to: TypeExpression {
183 fragment: Fragment::testing_empty(),
184 ty: Type::Int4,
185 },
186 }),
187 &Functions::empty(),
188 &Clock::default(),
189 )
190 .unwrap();
191
192 assert_eq!(*result.data(), ColumnData::int4([-42]));
193 }
194
195 #[test]
196 fn test_cast_negative_min() {
197 let mut ctx = EvalContext::testing();
198 let result = evaluate(
199 &mut ctx,
200 &Cast(CastExpression {
201 fragment: Fragment::testing_empty(),
202 expression: Box::new(Prefix(PrefixExpression {
203 operator: PrefixOperator::Minus(Fragment::testing_empty()),
204 expression: Box::new(Constant(Number {
205 fragment: Fragment::internal("128"),
206 })),
207 fragment: Fragment::testing_empty(),
208 })),
209 to: TypeExpression {
210 fragment: Fragment::testing_empty(),
211 ty: Type::Int1,
212 },
213 }),
214 &Functions::empty(),
215 &Clock::default(),
216 )
217 .unwrap();
218
219 assert_eq!(*result.data(), ColumnData::int1([-128]));
220 }
221
222 #[test]
223 fn test_cast_float_8() {
224 let mut ctx = EvalContext::testing();
225 let result = evaluate(
226 &mut ctx,
227 &Cast(CastExpression {
228 fragment: Fragment::testing_empty(),
229 expression: Box::new(Constant(Number {
230 fragment: Fragment::internal("4.2"),
231 })),
232 to: TypeExpression {
233 fragment: Fragment::testing_empty(),
234 ty: Type::Float8,
235 },
236 }),
237 &Functions::empty(),
238 &Clock::default(),
239 )
240 .unwrap();
241
242 assert_eq!(*result.data(), ColumnData::float8([4.2]));
243 }
244
245 #[test]
246 fn test_cast_float_4() {
247 let mut ctx = EvalContext::testing();
248 let result = evaluate(
249 &mut ctx,
250 &Cast(CastExpression {
251 fragment: Fragment::testing_empty(),
252 expression: Box::new(Constant(Number {
253 fragment: Fragment::internal("4.2"),
254 })),
255 to: TypeExpression {
256 fragment: Fragment::testing_empty(),
257 ty: Type::Float4,
258 },
259 }),
260 &Functions::empty(),
261 &Clock::default(),
262 )
263 .unwrap();
264
265 assert_eq!(*result.data(), ColumnData::float4([4.2]));
266 }
267
268 #[test]
269 fn test_cast_negative_float_4() {
270 let mut ctx = EvalContext::testing();
271 let result = evaluate(
272 &mut ctx,
273 &Cast(CastExpression {
274 fragment: Fragment::testing_empty(),
275 expression: Box::new(Constant(Number {
276 fragment: Fragment::internal("-1.1"),
277 })),
278 to: TypeExpression {
279 fragment: Fragment::testing_empty(),
280 ty: Type::Float4,
281 },
282 }),
283 &Functions::empty(),
284 &Clock::default(),
285 )
286 .unwrap();
287
288 assert_eq!(*result.data(), ColumnData::float4([-1.1]));
289 }
290
291 #[test]
292 fn test_cast_negative_float_8() {
293 let mut ctx = EvalContext::testing();
294 let result = evaluate(
295 &mut ctx,
296 &Cast(CastExpression {
297 fragment: Fragment::testing_empty(),
298 expression: Box::new(Constant(Number {
299 fragment: Fragment::internal("-1.1"),
300 })),
301 to: TypeExpression {
302 fragment: Fragment::testing_empty(),
303 ty: Type::Float8,
304 },
305 }),
306 &Functions::empty(),
307 &Clock::default(),
308 )
309 .unwrap();
310
311 assert_eq!(*result.data(), ColumnData::float8([-1.1]));
312 }
313
314 #[test]
315 fn test_cast_string_to_bool() {
316 let mut ctx = EvalContext::testing();
317 let result = evaluate(
318 &mut ctx,
319 &Cast(CastExpression {
320 fragment: Fragment::testing_empty(),
321 expression: Box::new(Constant(ConstantExpression::Text {
322 fragment: Fragment::internal("0"),
323 })),
324 to: TypeExpression {
325 fragment: Fragment::testing_empty(),
326 ty: Type::Boolean,
327 },
328 }),
329 &Functions::empty(),
330 &Clock::default(),
331 )
332 .unwrap();
333
334 assert_eq!(*result.data(), ColumnData::bool([false]));
335 }
336
337 #[test]
338 fn test_cast_string_neg_one_to_bool_should_fail() {
339 let mut ctx = EvalContext::testing();
340 let result = evaluate(
341 &mut ctx,
342 &Cast(CastExpression {
343 fragment: Fragment::testing_empty(),
344 expression: Box::new(Constant(ConstantExpression::Text {
345 fragment: Fragment::internal("-1"),
346 })),
347 to: TypeExpression {
348 fragment: Fragment::testing_empty(),
349 ty: Type::Boolean,
350 },
351 }),
352 &Functions::empty(),
353 &Clock::default(),
354 );
355
356 assert!(result.is_err());
357
358 let err = result.unwrap_err();
361 let diagnostic = err.0;
362 assert_eq!(diagnostic.code, "CAST_004");
363 assert!(diagnostic.cause.is_some());
364 let cause = diagnostic.cause.unwrap();
365 assert_eq!(cause.code, "BOOLEAN_003"); }
367
368 #[test]
369 fn test_cast_boolean_to_date_should_fail() {
370 let mut ctx = EvalContext::testing();
371 let result = evaluate(
372 &mut ctx,
373 &Cast(CastExpression {
374 fragment: Fragment::testing_empty(),
375 expression: Box::new(Constant(ConstantExpression::Bool {
376 fragment: Fragment::internal("true"),
377 })),
378 to: TypeExpression {
379 fragment: Fragment::testing_empty(),
380 ty: Type::Date,
381 },
382 }),
383 &Functions::empty(),
384 &Clock::default(),
385 );
386
387 assert!(result.is_err());
388
389 let err = result.unwrap_err();
392 let diagnostic = err.0;
393 assert_eq!(diagnostic.code, "CAST_001");
394 }
395
396 #[test]
397 fn test_cast_text_to_decimal() {
398 let mut ctx = EvalContext::testing();
399 let result = evaluate(
400 &mut ctx,
401 &Cast(CastExpression {
402 fragment: Fragment::testing_empty(),
403 expression: Box::new(Constant(ConstantExpression::Text {
404 fragment: Fragment::internal("123.456789"),
405 })),
406 to: TypeExpression {
407 fragment: Fragment::testing_empty(),
408 ty: Type::Decimal,
409 },
410 }),
411 &Functions::empty(),
412 &Clock::default(),
413 )
414 .unwrap();
415
416 if let ColumnData::Decimal {
417 container,
418 ..
419 } = result.data()
420 {
421 assert_eq!(container.len(), 1);
422 assert!(container.is_defined(0));
423 let value = &container[0];
424 assert_eq!(value.to_string(), "123.456789");
425 } else {
426 panic!("Expected Decimal column data");
427 }
428 }
429}