diff --git a/lib/src/dbs/iterator.rs b/lib/src/dbs/iterator.rs index c47bb6fe..7750c054 100644 --- a/lib/src/dbs/iterator.rs +++ b/lib/src/dbs/iterator.rs @@ -17,6 +17,7 @@ use crate::sql::statements::update::UpdateStatement; use crate::sql::table::Table; use crate::sql::thing::Thing; use crate::sql::value::Value; +use std::cmp::Ordering; use std::mem; use std::sync::Arc; @@ -209,8 +210,22 @@ impl Iterator { _opt: &Options, _txn: &Transaction, ) -> Result<(), Error> { - if self.stm.order().is_some() { - // Ignore + if let Some(orders) = self.stm.order() { + self.results.sort_by(|a, b| { + for order in &orders.0 { + let o = match order.direction { + true => a.compare(b, &order.order), + false => b.compare(a, &order.order), + }; + match o { + Some(Ordering::Greater) => return Ordering::Greater, + Some(Ordering::Equal) => continue, + Some(Ordering::Less) => return Ordering::Less, + None => continue, + } + } + Ordering::Equal + }) } Ok(()) } diff --git a/lib/src/sql/function.rs b/lib/src/sql/function.rs index 3194ade2..cb80a473 100644 --- a/lib/src/sql/function.rs +++ b/lib/src/sql/function.rs @@ -27,7 +27,7 @@ pub enum Function { impl PartialOrd for Function { #[inline] fn partial_cmp(&self, _: &Self) -> Option { - unreachable!() + None } } diff --git a/lib/src/sql/geometry.rs b/lib/src/sql/geometry.rs index b38a87bc..68338916 100644 --- a/lib/src/sql/geometry.rs +++ b/lib/src/sql/geometry.rs @@ -36,7 +36,7 @@ pub enum Geometry { impl PartialOrd for Geometry { #[inline] fn partial_cmp(&self, _: &Self) -> Option { - unreachable!() + None } } diff --git a/lib/src/sql/subquery.rs b/lib/src/sql/subquery.rs index d50ab914..599e23a6 100644 --- a/lib/src/sql/subquery.rs +++ b/lib/src/sql/subquery.rs @@ -35,7 +35,7 @@ pub enum Subquery { impl PartialOrd for Subquery { #[inline] fn partial_cmp(&self, _: &Self) -> Option { - unreachable!() + None } } diff --git a/lib/src/sql/value/compare.rs b/lib/src/sql/value/compare.rs new file mode 100644 index 00000000..4d9f8d8b --- /dev/null +++ b/lib/src/sql/value/compare.rs @@ -0,0 +1,186 @@ +use crate::sql::part::Next; +use crate::sql::part::Part; +use crate::sql::value::Value; +use std::cmp::Ordering; + +impl Value { + pub fn compare(&self, other: &Self, path: &[Part]) -> Option { + match path.first() { + // Get the current path part + Some(p) => match (self, other) { + // Current path part is an object + (Value::Object(a), Value::Object(b)) => match p { + Part::Field(f) => match (a.value.get(&f.name), b.value.get(&f.name)) { + (Some(a), Some(b)) => a.compare(b, path.next()), + (Some(_), None) => Some(Ordering::Greater), + (None, Some(_)) => Some(Ordering::Less), + (_, _) => Some(Ordering::Equal), + }, + _ => None, + }, + // Current path part is an array + (Value::Array(a), Value::Array(b)) => match p { + Part::All => { + for (a, b) in a.value.iter().zip(b.value.iter()) { + match a.compare(b, path.next()) { + Some(Ordering::Equal) => continue, + None => continue, + o => return o, + } + } + match (a.len(), b.len()) { + (a, b) if a > b => Some(Ordering::Greater), + (a, b) if a < b => Some(Ordering::Less), + _ => Some(Ordering::Equal), + } + } + Part::First => match (a.value.first(), b.value.first()) { + (Some(a), Some(b)) => a.compare(b, path.next()), + (Some(_), None) => Some(Ordering::Greater), + (None, Some(_)) => Some(Ordering::Less), + (_, _) => Some(Ordering::Equal), + }, + Part::Last => match (a.value.first(), b.value.first()) { + (Some(a), Some(b)) => a.compare(b, path.next()), + (Some(_), None) => Some(Ordering::Greater), + (None, Some(_)) => Some(Ordering::Less), + (_, _) => Some(Ordering::Equal), + }, + Part::Index(i) => { + match (a.value.get(i.to_usize()), b.value.get(i.to_usize())) { + (Some(a), Some(b)) => a.compare(b, path.next()), + (Some(_), None) => Some(Ordering::Greater), + (None, Some(_)) => Some(Ordering::Less), + (_, _) => Some(Ordering::Equal), + } + } + _ => { + for (a, b) in a.value.iter().zip(b.value.iter()) { + match a.compare(b, path) { + Some(Ordering::Equal) => continue, + None => continue, + o => return o, + } + } + match (a.len(), b.len()) { + (a, b) if a > b => Some(Ordering::Greater), + (a, b) if a < b => Some(Ordering::Less), + _ => Some(Ordering::Equal), + } + } + }, + // Ignore everything else + (a, b) => a.compare(b, path.next()), + }, + // No more parts so get the value + None => self.partial_cmp(other), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::idiom::Idiom; + use crate::sql::test::Parse; + + #[test] + fn compare_none() { + let idi = Idiom::default(); + let one = Value::parse("{ test: { other: null, something: 456 } }"); + let two = Value::parse("{ test: { other: null, something: 123 } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_basic() { + let idi = Idiom::parse("test.something"); + let one = Value::parse("{ test: { other: null, something: 456 } }"); + let two = Value::parse("{ test: { other: null, something: 123 } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_basic_missing_left() { + let idi = Idiom::parse("test.something"); + let one = Value::parse("{ test: { other: null } }"); + let two = Value::parse("{ test: { other: null, something: 123 } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Less)); + } + + #[test] + fn compare_basic_missing_right() { + let idi = Idiom::parse("test.something"); + let one = Value::parse("{ test: { other: null, something: 456 } }"); + let two = Value::parse("{ test: { other: null } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_array() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [4, 5, 6] } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_array_longer_left() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [1, 2, 3, 4, 5, 6] } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_array_longer_right() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3, 4, 5, 6] } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Less)); + } + + #[test] + fn compare_array_missing_left() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: null } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Less)); + } + + #[test] + fn compare_array_missing_right() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [4, 5, 6] } }"); + let two = Value::parse("{ test: { other: null, something: null } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_array_missing_value_left() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [1, null, 3] } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Less)); + } + + #[test] + fn compare_array_missing_value_right() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let two = Value::parse("{ test: { other: null, something: [1, null, 3] } }"); + let res = one.compare(&two, &idi); + assert_eq!(res, Some(Ordering::Greater)); + } +} diff --git a/lib/src/sql/value/mod.rs b/lib/src/sql/value/mod.rs index 28e31fe0..bfd6f56d 100644 --- a/lib/src/sql/value/mod.rs +++ b/lib/src/sql/value/mod.rs @@ -2,6 +2,7 @@ pub use self::value::*; mod array; mod clear; +mod compare; mod decrement; mod def; mod del; diff --git a/lib/src/sql/value/value.rs b/lib/src/sql/value/value.rs index e589a1a0..157ebf2b 100644 --- a/lib/src/sql/value/value.rs +++ b/lib/src/sql/value/value.rs @@ -83,19 +83,20 @@ pub enum Value { Null, False, True, + Number(Number), + Strand(Strand), + Datetime(Datetime), + Duration(Duration), + Array(Array), + Object(Object), + Geometry(Geometry), + // --- Param(Param), Idiom(Idiom), Table(Table), Thing(Thing), Model(Model), Regex(Regex), - Array(Array), - Object(Object), - Number(Number), - Strand(Strand), - Geometry(Geometry), - Duration(Duration), - Datetime(Datetime), Function(Box), Subquery(Box), Expression(Box), @@ -765,19 +766,19 @@ impl fmt::Display for Value { Value::Null => write!(f, "NULL"), Value::True => write!(f, "true"), Value::False => write!(f, "false"), + Value::Number(v) => write!(f, "{}", v), + Value::Strand(v) => write!(f, "{}", v), + Value::Duration(v) => write!(f, "{}", v), + Value::Datetime(v) => write!(f, "{}", v), + Value::Array(v) => write!(f, "{}", v), + Value::Object(v) => write!(f, "{}", v), + Value::Geometry(v) => write!(f, "{}", v), Value::Param(v) => write!(f, "{}", v), Value::Idiom(v) => write!(f, "{}", v), Value::Table(v) => write!(f, "{}", v), Value::Thing(v) => write!(f, "{}", v), Value::Model(v) => write!(f, "{}", v), Value::Regex(v) => write!(f, "{}", v), - Value::Array(v) => write!(f, "{}", v), - Value::Object(v) => write!(f, "{}", v), - Value::Number(v) => write!(f, "{}", v), - Value::Strand(v) => write!(f, "{}", v), - Value::Geometry(v) => write!(f, "{}", v), - Value::Duration(v) => write!(f, "{}", v), - Value::Datetime(v) => write!(f, "{}", v), Value::Function(v) => write!(f, "{}", v), Value::Subquery(v) => write!(f, "{}", v), Value::Expression(v) => write!(f, "{}", v), @@ -842,19 +843,19 @@ impl Serialize for Value { Value::Null => s.serialize_unit_variant("Value", 2, "Null"), Value::True => s.serialize_unit_variant("Value", 3, "True"), Value::False => s.serialize_unit_variant("Value", 4, "False"), - Value::Param(v) => s.serialize_newtype_variant("Value", 5, "Param", v), - Value::Idiom(v) => s.serialize_newtype_variant("Value", 6, "Idiom", v), - Value::Table(v) => s.serialize_newtype_variant("Value", 7, "Table", v), - Value::Thing(v) => s.serialize_newtype_variant("Value", 8, "Thing", v), - Value::Model(v) => s.serialize_newtype_variant("Value", 9, "Model", v), - Value::Regex(v) => s.serialize_newtype_variant("Value", 10, "Regex", v), - Value::Array(v) => s.serialize_newtype_variant("Value", 11, "Array", v), - Value::Object(v) => s.serialize_newtype_variant("Value", 12, "Object", v), - Value::Number(v) => s.serialize_newtype_variant("Value", 13, "Number", v), - Value::Strand(v) => s.serialize_newtype_variant("Value", 14, "Strand", v), - Value::Geometry(v) => s.serialize_newtype_variant("Value", 15, "Geometry", v), - Value::Duration(v) => s.serialize_newtype_variant("Value", 16, "Duration", v), - Value::Datetime(v) => s.serialize_newtype_variant("Value", 17, "Datetime", v), + Value::Number(v) => s.serialize_newtype_variant("Value", 5, "Number", v), + Value::Strand(v) => s.serialize_newtype_variant("Value", 6, "Strand", v), + Value::Duration(v) => s.serialize_newtype_variant("Value", 7, "Duration", v), + Value::Datetime(v) => s.serialize_newtype_variant("Value", 8, "Datetime", v), + Value::Array(v) => s.serialize_newtype_variant("Value", 9, "Array", v), + Value::Object(v) => s.serialize_newtype_variant("Value", 10, "Object", v), + Value::Geometry(v) => s.serialize_newtype_variant("Value", 11, "Geometry", v), + Value::Param(v) => s.serialize_newtype_variant("Value", 12, "Param", v), + Value::Idiom(v) => s.serialize_newtype_variant("Value", 13, "Idiom", v), + Value::Table(v) => s.serialize_newtype_variant("Value", 14, "Table", v), + Value::Thing(v) => s.serialize_newtype_variant("Value", 15, "Thing", v), + Value::Model(v) => s.serialize_newtype_variant("Value", 16, "Model", v), + Value::Regex(v) => s.serialize_newtype_variant("Value", 17, "Regex", v), Value::Function(v) => s.serialize_newtype_variant("Value", 18, "Function", v), Value::Subquery(v) => s.serialize_newtype_variant("Value", 19, "Subquery", v), Value::Expression(v) => s.serialize_newtype_variant("Value", 20, "Expression", v),