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