Implement ORDER BY clauses
This commit is contained in:
parent
3370b20c38
commit
f674ea0544
7 changed files with 235 additions and 32 deletions
|
@ -17,6 +17,7 @@ use crate::sql::statements::update::UpdateStatement;
|
||||||
use crate::sql::table::Table;
|
use crate::sql::table::Table;
|
||||||
use crate::sql::thing::Thing;
|
use crate::sql::thing::Thing;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -209,8 +210,22 @@ impl Iterator {
|
||||||
_opt: &Options,
|
_opt: &Options,
|
||||||
_txn: &Transaction,
|
_txn: &Transaction,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
if self.stm.order().is_some() {
|
if let Some(orders) = self.stm.order() {
|
||||||
// Ignore
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub enum Function {
|
||||||
impl PartialOrd for Function {
|
impl PartialOrd for Function {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
|
||||||
unreachable!()
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub enum Geometry {
|
||||||
impl PartialOrd for Geometry {
|
impl PartialOrd for Geometry {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
|
||||||
unreachable!()
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub enum Subquery {
|
||||||
impl PartialOrd for Subquery {
|
impl PartialOrd for Subquery {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
|
||||||
unreachable!()
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
186
lib/src/sql/value/compare.rs
Normal file
186
lib/src/sql/value/compare.rs
Normal file
|
@ -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<Ordering> {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ pub use self::value::*;
|
||||||
|
|
||||||
mod array;
|
mod array;
|
||||||
mod clear;
|
mod clear;
|
||||||
|
mod compare;
|
||||||
mod decrement;
|
mod decrement;
|
||||||
mod def;
|
mod def;
|
||||||
mod del;
|
mod del;
|
||||||
|
|
|
@ -83,19 +83,20 @@ pub enum Value {
|
||||||
Null,
|
Null,
|
||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
|
Number(Number),
|
||||||
|
Strand(Strand),
|
||||||
|
Datetime(Datetime),
|
||||||
|
Duration(Duration),
|
||||||
|
Array(Array),
|
||||||
|
Object(Object),
|
||||||
|
Geometry(Geometry),
|
||||||
|
// ---
|
||||||
Param(Param),
|
Param(Param),
|
||||||
Idiom(Idiom),
|
Idiom(Idiom),
|
||||||
Table(Table),
|
Table(Table),
|
||||||
Thing(Thing),
|
Thing(Thing),
|
||||||
Model(Model),
|
Model(Model),
|
||||||
Regex(Regex),
|
Regex(Regex),
|
||||||
Array(Array),
|
|
||||||
Object(Object),
|
|
||||||
Number(Number),
|
|
||||||
Strand(Strand),
|
|
||||||
Geometry(Geometry),
|
|
||||||
Duration(Duration),
|
|
||||||
Datetime(Datetime),
|
|
||||||
Function(Box<Function>),
|
Function(Box<Function>),
|
||||||
Subquery(Box<Subquery>),
|
Subquery(Box<Subquery>),
|
||||||
Expression(Box<Expression>),
|
Expression(Box<Expression>),
|
||||||
|
@ -765,19 +766,19 @@ impl fmt::Display for Value {
|
||||||
Value::Null => write!(f, "NULL"),
|
Value::Null => write!(f, "NULL"),
|
||||||
Value::True => write!(f, "true"),
|
Value::True => write!(f, "true"),
|
||||||
Value::False => write!(f, "false"),
|
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::Param(v) => write!(f, "{}", v),
|
||||||
Value::Idiom(v) => write!(f, "{}", v),
|
Value::Idiom(v) => write!(f, "{}", v),
|
||||||
Value::Table(v) => write!(f, "{}", v),
|
Value::Table(v) => write!(f, "{}", v),
|
||||||
Value::Thing(v) => write!(f, "{}", v),
|
Value::Thing(v) => write!(f, "{}", v),
|
||||||
Value::Model(v) => write!(f, "{}", v),
|
Value::Model(v) => write!(f, "{}", v),
|
||||||
Value::Regex(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::Function(v) => write!(f, "{}", v),
|
||||||
Value::Subquery(v) => write!(f, "{}", v),
|
Value::Subquery(v) => write!(f, "{}", v),
|
||||||
Value::Expression(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::Null => s.serialize_unit_variant("Value", 2, "Null"),
|
||||||
Value::True => s.serialize_unit_variant("Value", 3, "True"),
|
Value::True => s.serialize_unit_variant("Value", 3, "True"),
|
||||||
Value::False => s.serialize_unit_variant("Value", 4, "False"),
|
Value::False => s.serialize_unit_variant("Value", 4, "False"),
|
||||||
Value::Param(v) => s.serialize_newtype_variant("Value", 5, "Param", v),
|
Value::Number(v) => s.serialize_newtype_variant("Value", 5, "Number", v),
|
||||||
Value::Idiom(v) => s.serialize_newtype_variant("Value", 6, "Idiom", v),
|
Value::Strand(v) => s.serialize_newtype_variant("Value", 6, "Strand", v),
|
||||||
Value::Table(v) => s.serialize_newtype_variant("Value", 7, "Table", v),
|
Value::Duration(v) => s.serialize_newtype_variant("Value", 7, "Duration", v),
|
||||||
Value::Thing(v) => s.serialize_newtype_variant("Value", 8, "Thing", v),
|
Value::Datetime(v) => s.serialize_newtype_variant("Value", 8, "Datetime", v),
|
||||||
Value::Model(v) => s.serialize_newtype_variant("Value", 9, "Model", v),
|
Value::Array(v) => s.serialize_newtype_variant("Value", 9, "Array", v),
|
||||||
Value::Regex(v) => s.serialize_newtype_variant("Value", 10, "Regex", v),
|
Value::Object(v) => s.serialize_newtype_variant("Value", 10, "Object", v),
|
||||||
Value::Array(v) => s.serialize_newtype_variant("Value", 11, "Array", v),
|
Value::Geometry(v) => s.serialize_newtype_variant("Value", 11, "Geometry", v),
|
||||||
Value::Object(v) => s.serialize_newtype_variant("Value", 12, "Object", v),
|
Value::Param(v) => s.serialize_newtype_variant("Value", 12, "Param", v),
|
||||||
Value::Number(v) => s.serialize_newtype_variant("Value", 13, "Number", v),
|
Value::Idiom(v) => s.serialize_newtype_variant("Value", 13, "Idiom", v),
|
||||||
Value::Strand(v) => s.serialize_newtype_variant("Value", 14, "Strand", v),
|
Value::Table(v) => s.serialize_newtype_variant("Value", 14, "Table", v),
|
||||||
Value::Geometry(v) => s.serialize_newtype_variant("Value", 15, "Geometry", v),
|
Value::Thing(v) => s.serialize_newtype_variant("Value", 15, "Thing", v),
|
||||||
Value::Duration(v) => s.serialize_newtype_variant("Value", 16, "Duration", v),
|
Value::Model(v) => s.serialize_newtype_variant("Value", 16, "Model", v),
|
||||||
Value::Datetime(v) => s.serialize_newtype_variant("Value", 17, "Datetime", v),
|
Value::Regex(v) => s.serialize_newtype_variant("Value", 17, "Regex", v),
|
||||||
Value::Function(v) => s.serialize_newtype_variant("Value", 18, "Function", v),
|
Value::Function(v) => s.serialize_newtype_variant("Value", 18, "Function", v),
|
||||||
Value::Subquery(v) => s.serialize_newtype_variant("Value", 19, "Subquery", v),
|
Value::Subquery(v) => s.serialize_newtype_variant("Value", 19, "Subquery", v),
|
||||||
Value::Expression(v) => s.serialize_newtype_variant("Value", 20, "Expression", v),
|
Value::Expression(v) => s.serialize_newtype_variant("Value", 20, "Expression", v),
|
||||||
|
|
Loading…
Reference in a new issue