mz_expr/scalar/func/impls/
numeric.rs1use std::fmt;
11
12use dec::{OrderedDecimal, Rounding};
13use mz_expr_derive::sqlfunc;
14use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale};
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!(NegNumeric),
25 is_monotone = true
26)]
27fn neg_numeric(mut a: Numeric) -> Numeric {
28 numeric::cx_datum().neg(&mut a);
29 numeric::munge_numeric(&mut a).unwrap();
30 a
31}
32
33#[sqlfunc(sqlname = "abs")]
34fn abs_numeric(mut a: Numeric) -> Numeric {
35 numeric::cx_datum().abs(&mut a);
36 a
37}
38
39#[sqlfunc(sqlname = "ceilnumeric", is_monotone = true)]
40fn ceil_numeric(mut a: Numeric) -> Numeric {
41 if a.exponent() >= 0 {
43 return a;
44 }
45 let mut cx = numeric::cx_datum();
46 cx.set_rounding(Rounding::Ceiling);
47 cx.round(&mut a);
48 numeric::munge_numeric(&mut a).unwrap();
49 a
50}
51
52#[sqlfunc(sqlname = "expnumeric")]
53fn exp_numeric(mut a: Numeric) -> Result<Numeric, EvalError> {
54 let mut cx = numeric::cx_datum();
55 cx.exp(&mut a);
56 let cx_status = cx.status();
57 if cx_status.overflow() {
58 Err(EvalError::FloatOverflow)
59 } else if cx_status.subnormal() {
60 Err(EvalError::FloatUnderflow)
61 } else {
62 numeric::munge_numeric(&mut a).unwrap();
63 Ok(a)
64 }
65}
66
67#[sqlfunc(sqlname = "floornumeric", is_monotone = true)]
68fn floor_numeric(mut a: Numeric) -> Numeric {
69 if a.exponent() >= 0 {
71 return a;
72 }
73 let mut cx = numeric::cx_datum();
74 cx.set_rounding(Rounding::Floor);
75 cx.round(&mut a);
76 numeric::munge_numeric(&mut a).unwrap();
77 a
78}
79
80fn log_guard_numeric(val: &Numeric, function_name: &str) -> Result<(), EvalError> {
81 if val.is_negative() {
82 return Err(EvalError::NegativeOutOfDomain(function_name.into()));
83 }
84 if val.is_zero() {
85 return Err(EvalError::ZeroOutOfDomain(function_name.into()));
86 }
87 Ok(())
88}
89
90fn log_numeric<F>(mut a: Numeric, logic: F, name: &'static str) -> Result<Numeric, EvalError>
96where
97 F: Fn(&mut dec::Context<Numeric>, &mut Numeric),
98{
99 log_guard_numeric(&a, name)?;
100 let mut cx = numeric::cx_datum();
101 logic(&mut cx, &mut a);
102 numeric::munge_numeric(&mut a).unwrap();
103 Ok(a)
104}
105
106#[sqlfunc(sqlname = "lnnumeric")]
107fn ln_numeric(a: Numeric) -> Result<Numeric, EvalError> {
108 log_numeric(a, dec::Context::ln, "ln")
109}
110
111#[sqlfunc(sqlname = "log10numeric")]
112fn log10_numeric(a: Numeric) -> Result<Numeric, EvalError> {
113 log_numeric(a, dec::Context::log10, "log10")
114}
115
116#[sqlfunc(sqlname = "roundnumeric", is_monotone = true)]
117fn round_numeric(mut a: Numeric) -> Numeric {
118 if a.exponent() >= 0 {
120 return a;
121 }
122 numeric::cx_datum().round(&mut a);
123 numeric::munge_numeric(&mut a).unwrap();
127 a
128}
129
130#[sqlfunc(sqlname = "truncnumeric", is_monotone = true)]
131fn trunc_numeric(mut a: Numeric) -> Numeric {
132 if a.exponent() >= 0 {
134 return a;
135 }
136 let mut cx = numeric::cx_datum();
137 cx.set_rounding(Rounding::Down);
138 cx.round(&mut a);
139 numeric::munge_numeric(&mut a).unwrap();
140 a
141}
142
143#[sqlfunc(sqlname = "sqrtnumeric")]
144fn sqrt_numeric(mut a: Numeric) -> Result<Numeric, EvalError> {
145 if a.is_negative() {
146 return Err(EvalError::NegSqrt);
147 }
148 let mut cx = numeric::cx_datum();
149 cx.sqrt(&mut a);
150 numeric::munge_numeric(&mut a).unwrap();
151 Ok(a)
152}
153
154#[sqlfunc(
155 sqlname = "numeric_to_smallint",
156 preserves_uniqueness = false,
157 inverse = to_unary!(super::CastInt16ToNumeric(None)),
158 is_monotone = true
159)]
160pub fn cast_numeric_to_int16(mut a: Numeric) -> Result<i16, EvalError> {
161 let mut cx = numeric::cx_datum();
162 cx.round(&mut a);
163 cx.clear_status();
164 let i = cx
165 .try_into_i32(a)
166 .or_else(|_| Err(EvalError::Int16OutOfRange(a.to_string().into())))?;
167 i16::try_from(i).or_else(|_| Err(EvalError::Int16OutOfRange(i.to_string().into())))
168}
169
170#[sqlfunc(
171 sqlname = "numeric_to_integer",
172 preserves_uniqueness = false,
173 inverse = to_unary!(super::CastInt32ToNumeric(None)),
174 is_monotone = true
175)]
176pub fn cast_numeric_to_int32(mut a: Numeric) -> Result<i32, EvalError> {
177 let mut cx = numeric::cx_datum();
178 cx.round(&mut a);
179 cx.clear_status();
180 cx.try_into_i32(a)
181 .or_else(|_| Err(EvalError::Int32OutOfRange(a.to_string().into())))
182}
183
184#[sqlfunc(
185 sqlname = "numeric_to_bigint",
186 preserves_uniqueness = false,
187 inverse = to_unary!(super::CastInt64ToNumeric(None)),
188 is_monotone = true
189)]
190pub fn cast_numeric_to_int64(mut a: Numeric) -> Result<i64, EvalError> {
191 let mut cx = numeric::cx_datum();
192 cx.round(&mut a);
193 cx.clear_status();
194 cx.try_into_i64(a)
195 .or_else(|_| Err(EvalError::Int64OutOfRange(a.to_string().into())))
196}
197
198#[sqlfunc(
199 sqlname = "numeric_to_real",
200 preserves_uniqueness = false,
201 inverse = to_unary!(super::CastFloat32ToNumeric(None)),
202 is_monotone = true
203)]
204pub fn cast_numeric_to_float32(a: Numeric) -> Result<f32, EvalError> {
205 let i = a.to_string().parse::<f32>().unwrap();
206 if i.is_infinite() {
207 Err(EvalError::Float32OutOfRange(i.to_string().into()))
208 } else {
209 Ok(i)
210 }
211}
212
213#[sqlfunc(
214 sqlname = "numeric_to_double",
215 preserves_uniqueness = false,
216 inverse = to_unary!(super::CastFloat64ToNumeric(None)),
217 is_monotone = true
218)]
219pub fn cast_numeric_to_float64(a: Numeric) -> Result<f64, EvalError> {
220 let i = a.to_string().parse::<f64>().unwrap();
221 if i.is_infinite() {
222 Err(EvalError::Float64OutOfRange(i.to_string().into()))
223 } else {
224 Ok(i)
225 }
226}
227
228#[sqlfunc(
229 sqlname = "numeric_to_text",
230 preserves_uniqueness = false,
231 inverse = to_unary!(super::CastStringToNumeric(None))
232)]
233fn cast_numeric_to_string(a: Numeric) -> String {
234 let mut buf = String::new();
235 strconv::format_numeric(&mut buf, &OrderedDecimal(a));
236 buf
237}
238
239#[sqlfunc(
240 sqlname = "numeric_to_uint2",
241 preserves_uniqueness = false,
242 inverse = to_unary!(super::CastUint16ToNumeric(None)),
243 is_monotone = true
244)]
245fn cast_numeric_to_uint16(mut a: Numeric) -> Result<u16, EvalError> {
246 let mut cx = numeric::cx_datum();
247 cx.round(&mut a);
248 cx.clear_status();
249 let u = cx
250 .try_into_u32(a)
251 .or_else(|_| Err(EvalError::UInt16OutOfRange(a.to_string().into())))?;
252 u16::try_from(u).or_else(|_| Err(EvalError::UInt16OutOfRange(u.to_string().into())))
253}
254
255#[sqlfunc(
256 sqlname = "numeric_to_uint4",
257 preserves_uniqueness = false,
258 inverse = to_unary!(super::CastUint32ToNumeric(None)),
259 is_monotone = true
260)]
261fn cast_numeric_to_uint32(mut a: Numeric) -> Result<u32, EvalError> {
262 let mut cx = numeric::cx_datum();
263 cx.round(&mut a);
264 cx.clear_status();
265 cx.try_into_u32(a)
266 .or_else(|_| Err(EvalError::UInt32OutOfRange(a.to_string().into())))
267}
268
269#[sqlfunc(
270 sqlname = "numeric_to_uint8",
271 preserves_uniqueness = false,
272 inverse = to_unary!(super::CastUint64ToNumeric(None)),
273 is_monotone = true
274)]
275fn cast_numeric_to_uint64(mut a: Numeric) -> Result<u64, EvalError> {
276 let mut cx = numeric::cx_datum();
277 cx.round(&mut a);
278 cx.clear_status();
279 cx.try_into_u64(a)
280 .or_else(|_| Err(EvalError::UInt64OutOfRange(a.to_string().into())))
281}
282
283#[sqlfunc(sqlname = "pg_size_pretty", preserves_uniqueness = false)]
284fn pg_size_pretty(mut a: Numeric) -> Result<String, EvalError> {
285 let mut cx = numeric::cx_datum();
286 let units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
287
288 for (pos, unit) in units.iter().rev().skip(1).rev().enumerate() {
289 if Numeric::from(-10239.5) < a && a < Numeric::from(10239.5) {
291 if pos > 0 {
293 cx.round(&mut a);
294 }
295
296 return Ok(format!("{} {unit}", a.to_standard_notation_string()));
297 }
298
299 cx.div(&mut a, &Numeric::from(1024));
300 numeric::munge_numeric(&mut a).unwrap();
301 }
302
303 cx.round(&mut a);
304 Ok(format!(
305 "{} {}",
306 a.to_standard_notation_string(),
307 units.last().unwrap()
308 ))
309}
310
311#[derive(
312 Ord,
313 PartialOrd,
314 Clone,
315 Debug,
316 Eq,
317 PartialEq,
318 Serialize,
319 Deserialize,
320 Hash
321)]
322pub struct AdjustNumericScale(pub NumericMaxScale);
323
324impl EagerUnaryFunc for AdjustNumericScale {
325 type Input<'a> = Numeric;
326 type Output<'a> = Result<Numeric, EvalError>;
327
328 fn call<'a>(&self, mut d: Self::Input<'a>) -> Self::Output<'a> {
329 if numeric::rescale(&mut d, self.0.into_u8()).is_err() {
330 return Err(EvalError::NumericFieldOverflow);
331 };
332 Ok(d)
333 }
334
335 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
336 SqlScalarType::Numeric {
337 max_scale: Some(self.0),
338 }
339 .nullable(input.nullable)
340 }
341}
342
343impl fmt::Display for AdjustNumericScale {
344 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
345 f.write_str("adjust_numeric_scale")
346 }
347}