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