Use BigDecimal for large number handling

This commit is contained in:
Tobie Morgan Hitchcock 2022-03-17 21:01:29 +00:00
parent 5d554d07d3
commit 73df91a438
5 changed files with 72 additions and 85 deletions

49
Cargo.lock generated
View file

@ -64,12 +64,6 @@ dependencies = [
"password-hash",
]
[[package]]
name = "arrayvec"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "as-slice"
version = "0.1.5"
@ -147,6 +141,18 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b"
[[package]]
name = "bigdecimal"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
"serde",
]
[[package]]
name = "bindgen"
version = "0.57.0"
@ -1218,9 +1224,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ba42135c6a5917b9db9cd7b293e5409e1c6b041e6f9825e92e55a894c63b6f8"
checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
dependencies = [
"libc",
"log",
@ -1319,6 +1325,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.44"
@ -1927,16 +1944,6 @@ dependencies = [
"smallvec",
]
[[package]]
name = "rust_decimal"
version = "1.22.0"
dependencies = [
"arrayvec",
"num-traits",
"serde",
"serde_json",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
@ -2225,6 +2232,7 @@ version = "0.1.0"
dependencies = [
"argon2",
"async-recursion",
"bigdecimal",
"byteorder",
"chrono",
"dmp",
@ -2242,7 +2250,6 @@ dependencies = [
"rand 0.8.5",
"regex",
"rmp-serde",
"rust_decimal",
"scrypt",
"serde",
"sha-1 0.10.0",
@ -2271,9 +2278,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.88"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebd69e719f31e88618baa1eaa6ee2de5c9a1c004f1e9ecdb58e8352a13f20a01"
checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
dependencies = [
"proc-macro2",
"quote",

View file

@ -16,9 +16,9 @@ kv-yokudb = []
[dependencies]
argon2 = "0.3.4"
async-recursion = "1.0.0"
bigdecimal = { version = "0.3.0", features = ["serde", "string-only"] }
byteorder = "1.4.3"
chrono = { version = "0.4.19", features = ["serde"] }
dec = { version = "1.21.0", package = "rust_decimal", features = ["maths", "serde-float"] }
derive = { version = "0.1.2", package = "surrealdb-derive" }
dmp = "0.1.1"
echodb = { version = "0.2.1", optional = true }

View file

@ -1,20 +1,17 @@
use crate::sql::comment::comment;
use crate::sql::error::Error::ParserError;
use crate::sql::error::IResult;
use crate::sql::operator::{assigner, operator};
use dec::prelude::FromPrimitive;
use dec::prelude::ToPrimitive;
use dec::Decimal;
use dec::MathematicalOps;
use bigdecimal::BigDecimal;
use bigdecimal::FromPrimitive;
use bigdecimal::ToPrimitive;
use nom::branch::alt;
use nom::character::complete::char;
use nom::character::complete::digit1;
use nom::character::complete::i64;
use nom::character::complete::multispace1;
use nom::combinator::eof;
use nom::combinator::map;
use nom::combinator::peek;
use nom::number::complete::recognize_float;
use nom::Err::Error;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt;
@ -27,7 +24,7 @@ use std::str::FromStr;
pub enum Number {
Int(i64),
Float(f64),
Decimal(Decimal),
Decimal(BigDecimal),
}
impl Default for Number {
@ -110,24 +107,18 @@ impl From<f64> for Number {
impl<'a> From<&'a str> for Number {
fn from(s: &str) -> Self {
match s.contains(&['e', 'E'][..]) {
true => Number::Decimal(Decimal::from_scientific(s).unwrap_or_default()),
false => Number::Decimal(Decimal::from_str(s).unwrap_or_default()),
}
Number::Decimal(BigDecimal::from_str(s).unwrap_or_default())
}
}
impl From<String> for Number {
fn from(s: String) -> Self {
match s.contains(&['e', 'E'][..]) {
true => Number::Decimal(Decimal::from_scientific(&s).unwrap_or_default()),
false => Number::Decimal(Decimal::from_str(&s).unwrap_or_default()),
}
Number::Decimal(BigDecimal::from_str(&s).unwrap_or_default())
}
}
impl From<Decimal> for Number {
fn from(v: Decimal) -> Self {
impl From<BigDecimal> for Number {
fn from(v: BigDecimal) -> Self {
Number::Decimal(v)
}
}
@ -172,7 +163,7 @@ impl Number {
match self {
Number::Int(v) => v != &0,
Number::Float(v) => v != &0.0,
Number::Decimal(v) => v != &Decimal::default(),
Number::Decimal(v) => v != &BigDecimal::default(),
}
}
@ -184,7 +175,7 @@ impl Number {
match self {
Number::Int(v) => v,
Number::Float(v) => v as i64,
Number::Decimal(v) => v.to_i64().unwrap_or(0),
Number::Decimal(v) => v.to_i64().unwrap_or_default(),
}
}
@ -192,27 +183,27 @@ impl Number {
match self {
Number::Int(v) => v as f64,
Number::Float(v) => v,
Number::Decimal(v) => v.to_f64().unwrap_or(0.0),
Number::Decimal(v) => v.to_f64().unwrap_or_default(),
}
}
pub fn as_decimal(self) -> Decimal {
pub fn as_decimal(self) -> BigDecimal {
match self {
Number::Int(v) => Decimal::from(v),
Number::Float(v) => Decimal::from_f64(v).unwrap_or_default(),
Number::Int(v) => BigDecimal::from_i64(v).unwrap_or_default(),
Number::Float(v) => BigDecimal::from_f64(v).unwrap_or_default(),
Number::Decimal(v) => v,
}
}
// -----------------------------------
//
// Complex conversion of number
// -----------------------------------
pub fn to_usize(&self) -> usize {
match self {
Number::Int(v) => *v as usize,
Number::Float(v) => *v as usize,
Number::Decimal(v) => v.to_usize().unwrap_or(0),
Number::Decimal(v) => v.to_usize().unwrap_or_default(),
}
}
@ -220,7 +211,7 @@ impl Number {
match self {
Number::Int(v) => *v,
Number::Float(v) => *v as i64,
Number::Decimal(v) => v.to_i64().unwrap_or(0),
Number::Decimal(v) => v.to_i64().unwrap_or_default(),
}
}
@ -228,15 +219,15 @@ impl Number {
match self {
Number::Int(v) => *v as f64,
Number::Float(v) => *v,
Number::Decimal(v) => v.to_f64().unwrap_or(0.0),
Number::Decimal(v) => v.to_f64().unwrap_or_default(),
}
}
pub fn to_decimal(&self) -> Decimal {
pub fn to_decimal(&self) -> BigDecimal {
match self {
Number::Int(v) => Decimal::from(*v),
Number::Float(v) => Decimal::from_f64(*v).unwrap_or_default(),
Number::Decimal(v) => *v,
Number::Int(v) => BigDecimal::from_i64(*v).unwrap_or_default(),
Number::Float(v) => BigDecimal::from_f64(*v).unwrap_or_default(),
Number::Decimal(v) => v.clone(),
}
}
@ -256,7 +247,7 @@ impl Number {
match self {
Number::Int(v) => v.into(),
Number::Float(v) => v.ceil().into(),
Number::Decimal(v) => v.ceil().into(),
Number::Decimal(v) => (v + BigDecimal::from_f32(0.5).unwrap()).round(0).into(),
}
}
@ -264,7 +255,7 @@ impl Number {
match self {
Number::Int(v) => v.into(),
Number::Float(v) => v.floor().into(),
Number::Decimal(v) => v.floor().into(),
Number::Decimal(v) => (v - BigDecimal::from_f32(0.5).unwrap()).round(0).into(),
}
}
@ -272,7 +263,7 @@ impl Number {
match self {
Number::Int(v) => v.into(),
Number::Float(v) => v.round().into(),
Number::Decimal(v) => v.round().into(),
Number::Decimal(v) => v.round(0).into(),
}
}
@ -292,7 +283,7 @@ impl Number {
match self {
Number::Int(v) => format!("{:.1$}", v, precision).into(),
Number::Float(v) => format!("{:.1$}", v, precision).into(),
Number::Decimal(v) => v.round_dp(precision as u32).into(),
Number::Decimal(v) => v.round(precision as i64).into(),
}
}
}
@ -315,14 +306,14 @@ impl PartialEq for Number {
(Number::Int(v), Number::Float(w)) => (*v as f64).eq(w),
(Number::Float(v), Number::Int(w)) => v.eq(&(*w as f64)),
// ------------------------------
(Number::Int(v), Number::Decimal(w)) => Decimal::from(*v).eq(w),
(Number::Decimal(v), Number::Int(w)) => v.eq(&Decimal::from(*w)),
(Number::Int(v), Number::Decimal(w)) => BigDecimal::from(*v).eq(w),
(Number::Decimal(v), Number::Int(w)) => v.eq(&BigDecimal::from(*w)),
// ------------------------------
(Number::Float(v), Number::Decimal(w)) => {
Decimal::from_f64(*v).unwrap_or_default().eq(w)
BigDecimal::from_f64(*v).unwrap_or_default().eq(w)
}
(Number::Decimal(v), Number::Float(w)) => {
v.eq(&Decimal::from_f64(*w).unwrap_or_default())
v.eq(&BigDecimal::from_f64(*w).unwrap_or_default())
}
}
}
@ -338,14 +329,14 @@ impl PartialOrd for Number {
(Number::Int(v), Number::Float(w)) => (*v as f64).partial_cmp(w),
(Number::Float(v), Number::Int(w)) => v.partial_cmp(&(*w as f64)),
// ------------------------------
(Number::Int(v), Number::Decimal(w)) => Decimal::from(*v).partial_cmp(w),
(Number::Decimal(v), Number::Int(w)) => v.partial_cmp(&Decimal::from(*w)),
(Number::Int(v), Number::Decimal(w)) => BigDecimal::from(*v).partial_cmp(w),
(Number::Decimal(v), Number::Int(w)) => v.partial_cmp(&BigDecimal::from(*w)),
// ------------------------------
(Number::Float(v), Number::Decimal(w)) => {
Decimal::from_f64(*v).unwrap_or_default().partial_cmp(w)
BigDecimal::from_f64(*v).unwrap_or_default().partial_cmp(w)
}
(Number::Decimal(v), Number::Float(w)) => {
v.partial_cmp(&Decimal::from_f64(*w).unwrap_or_default())
v.partial_cmp(&BigDecimal::from_f64(*w).unwrap_or_default())
}
}
}
@ -439,7 +430,6 @@ impl ops::Div for Number {
type Output = Self;
fn div(self, other: Self) -> Self {
match (self, other) {
(Number::Int(v), Number::Int(w)) => Number::Int(v / w),
(Number::Float(v), Number::Float(w)) => Number::Float(v / w),
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v / w),
(Number::Int(v), Number::Float(w)) => Number::Float(v as f64 / w),
@ -453,7 +443,6 @@ impl<'a, 'b> ops::Div<&'b Number> for &'a Number {
type Output = Number;
fn div(self, other: &'b Number) -> Number {
match (self, other) {
(Number::Int(v), Number::Int(w)) => Number::Int(v / w),
(Number::Float(v), Number::Float(w)) => Number::Float(v / w),
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v / w),
(Number::Int(v), Number::Float(w)) => Number::Float(*v as f64 / w),
@ -506,7 +495,7 @@ pub fn number(i: &str) -> IResult<&str, Number> {
}
fn integer(i: &str) -> IResult<&str, Number> {
let (i, v) = digit1(i)?;
let (i, v) = i64(i)?;
let (i, _) = peek(alt((
map(multispace1, |_| ()),
map(operator, |_| ()),
@ -519,10 +508,7 @@ fn integer(i: &str) -> IResult<&str, Number> {
map(char(','), |_| ()),
map(eof, |_| ()),
)))(i)?;
match v.parse::<i64>() {
Ok(v) => Ok((i, Number::from(v))),
_ => Err(Error(ParserError(i))),
}
Ok((i, Number::from(v)))
}
fn decimal(i: &str) -> IResult<&str, Number> {

View file

@ -49,8 +49,8 @@ impl Serialize for Thing {
pub fn thing(i: &str) -> IResult<&str, Thing> {
let (i, t) = ident_raw(i)?;
let (i, v) = ident_raw(i)?;
let (i, _) = char(':')(i)?;
let (i, v) = ident_raw(i)?;
Ok((
i,
Thing {

View file

@ -23,8 +23,8 @@ use crate::sql::subquery::{subquery, Subquery};
use crate::sql::table::{table, Table};
use crate::sql::thing::{thing, Thing};
use async_recursion::async_recursion;
use bigdecimal::BigDecimal;
use chrono::{DateTime, Utc};
use dec::Decimal;
use derive::Store;
use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher;
@ -279,12 +279,6 @@ impl From<f64> for Value {
}
}
impl From<Decimal> for Value {
fn from(v: Decimal) -> Self {
Value::Number(Number::from(v))
}
}
impl From<String> for Value {
fn from(v: String) -> Self {
Value::Strand(Strand::from(v))
@ -496,14 +490,14 @@ impl Value {
}
}
pub fn as_decimal(self) -> Decimal {
pub fn as_decimal(self) -> BigDecimal {
match self {
Value::True => Decimal::from(1),
Value::True => BigDecimal::from(1),
Value::Number(v) => v.as_decimal(),
Value::Strand(v) => Decimal::from_str(v.as_str()).unwrap_or_default(),
Value::Strand(v) => BigDecimal::from_str(v.as_str()).unwrap_or_default(),
Value::Duration(v) => v.value.as_secs().into(),
Value::Datetime(v) => v.value.timestamp().into(),
_ => Decimal::default(),
_ => BigDecimal::default(),
}
}