Query planner to support mixed number types on range queries (#4747)

This commit is contained in:
Emmanuel Keller 2024-09-16 16:49:22 +01:00 committed by GitHub
parent 0fe9807096
commit 3d505697de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 863 additions and 70 deletions

View file

@ -12,9 +12,10 @@ use crate::idx::ft::{FtIndex, MatchRef};
use crate::idx::planner::checker::{HnswConditionChecker, MTreeConditionChecker};
use crate::idx::planner::iterators::{
IndexEqualThingIterator, IndexJoinThingIterator, IndexRangeThingIterator,
IndexUnionThingIterator, IteratorRecord, IteratorRef, KnnIterator, KnnIteratorResult,
MatchesThingIterator, MultipleIterators, ThingIterator, UniqueEqualThingIterator,
UniqueJoinThingIterator, UniqueRangeThingIterator, UniqueUnionThingIterator,
IndexUnionThingIterator, IteratorRange, IteratorRecord, IteratorRef, KnnIterator,
KnnIteratorResult, MatchesThingIterator, MultipleIterators, ThingIterator,
UniqueEqualThingIterator, UniqueJoinThingIterator, UniqueRangeThingIterator,
UniqueUnionThingIterator, ValueType,
};
use crate::idx::planner::knn::{KnnBruteForceResult, KnnPriorityList};
use crate::idx::planner::plan::IndexOperator::Matches;
@ -391,7 +392,7 @@ impl QueryExecutor {
Ok(match io.op() {
IndexOperator::Equality(value) | IndexOperator::Exactness(value) => {
if let Value::Number(n) = value.as_ref() {
let values = Self::get_number_variants(n);
let values = Self::get_equal_number_variants(n);
if values.len() == 1 {
Some(Self::new_index_equal_iterator(irf, opt, &ix, &values[0])?)
} else {
@ -444,47 +445,224 @@ impl QueryExecutor {
Ok(ThingIterator::Multiples(Box::new(MultipleIterators::new(iterators))))
}
/// This function takes a reference to a `Number` enum and returns a vector of `Value` enum.
/// The `Number` enum can be either an `Int`, `Float`, or `Decimal`.
/// The function first initializes an empty vector with a capacity of 3 to store the converted values.
/// It then matches on the input number and performs the appropriate conversions.
/// For `Int`, it pushes the original `Int` value, the equivalent `Float` value, and if possible, the equivalent `Decimal` value.
/// For `Float`, it pushes the original `Float` value, the truncated `Int` value if it is a whole number, and if possible, the equivalent `Decimal` value.
/// For `Decimal`, it pushes the equivalent `Int` value if it is representable as an `i64`, and the equivalent `Float` value if it is representable as an `f64`.
/// Finally, it returns the vector of converted values.
fn get_number_variants(n: &Number) -> Vec<Value> {
let mut values = Vec::with_capacity(3);
/// This function takes a reference to a `Number` enum and a conversion function `float_to_int`.
/// It returns a tuple containing the variants of the `Number` as `Option<i64>`, `Option<f64>`, and `Option<Decimal>`.
///
/// The `Number` enum can be one of the following:
/// - `Int(i64)`: Integer value.
/// - `Float(f64)`: Floating point value.
/// - `Decimal(Decimal)`: Decimal value.
///
/// The function performs the following conversions based on the type of the `Number`:
/// - For `Int`, it returns the original `Int` value as `Option<i64>`, the equivalent `Float` value as `Option<f64>`, and the equivalent `Decimal` value as `Option<Decimal>`.
/// - For `Float`, it uses the provided `float_to_int` function to convert the `Float` to `Option<i64>`, returns the original `Float` value as `Option<f64>`, and the equivalent `Decimal` value as `Option<Decimal>`.
/// - For `Decimal`, it converts the `Decimal` to `Option<i64>` (if representable as `i64`), returns the equivalent `Float` value as `Option<f64>` (if representable as `f64`), and the original `Decimal` value as `Option<Decimal>`.
///
/// # Parameters
/// - `n`: A reference to a `Number` enum.
/// - `float_to_int`: A function that converts a reference to `f64` to `Option<i64>`.
///
/// # Returns
/// A tuple of `(Option<i64>, Option<f64>, Option<Decimal>)` representing the converted variants of the input `Number`.
fn get_number_variants<F>(
n: &Number,
float_to_int: F,
) -> (Option<i64>, Option<f64>, Option<Decimal>)
where
F: Fn(&f64) -> Option<i64>,
{
let oi;
let of;
let od;
match n {
Number::Int(i) => {
values.push(Number::Int(*i).into());
values.push(Number::Float(*i as f64).into());
if let Some(d) = Decimal::from_i64(*i) {
values.push(Number::Decimal(d.normalize()).into());
}
oi = Some(*i);
of = Some(*i as f64);
od = Decimal::from_i64(*i);
}
Number::Float(f) => {
values.push(Number::Float(*f).into());
if f.trunc().eq(f) {
values.push(Number::Int(*f as i64).into());
}
if let Some(d) = Decimal::from_f64(*f) {
values.push(Number::Decimal(d.normalize()).into());
}
oi = float_to_int(f);
of = Some(*f);
od = Decimal::from_f64(*f);
}
Number::Decimal(d) => {
values.push(Number::Decimal(d.normalize()).into());
if let Some(i) = d.to_i64() {
values.push(Number::Int(i).into());
}
if let Some(f) = d.to_f64() {
values.push(Number::Float(f).into());
}
oi = d.to_i64();
of = d.to_f64();
od = Some(*d);
}
};
println!("VALUES: {:?}", values);
(oi, of, od)
}
fn get_equal_number_variants(n: &Number) -> Vec<Value> {
let (oi, of, od) = Self::get_number_variants(n, |f| {
if f.trunc().eq(f) {
f.to_i64()
} else {
None
}
});
let mut values = Vec::with_capacity(3);
if let Some(i) = oi {
values.push(Number::Int(i).into());
}
if let Some(f) = of {
values.push(Number::Float(f).into());
}
if let Some(d) = od {
values.push(Number::Decimal(d).into());
}
values
}
fn get_range_number_from_variants(n: &Number) -> (Option<i64>, Option<f64>, Option<Decimal>) {
Self::get_number_variants(n, |f| f.floor().to_i64())
}
fn get_range_number_to_variants(n: &Number) -> (Option<i64>, Option<f64>, Option<Decimal>) {
Self::get_number_variants(n, |f| f.ceil().to_i64())
}
fn get_from_range_number_variants<'a>(from: &Number, from_inc: bool) -> Vec<IteratorRange<'a>> {
let (from_i, from_f, from_d) = Self::get_range_number_from_variants(from);
let mut vec = Vec::with_capacity(3);
if let Some(from) = from_i {
vec.push(IteratorRange::new(
ValueType::NumberInt,
RangeValue {
value: Number::Int(from).into(),
inclusive: from_inc,
},
RangeValue {
value: Value::None,
inclusive: false,
},
));
}
if let Some(from) = from_f {
vec.push(IteratorRange::new(
ValueType::NumberFloat,
RangeValue {
value: Number::Float(from).into(),
inclusive: from_inc,
},
RangeValue {
value: Value::None,
inclusive: false,
},
));
}
if let Some(from) = from_d {
vec.push(IteratorRange::new(
ValueType::NumberDecimal,
RangeValue {
value: Number::Decimal(from).into(),
inclusive: from_inc,
},
RangeValue {
value: Value::None,
inclusive: false,
},
));
}
vec
}
fn get_to_range_number_variants<'a>(to: &Number, to_inc: bool) -> Vec<IteratorRange<'a>> {
let (from_i, from_f, from_d) = Self::get_range_number_to_variants(to);
let mut vec = Vec::with_capacity(3);
if let Some(to) = from_i {
vec.push(IteratorRange::new(
ValueType::NumberInt,
RangeValue {
value: Value::None,
inclusive: false,
},
RangeValue {
value: Number::Int(to).into(),
inclusive: to_inc,
},
));
}
if let Some(to) = from_f {
vec.push(IteratorRange::new(
ValueType::NumberFloat,
RangeValue {
value: Value::None,
inclusive: false,
},
RangeValue {
value: Number::Float(to).into(),
inclusive: to_inc,
},
));
}
if let Some(to) = from_d {
vec.push(IteratorRange::new(
ValueType::NumberDecimal,
RangeValue {
value: Value::None,
inclusive: false,
},
RangeValue {
value: Number::Decimal(to).into(),
inclusive: to_inc,
},
));
}
vec
}
fn get_ranges_number_variants<'a>(
from: &Number,
from_inc: bool,
to: &Number,
to_inc: bool,
) -> Vec<IteratorRange<'a>> {
let (from_i, from_f, from_d) = Self::get_range_number_from_variants(from);
let (to_i, to_f, to_d) = Self::get_range_number_to_variants(to);
let mut vec = Vec::with_capacity(3);
if let (Some(from), Some(to)) = (from_i, to_i) {
vec.push(IteratorRange::new(
ValueType::NumberInt,
RangeValue {
value: Number::Int(from).into(),
inclusive: from_inc,
},
RangeValue {
value: Number::Int(to).into(),
inclusive: to_inc,
},
));
}
if let (Some(from), Some(to)) = (from_f, to_f) {
vec.push(IteratorRange::new(
ValueType::NumberFloat,
RangeValue {
value: Number::Float(from).into(),
inclusive: from_inc,
},
RangeValue {
value: Number::Float(to).into(),
inclusive: to_inc,
},
));
}
if let (Some(from), Some(to)) = (from_d, to_d) {
vec.push(IteratorRange::new(
ValueType::NumberDecimal,
RangeValue {
value: Number::Decimal(from).into(),
inclusive: from_inc,
},
RangeValue {
value: Number::Decimal(to).into(),
inclusive: to_inc,
},
));
}
vec
}
fn new_range_iterator(
&self,
opt: &Options,
@ -495,26 +673,44 @@ impl QueryExecutor {
if let Some(ix) = self.get_index_def(ir) {
match ix.index {
Index::Idx => {
return Ok(Some(ThingIterator::IndexRange(IndexRangeThingIterator::new(
let ranges = Self::get_ranges_variants(from, to);
if let Some(ranges) = ranges {
if ranges.len() == 1 {
return Ok(Some(Self::new_index_range_iterator(
ir, opt, ix, &ranges[0],
)?));
} else {
return Ok(Some(Self::new_multiple_index_range_iterator(
ir, opt, ix, &ranges,
)?));
}
}
return Ok(Some(Self::new_index_range_iterator(
ir,
opt.ns()?,
opt.db()?,
&ix.what,
&ix.name,
from,
to,
))))
opt,
ix,
&IteratorRange::new_ref(ValueType::None, from, to),
)?));
}
Index::Uniq => {
return Ok(Some(ThingIterator::UniqueRange(UniqueRangeThingIterator::new(
let ranges = Self::get_ranges_variants(from, to);
if let Some(ranges) = ranges {
if ranges.len() == 1 {
return Ok(Some(Self::new_unique_range_iterator(
ir, opt, ix, &ranges[0],
)?));
} else {
return Ok(Some(Self::new_multiple_unique_range_iterator(
ir, opt, ix, &ranges,
)?));
}
}
return Ok(Some(Self::new_unique_range_iterator(
ir,
opt.ns()?,
opt.db()?,
&ix.what,
&ix.name,
from,
to,
))))
opt,
ix,
&IteratorRange::new_ref(ValueType::None, from, to),
)?));
}
_ => {}
}
@ -522,6 +718,82 @@ impl QueryExecutor {
Ok(None)
}
fn get_ranges_variants<'a>(
from: &'a RangeValue,
to: &'a RangeValue,
) -> Option<Vec<IteratorRange<'a>>> {
match (&from.value, &to.value) {
(Value::Number(from_n), Value::Number(to_n)) => {
Some(Self::get_ranges_number_variants(from_n, from.inclusive, to_n, to.inclusive))
}
(Value::Number(from_n), Value::None) => {
Some(Self::get_from_range_number_variants(from_n, from.inclusive))
}
(Value::None, Value::Number(to_n)) => {
Some(Self::get_to_range_number_variants(to_n, to.inclusive))
}
_ => None,
}
}
fn new_index_range_iterator(
ir: IndexRef,
opt: &Options,
ix: &DefineIndexStatement,
range: &IteratorRange,
) -> Result<ThingIterator, Error> {
Ok(ThingIterator::IndexRange(IndexRangeThingIterator::new(
ir,
opt.ns()?,
opt.db()?,
&ix.what,
&ix.name,
range,
)))
}
fn new_unique_range_iterator(
ir: IndexRef,
opt: &Options,
ix: &DefineIndexStatement,
range: &IteratorRange<'_>,
) -> Result<ThingIterator, Error> {
Ok(ThingIterator::UniqueRange(UniqueRangeThingIterator::new(
ir,
opt.ns()?,
opt.db()?,
&ix.what,
&ix.name,
range,
)))
}
fn new_multiple_index_range_iterator(
ir: IndexRef,
opt: &Options,
ix: &DefineIndexStatement,
ranges: &[IteratorRange],
) -> Result<ThingIterator, Error> {
let mut iterators = VecDeque::with_capacity(ranges.len());
for range in ranges {
iterators.push_back(Self::new_index_range_iterator(ir, opt, ix, range)?);
}
Ok(ThingIterator::Multiples(Box::new(MultipleIterators::new(iterators))))
}
fn new_multiple_unique_range_iterator(
ir: IndexRef,
opt: &Options,
ix: &DefineIndexStatement,
ranges: &[IteratorRange<'_>],
) -> Result<ThingIterator, Error> {
let mut iterators = VecDeque::with_capacity(ranges.len());
for range in ranges {
iterators.push_back(Self::new_unique_range_iterator(ir, opt, ix, range)?);
}
Ok(ThingIterator::Multiples(Box::new(MultipleIterators::new(iterators))))
}
async fn new_unique_index_iterator(
&self,
opt: &Options,
@ -532,7 +804,7 @@ impl QueryExecutor {
Ok(match io.op() {
IndexOperator::Equality(value) | IndexOperator::Exactness(value) => {
if let Value::Number(n) = value.as_ref() {
let values = Self::get_number_variants(n);
let values = Self::get_equal_number_variants(n);
if values.len() == 1 {
Some(Self::new_unique_equal_iterator(irf, opt, ix, &values[0])?)
} else {

View file

@ -9,8 +9,10 @@ use crate::key::index::Index;
use crate::kvs::Key;
use crate::kvs::Transaction;
use crate::sql::statements::DefineIndexStatement;
use crate::sql::{Array, Ident, Thing, Value};
use crate::sql::{Array, Ident, Number, Thing, Value};
use radix_trie::Trie;
use rust_decimal::Decimal;
use std::borrow::Cow;
use std::collections::VecDeque;
use std::sync::Arc;
@ -247,6 +249,96 @@ impl RangeScan {
}
}
pub(super) struct IteratorRange<'a> {
value_type: ValueType,
from: Cow<'a, RangeValue>,
to: Cow<'a, RangeValue>,
}
impl<'a> IteratorRange<'a> {
pub(super) fn new(t: ValueType, from: RangeValue, to: RangeValue) -> Self {
IteratorRange {
value_type: t,
from: Cow::Owned(from),
to: Cow::Owned(to),
}
}
pub(super) fn new_ref(t: ValueType, from: &'a RangeValue, to: &'a RangeValue) -> Self {
IteratorRange {
value_type: t,
from: Cow::Borrowed(from),
to: Cow::Borrowed(to),
}
}
}
// When we know the type of the range values, we have the opportunity
// to restrict the key range to the exact prefixes according to the type.
#[derive(Copy, Clone)]
pub(super) enum ValueType {
None,
NumberInt,
NumberFloat,
NumberDecimal,
}
impl ValueType {
fn prefix_beg(&self, ns: &str, db: &str, ix_what: &Ident, ix_name: &Ident) -> Vec<u8> {
match self {
Self::None => Index::prefix_beg(ns, db, ix_what, ix_name),
Self::NumberInt => Index::prefix_ids_beg(
ns,
db,
ix_what,
ix_name,
&Array(vec![Value::Number(Number::Int(i64::MIN))]),
),
Self::NumberFloat => Index::prefix_ids_beg(
ns,
db,
ix_what,
ix_name,
&Array(vec![Value::Number(Number::Float(f64::MIN))]),
),
Self::NumberDecimal => Index::prefix_ids_beg(
ns,
db,
ix_what,
ix_name,
&Array(vec![Value::Number(Number::Decimal(Decimal::MIN))]),
),
}
}
fn prefix_end(&self, ns: &str, db: &str, ix_what: &Ident, ix_name: &Ident) -> Vec<u8> {
match self {
Self::None => Index::prefix_end(ns, db, ix_what, ix_name),
Self::NumberInt => Index::prefix_ids_end(
ns,
db,
ix_what,
ix_name,
&Array(vec![Value::Number(Number::Int(i64::MAX))]),
),
Self::NumberFloat => Index::prefix_ids_end(
ns,
db,
ix_what,
ix_name,
&Array(vec![Value::Number(Number::Float(f64::MAX))]),
),
Self::NumberDecimal => Index::prefix_ids_end(
ns,
db,
ix_what,
ix_name,
&Array(vec![Value::Number(Number::Decimal(Decimal::MAX))]),
),
}
}
}
pub(crate) struct IndexRangeThingIterator {
irf: IteratorRef,
r: RangeScan,
@ -259,14 +351,13 @@ impl IndexRangeThingIterator {
db: &str,
ix_what: &Ident,
ix_name: &Ident,
from: &RangeValue,
to: &RangeValue,
range: &IteratorRange<'_>,
) -> Self {
let beg = Self::compute_beg(ns, db, ix_what, ix_name, from);
let end = Self::compute_end(ns, db, ix_what, ix_name, to);
let beg = Self::compute_beg(ns, db, ix_what, ix_name, &range.from, range.value_type);
let end = Self::compute_end(ns, db, ix_what, ix_name, &range.to, range.value_type);
Self {
irf,
r: RangeScan::new(beg, from.inclusive, end, to.inclusive),
r: RangeScan::new(beg, range.from.inclusive, end, range.to.inclusive),
}
}
@ -281,7 +372,12 @@ impl IndexRangeThingIterator {
value: Value::None,
inclusive: true,
};
Self::new(irf, ns, db, ix_what, ix_name, &full_range, &full_range)
let range = IteratorRange {
value_type: ValueType::None,
from: Cow::Borrowed(&full_range),
to: Cow::Borrowed(&full_range),
};
Self::new(irf, ns, db, ix_what, ix_name, &range)
}
fn compute_beg(
@ -290,9 +386,10 @@ impl IndexRangeThingIterator {
ix_what: &Ident,
ix_name: &Ident,
from: &RangeValue,
value_type: ValueType,
) -> Vec<u8> {
if from.value == Value::None {
return Index::prefix_beg(ns, db, ix_what, ix_name);
return value_type.prefix_beg(ns, db, ix_what, ix_name);
}
let fd = Array::from(from.value.to_owned());
if from.inclusive {
@ -308,9 +405,10 @@ impl IndexRangeThingIterator {
ix_what: &Ident,
ix_name: &Ident,
to: &RangeValue,
value_type: ValueType,
) -> Vec<u8> {
if to.value == Value::None {
return Index::prefix_end(ns, db, ix_what, ix_name);
return value_type.prefix_end(ns, db, ix_what, ix_name);
}
let fd = Array::from(to.value.to_owned());
if to.inclusive {
@ -571,14 +669,13 @@ impl UniqueRangeThingIterator {
db: &str,
ix_what: &Ident,
ix_name: &Ident,
from: &RangeValue,
to: &RangeValue,
range: &IteratorRange<'_>,
) -> Self {
let beg = Self::compute_beg(ns, db, ix_what, ix_name, from);
let end = Self::compute_end(ns, db, ix_what, ix_name, to);
let beg = Self::compute_beg(ns, db, ix_what, ix_name, &range.from, range.value_type);
let end = Self::compute_end(ns, db, ix_what, ix_name, &range.to, range.value_type);
Self {
irf,
r: RangeScan::new(beg, from.inclusive, end, to.inclusive),
r: RangeScan::new(beg, range.from.inclusive, end, range.to.inclusive),
done: false,
}
}
@ -589,11 +686,16 @@ impl UniqueRangeThingIterator {
db: &str,
ix: &DefineIndexStatement,
) -> Self {
let full_range = RangeValue {
let value = RangeValue {
value: Value::None,
inclusive: true,
};
Self::new(irf, ns, db, &ix.what, &ix.name, &full_range, &full_range)
let range = IteratorRange {
value_type: ValueType::None,
from: Cow::Borrowed(&value),
to: Cow::Borrowed(&value),
};
Self::new(irf, ns, db, &ix.what, &ix.name, &range)
}
fn compute_beg(
@ -602,9 +704,10 @@ impl UniqueRangeThingIterator {
ix_what: &Ident,
ix_name: &Ident,
from: &RangeValue,
value_type: ValueType,
) -> Vec<u8> {
if from.value == Value::None {
return Index::prefix_beg(ns, db, ix_what, ix_name);
return value_type.prefix_beg(ns, db, ix_what, ix_name);
}
Index::new(ns, db, ix_what, ix_name, &Array::from(from.value.to_owned()), None)
.encode()
@ -617,9 +720,10 @@ impl UniqueRangeThingIterator {
ix_what: &Ident,
ix_name: &Ident,
to: &RangeValue,
value_type: ValueType,
) -> Vec<u8> {
if to.value == Value::None {
return Index::prefix_end(ns, db, ix_what, ix_name);
return value_type.prefix_end(ns, db, ix_what, ix_name);
}
Index::new(ns, db, ix_what, ix_name, &Array::from(to.value.to_owned()), None)
.encode()

View file

@ -298,7 +298,7 @@ impl IndexOption {
}
}
#[derive(Debug, Default, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
pub(super) struct RangeValue {
pub(super) value: Value,
pub(super) inclusive: bool,

View file

@ -797,6 +797,423 @@ async fn define_statement_unique_index_numbers() -> Result<(), Error> {
Ok(())
}
fn check_range(t: &mut Test, result: &str, explain: &str) -> Result<(), Error> {
for _ in 0..2 {
t.expect_val(result)?;
}
t.expect_val(explain)?;
Ok(())
}
async fn define_statement_mixed_numbers_range(unique: bool) -> Result<(), Error> {
let sql = format!(
"
DEFINE INDEX index ON TABLE test COLUMNS number {};
CREATE test:int0 SET number = 0;
CREATE test:float0 SET number = 0.5;
CREATE test:int1 SET number = 1;
CREATE test:float1 SET number = 1.5;
CREATE test:int2 SET number = 2;
SELECT * FROM test WITH NOINDEX WHERE number > 0 AND number < 2 ORDER BY id;
SELECT * FROM test WHERE number > 0 AND number < 2 ORDER BY id;
SELECT * FROM test WHERE number > 0 AND number < 2 ORDER BY id EXPLAIN;
SELECT * FROM test WITH NOINDEX WHERE number > 0.1 AND number < 2 ORDER BY id;
SELECT * FROM test WHERE number > 0.1 AND number < 2 ORDER BY id;
SELECT * FROM test WHERE number > 0.1 AND number < 2 ORDER BY id EXPLAIN;
SELECT * FROM test WITH NOINDEX WHERE number > 0.1 AND number < 1.5 ORDER BY id;
SELECT * FROM test WHERE number > 0.1 AND number < 1.5 ORDER BY id;
SELECT * FROM test WHERE number > 0.1 AND number < 1.5 ORDER BY id EXPLAIN;
SELECT * FROM test WITH NOINDEX WHERE number > 0 AND number < 1.5 ORDER BY id;
SELECT * FROM test WHERE number > 0 AND number < 1.5 ORDER BY id;
SELECT * FROM test WHERE number > 0 AND number < 1.5 ORDER BY id EXPLAIN;
SELECT * FROM test WITH NOINDEX WHERE number > 0.1 ORDER BY id;
SELECT * FROM test WHERE number > 0.1 ORDER BY id;
SELECT * FROM test WHERE number > 0.1 ORDER BY id EXPLAIN;
SELECT * FROM test WITH NOINDEX WHERE number > 0 ORDER BY id;
SELECT * FROM test WHERE number > 0 ORDER BY id;
SELECT * FROM test WHERE number > 0 ORDER BY id EXPLAIN;
SELECT * FROM test WITH NOINDEX WHERE number < 2 ORDER BY id;
SELECT * FROM test WHERE number < 2 ORDER BY id;
SELECT * FROM test WHERE number < 2 ORDER BY id EXPLAIN;
SELECT * FROM test WITH NOINDEX WHERE number < 1.9 ORDER BY id;
SELECT * FROM test WHERE number < 1.9 ORDER BY id;
SELECT * FROM test WHERE number < 1.9 ORDER BY id EXPLAIN;
",
if unique {
"UNIQUE"
} else {
""
}
);
let mut t = Test::new(&sql).await?;
t.expect_size(30)?;
t.skip_ok(6)?;
// number > 0 AND number < 2
check_range(
&mut t,
"[
{
id: test:float0,
number: 0.5f
},
{
id: test:float1,
number: 1.5f
},
{
id: test:int1,
number: 1
}
]",
"[
{
detail: {
plan: {
from: {
inclusive: false,
value: 0
},
index: 'index',
to: {
inclusive: false,
value: 2
}
},
table: 'test'
},
operation: 'Iterate Index'
},
{
detail: {
type: 'Memory'
},
operation: 'Collector'
}
]",
)?;
// number > 0.1 AND number < 2
check_range(
&mut t,
"[
{
id: test:float0,
number: 0.5f
},
{
id: test:float1,
number: 1.5f
},
{
id: test:int1,
number: 1
}
]",
"[
{
detail: {
plan: {
from: {
inclusive: false,
value: 0.1f
},
index: 'index',
to: {
inclusive: false,
value: 2
}
},
table: 'test'
},
operation: 'Iterate Index'
},
{
detail: {
type: 'Memory'
},
operation: 'Collector'
}
]",
)?;
// number > 0.1 AND number < 1.5
check_range(
&mut t,
"[
{
id: test:float0,
number: 0.5f
},
{
id: test:int1,
number: 1
}
]",
"[
{
detail: {
plan: {
from: {
inclusive: false,
value: 0.1f
},
index: 'index',
to: {
inclusive: false,
value: 1.5f
}
},
table: 'test'
},
operation: 'Iterate Index'
},
{
detail: {
type: 'Memory'
},
operation: 'Collector'
}
]",
)?;
// number > 0 AND number < 1.5
check_range(
&mut t,
"[
{
id: test:float0,
number: 0.5f
},
{
id: test:int1,
number: 1
}
]",
"[
{
detail: {
plan: {
from: {
inclusive: false,
value: 0
},
index: 'index',
to: {
inclusive: false,
value: 1.5f
}
},
table: 'test'
},
operation: 'Iterate Index'
},
{
detail: {
type: 'Memory'
},
operation: 'Collector'
}
]",
)?;
// number > 0.1
check_range(
&mut t,
"[
{
id: test:float0,
number: 0.5f
},
{
id: test:float1,
number: 1.5f
},
{
id: test:int1,
number: 1
},
{
id: test:int2,
number: 2
}
]",
"[
{
detail: {
plan: {
from: {
inclusive: false,
value: 0.1f
},
index: 'index',
to: {
inclusive: false,
value: NONE
}
},
table: 'test'
},
operation: 'Iterate Index'
},
{
detail: {
type: 'Memory'
},
operation: 'Collector'
}
]",
)?;
// number > 0
check_range(
&mut t,
"[
{
id: test:float0,
number: 0.5f
},
{
id: test:float1,
number: 1.5f
},
{
id: test:int1,
number: 1
},
{
id: test:int2,
number: 2
}
]",
"[
{
detail: {
plan: {
from: {
inclusive: false,
value: 0
},
index: 'index',
to: {
inclusive: false,
value: NONE
}
},
table: 'test'
},
operation: 'Iterate Index'
},
{
detail: {
type: 'Memory'
},
operation: 'Collector'
}
]",
)?;
// number < 2
check_range(
&mut t,
"[
{
id: test:float0,
number: 0.5f
},
{
id: test:float1,
number: 1.5f
},
{
id: test:int0,
number: 0
},
{
id: test:int1,
number: 1
}
]",
"[
{
detail: {
plan: {
from: {
inclusive: false,
value: NONE
},
index: 'index',
to: {
inclusive: false,
value: 2
}
},
table: 'test'
},
operation: 'Iterate Index'
},
{
detail: {
type: 'Memory'
},
operation: 'Collector'
}
]",
)?;
// number < 1.9
check_range(
&mut t,
"[
{
id: test:float0,
number: 0.5f
},
{
id: test:float1,
number: 1.5f
},
{
id: test:int0,
number: 0
},
{
id: test:int1,
number: 1
}
]",
"[
{
detail: {
plan: {
from: {
inclusive: false,
value: NONE
},
index: 'index',
to: {
inclusive: false,
value: 1.9f
}
},
table: 'test'
},
operation: 'Iterate Index'
},
{
detail: {
type: 'Memory'
},
operation: 'Collector'
}
]",
)?;
//
Ok(())
}
#[tokio::test]
async fn define_statement_index_mixed_numbers_range() -> Result<(), Error> {
define_statement_mixed_numbers_range(false).await
}
#[tokio::test]
async fn define_statement_unique_mixed_numbers_range() -> Result<(), Error> {
define_statement_mixed_numbers_range(true).await
}
#[tokio::test]
async fn define_statement_index_concurrently() -> Result<(), Error> {
let sql = "