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 (_, Type::IdentityId) => to_uuid(data, target, lazy_fragment),
120 (Type::IdentityId, _) => to_uuid(data, target, lazy_fragment),
121 (_, t) if t.is_uuid() => to_uuid(data, target, lazy_fragment),
122 (source, t) if source.is_uuid() || t.is_uuid() => to_uuid(data, target, lazy_fragment),
123 _ => Err(TypeError::UnsupportedCast {
124 from: source_type,
125 to: target,
126 fragment: lazy_fragment.fragment(),
127 }
128 .into()),
129 }
130}
131
132#[cfg(test)]
133pub mod tests {
134 use reifydb_core::value::column::data::ColumnData;
135 use reifydb_rql::expression::{
136 CastExpression, ConstantExpression,
137 ConstantExpression::Number,
138 Expression::{Cast, Constant, Prefix},
139 PrefixExpression, PrefixOperator, TypeExpression,
140 };
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 )
161 .unwrap();
162
163 assert_eq!(*result.data(), ColumnData::int4([42]));
164 }
165
166 #[test]
167 fn test_cast_negative_integer() {
168 let mut ctx = EvalContext::testing();
169 let result = evaluate(
170 &mut ctx,
171 &Cast(CastExpression {
172 fragment: Fragment::testing_empty(),
173 expression: Box::new(Prefix(PrefixExpression {
174 operator: PrefixOperator::Minus(Fragment::testing_empty()),
175 expression: Box::new(Constant(Number {
176 fragment: Fragment::internal("42"),
177 })),
178 fragment: Fragment::testing_empty(),
179 })),
180 to: TypeExpression {
181 fragment: Fragment::testing_empty(),
182 ty: Type::Int4,
183 },
184 }),
185 )
186 .unwrap();
187
188 assert_eq!(*result.data(), ColumnData::int4([-42]));
189 }
190
191 #[test]
192 fn test_cast_negative_min() {
193 let mut ctx = EvalContext::testing();
194 let result = evaluate(
195 &mut ctx,
196 &Cast(CastExpression {
197 fragment: Fragment::testing_empty(),
198 expression: Box::new(Prefix(PrefixExpression {
199 operator: PrefixOperator::Minus(Fragment::testing_empty()),
200 expression: Box::new(Constant(Number {
201 fragment: Fragment::internal("128"),
202 })),
203 fragment: Fragment::testing_empty(),
204 })),
205 to: TypeExpression {
206 fragment: Fragment::testing_empty(),
207 ty: Type::Int1,
208 },
209 }),
210 )
211 .unwrap();
212
213 assert_eq!(*result.data(), ColumnData::int1([-128]));
214 }
215
216 #[test]
217 fn test_cast_float_8() {
218 let mut ctx = EvalContext::testing();
219 let result = evaluate(
220 &mut ctx,
221 &Cast(CastExpression {
222 fragment: Fragment::testing_empty(),
223 expression: Box::new(Constant(Number {
224 fragment: Fragment::internal("4.2"),
225 })),
226 to: TypeExpression {
227 fragment: Fragment::testing_empty(),
228 ty: Type::Float8,
229 },
230 }),
231 )
232 .unwrap();
233
234 assert_eq!(*result.data(), ColumnData::float8([4.2]));
235 }
236
237 #[test]
238 fn test_cast_float_4() {
239 let mut ctx = EvalContext::testing();
240 let result = evaluate(
241 &mut ctx,
242 &Cast(CastExpression {
243 fragment: Fragment::testing_empty(),
244 expression: Box::new(Constant(Number {
245 fragment: Fragment::internal("4.2"),
246 })),
247 to: TypeExpression {
248 fragment: Fragment::testing_empty(),
249 ty: Type::Float4,
250 },
251 }),
252 )
253 .unwrap();
254
255 assert_eq!(*result.data(), ColumnData::float4([4.2]));
256 }
257
258 #[test]
259 fn test_cast_negative_float_4() {
260 let mut ctx = EvalContext::testing();
261 let result = evaluate(
262 &mut ctx,
263 &Cast(CastExpression {
264 fragment: Fragment::testing_empty(),
265 expression: Box::new(Constant(Number {
266 fragment: Fragment::internal("-1.1"),
267 })),
268 to: TypeExpression {
269 fragment: Fragment::testing_empty(),
270 ty: Type::Float4,
271 },
272 }),
273 )
274 .unwrap();
275
276 assert_eq!(*result.data(), ColumnData::float4([-1.1]));
277 }
278
279 #[test]
280 fn test_cast_negative_float_8() {
281 let mut ctx = EvalContext::testing();
282 let result = evaluate(
283 &mut ctx,
284 &Cast(CastExpression {
285 fragment: Fragment::testing_empty(),
286 expression: Box::new(Constant(Number {
287 fragment: Fragment::internal("-1.1"),
288 })),
289 to: TypeExpression {
290 fragment: Fragment::testing_empty(),
291 ty: Type::Float8,
292 },
293 }),
294 )
295 .unwrap();
296
297 assert_eq!(*result.data(), ColumnData::float8([-1.1]));
298 }
299
300 #[test]
301 fn test_cast_string_to_bool() {
302 let mut ctx = EvalContext::testing();
303 let result = evaluate(
304 &mut ctx,
305 &Cast(CastExpression {
306 fragment: Fragment::testing_empty(),
307 expression: Box::new(Constant(ConstantExpression::Text {
308 fragment: Fragment::internal("0"),
309 })),
310 to: TypeExpression {
311 fragment: Fragment::testing_empty(),
312 ty: Type::Boolean,
313 },
314 }),
315 )
316 .unwrap();
317
318 assert_eq!(*result.data(), ColumnData::bool([false]));
319 }
320
321 #[test]
322 fn test_cast_string_neg_one_to_bool_should_fail() {
323 let mut ctx = EvalContext::testing();
324 let result = evaluate(
325 &mut ctx,
326 &Cast(CastExpression {
327 fragment: Fragment::testing_empty(),
328 expression: Box::new(Constant(ConstantExpression::Text {
329 fragment: Fragment::internal("-1"),
330 })),
331 to: TypeExpression {
332 fragment: Fragment::testing_empty(),
333 ty: Type::Boolean,
334 },
335 }),
336 );
337
338 assert!(result.is_err());
339
340 let err = result.unwrap_err();
343 let diagnostic = err.0;
344 assert_eq!(diagnostic.code, "CAST_004");
345 assert!(diagnostic.cause.is_some());
346 let cause = diagnostic.cause.unwrap();
347 assert_eq!(cause.code, "BOOLEAN_003"); }
349
350 #[test]
351 fn test_cast_boolean_to_date_should_fail() {
352 let mut ctx = EvalContext::testing();
353 let result = evaluate(
354 &mut ctx,
355 &Cast(CastExpression {
356 fragment: Fragment::testing_empty(),
357 expression: Box::new(Constant(ConstantExpression::Bool {
358 fragment: Fragment::internal("true"),
359 })),
360 to: TypeExpression {
361 fragment: Fragment::testing_empty(),
362 ty: Type::Date,
363 },
364 }),
365 );
366
367 assert!(result.is_err());
368
369 let err = result.unwrap_err();
372 let diagnostic = err.0;
373 assert_eq!(diagnostic.code, "CAST_001");
374 }
375
376 #[test]
377 fn test_cast_text_to_decimal() {
378 let mut ctx = EvalContext::testing();
379 let result = evaluate(
380 &mut ctx,
381 &Cast(CastExpression {
382 fragment: Fragment::testing_empty(),
383 expression: Box::new(Constant(ConstantExpression::Text {
384 fragment: Fragment::internal("123.456789"),
385 })),
386 to: TypeExpression {
387 fragment: Fragment::testing_empty(),
388 ty: Type::Decimal,
389 },
390 }),
391 )
392 .unwrap();
393
394 if let ColumnData::Decimal {
395 container,
396 ..
397 } = result.data()
398 {
399 assert_eq!(container.len(), 1);
400 assert!(container.is_defined(0));
401 let value = &container[0];
402 assert_eq!(value.to_string(), "123.456789");
403 } else {
404 panic!("Expected Decimal column data");
405 }
406 }
407}