diff --git a/Cargo.toml b/Cargo.toml index a2e9b46d..ea62e116 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ rmp-serde = "1.1.0" rmpv = "1.0.0" base64 = "0.13.0" chrono = "0.4.19" -ordered-float = { version = "3.0", features = ["serde"] } +num-traits = "0.2.15" itertools = "0.10.3" pest = "2.2.1" pest_derive = "2.2.1" diff --git a/README.md b/README.md index dd7aaab9..e67c69d7 100644 --- a/README.md +++ b/README.md @@ -12,5 +12,3 @@ * [x] public API * [x] sorting * [ ] range scan - -comparators can have problems when sorting mixed integers and floats diff --git a/src/data/aggr.rs b/src/data/aggr.rs index da1e03ef..8d583058 100644 --- a/src/data/aggr.rs +++ b/src/data/aggr.rs @@ -1,9 +1,9 @@ -use std::cmp::{max, min}; +use std::cmp::{max, min, Ordering}; use std::fmt::{Debug, Formatter}; use anyhow::{bail, Result}; -use crate::data::value::DataValue; +use crate::data::value::{DataValue, Number}; #[derive(Clone)] pub(crate) struct Aggregation { @@ -37,10 +37,12 @@ macro_rules! define_aggr { define_aggr!(AGGR_COUNT, false); fn aggr_count(accum: &DataValue, current: &DataValue) -> Result { match (accum, current) { - (DataValue::Guard, DataValue::Guard) => Ok(DataValue::Int(0)), - (DataValue::Guard, _) => Ok(DataValue::Int(1)), - (DataValue::Int(i), DataValue::Guard) => Ok(DataValue::Int(*i)), - (DataValue::Int(i), _) => Ok(DataValue::Int(*i + 1)), + (DataValue::Guard, DataValue::Guard) => Ok(DataValue::Number(Number::Int(0))), + (DataValue::Guard, _) => Ok(DataValue::Number(Number::Int(1))), + (DataValue::Number(Number::Int(i)), DataValue::Guard) => { + Ok(DataValue::Number(Number::Int(*i))) + } + (DataValue::Number(Number::Int(i)), _) => Ok(DataValue::Number(Number::Int(*i + 1))), _ => unreachable!(), } } @@ -48,16 +50,29 @@ fn aggr_count(accum: &DataValue, current: &DataValue) -> Result { define_aggr!(AGGR_SUM, false); fn aggr_sum(accum: &DataValue, current: &DataValue) -> Result { match (accum, current) { - (DataValue::Guard, DataValue::Guard) => Ok(DataValue::Int(0)), - (DataValue::Guard, DataValue::Int(i)) => Ok(DataValue::Int(*i)), - (DataValue::Guard, DataValue::Float(f)) => Ok(DataValue::Float(f.0.into())), - (DataValue::Int(i), DataValue::Guard) => Ok(DataValue::Int(*i)), - (DataValue::Float(f), DataValue::Guard) => Ok(DataValue::Float(f.0.into())), - (DataValue::Int(i), DataValue::Int(j)) => Ok(DataValue::Int(*i + *j)), - (DataValue::Int(j), DataValue::Float(i)) | (DataValue::Float(i), DataValue::Int(j)) => { - Ok(DataValue::Float((i.0 + (*j as f64)).into())) - } - (DataValue::Float(i), DataValue::Float(j)) => Ok(DataValue::Float((i.0 + j.0).into())), + (DataValue::Guard, DataValue::Guard) => Ok(DataValue::Number(Number::Int(0))), + (DataValue::Guard, DataValue::Number(Number::Int(i))) => { + Ok(DataValue::Number(Number::Int(*i))) + } + (DataValue::Guard, DataValue::Number(Number::Float(f))) => { + Ok(DataValue::Number(Number::Float(*f))) + } + (DataValue::Number(Number::Int(i)), DataValue::Guard) => { + Ok(DataValue::Number(Number::Int(*i))) + } + (DataValue::Number(Number::Float(f)), DataValue::Guard) => { + Ok(DataValue::Number(Number::Float(*f))) + } + (DataValue::Number(Number::Int(i)), DataValue::Number(Number::Int(j))) => { + Ok(DataValue::Number(Number::Int(*i + *j))) + } + (DataValue::Number(Number::Int(j)), DataValue::Number(Number::Float(i))) + | (DataValue::Number(Number::Float(i)), DataValue::Number(Number::Int(j))) => { + Ok(DataValue::Number(Number::Float(*i + (*j as f64)))) + } + (DataValue::Number(Number::Float(i)), DataValue::Number(Number::Float(j))) => { + Ok(DataValue::Number(Number::Float(*i + *j))) + } (i, j) => bail!( "cannot compute min: encountered value {:?} for aggregate {:?}", j, @@ -69,14 +84,31 @@ fn aggr_sum(accum: &DataValue, current: &DataValue) -> Result { define_aggr!(AGGR_MIN, true); fn aggr_min(accum: &DataValue, current: &DataValue) -> Result { match (accum, current) { - (DataValue::Guard, DataValue::Int(i)) => Ok(DataValue::Int(*i)), - (DataValue::Guard, DataValue::Float(f)) => Ok(DataValue::Float(f.0.into())), - (DataValue::Int(i), DataValue::Int(j)) => Ok(DataValue::Int(min(*i, *j))), - (DataValue::Int(j), DataValue::Float(i)) | (DataValue::Float(i), DataValue::Int(j)) => { - Ok(DataValue::Float(min(i.clone(), (*j as f64).into()))) + (DataValue::Guard, DataValue::Number(Number::Int(i))) => { + Ok(DataValue::Number(Number::Int(*i))) } - (DataValue::Float(i), DataValue::Float(j)) => { - Ok(DataValue::Float(min(i.clone(), j.clone()))) + (DataValue::Guard, DataValue::Number(Number::Float(f))) => { + Ok(DataValue::Number(Number::Float(*f))) + } + (DataValue::Number(Number::Int(i)), DataValue::Number(Number::Int(j))) => { + Ok(DataValue::Number(Number::Int(min(*i, *j)))) + } + (DataValue::Number(Number::Int(j)), DataValue::Number(Number::Float(i))) + | (DataValue::Number(Number::Float(i)), DataValue::Number(Number::Int(j))) => { + let m = match i.total_cmp(&(*j as f64)) { + Ordering::Less => *i, + Ordering::Equal => *i, + Ordering::Greater => *j as f64, + }; + Ok(DataValue::Number(Number::Float(m))) + } + (DataValue::Number(Number::Float(i)), DataValue::Number(Number::Float(j))) => { + let m = match i.total_cmp(j) { + Ordering::Less => *i, + Ordering::Equal => *i, + Ordering::Greater => *j, + }; + Ok(DataValue::Number(Number::Float(m))) } (i, j) => bail!( "cannot compute min: encountered value {:?} for aggregate {:?}", @@ -89,15 +121,34 @@ fn aggr_min(accum: &DataValue, current: &DataValue) -> Result { define_aggr!(AGGR_MAX, true); fn aggr_max(accum: &DataValue, current: &DataValue) -> Result { match (accum, current) { - (DataValue::Guard, DataValue::Int(i)) => Ok(DataValue::Int(*i)), - (DataValue::Guard, DataValue::Float(f)) => Ok(DataValue::Float(f.0.into())), - (DataValue::Float(f), DataValue::Guard) => Ok(DataValue::Float(f.0.into())), - (DataValue::Int(i), DataValue::Int(j)) => Ok(DataValue::Int(max(*i, *j))), - (DataValue::Int(j), DataValue::Float(i)) | (DataValue::Float(i), DataValue::Int(j)) => { - Ok(DataValue::Float(max(i.clone(), (*j as f64).into()))) - } - (DataValue::Float(i), DataValue::Float(j)) => { - Ok(DataValue::Float(max(i.clone(), j.clone()))) + (DataValue::Guard, DataValue::Number(Number::Int(i))) => { + Ok(DataValue::Number(Number::Int(*i))) + } + (DataValue::Guard, DataValue::Number(Number::Float(f))) => { + Ok(DataValue::Number(Number::Float(*f))) + } + (DataValue::Number(Number::Float(f)), DataValue::Guard) => { + Ok(DataValue::Number(Number::Float(*f))) + } + (DataValue::Number(Number::Int(i)), DataValue::Number(Number::Int(j))) => { + Ok(DataValue::Number(Number::Int(max(*i, *j)))) + } + (DataValue::Number(Number::Int(j)), DataValue::Number(Number::Float(i))) + | (DataValue::Number(Number::Float(i)), DataValue::Number(Number::Int(j))) => { + let m = match i.total_cmp(&(*j as f64)) { + Ordering::Less => *j as f64, + Ordering::Equal => *i, + Ordering::Greater => *i, + }; + Ok(DataValue::Number(Number::Float(m))) + } + (DataValue::Number(Number::Float(i)), DataValue::Number(Number::Float(j))) => { + let m = match i.total_cmp(j) { + Ordering::Less => *j, + Ordering::Equal => *i, + Ordering::Greater => *i, + }; + Ok(DataValue::Number(Number::Float(m))) } (i, j) => bail!( "cannot compute min: encountered value {:?} for aggregate {:?}", diff --git a/src/data/attr.rs b/src/data/attr.rs index d87f3b1e..16cbcccb 100644 --- a/src/data/attr.rs +++ b/src/data/attr.rs @@ -12,7 +12,7 @@ use crate::data::id::{AttrId, TxId}; use crate::data::json::JsonValue; use crate::data::symb::Symbol; use crate::data::triple::StoreOp; -use crate::data::value::DataValue; +use crate::data::value::{DataValue, Number}; use crate::parse::triple::TempIdCtx; #[repr(u8)] @@ -116,7 +116,7 @@ impl AttributeTyping { pub(crate) fn coerce_value(&self, val: DataValue) -> Result { match self { AttributeTyping::Ref | AttributeTyping::Component => match val { - DataValue::Int(s) if s > 0 => Ok(DataValue::Int(s)), + DataValue::Number(Number::Int(s)) if s > 0 => Ok(DataValue::Number(Number::Int(s))), val => Err(self.type_err(val).into()), }, AttributeTyping::Bool => { @@ -127,15 +127,15 @@ impl AttributeTyping { } } AttributeTyping::Int => { - if matches!(val, DataValue::Int(_)) { + if matches!(val, DataValue::Number(Number::Int(_))) { Ok(val) } else { Err(self.type_err(val).into()) } } AttributeTyping::Float => match val { - v @ DataValue::Float(_) => Ok(v), - DataValue::Int(i) => Ok(DataValue::Float((i as f64).into())), + v @ DataValue::Number(Number::Float(_)) => Ok(v), + DataValue::Number(Number::Int(i)) => Ok(DataValue::Number(Number::Float(i as f64))), val => Err(self.type_err(val).into()), }, AttributeTyping::String => { @@ -154,7 +154,7 @@ impl AttributeTyping { } AttributeTyping::Timestamp => match val { val @ DataValue::Timestamp(_) => Ok(val), - DataValue::Int(i) => Ok(DataValue::Timestamp(i)), + DataValue::Number(Number::Int(i)) => Ok(DataValue::Timestamp(i)), val => Err(self.type_err(val).into()), }, AttributeTyping::Bytes => { diff --git a/src/data/expr.rs b/src/data/expr.rs index ee3b1765..83c1b2db 100644 --- a/src/data/expr.rs +++ b/src/data/expr.rs @@ -5,11 +5,10 @@ use std::ops::Rem; use anyhow::{bail, Result}; use itertools::Itertools; -use ordered_float::{Float, OrderedFloat}; use crate::data::symb::Symbol; use crate::data::tuple::Tuple; -use crate::data::value::DataValue; +use crate::data::value::{DataValue, Number}; #[derive(Debug, Clone)] pub(crate) enum Expr { @@ -139,38 +138,22 @@ fn op_neq(args: &[DataValue]) -> Result { define_op!(OP_GT, 2, false, true); fn op_gt(args: &[DataValue]) -> Result { - Ok(DataValue::Bool(match (&args[0], &args[1]) { - (DataValue::Int(a), DataValue::Float(b)) => OrderedFloat(*a as f64) > *b, - (DataValue::Float(a), DataValue::Int(b)) => *a > OrderedFloat(*b as f64), - (_, _) => args[0] > args[1], - })) + Ok(DataValue::Bool(args[0] > args[1])) } define_op!(OP_GE, 2, false, true); fn op_ge(args: &[DataValue]) -> Result { - Ok(DataValue::Bool(match (&args[0], &args[1]) { - (DataValue::Int(a), DataValue::Float(b)) => OrderedFloat(*a as f64) >= *b, - (DataValue::Float(a), DataValue::Int(b)) => *a >= OrderedFloat(*b as f64), - (_, _) => args[0] >= args[1], - })) + Ok(DataValue::Bool(args[0] >= args[1])) } define_op!(OP_LT, 2, false, true); fn op_lt(args: &[DataValue]) -> Result { - Ok(DataValue::Bool(match (&args[0], &args[1]) { - (DataValue::Int(a), DataValue::Float(b)) => OrderedFloat(*a as f64) < *b, - (DataValue::Float(a), DataValue::Int(b)) => *a < OrderedFloat(*b as f64), - (_, _) => args[0] < args[1], - })) + Ok(DataValue::Bool(args[0] < args[1])) } define_op!(OP_LE, 2, false, true); fn op_le(args: &[DataValue]) -> Result { - Ok(DataValue::Bool(match (&args[0], &args[1]) { - (DataValue::Int(a), DataValue::Float(b)) => OrderedFloat(*a as f64) <= *b, - (DataValue::Float(a), DataValue::Int(b)) => *a <= OrderedFloat(*b as f64), - (_, _) => args[0] <= args[1], - })) + Ok(DataValue::Bool(args[0] <= args[1])) } define_op!(OP_ADD, 0, true, false); @@ -179,15 +162,15 @@ fn op_add(args: &[DataValue]) -> Result { let mut f_accum = 0.0f64; for arg in args { match arg { - DataValue::Int(i) => i_accum += i, - DataValue::Float(f) => f_accum += f.0, + DataValue::Number(Number::Int(i)) => i_accum += i, + DataValue::Number(Number::Float(f)) => f_accum += f, v => bail!("unexpected arg {:?} for OP_ADD", v), } } if f_accum == 0.0f64 { - Ok(DataValue::Int(i_accum)) + Ok(DataValue::Number(Number::Int(i_accum))) } else { - Ok(DataValue::Float((i_accum as f64 + f_accum).into())) + Ok(DataValue::Number(Number::Float(i_accum as f64 + f_accum))) } } @@ -196,22 +179,14 @@ fn op_max(args: &[DataValue]) -> Result { let res = args .iter() .try_fold(None, |accum, nxt| match (accum, nxt) { - (None, d @ DataValue::Int(_)) => Ok(Some(d.clone())), - (None, d @ DataValue::Float(_)) => Ok(Some(d.clone())), - (Some(DataValue::Int(a)), DataValue::Int(b)) => Ok(Some(DataValue::Int(a.max(*b)))), - (Some(DataValue::Int(a)), DataValue::Float(b)) => { - Ok(Some(DataValue::Float(b.0.max(a as f64).into()))) - } - (Some(DataValue::Float(a)), DataValue::Int(b)) => { - Ok(Some(DataValue::Float(a.0.max(*b as f64).into()))) - } - (Some(DataValue::Float(a)), DataValue::Float(b)) => { - Ok(Some(DataValue::Float(a.0.max(b.0).into()))) + (None, d @ DataValue::Number(_)) => Ok(Some(d.clone())), + (Some(DataValue::Number(a)), DataValue::Number(b)) => { + Ok(Some(DataValue::Number(a.max(*b)))) } v => bail!("unexpected arg {:?} for OP_MAX", v), })?; match res { - None => Ok(DataValue::Float(f64::neg_infinity().into())), + None => Ok(DataValue::Number(Number::Float(f64::NEG_INFINITY))), Some(v) => Ok(v), } } @@ -221,22 +196,14 @@ fn op_min(args: &[DataValue]) -> Result { let res = args .iter() .try_fold(None, |accum, nxt| match (accum, nxt) { - (None, d @ DataValue::Int(_)) => Ok(Some(d.clone())), - (None, d @ DataValue::Float(_)) => Ok(Some(d.clone())), - (Some(DataValue::Int(a)), DataValue::Int(b)) => Ok(Some(DataValue::Int(a.min(*b)))), - (Some(DataValue::Int(a)), DataValue::Float(b)) => { - Ok(Some(DataValue::Float(b.0.min(a as f64).into()))) - } - (Some(DataValue::Float(a)), DataValue::Int(b)) => { - Ok(Some(DataValue::Float(a.0.min(*b as f64).into()))) - } - (Some(DataValue::Float(a)), DataValue::Float(b)) => { - Ok(Some(DataValue::Float(a.0.min(b.0).into()))) + (None, d @ DataValue::Number(_)) => Ok(Some(d.clone())), + (Some(DataValue::Number(a)), DataValue::Number(b)) => { + Ok(Some(DataValue::Number(a.min(*b)))) } v => bail!("unexpected arg {:?} for OP_MIN", v), })?; match res { - None => Ok(DataValue::Float(f64::infinity().into())), + None => Ok(DataValue::Number(Number::Float(f64::INFINITY))), Some(v) => Ok(v), } } @@ -244,10 +211,18 @@ fn op_min(args: &[DataValue]) -> Result { define_op!(OP_SUB, 2, false, false); fn op_sub(args: &[DataValue]) -> Result { Ok(match (&args[0], &args[1]) { - (DataValue::Int(a), DataValue::Int(b)) => DataValue::Int(*a - *b), - (DataValue::Float(a), DataValue::Float(b)) => DataValue::Float(*a - *b), - (DataValue::Int(a), DataValue::Float(b)) => DataValue::Float(((*a as f64) - b.0).into()), - (DataValue::Float(a), DataValue::Int(b)) => DataValue::Float((a.0 - (*b as f64)).into()), + (DataValue::Number(Number::Int(a)), DataValue::Number(Number::Int(b))) => { + DataValue::Number(Number::Int(*a - *b)) + } + (DataValue::Number(Number::Float(a)), DataValue::Number(Number::Float(b))) => { + DataValue::Number(Number::Float(*a - *b)) + } + (DataValue::Number(Number::Int(a)), DataValue::Number(Number::Float(b))) => { + DataValue::Number(Number::Float((*a as f64) - b)) + } + (DataValue::Number(Number::Float(a)), DataValue::Number(Number::Int(b))) => { + DataValue::Number(Number::Float(a - (*b as f64))) + } v => bail!("unexpected arg {:?} for OP_SUB", v), }) } @@ -258,27 +233,33 @@ fn op_mul(args: &[DataValue]) -> Result { let mut f_accum = 1.0f64; for arg in args { match arg { - DataValue::Int(i) => i_accum *= i, - DataValue::Float(f) => f_accum *= f.0, + DataValue::Number(Number::Int(i)) => i_accum *= i, + DataValue::Number(Number::Float(f)) => f_accum *= f, v => bail!("unexpected arg {:?} for OP_MUL", v), } } if f_accum == 1.0f64 { - Ok(DataValue::Int(i_accum)) + Ok(DataValue::Number(Number::Int(i_accum))) } else { - Ok(DataValue::Float((i_accum as f64 + f_accum).into())) + Ok(DataValue::Number(Number::Float(i_accum as f64 + f_accum))) } } define_op!(OP_DIV, 2, false, false); fn op_div(args: &[DataValue]) -> Result { Ok(match (&args[0], &args[1]) { - (DataValue::Int(a), DataValue::Int(b)) => { - DataValue::Float(((*a as f64) / (*b as f64)).into()) + (DataValue::Number(Number::Int(a)), DataValue::Number(Number::Int(b))) => { + DataValue::Number(Number::Float((*a as f64) / (*b as f64))) + } + (DataValue::Number(Number::Float(a)), DataValue::Number(Number::Float(b))) => { + DataValue::Number(Number::Float(*a / *b)) + } + (DataValue::Number(Number::Int(a)), DataValue::Number(Number::Float(b))) => { + DataValue::Number(Number::Float((*a as f64) / b)) + } + (DataValue::Number(Number::Float(a)), DataValue::Number(Number::Int(b))) => { + DataValue::Number(Number::Float(a / (*b as f64))) } - (DataValue::Float(a), DataValue::Float(b)) => DataValue::Float(*a / *b), - (DataValue::Int(a), DataValue::Float(b)) => DataValue::Float(((*a as f64) / b.0).into()), - (DataValue::Float(a), DataValue::Int(b)) => DataValue::Float((a.0 / (*b as f64)).into()), v => bail!("unexpected arg {:?} for OP_DIV", v), }) } @@ -286,8 +267,8 @@ fn op_div(args: &[DataValue]) -> Result { define_op!(OP_MINUS, 1, false, false); fn op_minus(args: &[DataValue]) -> Result { Ok(match &args[0] { - DataValue::Int(i) => DataValue::Int(-(*i)), - DataValue::Float(f) => DataValue::Float(-(*f)), + DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(-(*i))), + DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(-(*f))), v => bail!("unexpected arg {:?} for OP_MINUS", v), }) } @@ -295,8 +276,8 @@ fn op_minus(args: &[DataValue]) -> Result { define_op!(OP_ABS, 1, false, false); fn op_abs(args: &[DataValue]) -> Result { Ok(match &args[0] { - DataValue::Int(i) => DataValue::Int(i.abs()), - DataValue::Float(f) => DataValue::Float(f.abs()), + DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(i.abs())), + DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.abs())), v => bail!("unexpected arg {:?} for OP_ABS", v), }) } @@ -304,8 +285,8 @@ fn op_abs(args: &[DataValue]) -> Result { define_op!(OP_SIGNUM, 1, false, false); fn op_signum(args: &[DataValue]) -> Result { Ok(match &args[0] { - DataValue::Int(i) => DataValue::Int(i.signum()), - DataValue::Float(f) => DataValue::Float(f.signum()), + DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(i.signum())), + DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.signum())), v => bail!("unexpected arg {:?} for OP_SIGNUM", v), }) } @@ -313,8 +294,8 @@ fn op_signum(args: &[DataValue]) -> Result { define_op!(OP_FLOOR, 1, false, false); fn op_floor(args: &[DataValue]) -> Result { Ok(match &args[0] { - DataValue::Int(i) => DataValue::Int(*i), - DataValue::Float(f) => DataValue::Float(f.floor()), + DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(*i)), + DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.floor())), v => bail!("unexpected arg {:?} for OP_FLOOR", v), }) } @@ -322,8 +303,8 @@ fn op_floor(args: &[DataValue]) -> Result { define_op!(OP_CEIL, 1, false, false); fn op_ceil(args: &[DataValue]) -> Result { Ok(match &args[0] { - DataValue::Int(i) => DataValue::Int(*i), - DataValue::Float(f) => DataValue::Float(f.ceil()), + DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(*i)), + DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.ceil())), v => bail!("unexpected arg {:?} for OP_CEIL", v), }) } @@ -331,8 +312,8 @@ fn op_ceil(args: &[DataValue]) -> Result { define_op!(OP_ROUND, 1, false, false); fn op_round(args: &[DataValue]) -> Result { Ok(match &args[0] { - DataValue::Int(i) => DataValue::Int(*i), - DataValue::Float(f) => DataValue::Float(f.round()), + DataValue::Number(Number::Int(i)) => DataValue::Number(Number::Int(*i)), + DataValue::Number(Number::Float(f)) => DataValue::Number(Number::Float(f.round())), v => bail!("unexpected arg {:?} for OP_ROUND", v), }) } @@ -340,211 +321,219 @@ fn op_round(args: &[DataValue]) -> Result { define_op!(OP_EXP, 1, false, false); fn op_exp(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_EXP", v), }; - Ok(DataValue::Float(a.exp().into())) + Ok(DataValue::Number(Number::Float(a.exp()))) } define_op!(OP_EXP2, 1, false, false); fn op_exp2(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_EXP2", v), }; - Ok(DataValue::Float(a.exp2().into())) + Ok(DataValue::Number(Number::Float(a.exp2()))) } define_op!(OP_LN, 1, false, false); fn op_ln(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_LN", v), }; - Ok(DataValue::Float(a.ln().into())) + Ok(DataValue::Number(Number::Float(a.ln()))) } define_op!(OP_LOG2, 1, false, false); fn op_log2(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_LOG2", v), }; - Ok(DataValue::Float(a.log2().into())) + Ok(DataValue::Number(Number::Float(a.log2()))) } define_op!(OP_LOG10, 1, false, false); fn op_log10(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_LOG10", v), }; - Ok(DataValue::Float(a.log10().into())) + Ok(DataValue::Number(Number::Float(a.log10()))) } define_op!(OP_SIN, 1, false, false); fn op_sin(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_SIN", v), }; - Ok(DataValue::Float(a.sin().into())) + Ok(DataValue::Number(Number::Float(a.sin()))) } define_op!(OP_COS, 1, false, false); fn op_cos(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_COS", v), }; - Ok(DataValue::Float(a.cos().into())) + Ok(DataValue::Number(Number::Float(a.cos()))) } define_op!(OP_TAN, 1, false, false); fn op_tan(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_TAN", v), }; - Ok(DataValue::Float(a.tan().into())) + Ok(DataValue::Number(Number::Float(a.tan()))) } define_op!(OP_ASIN, 1, false, false); fn op_asin(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_ASIN", v), }; - Ok(DataValue::Float(a.asin().into())) + Ok(DataValue::Number(Number::Float(a.asin()))) } define_op!(OP_ACOS, 1, false, false); fn op_acos(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_ACOS", v), }; - Ok(DataValue::Float(a.acos().into())) + Ok(DataValue::Number(Number::Float(a.acos()))) } define_op!(OP_ATAN, 1, false, false); fn op_atan(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_ATAN", v), }; - Ok(DataValue::Float(a.atan().into())) + Ok(DataValue::Number(Number::Float(a.atan()))) } define_op!(OP_ATAN2, 2, false, false); fn op_atan2(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_ATAN2", v), }; let b = match &args[1] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_ATAN2", v), }; - Ok(DataValue::Float(a.atan2(b).into())) + Ok(DataValue::Number(Number::Float(a.atan2(b)))) } define_op!(OP_SINH, 1, false, false); fn op_sinh(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_SINH", v), }; - Ok(DataValue::Float(a.sinh().into())) + Ok(DataValue::Number(Number::Float(a.sinh()))) } define_op!(OP_COSH, 1, false, false); fn op_cosh(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_COSH", v), }; - Ok(DataValue::Float(a.cosh().into())) + Ok(DataValue::Number(Number::Float(a.cosh()))) } define_op!(OP_TANH, 1, false, false); fn op_tanh(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_TANH", v), }; - Ok(DataValue::Float(a.tanh().into())) + Ok(DataValue::Number(Number::Float(a.tanh()))) } define_op!(OP_ASINH, 1, false, false); fn op_asinh(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_ASINH", v), }; - Ok(DataValue::Float(a.asinh().into())) + Ok(DataValue::Number(Number::Float(a.asinh()))) } define_op!(OP_ACOSH, 1, false, false); fn op_acosh(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_ACOSH", v), }; - Ok(DataValue::Float(a.acosh().into())) + Ok(DataValue::Number(Number::Float(a.acosh()))) } define_op!(OP_ATANH, 1, false, false); fn op_atanh(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_ATANH", v), }; - Ok(DataValue::Float(a.atanh().into())) + Ok(DataValue::Number(Number::Float(a.atanh()))) } define_op!(OP_POW, 2, false, false); fn op_pow(args: &[DataValue]) -> Result { let a = match &args[0] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_POW", v), }; let b = match &args[1] { - DataValue::Int(i) => *i as f64, - DataValue::Float(f) => f.0, + DataValue::Number(Number::Int(i)) => *i as f64, + DataValue::Number(Number::Float(f)) => *f, v => bail!("unexpected arg {:?} for OP_POW", v), }; - Ok(DataValue::Float(a.powf(b).into())) + Ok(DataValue::Number(Number::Float(a.powf(b)))) } define_op!(OP_MOD, 2, false, false); fn op_mod(args: &[DataValue]) -> Result { Ok(match (&args[0], &args[1]) { - (DataValue::Int(a), DataValue::Int(b)) => DataValue::Int(a.rem(b)), - (DataValue::Float(a), DataValue::Float(b)) => DataValue::Float(a.rem(*b)), - (DataValue::Int(a), DataValue::Float(b)) => DataValue::Float(((*a as f64).rem(b.0)).into()), - (DataValue::Float(a), DataValue::Int(b)) => DataValue::Float((a.0.rem(*b as f64)).into()), + (DataValue::Number(Number::Int(a)), DataValue::Number(Number::Int(b))) => { + DataValue::Number(Number::Int(a.rem(b))) + } + (DataValue::Number(Number::Float(a)), DataValue::Number(Number::Float(b))) => { + DataValue::Number(Number::Float(a.rem(*b))) + } + (DataValue::Number(Number::Int(a)), DataValue::Number(Number::Float(b))) => { + DataValue::Number(Number::Float((*a as f64).rem(b))) + } + (DataValue::Number(Number::Float(a)), DataValue::Number(Number::Int(b))) => { + DataValue::Number(Number::Float(a.rem(*b as f64))) + } v => bail!("unexpected arg {:?} for OP_MOD", v), }) } @@ -632,19 +621,25 @@ fn op_is_null(args: &[DataValue]) -> Result { define_op!(OP_IS_INT, 1, false, true); fn op_is_int(args: &[DataValue]) -> Result { - Ok(DataValue::Bool(matches!(args[0], DataValue::Int(_)))) + Ok(DataValue::Bool(matches!( + args[0], + DataValue::Number(Number::Int(_)) + ))) } define_op!(OP_IS_FLOAT, 1, false, true); fn op_is_float(args: &[DataValue]) -> Result { - Ok(DataValue::Bool(matches!(args[0], DataValue::Float(_)))) + Ok(DataValue::Bool(matches!( + args[0], + DataValue::Number(Number::Float(_)) + ))) } define_op!(OP_IS_NUM, 1, false, true); fn op_is_num(args: &[DataValue]) -> Result { Ok(DataValue::Bool(matches!( args[0], - DataValue::Int(_) | DataValue::Float(_) + DataValue::Number(Number::Int(_)) | DataValue::Number(Number::Float(_)) ))) } diff --git a/src/data/id.rs b/src/data/id.rs index 747642f7..6f9f4f23 100644 --- a/src/data/id.rs +++ b/src/data/id.rs @@ -90,7 +90,7 @@ impl EntityId { pub(crate) const MAX_PERM: EntityId = EntityId(0x00ff_ffff_ff00_0000); pub(crate) fn to_value(&self) -> DataValue { - DataValue::Int(self.0 as i64) + DataValue::from(self.0 as i64) } pub(crate) fn from_bytes(b: &[u8]) -> Self { diff --git a/src/data/json.rs b/src/data/json.rs index 436d98bc..9ffb037e 100644 --- a/src/data/json.rs +++ b/src/data/json.rs @@ -5,7 +5,7 @@ pub(crate) use serde_json::Value as JsonValue; use crate::data::attr::{Attribute, AttributeCardinality, AttributeIndex, AttributeTyping}; use crate::data::id::{AttrId, EntityId, TxId}; use crate::data::symb::Symbol; -use crate::data::value::DataValue; +use crate::data::value::{DataValue, Number}; impl From for DataValue { fn from(v: JsonValue) -> Self { @@ -13,9 +13,9 @@ impl From for DataValue { JsonValue::Null => DataValue::Null, JsonValue::Bool(b) => DataValue::Bool(b), JsonValue::Number(n) => match n.as_i64() { - Some(i) => DataValue::Int(i), + Some(i) => DataValue::from(i), None => match n.as_f64() { - Some(f) => DataValue::Float(f.into()), + Some(f) => DataValue::from(f), None => DataValue::String(n.to_string().into()), }, }, @@ -38,9 +38,9 @@ impl<'a> From<&'a JsonValue> for DataValue { JsonValue::Null => DataValue::Null, JsonValue::Bool(b) => DataValue::Bool(*b), JsonValue::Number(n) => match n.as_i64() { - Some(i) => DataValue::Int(i), + Some(i) => DataValue::from(i), None => match n.as_f64() { - Some(f) => DataValue::Float(f.into()), + Some(f) => DataValue::from(f), None => DataValue::String(n.to_string().into()), }, }, @@ -62,8 +62,8 @@ impl From for JsonValue { match v { DataValue::Null => JsonValue::Null, DataValue::Bool(b) => JsonValue::Bool(b), - DataValue::Int(i) => JsonValue::Number(i.into()), - DataValue::Float(f) => json!(f.0), + DataValue::Number(Number::Int(i)) => JsonValue::Number(i.into()), + DataValue::Number(Number::Float(f)) => json!(f), DataValue::String(t) => JsonValue::String(t.into()), DataValue::Uuid(uuid) => JsonValue::String(uuid.to_string()), DataValue::Bytes(bytes) => JsonValue::String(base64::encode(bytes)), @@ -73,7 +73,7 @@ impl From for JsonValue { DataValue::DescVal(v) => JsonValue::from(*v.0), DataValue::Bottom => JsonValue::Null, DataValue::Timestamp(i) => JsonValue::Number(i.into()), - DataValue::Guard => JsonValue::Null + DataValue::Guard => JsonValue::Null, } } } @@ -99,18 +99,11 @@ impl TryFrom<&'_ JsonValue> for Attribute { None => AttrId(0), Some(v) => AttrId::try_from(v)?, }; - let name = map.get("name").ok_or_else(|| { - anyhow!( - "expect field 'name' in attribute definition, got {}", - value - ) - })?; + let name = map + .get("name") + .ok_or_else(|| anyhow!("expect field 'name' in attribute definition, got {}", value))?; let symb = Symbol::try_from(name)?; - ensure!( - !symb.is_reserved(), - "cannot use reserved symbol {}", - symb - ); + ensure!(!symb.is_reserved(), "cannot use reserved symbol {}", symb); let cardinality = map .get("cardinality") .ok_or_else(|| anyhow!("expect field 'cardinality' in {}", value))? diff --git a/src/data/value.rs b/src/data/value.rs index c511c8ee..161da231 100644 --- a/src/data/value.rs +++ b/src/data/value.rs @@ -1,8 +1,7 @@ -use std::cmp::Reverse; -use std::fmt::{Debug, Formatter}; +use std::cmp::{Ordering, Reverse}; +use std::fmt::{Debug, Display, Formatter}; use anyhow::{bail, Result}; -use ordered_float::OrderedFloat; use rmp_serde::Serializer; use serde::Serialize; use serde_derive::{Deserialize, Serialize}; @@ -21,9 +20,7 @@ pub(crate) enum DataValue { #[serde(rename = "b")] Bool(bool), #[serde(rename = "i")] - Int(i64), - #[serde(rename = "f")] - Float(OrderedFloat), + Number(Number), #[serde(rename = "s")] String(SmartString), #[serde(rename = "u")] @@ -43,6 +40,83 @@ pub(crate) enum DataValue { Bottom, } +impl From for DataValue { + fn from(v: i64) -> Self { + DataValue::Number(Number::Int(v)) + } +} + +impl From for DataValue { + fn from(v: f64) -> Self { + DataValue::Number(Number::Float(v)) + } +} + +#[derive(Copy, Clone, Deserialize, Serialize)] +pub(crate) enum Number { + #[serde(rename = "i")] + Int(i64), + #[serde(rename = "f")] + Float(f64), +} + +impl PartialEq for Number { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for Number {} + +impl Display for Number { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Number::Int(i) => write!(f, "{}", i), + Number::Float(n) => write!(f, "{}", n), + } + } +} + +impl Debug for Number { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Number::Int(i) => write!(f, "{}", i), + Number::Float(n) => write!(f, "{}", n), + } + } +} + +impl PartialOrd for Number { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Number { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Number::Int(i), Number::Float(r)) => { + let l = *i as f64; + match l.total_cmp(&r) { + Ordering::Less => Ordering::Less, + Ordering::Equal => Ordering::Less, + Ordering::Greater => Ordering::Greater, + } + } + (Number::Float(l), Number::Int(i)) => { + let r = *i as f64; + match l.total_cmp(&r) { + Ordering::Less => Ordering::Less, + Ordering::Equal => Ordering::Greater, + Ordering::Greater => Ordering::Greater, + } + } + (Number::Int(l), Number::Int(r)) => l.cmp(r), + (Number::Float(l), Number::Float(r)) => l.total_cmp(r), + } + } +} + impl Debug for DataValue { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { @@ -52,12 +126,9 @@ impl Debug for DataValue { DataValue::Bool(b) => { write!(f, "{}", b) } - DataValue::Int(i) => { + DataValue::Number(i) => { write!(f, "{}", i) } - DataValue::Float(n) => { - write!(f, "{}", n.0) - } DataValue::String(s) => { write!(f, "{:?}", s) } @@ -101,7 +172,7 @@ impl DataValue { pub(crate) fn get_entity_id(&self) -> Result { match self { - DataValue::Int(id) => Ok(EntityId(*id as u64)), + DataValue::Number(Number::Int(id)) => Ok(EntityId(*id as u64)), _ => bail!("type mismatch: expect type EntId, got value {:?}", self), } } diff --git a/src/parse/query.rs b/src/parse/query.rs index 0444d714..da809be0 100644 --- a/src/parse/query.rs +++ b/src/parse/query.rs @@ -633,7 +633,7 @@ impl SessionTx { if let Some(o) = value_rep.as_object() { return if attr.val_type.is_ref_type() { let eid = self.parse_eid_from_map(o, vld)?; - Ok(InputTerm::Const(DataValue::Int(eid.0 as i64))) + Ok(InputTerm::Const(DataValue::from(eid.0 as i64))) } else { Ok(InputTerm::Const(self.parse_value_from_map(o, attr)?)) }; diff --git a/src/query/sort.rs b/src/query/sort.rs index 1ef44ae0..f19ca23d 100644 --- a/src/query/sort.rs +++ b/src/query/sort.rs @@ -36,7 +36,7 @@ impl SessionTx { val }) .collect_vec(); - key.push(DataValue::Int(idx as i64)); + key.push(DataValue::from(idx as i64)); let key = Tuple(key); let encoded_key = key.encode_as_key_for_epoch(ret.id, 0); let encoded_val = tuple.encode_as_key_for_epoch(ret.id, 0); diff --git a/src/runtime/temp_store.rs b/src/runtime/temp_store.rs index 6233133a..4b4c085f 100644 --- a/src/runtime/temp_store.rs +++ b/src/runtime/temp_store.rs @@ -136,7 +136,7 @@ impl TempStore { vals.push(tuple.0[idx].clone()); } } - vals.push(DataValue::Int(serial as i64)); + vals.push(DataValue::from(serial as i64)); self.db .put(&Tuple(vals).encode_as_key_for_epoch(self.id, 0), &[]) } diff --git a/tests/air-routes-data.json b/tests/air-routes-data.json index cb730aca..e33dadd5 100644 --- a/tests/air-routes-data.json +++ b/tests/air-routes-data.json @@ -1,6 +1,6 @@ {"_temp_id": "3742", "continent.code": "EU", "continent.desc": "Europe"} {"_temp_id": "3743", "continent.code": "AF", "continent.desc": "Africa"} -{"_temp_id": "3744", "continent.desc": "North America"} +{"_temp_id": "3744", "continent.code": "NA", "continent.desc": "North America"} {"_temp_id": "3745", "continent.code": "SA", "continent.desc": "South America"} {"_temp_id": "3746", "continent.code": "AS", "continent.desc": "Asia"} {"_temp_id": "3747", "continent.code": "OC", "continent.desc": "Oceania"} diff --git a/tests/air_routes.rs b/tests/air_routes.rs index 68b3ac3f..e1ba098c 100644 --- a/tests/air_routes.rs +++ b/tests/air_routes.rs @@ -287,5 +287,22 @@ fn air_routes() -> Result<()> { .unwrap() ); + let n_airports_by_continent_time = Instant::now(); + let res = db.run_script( + r#" + airports_by_continent[?c, count(?a)] := [?a airport.iata ?_], [?c geo.contains ?a]; + ?[?cont, max(?count)] := airports_by_continent[?c, ?count], [?c continent.code ?cont]; + ?[?cont, max(?count)] := [?_ continent.code ?cont], ?count is 0; + "#, + )?; + dbg!(n_airports_by_continent_time.elapsed()); + assert_eq!( + res, + serde_json::Value::from_str( + r#"[["AF",321],["AN",0],["AS",971],["EU",605],["NA",989],["OC",305],["SA",313]]"# + ) + .unwrap() + ); + Ok(()) }