1use crate::data_type::{ConcreteDataType, DataType};
16use crate::error::{self, Error, Result};
17use crate::types::TimeType;
18use crate::value::Value;
19use crate::vectors::Helper;
20
21pub fn cast(src_value: Value, dest_type: &ConcreteDataType) -> Result<Value> {
24 if src_value == Value::Null {
25 return Ok(Value::Null);
26 }
27 let src_type = src_value.data_type();
28 let scalar_value = src_value.try_to_scalar_value(&src_type)?;
29 let new_value = Helper::try_from_scalar_value(scalar_value, 1)?
30 .cast(dest_type)?
31 .get(0);
32 Ok(new_value)
33}
34
35#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
37pub struct CastOption {
38 pub strict: bool,
41}
42
43impl CastOption {
44 pub fn is_strict(&self) -> bool {
45 self.strict
46 }
47}
48
49pub fn cast_with_opt(
61 src_value: Value,
62 dest_type: &ConcreteDataType,
63 cast_option: &CastOption,
64) -> Result<Value> {
65 if !can_cast_type(&src_value, dest_type) {
66 if cast_option.strict {
67 return Err(invalid_type_cast(&src_value, dest_type));
68 } else {
69 return Ok(Value::Null);
70 }
71 }
72 let new_value = dest_type.try_cast(src_value.clone());
73 match new_value {
74 Some(v) => Ok(v),
75 None => {
76 if cast_option.strict && !src_value.is_null() {
77 Err(invalid_type_cast(&src_value, dest_type))
78 } else {
79 Ok(Value::Null)
80 }
81 }
82 }
83}
84
85pub fn can_cast_type(src_value: &Value, dest_type: &ConcreteDataType) -> bool {
90 use ConcreteDataType::*;
91 use TimeType::*;
92 let src_type = &src_value.data_type();
93
94 if src_type == dest_type {
95 return true;
96 }
97
98 match (src_type, dest_type) {
99 (_, Null(_)) => true,
101 (Null(_), _) => true,
102
103 (_, Boolean(_)) => src_type.is_numeric() || src_type.is_string(),
105 (Boolean(_), _) => dest_type.is_numeric() || dest_type.is_string(),
106
107 (
109 UInt8(_) | UInt16(_) | UInt32(_) | UInt64(_) | Int8(_) | Int16(_) | Int32(_) | Int64(_)
110 | Float32(_) | Float64(_) | String(_),
111 UInt8(_) | UInt16(_) | UInt32(_) | UInt64(_) | Int8(_) | Int16(_) | Int32(_) | Int64(_)
112 | Float32(_) | Float64(_) | String(_),
113 ) => true,
114
115 (String(_), Binary(_)) => true,
116
117 (Date(_), Int32(_) | Timestamp(_) | String(_)) => true,
120 (Int32(_) | String(_) | Timestamp(_), Date(_)) => true,
121 (Date(_), Date(_)) => true,
122 (Timestamp(_), Int64(_) | String(_)) => true,
124 (Int64(_) | String(_), Timestamp(_)) => true,
125 (Timestamp(_), Timestamp(_)) => true,
126 (Time(_), String(_)) => true,
128 (Time(Second(_)), Int32(_)) => true,
129 (Time(Millisecond(_)), Int32(_)) => true,
130 (Time(Microsecond(_)), Int64(_)) => true,
131 (Time(Nanosecond(_)), Int64(_)) => true,
132 (Time(_), Time(_)) => true,
133 (Interval(_), String(_)) => true,
135 (Duration(_), String(_)) => true,
136 (_, _) => false,
138 }
139}
140
141fn invalid_type_cast(src_value: &Value, dest_type: &ConcreteDataType) -> Error {
142 let src_type = src_value.data_type();
143 if src_type.is_string() {
144 error::CastTypeSnafu {
145 msg: format!("Could not parse string '{}' to {}", src_value, dest_type),
146 }
147 .build()
148 } else if src_type.is_numeric() && dest_type.is_numeric() {
149 error::CastTypeSnafu {
150 msg: format!(
151 "Type {} with value {} can't be cast because the value is out of range for the destination type {}",
152 src_type,
153 src_value,
154 dest_type
155 ),
156 }
157 .build()
158 } else {
159 error::CastTypeSnafu {
160 msg: format!(
161 "Type {} with value {} can't be cast to the destination type {}",
162 src_type, src_value, dest_type
163 ),
164 }
165 .build()
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use common_base::bytes::StringBytes;
172 use common_time::time::Time;
173 use common_time::timezone::set_default_timezone;
174 use common_time::{Date, Timestamp};
175 use ordered_float::OrderedFloat;
176
177 use super::*;
178
179 macro_rules! test_can_cast {
180 ($src_value: expr, $($dest_type: ident),+) => {
181 $(
182 let val = $src_value;
183 let t = ConcreteDataType::$dest_type();
184 assert_eq!(can_cast_type(&val, &t), true);
185 )*
186 };
187 }
188
189 macro_rules! test_primitive_cast {
190 ($($value: expr),*) => {
191 $(
192 test_can_cast!(
193 $value,
194 uint8_datatype,
195 uint16_datatype,
196 uint32_datatype,
197 uint64_datatype,
198 int8_datatype,
199 int16_datatype,
200 int32_datatype,
201 int64_datatype,
202 float32_datatype,
203 float64_datatype
204 );
205 )*
206 };
207 }
208
209 #[test]
210 fn test_cast_with_opt() {
211 set_default_timezone(Some("Asia/Shanghai")).unwrap();
212 let cast_option = CastOption { strict: false };
214 let src_value = Value::Int8(-1);
215 let dest_type = ConcreteDataType::uint8_datatype();
216 let res = cast_with_opt(src_value, &dest_type, &cast_option);
217 assert!(res.is_ok());
218 assert_eq!(res.unwrap(), Value::Null);
219
220 let cast_option = CastOption { strict: true };
222 let src_value = Value::Int8(-1);
223 let dest_type = ConcreteDataType::uint8_datatype();
224 let res = cast_with_opt(src_value, &dest_type, &cast_option);
225 assert!(res.is_err());
226 assert_eq!(
227 res.unwrap_err().to_string(),
228 "Type Int8 with value -1 can't be cast because the value is out of range for the destination type UInt8"
229 );
230
231 let src_value = Value::String(StringBytes::from("abc"));
232 let dest_type = ConcreteDataType::uint8_datatype();
233 let res = cast_with_opt(src_value, &dest_type, &cast_option);
234 assert!(res.is_err());
235 assert_eq!(
236 res.unwrap_err().to_string(),
237 "Could not parse string 'abc' to UInt8"
238 );
239
240 let src_value = Value::Timestamp(Timestamp::new_second(10));
241 let dest_type = ConcreteDataType::int8_datatype();
242 let res = cast_with_opt(src_value, &dest_type, &cast_option);
243 assert!(res.is_err());
244 assert_eq!(
245 res.unwrap_err().to_string(),
246 "Type TimestampSecond with value 1970-01-01 08:00:10+0800 can't be cast to the destination type Int8"
247 );
248 }
249
250 #[test]
251 fn test_can_cast_type() {
252 test_primitive_cast!(
254 Value::UInt8(0),
255 Value::UInt16(1),
256 Value::UInt32(2),
257 Value::UInt64(3),
258 Value::Int8(4),
259 Value::Int16(5),
260 Value::Int32(6),
261 Value::Int64(7),
262 Value::Float32(OrderedFloat(8.0)),
263 Value::Float64(OrderedFloat(9.0)),
264 Value::String(StringBytes::from("10"))
265 );
266
267 test_can_cast!(
269 Value::String(StringBytes::from("0")),
270 null_datatype,
271 boolean_datatype,
272 date_datatype,
273 timestamp_second_datatype,
274 binary_datatype
275 );
276
277 test_can_cast!(
279 Value::Date(Date::from_str_utc("2021-01-01").unwrap()),
280 null_datatype,
281 int32_datatype,
282 timestamp_second_datatype,
283 string_datatype
284 );
285 test_can_cast!(
287 Value::Timestamp(Timestamp::from_str_utc("2021-01-01 00:00:00").unwrap()),
288 null_datatype,
289 int64_datatype,
290 date_datatype,
291 string_datatype
292 );
293
294 test_can_cast!(
296 Value::Time(Time::new_second(0)),
297 null_datatype,
298 string_datatype
299 );
300 }
301}