mz_expr/scalar/func/impls/
int64.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::fmt;
11
12use mz_lowertest::MzReflect;
13use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale};
14use mz_repr::adt::system::Oid;
15use mz_repr::{ColumnType, ScalarType, strconv};
16use serde::{Deserialize, Serialize};
17
18use crate::EvalError;
19use crate::scalar::func::EagerUnaryFunc;
20
21sqlfunc!(
22    #[sqlname = "-"]
23    #[preserves_uniqueness = true]
24    #[inverse = to_unary!(NegInt64)]
25    #[is_monotone = true]
26    fn neg_int64(a: i64) -> Result<i64, EvalError> {
27        a.checked_neg()
28            .ok_or_else(|| EvalError::Int64OutOfRange(a.to_string().into()))
29    }
30);
31
32sqlfunc!(
33    #[sqlname = "~"]
34    #[preserves_uniqueness = true]
35    #[inverse = to_unary!(BitNotInt64)]
36    fn bit_not_int64(a: i64) -> i64 {
37        !a
38    }
39);
40
41sqlfunc!(
42    #[sqlname = "abs"]
43    fn abs_int64(a: i64) -> Result<i64, EvalError> {
44        a.checked_abs()
45            .ok_or_else(|| EvalError::Int64OutOfRange(a.to_string().into()))
46    }
47);
48
49sqlfunc!(
50    #[sqlname = "bigint_to_boolean"]
51    #[preserves_uniqueness = false]
52    #[inverse = to_unary!(super::CastBoolToInt64)]
53    fn cast_int64_to_bool(a: i64) -> bool {
54        a != 0
55    }
56);
57
58sqlfunc!(
59    #[sqlname = "bigint_to_smallint"]
60    #[preserves_uniqueness = true]
61    #[inverse = to_unary!(super::CastInt16ToInt64)]
62    #[is_monotone = true]
63    fn cast_int64_to_int16(a: i64) -> Result<i16, EvalError> {
64        i16::try_from(a).or_else(|_| Err(EvalError::Int16OutOfRange(a.to_string().into())))
65    }
66);
67
68sqlfunc!(
69    #[sqlname = "bigint_to_integer"]
70    #[preserves_uniqueness = true]
71    #[inverse = to_unary!(super::CastInt32ToInt64)]
72    #[is_monotone = true]
73    fn cast_int64_to_int32(a: i64) -> Result<i32, EvalError> {
74        i32::try_from(a).or_else(|_| Err(EvalError::Int32OutOfRange(a.to_string().into())))
75    }
76);
77
78sqlfunc!(
79    #[sqlname = "bigint_to_oid"]
80    #[preserves_uniqueness = true]
81    #[inverse = to_unary!(super::CastOidToInt64)]
82    fn cast_int64_to_oid(a: i64) -> Result<Oid, EvalError> {
83        // Unlike casting a 16-bit or 32-bit integers to OID, casting a 64-bit
84        // integers to an OID rejects negative values.
85        u32::try_from(a)
86            .map(Oid)
87            .or_else(|_| Err(EvalError::OidOutOfRange(a.to_string().into())))
88    }
89);
90
91sqlfunc!(
92    #[sqlname = "bigint_to_uint2"]
93    #[preserves_uniqueness = true]
94    #[inverse = to_unary!(super::CastUint16ToInt64)]
95    #[is_monotone = true]
96    fn cast_int64_to_uint16(a: i64) -> Result<u16, EvalError> {
97        u16::try_from(a).or_else(|_| Err(EvalError::UInt16OutOfRange(a.to_string().into())))
98    }
99);
100
101sqlfunc!(
102    #[sqlname = "bigint_to_uint4"]
103    #[preserves_uniqueness = true]
104    #[inverse = to_unary!(super::CastUint32ToInt64)]
105    #[is_monotone = true]
106    fn cast_int64_to_uint32(a: i64) -> Result<u32, EvalError> {
107        u32::try_from(a).or_else(|_| Err(EvalError::UInt32OutOfRange(a.to_string().into())))
108    }
109);
110
111sqlfunc!(
112    #[sqlname = "bigint_to_uint8"]
113    #[preserves_uniqueness = true]
114    #[inverse = to_unary!(super::CastUint64ToInt64)]
115    #[is_monotone = true]
116    fn cast_int64_to_uint64(a: i64) -> Result<u64, EvalError> {
117        u64::try_from(a).or_else(|_| Err(EvalError::UInt64OutOfRange(a.to_string().into())))
118    }
119);
120
121#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
122pub struct CastInt64ToNumeric(pub Option<NumericMaxScale>);
123
124impl<'a> EagerUnaryFunc<'a> for CastInt64ToNumeric {
125    type Input = i64;
126    type Output = Result<Numeric, EvalError>;
127
128    fn call(&self, a: i64) -> Result<Numeric, EvalError> {
129        let mut a = Numeric::from(a);
130        if let Some(scale) = self.0 {
131            if numeric::rescale(&mut a, scale.into_u8()).is_err() {
132                return Err(EvalError::NumericFieldOverflow);
133            }
134        }
135        // Besides `rescale`, cast is infallible.
136        Ok(a)
137    }
138
139    fn output_type(&self, input: ColumnType) -> ColumnType {
140        ScalarType::Numeric { max_scale: self.0 }.nullable(input.nullable)
141    }
142
143    fn could_error(&self) -> bool {
144        self.0.is_some()
145    }
146
147    fn inverse(&self) -> Option<crate::UnaryFunc> {
148        to_unary!(super::CastNumericToInt64)
149    }
150
151    fn is_monotone(&self) -> bool {
152        true
153    }
154}
155
156impl fmt::Display for CastInt64ToNumeric {
157    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158        f.write_str("bigint_to_numeric")
159    }
160}
161
162sqlfunc!(
163    #[sqlname = "bigint_to_real"]
164    #[preserves_uniqueness = false]
165    #[inverse = to_unary!(super::CastFloat32ToInt64)]
166    #[is_monotone = true]
167    fn cast_int64_to_float32(a: i64) -> f32 {
168        // TODO(benesch): remove potentially dangerous usage of `as`.
169        #[allow(clippy::as_conversions)]
170        {
171            a as f32
172        }
173    }
174);
175
176sqlfunc!(
177    #[sqlname = "bigint_to_double"]
178    #[preserves_uniqueness = false] // Witness: (1111111111111111111, 1111111111111111112).
179    #[inverse = to_unary!(super::CastFloat64ToInt64)]
180    #[is_monotone = true]
181    fn cast_int64_to_float64(a: i64) -> f64 {
182        // TODO(benesch): remove potentially dangerous usage of `as`.
183        #[allow(clippy::as_conversions)]
184        {
185            a as f64
186        }
187    }
188);
189
190sqlfunc!(
191    #[sqlname = "bigint_to_text"]
192    #[preserves_uniqueness = true]
193    #[inverse = to_unary!(super::CastStringToInt64)]
194    fn cast_int64_to_string(a: i64) -> String {
195        let mut buf = String::new();
196        strconv::format_int64(&mut buf, a);
197        buf
198    }
199);