datatypes/types/
cast.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::data_type::{ConcreteDataType, DataType};
16use crate::error::{self, Error, Result};
17use crate::types::TimeType;
18use crate::value::Value;
19use crate::vectors::Helper;
20
21/// Used to cast the value to dest ConcreteDataType temporarily.
22/// To keep the same behavior as arrow-rs.
23pub 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/// Cast options for cast functions.
36#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
37pub struct CastOption {
38    /// decide how to handle cast failures,
39    /// either return NULL (strict=false) or return ERR (strict=true)
40    pub strict: bool,
41}
42
43impl CastOption {
44    pub fn is_strict(&self) -> bool {
45        self.strict
46    }
47}
48
49/// Cast the value to dest_type with CastOption.
50///
51/// # Arguments
52/// * `src_value` - The value to be casted.
53/// * `dest_type` - The destination type.
54/// * `cast_option` - The CastOption.
55///
56/// # Returns
57/// If success, return the casted value.
58/// If CastOption's strict is true, return an error if the cast fails.
59/// If CastOption's strict is false, return NULL if the cast fails.
60pub 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
85/// Return true if the src_value can be casted to dest_type, Otherwise, return false.
86///
87/// Notice: this function does not promise that the `cast_with_opt` will succeed,
88/// it only checks whether the src_value can be casted to dest_type.
89pub 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 type cast
100        (_, Null(_)) => true,
101        (Null(_), _) => true,
102
103        // boolean type cast
104        (_, Boolean(_)) => src_type.is_numeric() || src_type.is_string(),
105        (Boolean(_), _) => dest_type.is_numeric() || dest_type.is_string(),
106
107        // numeric types cast
108        (
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        // temporal types cast
118        // Date type
119        (Date(_), Int32(_) | Timestamp(_) | String(_)) => true,
120        (Int32(_) | String(_) | Timestamp(_), Date(_)) => true,
121        (Date(_), Date(_)) => true,
122        // Timestamp type
123        (Timestamp(_), Int64(_) | String(_)) => true,
124        (Int64(_) | String(_), Timestamp(_)) => true,
125        (Timestamp(_), Timestamp(_)) => true,
126        // Time type
127        (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        // TODO(QuenKar): interval type cast
134        (Interval(_), String(_)) => true,
135        (Duration(_), String(_)) => true,
136        // other situations return false
137        (_, _) => 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        // non-strict mode
213        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        // strict mode
221        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        // numeric cast
253        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        // string -> other types
268        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        // date -> other types
278        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        // timestamp -> other types
286        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        // time -> other types
295        test_can_cast!(
296            Value::Time(Time::new_second(0)),
297            null_datatype,
298            string_datatype
299        );
300    }
301}