Feature - Use rust_decimal (128b) instead of bigdecimal ("arbitrary" precision) (#1991)
This commit is contained in:
parent
c490005007
commit
c45fd12509
19 changed files with 408 additions and 215 deletions
191
Cargo.lock
generated
191
Cargo.lock
generated
|
@ -625,18 +625,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bigdecimal"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
|
@ -790,6 +778,51 @@ dependencies = [
|
|||
"cmake",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
||||
dependencies = [
|
||||
"borsh-derive",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7"
|
||||
dependencies = [
|
||||
"borsh-derive-internal",
|
||||
"borsh-schema-derive-internal",
|
||||
"proc-macro-crate 0.1.5",
|
||||
"proc-macro2",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-derive-internal"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-schema-derive-internal"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "3.3.4"
|
||||
|
@ -828,6 +861,28 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627"
|
||||
dependencies = [
|
||||
"bytecheck_derive",
|
||||
"ptr_meta",
|
||||
"simdutf8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck_derive"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.13.1"
|
||||
|
@ -1953,6 +2008,9 @@ name = "hashbrown"
|
|||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash 0.7.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
|
@ -3185,6 +3243,15 @@ dependencies = [
|
|||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
|
||||
dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.1"
|
||||
|
@ -3391,6 +3458,26 @@ version = "2.0.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
|
||||
dependencies = [
|
||||
"ptr_meta_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta_derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.26.0"
|
||||
|
@ -3607,6 +3694,15 @@ version = "1.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bf2521270932c3c7bed1a59151222bd7643c79310f2916f01925e1e16255698"
|
||||
|
||||
[[package]]
|
||||
name = "rend"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab"
|
||||
dependencies = [
|
||||
"bytecheck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.18"
|
||||
|
@ -3696,6 +3792,34 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bytecheck",
|
||||
"hashbrown",
|
||||
"ptr_meta",
|
||||
"rend",
|
||||
"rkyv_derive",
|
||||
"seahash",
|
||||
"tinyvec",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv_derive"
|
||||
version = "0.7.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmp"
|
||||
version = "0.8.11"
|
||||
|
@ -3777,7 +3901,7 @@ dependencies = [
|
|||
"fnv",
|
||||
"ident_case",
|
||||
"indexmap",
|
||||
"proc-macro-crate",
|
||||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3806,6 +3930,24 @@ dependencies = [
|
|||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust_decimal"
|
||||
version = "1.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26bd36b60561ee1fb5ec2817f198b6fd09fa571c897a5e86d1487cfc2b096dfc"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"borsh",
|
||||
"bytecheck",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"num-traits",
|
||||
"rand 0.8.5",
|
||||
"rkyv",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
|
@ -3992,6 +4134,12 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "seahash"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.1"
|
||||
|
@ -4155,6 +4303,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdutf8"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.6.2"
|
||||
|
@ -4331,7 +4485,6 @@ dependencies = [
|
|||
"async-trait",
|
||||
"base64 0.21.2",
|
||||
"bcrypt",
|
||||
"bigdecimal",
|
||||
"bincode",
|
||||
"bung",
|
||||
"chrono",
|
||||
|
@ -4367,6 +4520,7 @@ dependencies = [
|
|||
"roaring",
|
||||
"rocksdb",
|
||||
"rquickjs",
|
||||
"rust_decimal",
|
||||
"rustls 0.20.8",
|
||||
"scrypt",
|
||||
"semver",
|
||||
|
@ -4822,6 +4976,15 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.2"
|
||||
|
|
|
@ -58,7 +58,6 @@ async-trait = "0.1.68"
|
|||
async-recursion = "1.0.4"
|
||||
base64_lib = { version = "0.21.1", package = "base64" }
|
||||
bcrypt = "0.14.0"
|
||||
bigdecimal = { version = "0.3.1", features = ["serde", "string-only"] }
|
||||
bung = "0.1.0"
|
||||
bincode = "1.3.3"
|
||||
channel = { version = "1.8.0", package = "async-channel" }
|
||||
|
@ -93,6 +92,7 @@ regex = "1.8.2"
|
|||
reqwest = { version = "0.11.18", default-features = false, features = ["json", "stream"], optional = true }
|
||||
roaring = { version = "0.10.1", features = ["serde"] }
|
||||
rocksdb = { version = "0.21.0", optional = true }
|
||||
rust_decimal = { version = "1.29.1", features = [ "maths" ] }
|
||||
rustls = { version = "0.20.8", optional = true }
|
||||
snap = "1.1.0"
|
||||
scrypt = "0.11.0"
|
||||
|
|
|
@ -262,7 +262,7 @@ mod tests {
|
|||
test(
|
||||
vec![Value::from(3.14), Value::from(2.72), Value::from(1.61)].into(),
|
||||
" is not ",
|
||||
"3.14 is not 2.72 is not 1.61",
|
||||
"3.14f is not 2.72f is not 1.61f",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -326,6 +326,6 @@ mod tests {
|
|||
let res = div(one, two);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap();
|
||||
assert_eq!("1.25", format!("{}", out));
|
||||
assert_eq!("1.25f", format!("{}", out));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::classes;
|
||||
use crate::sql::number::decimal_is_integer;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::value::Value;
|
||||
use bigdecimal::ToPrimitive;
|
||||
use js::Array;
|
||||
use js::Class;
|
||||
use js::Ctx;
|
||||
|
@ -26,9 +26,9 @@ impl<'js> IntoJs<'js> for &Value {
|
|||
Value::Strand(v) => js::String::from_str(ctx, v)?.into_js(ctx),
|
||||
Value::Number(Number::Int(v)) => Ok(js::Value::new_int(ctx, *v as i32)),
|
||||
Value::Number(Number::Float(v)) => Ok(js::Value::new_float(ctx, *v)),
|
||||
Value::Number(Number::Decimal(v)) => match v.is_integer() {
|
||||
true => Ok(js::Value::new_int(ctx, v.to_i32().unwrap_or_default())),
|
||||
false => Ok(js::Value::new_float(ctx, v.to_f64().unwrap_or_default())),
|
||||
&Value::Number(Number::Decimal(v)) => match decimal_is_integer(&v) {
|
||||
true => Ok(js::Value::new_int(ctx, v.try_into().unwrap_or_default())),
|
||||
false => Ok(js::Value::new_float(ctx, v.try_into().unwrap_or_default())),
|
||||
},
|
||||
Value::Datetime(v) => {
|
||||
let date: js::Function = ctx.globals().get("Date")?;
|
||||
|
|
|
@ -73,7 +73,7 @@ mod tests {
|
|||
let res = cast(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("<int> 1.2345", format!("{}", out));
|
||||
assert_eq!("<int> 1.2345f", format!("{}", out));
|
||||
assert_eq!(out, Cast(Kind::Int, 1.2345.into()));
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ mod tests {
|
|||
let res = cast(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("<string> 1.2345", format!("{}", out));
|
||||
assert_eq!("<string> 1.2345f", format!("{}", out));
|
||||
assert_eq!(out, Cast(Kind::String, 1.2345.into()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -552,12 +552,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn function_single_not() {
|
||||
let sql = "not(1.2345)";
|
||||
let sql = "not(10)";
|
||||
let res = function(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("not(1.2345)", format!("{}", out));
|
||||
assert_eq!(out, Function::Normal("not".to_owned(), vec![1.2345.into()]));
|
||||
assert_eq!("not(10)", format!("{}", out));
|
||||
assert_eq!(out, Function::Normal("not".to_owned(), vec![10.into()]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -65,11 +65,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn future_expression() {
|
||||
let sql = "<future> { 1.2345 + 5.4321 }";
|
||||
let sql = "<future> { 5 + 10 }";
|
||||
let res = future(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("<future> { 1.2345 + 5.4321 }", format!("{}", out));
|
||||
assert_eq!(out, Future(Block::from(Value::from(Expression::parse("1.2345 + 5.4321")))));
|
||||
assert_eq!("<future> { 5 + 10 }", format!("{}", out));
|
||||
assert_eq!(out, Future(Block::from(Value::from(Expression::parse("5 + 10")))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,13 @@ use crate::sql::ending::number as ending;
|
|||
use crate::sql::error::Error::Parser;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::strand::Strand;
|
||||
use bigdecimal::num_traits::Pow;
|
||||
use bigdecimal::BigDecimal;
|
||||
use bigdecimal::FromPrimitive;
|
||||
use bigdecimal::ToPrimitive;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::i64;
|
||||
use nom::combinator::{map, opt};
|
||||
use nom::number::complete::recognize_float;
|
||||
use nom::Err::Failure;
|
||||
use rust_decimal::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
@ -27,7 +26,7 @@ pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Number";
|
|||
pub enum Number {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Decimal(BigDecimal),
|
||||
Decimal(Decimal),
|
||||
// Add new variants here
|
||||
}
|
||||
|
||||
|
@ -63,8 +62,8 @@ impl From<f64> for Number {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<BigDecimal> for Number {
|
||||
fn from(v: BigDecimal) -> Self {
|
||||
impl From<Decimal> for Number {
|
||||
fn from(v: Decimal) -> Self {
|
||||
Self::Decimal(v)
|
||||
}
|
||||
}
|
||||
|
@ -97,10 +96,10 @@ impl TryFrom<&str> for Number {
|
|||
match v.parse::<i64>() {
|
||||
// Store it as an i64
|
||||
Ok(v) => Ok(Self::Int(v)),
|
||||
// It wasn't parsed as a i64 so parse as a decimal
|
||||
_ => match BigDecimal::from_str(v) {
|
||||
// Store it as a BigDecimal
|
||||
Ok(v) => Ok(Self::Decimal(v)),
|
||||
// It wasn't parsed as a i64 so parse as a float
|
||||
_ => match f64::from_str(v) {
|
||||
// Store it as a float
|
||||
Ok(v) => Ok(Self::Float(v)),
|
||||
// It wasn't parsed as a number
|
||||
_ => Err(()),
|
||||
},
|
||||
|
@ -141,17 +140,17 @@ try_into_prim!(
|
|||
f32 => to_f32, f64 => to_f64
|
||||
);
|
||||
|
||||
impl TryFrom<Number> for BigDecimal {
|
||||
impl TryFrom<Number> for Decimal {
|
||||
type Error = Error;
|
||||
fn try_from(value: Number) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Number::Int(v) => match BigDecimal::from_i64(v) {
|
||||
Number::Int(v) => match Decimal::from_i64(v) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
None => Err(Error::TryFrom(value.to_string(), "Decimal")),
|
||||
},
|
||||
Number::Float(v) => match BigDecimal::from_f64(v) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
Number::Float(v) => match Decimal::try_from(v) {
|
||||
Ok(v) => Ok(v),
|
||||
_ => Err(Error::TryFrom(value.to_string(), "Decimal")),
|
||||
},
|
||||
Number::Decimal(x) => Ok(x),
|
||||
}
|
||||
|
@ -162,8 +161,8 @@ impl Display for Number {
|
|||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Number::Int(v) => Display::fmt(v, f),
|
||||
Number::Float(v) => Display::fmt(v, f),
|
||||
Number::Decimal(v) => Display::fmt(v, f),
|
||||
Number::Float(v) => write!(f, "{v}f"),
|
||||
Number::Decimal(v) => write!(f, "{v}dec"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -198,8 +197,8 @@ impl Number {
|
|||
pub fn is_integer(&self) -> bool {
|
||||
match self {
|
||||
Number::Int(_) => true,
|
||||
Number::Float(_) => false,
|
||||
Number::Decimal(v) => v.is_integer(),
|
||||
Number::Float(v) => v.fract() == 0.0,
|
||||
Number::Decimal(v) => decimal_is_integer(v),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +206,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v != &0,
|
||||
Number::Float(v) => v != &0.0,
|
||||
Number::Decimal(v) => v != &BigDecimal::default(),
|
||||
Number::Decimal(v) => v != &Decimal::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +214,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v > &0,
|
||||
Number::Float(v) => v > &0.0,
|
||||
Number::Decimal(v) => v > &BigDecimal::default(),
|
||||
Number::Decimal(v) => v > &Decimal::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +222,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v < &0,
|
||||
Number::Float(v) => v < &0.0,
|
||||
Number::Decimal(v) => v < &BigDecimal::default(),
|
||||
Number::Decimal(v) => v < &Decimal::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,7 +230,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v >= &0,
|
||||
Number::Float(v) => v >= &0.0,
|
||||
Number::Decimal(v) => v >= &BigDecimal::default(),
|
||||
Number::Decimal(v) => v >= &Decimal::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,7 +238,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v <= &0,
|
||||
Number::Float(v) => v <= &0.0,
|
||||
Number::Decimal(v) => v <= &BigDecimal::default(),
|
||||
Number::Decimal(v) => v <= &Decimal::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,7 +250,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v as usize,
|
||||
Number::Float(v) => v as usize,
|
||||
Number::Decimal(v) => v.to_usize().unwrap_or_default(),
|
||||
Number::Decimal(v) => v.try_into().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,7 +258,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v,
|
||||
Number::Float(v) => v as i64,
|
||||
Number::Decimal(v) => v.to_i64().unwrap_or_default(),
|
||||
Number::Decimal(v) => v.try_into().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,14 +266,14 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v as f64,
|
||||
Number::Float(v) => v,
|
||||
Number::Decimal(v) => v.to_f64().unwrap_or_default(),
|
||||
Number::Decimal(v) => v.try_into().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_decimal(self) -> BigDecimal {
|
||||
pub fn as_decimal(self) -> Decimal {
|
||||
match self {
|
||||
Number::Int(v) => BigDecimal::from_i64(v).unwrap_or_default(),
|
||||
Number::Float(v) => BigDecimal::from_f64(v).unwrap_or_default(),
|
||||
Number::Int(v) => Decimal::from(v),
|
||||
Number::Float(v) => Decimal::try_from(v).unwrap_or_default(),
|
||||
Number::Decimal(v) => v,
|
||||
}
|
||||
}
|
||||
|
@ -303,14 +302,14 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => *v as f64,
|
||||
Number::Float(v) => *v,
|
||||
Number::Decimal(v) => v.to_f64().unwrap_or_default(),
|
||||
&Number::Decimal(v) => v.try_into().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_decimal(&self) -> BigDecimal {
|
||||
pub fn to_decimal(&self) -> Decimal {
|
||||
match self {
|
||||
Number::Int(v) => BigDecimal::from_i64(*v).unwrap_or_default(),
|
||||
Number::Float(v) => BigDecimal::from_f64(*v).unwrap_or_default(),
|
||||
Number::Int(v) => Decimal::try_from(*v).unwrap_or_default(),
|
||||
Number::Float(v) => Decimal::try_from(*v).unwrap_or_default(),
|
||||
Number::Decimal(v) => v.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -331,14 +330,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v.into(),
|
||||
Number::Float(v) => v.ceil().into(),
|
||||
Number::Decimal(v) => {
|
||||
if v.digits() > 16 {
|
||||
let v = (v.to_f64().unwrap_or_default() + 0.5).round();
|
||||
BigDecimal::from_f64(v).unwrap_or_default().into()
|
||||
} else {
|
||||
(v + BigDecimal::from_f32(0.5).unwrap()).round(0).into()
|
||||
}
|
||||
}
|
||||
Number::Decimal(v) => v.ceil().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,14 +338,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v.into(),
|
||||
Number::Float(v) => v.floor().into(),
|
||||
Number::Decimal(v) => {
|
||||
if v.digits() > 16 {
|
||||
let v = (v.to_f64().unwrap_or_default() - 0.5).round();
|
||||
BigDecimal::from_f64(v).unwrap_or_default().into()
|
||||
} else {
|
||||
(v - BigDecimal::from_f32(0.5).unwrap()).round(0).into()
|
||||
}
|
||||
}
|
||||
Number::Decimal(v) => v.floor().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,14 +346,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => v.into(),
|
||||
Number::Float(v) => v.round().into(),
|
||||
Number::Decimal(v) => {
|
||||
if v.digits() > 16 {
|
||||
let v = v.to_f64().unwrap_or_default().round();
|
||||
BigDecimal::from_f64(v).unwrap_or_default().into()
|
||||
} else {
|
||||
v.round(0).into()
|
||||
}
|
||||
}
|
||||
Number::Decimal(v) => v.round().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,7 +354,7 @@ impl Number {
|
|||
match self {
|
||||
Number::Int(v) => format!("{v:.precision$}").try_into().unwrap_or_default(),
|
||||
Number::Float(v) => format!("{v:.precision$}").try_into().unwrap_or_default(),
|
||||
Number::Decimal(v) => format!("{v:.precision$}").try_into().unwrap_or_default(),
|
||||
Number::Decimal(v) => v.round_dp(precision as u32).into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,16 +368,11 @@ impl Number {
|
|||
|
||||
pub fn pow(self, power: Number) -> Number {
|
||||
match (self, power) {
|
||||
(Number::Int(v), Number::Int(p)) if p >= 0 && p < u32::MAX as i64 => {
|
||||
Number::Int(v.pow(p as u32))
|
||||
}
|
||||
(Number::Decimal(v), Number::Int(p)) if p >= 0 && p < u32::MAX as i64 => {
|
||||
let (as_int, scale) = v.as_bigint_and_exponent();
|
||||
Number::Decimal(BigDecimal::new(as_int.pow(p as u32), scale * p))
|
||||
}
|
||||
(Number::Int(v), Number::Int(p)) => Number::Int(v.pow(p as u32)),
|
||||
(Number::Decimal(v), Number::Int(p)) => v.powi(p).into(),
|
||||
// TODO: (Number::Decimal(v), Number::Float(p)) => todo!(),
|
||||
// TODO: (Number::Decimal(v), Number::Decimal(p)) => todo!(),
|
||||
(v, p) => Number::Float(v.as_float().pow(p.as_float())),
|
||||
(v, p) => v.as_float().powf(p.as_float()).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -434,14 +407,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)) => BigDecimal::from(*v).eq(w),
|
||||
(Number::Decimal(v), Number::Int(w)) => v.eq(&BigDecimal::from(*w)),
|
||||
(Number::Int(v), Number::Decimal(w)) => Decimal::from(*v).eq(w),
|
||||
(Number::Decimal(v), Number::Int(w)) => v.eq(&Decimal::from(*w)),
|
||||
// ------------------------------
|
||||
(Number::Float(v), Number::Decimal(w)) => {
|
||||
BigDecimal::from_f64(*v).unwrap_or_default().eq(w)
|
||||
Decimal::from_f64(*v).unwrap_or_default().eq(w)
|
||||
}
|
||||
(Number::Decimal(v), Number::Float(w)) => {
|
||||
v.eq(&BigDecimal::from_f64(*w).unwrap_or_default())
|
||||
v.eq(&Decimal::from_f64(*w).unwrap_or_default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -457,14 +430,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)) => BigDecimal::from(*v).partial_cmp(w),
|
||||
(Number::Decimal(v), Number::Int(w)) => v.partial_cmp(&BigDecimal::from(*w)),
|
||||
(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::Float(v), Number::Decimal(w)) => {
|
||||
BigDecimal::from_f64(*v).unwrap_or_default().partial_cmp(w)
|
||||
Decimal::from_f64(*v).unwrap_or_default().partial_cmp(w)
|
||||
}
|
||||
(Number::Decimal(v), Number::Float(w)) => {
|
||||
v.partial_cmp(&BigDecimal::from_f64(*w).unwrap_or_default())
|
||||
v.partial_cmp(&Decimal::from_f64(*w).unwrap_or_default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -636,7 +609,28 @@ impl Sort for Vec<Number> {
|
|||
}
|
||||
|
||||
pub fn number(i: &str) -> IResult<&str, Number> {
|
||||
alt((int, decimal))(i)
|
||||
let (i, v) = recognize_float(i)?;
|
||||
let (i, suffix) = suffix(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
let number = match suffix {
|
||||
Suffix::None => Number::try_from(v).map_err(|_| Failure(Parser(i)))?,
|
||||
Suffix::Float => Number::from(f64::from_str(v).map_err(|_| Failure(Parser(i)))?),
|
||||
Suffix::Decimal => Number::from(Decimal::from_str(v).map_err(|_| Failure(Parser(i)))?),
|
||||
};
|
||||
Ok((i, number))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Suffix {
|
||||
None,
|
||||
Float,
|
||||
Decimal,
|
||||
}
|
||||
|
||||
fn suffix(i: &str) -> IResult<&str, Suffix> {
|
||||
let (i, opt_suffix) =
|
||||
opt(alt((map(tag("f"), |_| Suffix::Float), map(tag("dec"), |_| Suffix::Decimal))))(i)?;
|
||||
Ok((i, opt_suffix.unwrap_or(Suffix::None)))
|
||||
}
|
||||
|
||||
pub fn integer(i: &str) -> IResult<&str, i64> {
|
||||
|
@ -645,16 +639,9 @@ pub fn integer(i: &str) -> IResult<&str, i64> {
|
|||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn int(i: &str) -> IResult<&str, Number> {
|
||||
let (i, v) = i64(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, Number::from(v)))
|
||||
}
|
||||
|
||||
fn decimal(i: &str) -> IResult<&str, Number> {
|
||||
let (i, v) = recognize_float(i)?;
|
||||
let (i, _) = ending(i)?;
|
||||
Ok((i, Number::try_from(v).map_err(|_| Failure(Parser(i)))?))
|
||||
/// TODO: This slow but temporary (awaiting https://docs.rs/rust_decimal/latest/rust_decimal/ version >1.29.1)
|
||||
pub(crate) fn decimal_is_integer(decimal: &Decimal) -> bool {
|
||||
decimal.fract().is_zero()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -663,6 +650,18 @@ mod tests {
|
|||
use super::*;
|
||||
use std::ops::Div;
|
||||
|
||||
#[test]
|
||||
fn dec_is_integer() {
|
||||
assert!(decimal_is_integer(&Decimal::MAX));
|
||||
assert!(decimal_is_integer(&Decimal::MIN));
|
||||
for n in -10..10 {
|
||||
assert!(decimal_is_integer(&Decimal::from(n)));
|
||||
assert!(!decimal_is_integer(&(Decimal::from(n) + Decimal::from_f32(0.1).unwrap())));
|
||||
}
|
||||
|
||||
assert!(!decimal_is_integer(&Decimal::HALF_PI));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_int() {
|
||||
let sql = "123";
|
||||
|
@ -685,21 +684,21 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn number_float() {
|
||||
let sql = "123.45";
|
||||
let sql = "123.45f";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("123.45", format!("{}", out));
|
||||
assert_eq!(sql, format!("{}", out));
|
||||
assert_eq!(out, Number::Float(123.45));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_float_neg() {
|
||||
let sql = "-123.45";
|
||||
let sql = "-123.45f";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("-123.45", format!("{}", out));
|
||||
assert_eq!(sql, format!("{}", out));
|
||||
assert_eq!(out, Number::Float(-123.45));
|
||||
}
|
||||
|
||||
|
@ -709,7 +708,7 @@ mod tests {
|
|||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("1234.5", format!("{}", out));
|
||||
assert_eq!("1234.5f", format!("{}", out));
|
||||
assert_eq!(out, Number::Float(1234.5));
|
||||
}
|
||||
|
||||
|
@ -719,7 +718,7 @@ mod tests {
|
|||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("-1234.5", format!("{}", out));
|
||||
assert_eq!("-1234.5f", format!("{}", out));
|
||||
assert_eq!(out, Number::Float(-1234.5));
|
||||
}
|
||||
|
||||
|
@ -729,7 +728,7 @@ mod tests {
|
|||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("123.45", format!("{}", out));
|
||||
assert_eq!("123.45f", format!("{}", out));
|
||||
assert_eq!(out, Number::Float(123.45));
|
||||
}
|
||||
|
||||
|
@ -739,28 +738,26 @@ mod tests {
|
|||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("-123.45", format!("{}", out));
|
||||
assert_eq!("-123.45f", format!("{}", out));
|
||||
assert_eq!(out, Number::Float(-123.45));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_float_keeps_precision() {
|
||||
let sql = "13.571938471938472";
|
||||
let sql = "13.571938471938472f";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("13.571938471938472", format!("{}", out));
|
||||
assert_eq!(out, Number::try_from("13.571938471938472").unwrap());
|
||||
assert_eq!(sql, format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_decimal_keeps_precision() {
|
||||
let sql = "13.571938471938471938563985639413947693775636";
|
||||
let sql = "0.0000000000000000000000000321dec";
|
||||
let res = number(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("13.571938471938471938563985639413947693775636", format!("{}", out));
|
||||
assert_eq!(out, Number::try_from("13.571938471938471938563985639413947693775636").unwrap());
|
||||
assert_eq!(sql, format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -72,7 +72,7 @@ mod tests {
|
|||
let res = scoring(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("BM25(1.0,0.6,100)", format!("{}", out))
|
||||
assert_eq!("BM25(1f,0.6f,100)", format!("{}", out))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1427,7 +1427,7 @@ mod tests {
|
|||
},
|
||||
}
|
||||
);
|
||||
assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col SEARCH my_analyzer BM25(1.2,0.75,1000) HIGHLIGHTS");
|
||||
assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col SEARCH my_analyzer BM25(1.2f,0.75f,1000) HIGHLIGHTS");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -242,7 +242,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn select_statement_table_thing() {
|
||||
let sql = "SELECT *, ((1 + 3) / 4), 1.3999 AS tester FROM test, test:thingy";
|
||||
let sql = "SELECT *, ((1 + 3) / 4), 1.3999f AS tester FROM test, test:thingy";
|
||||
let res = select(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
|
|
|
@ -1,32 +1,28 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use bigdecimal::BigDecimal;
|
||||
use rust_decimal::Decimal;
|
||||
use serde::ser::Error as _;
|
||||
use serde::ser::Impossible;
|
||||
use std::fmt::Display;
|
||||
|
||||
pub(super) struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = BigDecimal;
|
||||
type Ok = Decimal;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<BigDecimal, Error>;
|
||||
type SerializeTuple = Impossible<BigDecimal, Error>;
|
||||
type SerializeTupleStruct = Impossible<BigDecimal, Error>;
|
||||
type SerializeTupleVariant = Impossible<BigDecimal, Error>;
|
||||
type SerializeMap = Impossible<BigDecimal, Error>;
|
||||
type SerializeStruct = Impossible<BigDecimal, Error>;
|
||||
type SerializeStructVariant = Impossible<BigDecimal, Error>;
|
||||
type SerializeSeq = Impossible<Decimal, Error>;
|
||||
type SerializeTuple = Impossible<Decimal, Error>;
|
||||
type SerializeTupleStruct = Impossible<Decimal, Error>;
|
||||
type SerializeTupleVariant = Impossible<Decimal, Error>;
|
||||
type SerializeMap = Impossible<Decimal, Error>;
|
||||
type SerializeStruct = Impossible<Decimal, Error>;
|
||||
type SerializeStructVariant = Impossible<Decimal, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "a struct `BigDecimal`";
|
||||
const EXPECTED: &'static str = "a struct `Decimal`";
|
||||
|
||||
#[inline]
|
||||
fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
value.to_string().parse::<BigDecimal>().map_err(Error::custom)
|
||||
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
|
||||
value.parse::<Decimal>().map_err(Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,8 +34,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn from_i32() {
|
||||
let decimal = BigDecimal::from(25);
|
||||
let serialized = decimal.serialize(Serializer.wrap()).unwrap();
|
||||
let decimal = Decimal::from(25);
|
||||
let serialized = Serialize::serialize(&decimal, Serializer.wrap()).unwrap();
|
||||
assert_eq!(decimal, serialized);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::Number;
|
||||
use bigdecimal::BigDecimal;
|
||||
use bigdecimal::FromPrimitive as _;
|
||||
use rust_decimal::Decimal;
|
||||
use serde::ser::Error as _;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
@ -44,9 +43,10 @@ impl ser::Serializer for Serializer {
|
|||
}
|
||||
|
||||
fn serialize_i128(self, value: i128) -> Result<Self::Ok, Error> {
|
||||
match BigDecimal::from_i128(value) {
|
||||
Some(decimal) => Ok(decimal.into()),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
// TODO: Replace with native 128-bit integer support.
|
||||
match Decimal::try_from(value) {
|
||||
Ok(decimal) => Ok(decimal.into()),
|
||||
_ => Err(Error::TryFrom(value.to_string(), "Decimal")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,9 +71,10 @@ impl ser::Serializer for Serializer {
|
|||
}
|
||||
|
||||
fn serialize_u128(self, value: u128) -> Result<Self::Ok, Error> {
|
||||
match BigDecimal::from_u128(value) {
|
||||
Some(decimal) => Ok(decimal.into()),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
// TODO: Replace with native 128-bit integer support.
|
||||
match Decimal::try_from(value) {
|
||||
Ok(decimal) => Ok(decimal.into()),
|
||||
_ => Err(Error::TryFrom(value.to_string(), "Decimal")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +90,7 @@ impl ser::Serializer for Serializer {
|
|||
|
||||
#[inline]
|
||||
fn serialize_str(self, value: &str) -> Result<Self::Ok, Error> {
|
||||
let decimal = value.parse::<BigDecimal>().map_err(Error::custom)?;
|
||||
let decimal = value.parse::<Decimal>().map_err(Error::custom)?;
|
||||
Ok(decimal.into())
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,8 @@ use crate::sql::Param;
|
|||
use crate::sql::Strand;
|
||||
use crate::sql::Table;
|
||||
use crate::sql::Uuid;
|
||||
use bigdecimal::BigDecimal;
|
||||
use bigdecimal::FromPrimitive;
|
||||
use map::SerializeValueMap;
|
||||
use rust_decimal::Decimal;
|
||||
use ser::edges::SerializeEdges;
|
||||
use ser::expression::SerializeExpression;
|
||||
use ser::function::SerializeFunction;
|
||||
|
@ -96,9 +95,10 @@ impl ser::Serializer for Serializer {
|
|||
}
|
||||
|
||||
fn serialize_i128(self, value: i128) -> Result<Self::Ok, Error> {
|
||||
match BigDecimal::from_i128(value) {
|
||||
Some(decimal) => Ok(decimal.into()),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
// TODO: Replace with native 128-bit integer support.
|
||||
match Decimal::try_from(value) {
|
||||
Ok(decimal) => Ok(decimal.into()),
|
||||
_ => Err(Error::TryFrom(value.to_string(), "Decimal")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,9 +123,10 @@ impl ser::Serializer for Serializer {
|
|||
}
|
||||
|
||||
fn serialize_u128(self, value: u128) -> Result<Self::Ok, Error> {
|
||||
match BigDecimal::from_u128(value) {
|
||||
Some(decimal) => Ok(decimal.into()),
|
||||
None => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
// TODO: replace with native 128-bit integer support.
|
||||
match Decimal::try_from(value) {
|
||||
Ok(decimal) => Ok(decimal.into()),
|
||||
_ => Err(Error::TryFrom(value.to_string(), "Decimal")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ use crate::sql::id::Id;
|
|||
use crate::sql::idiom::{self, Idiom};
|
||||
use crate::sql::kind::Kind;
|
||||
use crate::sql::model::{model, Model};
|
||||
use crate::sql::number::decimal_is_integer;
|
||||
use crate::sql::number::{number, Number};
|
||||
use crate::sql::object::{key, object, Object};
|
||||
use crate::sql::operation::Operation;
|
||||
|
@ -38,9 +39,6 @@ use crate::sql::table::{table, Table};
|
|||
use crate::sql::thing::{thing, Thing};
|
||||
use crate::sql::uuid::{uuid as unique, Uuid};
|
||||
use async_recursion::async_recursion;
|
||||
use bigdecimal::BigDecimal;
|
||||
use bigdecimal::FromPrimitive;
|
||||
use bigdecimal::ToPrimitive;
|
||||
use chrono::{DateTime, Utc};
|
||||
use derive::Store;
|
||||
use fuzzy_matcher::skim::SkimMatcherV2;
|
||||
|
@ -53,6 +51,7 @@ use nom::combinator::{map, opt};
|
|||
use nom::multi::separated_list0;
|
||||
use nom::multi::separated_list1;
|
||||
use once_cell::sync::Lazy;
|
||||
use rust_decimal::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as Json;
|
||||
use std::cmp::Ordering;
|
||||
|
@ -403,8 +402,8 @@ impl From<f64> for Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<BigDecimal> for Value {
|
||||
fn from(v: BigDecimal) -> Self {
|
||||
impl From<Decimal> for Value {
|
||||
fn from(v: Decimal) -> Self {
|
||||
Value::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
@ -654,12 +653,12 @@ impl TryFrom<Value> for f64 {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value> for BigDecimal {
|
||||
impl TryFrom<Value> for Decimal {
|
||||
type Error = Error;
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Value::Number(x) => x.try_into(),
|
||||
_ => Err(Error::TryFrom(value.to_string(), "BigDecimal")),
|
||||
_ => Err(Error::TryFrom(value.to_string(), "Decimal")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1175,9 +1174,9 @@ impl Value {
|
|||
// Attempt to convert an float number
|
||||
Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(v as i64),
|
||||
// Attempt to convert a decimal number
|
||||
Value::Number(Number::Decimal(ref v)) if v.is_integer() => match v.to_i64() {
|
||||
Value::Number(Number::Decimal(v)) if decimal_is_integer(&v) => match v.try_into() {
|
||||
// The Decimal can be represented as an i64
|
||||
Some(v) => Ok(v),
|
||||
Ok(v) => Ok(v),
|
||||
// The Decimal is out of bounds
|
||||
_ => Err(Error::CoerceTo {
|
||||
from: self,
|
||||
|
@ -1200,9 +1199,9 @@ impl Value {
|
|||
// Attempt to convert an float number
|
||||
Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(v as u64),
|
||||
// Attempt to convert a decimal number
|
||||
Value::Number(Number::Decimal(ref v)) if v.is_integer() => match v.to_u64() {
|
||||
Value::Number(Number::Decimal(v)) if decimal_is_integer(&v) => match v.try_into() {
|
||||
// The Decimal can be represented as an u64
|
||||
Some(v) => Ok(v),
|
||||
Ok(v) => Ok(v),
|
||||
// The Decimal is out of bounds
|
||||
_ => Err(Error::CoerceTo {
|
||||
from: self,
|
||||
|
@ -1225,11 +1224,11 @@ impl Value {
|
|||
// Attempt to convert an int number
|
||||
Value::Number(Number::Int(v)) => Ok(v as f64),
|
||||
// Attempt to convert a decimal number
|
||||
Value::Number(Number::Decimal(ref v)) => match v.to_f64() {
|
||||
Value::Number(Number::Decimal(v)) => match v.try_into() {
|
||||
// The Decimal can be represented as a f64
|
||||
Some(v) => Ok(v),
|
||||
Ok(v) => Ok(v),
|
||||
// Ths Decimal loses precision
|
||||
None => Err(Error::CoerceTo {
|
||||
_ => Err(Error::CoerceTo {
|
||||
from: self,
|
||||
into: "f64".into(),
|
||||
}),
|
||||
|
@ -1263,7 +1262,7 @@ impl Value {
|
|||
// Attempt to convert an float number
|
||||
Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(Number::Int(v as i64)),
|
||||
// Attempt to convert a decimal number
|
||||
Value::Number(Number::Decimal(ref v)) if v.is_integer() => match v.to_i64() {
|
||||
Value::Number(Number::Decimal(ref v)) if decimal_is_integer(v) => match v.to_i64() {
|
||||
// The Decimal can be represented as an Int
|
||||
Some(v) => Ok(Number::Int(v)),
|
||||
// The Decimal is out of bounds
|
||||
|
@ -1311,7 +1310,7 @@ impl Value {
|
|||
// Allow any decimal number
|
||||
Value::Number(v) if v.is_decimal() => Ok(v),
|
||||
// Attempt to convert an int number
|
||||
Value::Number(Number::Int(ref v)) => match BigDecimal::from_i64(*v) {
|
||||
Value::Number(Number::Int(v)) => match Decimal::from_i64(v) {
|
||||
// The Int can be represented as a Decimal
|
||||
Some(v) => Ok(Number::Decimal(v)),
|
||||
// Ths Int does not convert to a Decimal
|
||||
|
@ -1321,7 +1320,7 @@ impl Value {
|
|||
}),
|
||||
},
|
||||
// Attempt to convert an float number
|
||||
Value::Number(Number::Float(ref v)) => match BigDecimal::from_f64(*v) {
|
||||
Value::Number(Number::Float(v)) => match Decimal::from_f64(v) {
|
||||
// The Float can be represented as a Decimal
|
||||
Some(v) => Ok(Number::Decimal(v)),
|
||||
// Ths Float does not convert to a Decimal
|
||||
|
@ -1724,9 +1723,9 @@ impl Value {
|
|||
// Attempt to convert an float number
|
||||
Value::Number(Number::Float(v)) if v.fract() == 0.0 => Ok(Number::Int(v as i64)),
|
||||
// Attempt to convert a decimal number
|
||||
Value::Number(Number::Decimal(ref v)) if v.is_integer() => match v.to_i64() {
|
||||
Value::Number(Number::Decimal(v)) if decimal_is_integer(&v) => match v.try_into() {
|
||||
// The Decimal can be represented as an Int
|
||||
Some(v) => Ok(Number::Int(v)),
|
||||
Ok(v) => Ok(Number::Int(v)),
|
||||
// The Decimal is out of bounds
|
||||
_ => Err(Error::ConvertTo {
|
||||
from: self,
|
||||
|
@ -1759,11 +1758,11 @@ impl Value {
|
|||
// Attempt to convert an int number
|
||||
Value::Number(Number::Int(v)) => Ok(Number::Float(v as f64)),
|
||||
// Attempt to convert a decimal number
|
||||
Value::Number(Number::Decimal(ref v)) => match v.to_f64() {
|
||||
Value::Number(Number::Decimal(v)) => match v.try_into() {
|
||||
// The Decimal can be represented as a Float
|
||||
Some(v) => Ok(Number::Float(v)),
|
||||
// Ths BigDecimal loses precision
|
||||
None => Err(Error::ConvertTo {
|
||||
Ok(v) => Ok(Number::Float(v)),
|
||||
// The Decimal loses precision
|
||||
_ => Err(Error::ConvertTo {
|
||||
from: self,
|
||||
into: "float".into(),
|
||||
}),
|
||||
|
@ -1792,30 +1791,30 @@ impl Value {
|
|||
// Allow any decimal number
|
||||
Value::Number(v) if v.is_decimal() => Ok(v),
|
||||
// Attempt to convert an int number
|
||||
Value::Number(Number::Int(ref v)) => match BigDecimal::from_i64(*v) {
|
||||
Value::Number(Number::Int(ref v)) => match Decimal::try_from(*v) {
|
||||
// The Int can be represented as a Decimal
|
||||
Some(v) => Ok(Number::Decimal(v)),
|
||||
Ok(v) => Ok(Number::Decimal(v)),
|
||||
// Ths Int does not convert to a Decimal
|
||||
None => Err(Error::ConvertTo {
|
||||
_ => Err(Error::ConvertTo {
|
||||
from: self,
|
||||
into: "decimal".into(),
|
||||
}),
|
||||
},
|
||||
// Attempt to convert an float number
|
||||
Value::Number(Number::Float(ref v)) => match BigDecimal::from_f64(*v) {
|
||||
Value::Number(Number::Float(ref v)) => match Decimal::try_from(*v) {
|
||||
// The Float can be represented as a Decimal
|
||||
Some(v) => Ok(Number::Decimal(v)),
|
||||
Ok(v) => Ok(Number::Decimal(v)),
|
||||
// Ths Float does not convert to a Decimal
|
||||
None => Err(Error::ConvertTo {
|
||||
_ => Err(Error::ConvertTo {
|
||||
from: self,
|
||||
into: "decimal".into(),
|
||||
}),
|
||||
},
|
||||
// Attempt to convert a string value
|
||||
Value::Strand(ref v) => match BigDecimal::from_str(v) {
|
||||
// The string can be represented as a Float
|
||||
Value::Strand(ref v) => match Decimal::from_str(v) {
|
||||
// The string can be represented as a Decimal
|
||||
Ok(v) => Ok(Number::Decimal(v)),
|
||||
// Ths string is not a float
|
||||
// Ths string is not a Decimal
|
||||
_ => Err(Error::ConvertTo {
|
||||
from: self,
|
||||
into: "decimal".into(),
|
||||
|
@ -2523,6 +2522,15 @@ impl TryAdd for Value {
|
|||
(Number::Int(v), Number::Int(w)) if v.checked_add(w).is_none() => {
|
||||
Err(Error::TryAdd(v.to_string(), w.to_string()))
|
||||
}
|
||||
(Number::Decimal(v), Number::Decimal(w)) if v.checked_add(w).is_none() => {
|
||||
Err(Error::TryAdd(v.to_string(), w.to_string()))
|
||||
}
|
||||
(Number::Decimal(v), w) if v.checked_add(w.to_decimal()).is_none() => {
|
||||
Err(Error::TryAdd(v.to_string(), w.to_string()))
|
||||
}
|
||||
(v, Number::Decimal(w)) if v.to_decimal().checked_add(w).is_none() => {
|
||||
Err(Error::TryAdd(v.to_string(), w.to_string()))
|
||||
}
|
||||
(v, w) => Ok(Value::Number(v + w)),
|
||||
},
|
||||
(Value::Strand(v), Value::Strand(w)) => Ok(Value::Strand(v + w)),
|
||||
|
@ -2549,6 +2557,15 @@ impl TrySub for Value {
|
|||
(Number::Int(v), Number::Int(w)) if v.checked_sub(w).is_none() => {
|
||||
Err(Error::TrySub(v.to_string(), w.to_string()))
|
||||
}
|
||||
(Number::Decimal(v), Number::Decimal(w)) if v.checked_sub(w).is_none() => {
|
||||
Err(Error::TrySub(v.to_string(), w.to_string()))
|
||||
}
|
||||
(Number::Decimal(v), w) if v.checked_sub(w.to_decimal()).is_none() => {
|
||||
Err(Error::TrySub(v.to_string(), w.to_string()))
|
||||
}
|
||||
(v, Number::Decimal(w)) if v.to_decimal().checked_sub(w).is_none() => {
|
||||
Err(Error::TrySub(v.to_string(), w.to_string()))
|
||||
}
|
||||
(v, w) => Ok(Value::Number(v - w)),
|
||||
},
|
||||
(Value::Datetime(v), Value::Datetime(w)) => Ok(Value::Duration(v - w)),
|
||||
|
@ -2575,6 +2592,15 @@ impl TryMul for Value {
|
|||
(Number::Int(v), Number::Int(w)) if v.checked_mul(w).is_none() => {
|
||||
Err(Error::TryMul(v.to_string(), w.to_string()))
|
||||
}
|
||||
(Number::Decimal(v), Number::Decimal(w)) if v.checked_mul(w).is_none() => {
|
||||
Err(Error::TryMul(v.to_string(), w.to_string()))
|
||||
}
|
||||
(Number::Decimal(v), w) if v.checked_mul(w.to_decimal()).is_none() => {
|
||||
Err(Error::TryMul(v.to_string(), w.to_string()))
|
||||
}
|
||||
(v, Number::Decimal(w)) if v.to_decimal().checked_mul(w).is_none() => {
|
||||
Err(Error::TryMul(v.to_string(), w.to_string()))
|
||||
}
|
||||
(v, w) => Ok(Value::Number(v * w)),
|
||||
},
|
||||
(v, w) => Err(Error::TryMul(v.to_raw_string(), w.to_raw_string())),
|
||||
|
@ -2595,6 +2621,10 @@ impl TryDiv for Value {
|
|||
match (self, other) {
|
||||
(Value::Number(v), Value::Number(w)) => match (v, w) {
|
||||
(_, w) if w == Number::Int(0) => Ok(Value::None),
|
||||
(Number::Decimal(v), Number::Decimal(w)) if v.checked_div(w).is_none() => {
|
||||
// Divided a large number by a small number, got an overflowing number
|
||||
Err(Error::TryDiv(v.to_string(), w.to_string()))
|
||||
}
|
||||
(v, w) => Ok(Value::Number(v / w)),
|
||||
},
|
||||
(v, w) => Err(Error::TryDiv(v.to_raw_string(), w.to_raw_string())),
|
||||
|
@ -2619,6 +2649,9 @@ impl TryPow for Value {
|
|||
{
|
||||
Err(Error::TryPow(v.to_string(), w.to_string()))
|
||||
}
|
||||
(Number::Decimal(v), Number::Int(w)) if v.checked_powi(w).is_none() => {
|
||||
Err(Error::TryPow(v.to_string(), w.to_string()))
|
||||
}
|
||||
(v, w) => Ok(Value::Number(v.pow(w))),
|
||||
},
|
||||
(v, w) => Err(Error::TryPow(v.to_raw_string(), w.to_raw_string())),
|
||||
|
@ -2857,8 +2890,8 @@ mod tests {
|
|||
assert_eq!(String::from("0"), Value::from(0).as_string());
|
||||
assert_eq!(String::from("1"), Value::from(1).as_string());
|
||||
assert_eq!(String::from("-1"), Value::from(-1).as_string());
|
||||
assert_eq!(String::from("1.1"), Value::from(1.1).as_string());
|
||||
assert_eq!(String::from("-1.1"), Value::from(-1.1).as_string());
|
||||
assert_eq!(String::from("1.1f"), Value::from(1.1).as_string());
|
||||
assert_eq!(String::from("-1.1f"), Value::from(-1.1).as_string());
|
||||
assert_eq!(String::from("3"), Value::from("3").as_string());
|
||||
assert_eq!(String::from("true"), Value::from("true").as_string());
|
||||
assert_eq!(String::from("false"), Value::from("false").as_string());
|
||||
|
@ -2870,7 +2903,7 @@ mod tests {
|
|||
assert_eq!(64, std::mem::size_of::<Value>());
|
||||
assert_eq!(104, std::mem::size_of::<Error>());
|
||||
assert_eq!(104, std::mem::size_of::<Result<Value, Error>>());
|
||||
assert_eq!(40, std::mem::size_of::<crate::sql::number::Number>());
|
||||
assert_eq!(24, std::mem::size_of::<crate::sql::number::Number>());
|
||||
assert_eq!(24, std::mem::size_of::<crate::sql::strand::Strand>());
|
||||
assert_eq!(16, std::mem::size_of::<crate::sql::duration::Duration>());
|
||||
assert_eq!(12, std::mem::size_of::<crate::sql::datetime::Datetime>());
|
||||
|
|
|
@ -988,7 +988,7 @@ async fn define_statement_search_index() -> Result<(), Error> {
|
|||
events: {},
|
||||
fields: {},
|
||||
tables: {},
|
||||
indexes: { blog_title: 'DEFINE INDEX blog_title ON blog FIELDS title SEARCH english BM25(1.2,0.75,100) HIGHLIGHTS' },
|
||||
indexes: { blog_title: 'DEFINE INDEX blog_title ON blog FIELDS title SEARCH english BM25(1.2f,0.75f,100) HIGHLIGHTS' },
|
||||
}",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
|
|
|
@ -3,7 +3,7 @@ use parse::Parse;
|
|||
use surrealdb::dbs::Session;
|
||||
use surrealdb::err::Error;
|
||||
use surrealdb::kvs::Datastore;
|
||||
use surrealdb::sql::Value;
|
||||
use surrealdb::sql::{Number, Value};
|
||||
|
||||
// --------------------------------------------------
|
||||
// array
|
||||
|
@ -445,7 +445,7 @@ async fn function_string_join_arr() -> Result<(), Error> {
|
|||
RETURN array::join([], "");
|
||||
RETURN array::join(["hello", "world"], ", ");
|
||||
RETURN array::join(["again", "again", "again"], " and ");
|
||||
RETURN array::join([42, 3.14, 2.72, 1.61], " and ");
|
||||
RETURN array::join([42, true, "1.61"], " and ");
|
||||
"#;
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
|
@ -465,7 +465,7 @@ async fn function_string_join_arr() -> Result<(), Error> {
|
|||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::from("42 and 3.14 and 2.72 and 1.61");
|
||||
let val = Value::from("42 and true and 1.61");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
|
@ -4153,11 +4153,13 @@ async fn function_type_decimal() -> Result<(), Error> {
|
|||
assert_eq!(res.len(), 2);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("13.1043784018");
|
||||
let val = Value::Number(Number::Decimal("13.1043784018".parse().unwrap()));
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("13.5719384719384719385639856394139476937756394756");
|
||||
let val = Value::Number(Number::Decimal(
|
||||
"13.571938471938471938563985639413947693775639".parse().unwrap(),
|
||||
));
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
|
@ -4293,7 +4295,7 @@ async fn function_type_point() -> Result<(), Error> {
|
|||
async fn function_type_string() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN type::string(30s);
|
||||
RETURN type::string(13.58248);
|
||||
RETURN type::string(13);
|
||||
"#;
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
|
@ -4305,7 +4307,7 @@ async fn function_type_string() -> Result<(), Error> {
|
|||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::from("13.58248");
|
||||
let val = Value::from("13");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
|
|
|
@ -311,7 +311,7 @@ async fn select_multi_aggregate() -> Result<(), Error> {
|
|||
},
|
||||
{
|
||||
group: 2,
|
||||
one: 7.6,
|
||||
one: 7.6000000000000005,
|
||||
two: 12.7,
|
||||
}
|
||||
]",
|
||||
|
@ -328,7 +328,7 @@ async fn select_multi_aggregate() -> Result<(), Error> {
|
|||
},
|
||||
{
|
||||
group: 2,
|
||||
one: 7.6,
|
||||
one: 7.6000000000000005,
|
||||
two: 12.7,
|
||||
}
|
||||
]",
|
||||
|
@ -451,7 +451,7 @@ async fn select_multi_aggregate_composed() -> Result<(), Error> {
|
|||
{
|
||||
group: 2,
|
||||
one: 9,
|
||||
two: 14,
|
||||
two: 13,
|
||||
}
|
||||
]",
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue