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