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