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