Skip to main content

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