Skip to content

Commit

Permalink
Support text and real data comparison.
Browse files Browse the repository at this point in the history
This now supports Eq or Ne comparison only.
  • Loading branch information
kawasin73 committed Aug 8, 2023
1 parent acafa4a commit f2a23e1
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 67 deletions.
67 changes: 36 additions & 31 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl Connection {
}) = &selection
{
if let Selection::Column(column_number) = left.as_ref() {
if let Selection::LiteralValue(key) = right.as_ref() {
if let Selection::Integer(key) = right.as_ref() {
let mut next_index = table.indexes.as_ref();
while let Some(index) = next_index {
if index.columns[0] == *column_number {
Expand Down Expand Up @@ -217,16 +217,17 @@ enum Selection {
left: Box<Selection>,
right: Box<Selection>,
},
LiteralValue(i64),
Integer(i64),
Real(f64),
Text(Vec<u8>),
}

impl Selection {
fn from(expr: Expr, table: &Table) -> anyhow::Result<Self> {
match expr {
Expr::LiteralValue(value) => match value {
Value::Integer(i) => Ok(Self::LiteralValue(i)),
_ => bail!("unsupported literal value: {:?}", value),
},
Expr::Integer(i) => Ok(Self::Integer(i)),
Expr::Real(f) => Ok(Self::Real(f)),
Expr::Text(text) => Ok(Self::Text(text.dequote())),
Expr::BinaryOperator {
operator,
left,
Expand All @@ -249,37 +250,45 @@ impl Selection {
}
}

fn execute(&self, row: &RowRef) -> anyhow::Result<i64> {
fn execute<'a>(&'a self, row: &'a RowRef) -> anyhow::Result<Value<'a>> {
match self {
Self::Column(idx) => {
let value = row.get(idx)?;
match value {
Value::Integer(i) => Ok(i),
_ => bail!("invalid value for selection: {:?}", value),
}
}
Self::Column(idx) => row.get(idx),
Self::BinaryOperator {
operator,
left,
right,
} => {
let left = left.execute(row)?;
let right = right.execute(row)?;
let result = match operator {
BinaryOperator::Eq => left == right,
BinaryOperator::Ne => left != right,
BinaryOperator::Lt => left < right,
BinaryOperator::Le => left <= right,
BinaryOperator::Gt => left > right,
BinaryOperator::Ge => left >= right,
let result = if operator == &BinaryOperator::Eq {
left == right
} else if operator == &BinaryOperator::Ne {
left != right
} else {
let Value::Integer(left) = left else {
bail!("invalid value for selection: {:?}", left);
};
let Value::Integer(right) = right else {
bail!("invalid value for selection: {:?}", left);
};
match operator {
BinaryOperator::Eq => left == right,
BinaryOperator::Ne => left != right,
BinaryOperator::Lt => left < right,
BinaryOperator::Le => left <= right,
BinaryOperator::Gt => left > right,
BinaryOperator::Ge => left >= right,
}
};
if result {
Ok(1)
Ok(Value::Integer(1))
} else {
Ok(0)
Ok(Value::Integer(0))
}
}
Self::LiteralValue(value) => Ok(*value),
Self::Integer(value) => Ok(Value::Integer(*value)),
Self::Real(value) => Ok(Value::Real(*value)),
Self::Text(value) => Ok(Value::Text(value)),
}
}
}
Expand Down Expand Up @@ -307,12 +316,8 @@ impl<'conn> Statement<'conn> {
left,
right,
}) => match (left.as_ref(), right.as_ref()) {
(Selection::Column(ColumnNumber::RowId), Selection::LiteralValue(value)) => {
Some(*value)
}
(Selection::LiteralValue(value), Selection::Column(ColumnNumber::RowId)) => {
Some(*value)
}
(Selection::Column(ColumnNumber::RowId), Selection::Integer(value)) => Some(*value),
(Selection::Integer(value), Selection::Column(ColumnNumber::RowId)) => Some(*value),
_ => None,
},
_ => None,
Expand Down Expand Up @@ -432,7 +437,7 @@ impl<'conn> Rows<'conn> {
use_local_buffer,
content_offset,
};
if selection.execute(&column_value_loader)? == 0 {
if selection.execute(&column_value_loader)? == Value::Integer(0) {
continue;
}
}
Expand Down
57 changes: 21 additions & 36 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use crate::utils::parse_float_literal;
use crate::utils::parse_integer_literal;
use crate::utils::CaseInsensitiveBytes;
use crate::utils::MaybeQuotedBytes;
use crate::value::Value;

pub type Error = &'static str;
pub type Result<T> = std::result::Result<T, Error>;
Expand Down Expand Up @@ -331,7 +330,9 @@ pub enum Expr<'a> {
left: Box<Expr<'a>>,
right: Box<Expr<'a>>,
},
LiteralValue(Value<'a>),
Integer(i64),
Real(f64),
Text(MaybeQuotedBytes<'a>),
}

fn parse_expr(input: &[u8]) -> Result<(usize, Expr)> {
Expand All @@ -341,14 +342,13 @@ fn parse_expr(input: &[u8]) -> Result<(usize, Expr)> {
Some((n, Token::Integer(buf))) => {
let v = parse_integer_literal(buf);
if v < 0 {
(n, Expr::LiteralValue(Value::Real(parse_float_literal(buf))))
(n, Expr::Real(parse_float_literal(buf)))
} else {
(n, Expr::LiteralValue(Value::Integer(v)))
(n, Expr::Integer(v))
}
}
Some((n, Token::Float(buf))) => {
(n, Expr::LiteralValue(Value::Real(parse_float_literal(buf))))
}
Some((n, Token::Float(buf))) => (n, Expr::Real(parse_float_literal(buf))),
Some((n, Token::String(text))) => (n, Expr::Text(text)),
_ => return Err("no expr"),
};
let input = &input[n..];
Expand Down Expand Up @@ -545,7 +545,7 @@ mod tests {
Expr::BinaryOperator {
operator: BinaryOperator::Eq,
left: Box::new(Expr::Column(b"id".as_slice().into())),
right: Box::new(Expr::LiteralValue(Value::Integer(5))),
right: Box::new(Expr::Integer(5)),
}
);
}
Expand All @@ -562,58 +562,43 @@ mod tests {
// Parse integer
assert_eq!(
parse_expr(b"123456789a").unwrap(),
(9, Expr::LiteralValue(Value::Integer(123456789)))
(9, Expr::Integer(123456789))
);
assert_eq!(
parse_expr(b"00123456789a").unwrap(),
(11, Expr::LiteralValue(Value::Integer(123456789)))
(11, Expr::Integer(123456789))
);
assert_eq!(
parse_expr(b"00000000000000000001a").unwrap(),
(20, Expr::LiteralValue(Value::Integer(1)))
(20, Expr::Integer(1))
);
assert_eq!(
parse_expr(b"9223372036854775807").unwrap(),
(19, Expr::LiteralValue(Value::Integer(9223372036854775807)))
(19, Expr::Integer(9223372036854775807))
);
assert_eq!(
parse_expr(b"000000000000000000009223372036854775807").unwrap(),
(39, Expr::LiteralValue(Value::Integer(9223372036854775807)))
(39, Expr::Integer(9223372036854775807))
);
// integer -> float fallback
assert_eq!(
parse_expr(b"9223372036854775808").unwrap(),
(19, Expr::LiteralValue(Value::Real(9223372036854775808.0)))
(19, Expr::Real(9223372036854775808.0))
);
assert_eq!(
parse_expr(b"9999999999999999999").unwrap(),
(19, Expr::LiteralValue(Value::Real(9999999999999999999.0)))
(19, Expr::Real(9999999999999999999.0))
);
assert_eq!(
parse_expr(b"99999999999999999999a").unwrap(),
(20, Expr::LiteralValue(Value::Real(99999999999999999999.0)))
(20, Expr::Real(99999999999999999999.0))
);

// Parse float
assert_eq!(
parse_expr(b".1").unwrap(),
(2, Expr::LiteralValue(Value::Real(0.1)))
);
assert_eq!(
parse_expr(b"1.").unwrap(),
(2, Expr::LiteralValue(Value::Real(1.0)))
);
assert_eq!(
parse_expr(b"1.01").unwrap(),
(4, Expr::LiteralValue(Value::Real(1.01)))
);
assert_eq!(
parse_expr(b"1e1").unwrap(),
(3, Expr::LiteralValue(Value::Real(10.0)))
);
assert_eq!(
parse_expr(b"1e-1").unwrap(),
(4, Expr::LiteralValue(Value::Real(0.1)))
);
assert_eq!(parse_expr(b".1").unwrap(), (2, Expr::Real(0.1)));
assert_eq!(parse_expr(b"1.").unwrap(), (2, Expr::Real(1.0)));
assert_eq!(parse_expr(b"1.01").unwrap(), (4, Expr::Real(1.01)));
assert_eq!(parse_expr(b"1e1").unwrap(), (3, Expr::Real(10.0)));
assert_eq!(parse_expr(b"1e-1").unwrap(), (4, Expr::Real(0.1)));
}
}
129 changes: 129 additions & 0 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,135 @@ fn test_select_filter() {
assert!(rows.next_row().unwrap().is_none());
}

#[test]
fn test_select_filter_eq() {
let file = create_sqlite_database(&[
"CREATE TABLE example(col1, col2, col3);",
"INSERT INTO example(col1, col2, col3) VALUES ('hello', 2.0, 3);",
// TODO: col2 = 2 integer?
"INSERT INTO example(col1, col2, col3) VALUES ('world', 2.0, 9);",
"INSERT INTO example(col1, col2, col3) VALUES ('hello', 5.0, 9);",
]);

let mut conn = Connection::open(file.path()).unwrap();
let mut stmt = conn
.prepare("SELECT rowid, col1 FROM example WHERE col1 == 'hello';")
.unwrap();
let mut rows = stmt.execute().unwrap();

let row = rows.next_row().unwrap().unwrap();
let columns = row.parse().unwrap();
assert_eq!(columns.len(), 2);
assert_eq!(columns.get(0), &Value::Integer(1));
assert_eq!(columns.get(1), &Value::Text(b"hello"));
drop(row);

let row = rows.next_row().unwrap().unwrap();
let columns = row.parse().unwrap();
assert_eq!(columns.len(), 2);
assert_eq!(columns.get(0), &Value::Integer(3));
assert_eq!(columns.get(1), &Value::Text(b"hello"));
drop(row);

assert!(rows.next_row().unwrap().is_none());

let mut stmt = conn
.prepare("SELECT rowid, col2 FROM example WHERE col2 = 2.0;")
.unwrap();
let mut rows = stmt.execute().unwrap();

let row = rows.next_row().unwrap().unwrap();
let columns = row.parse().unwrap();
assert_eq!(columns.len(), 2);
assert_eq!(columns.get(0), &Value::Integer(1));
assert_eq!(columns.get(1), &Value::Real(2.0));
drop(row);

let row = rows.next_row().unwrap().unwrap();
let columns = row.parse().unwrap();
assert_eq!(columns.len(), 2);
assert_eq!(columns.get(0), &Value::Integer(2));
assert_eq!(columns.get(1), &Value::Real(2.0));
drop(row);

assert!(rows.next_row().unwrap().is_none());

let mut stmt = conn
.prepare("SELECT rowid, col3 FROM example WHERE col3 == 9;")
.unwrap();
let mut rows = stmt.execute().unwrap();

let row = rows.next_row().unwrap().unwrap();
let columns = row.parse().unwrap();
assert_eq!(columns.len(), 2);
assert_eq!(columns.get(0), &Value::Integer(2));
assert_eq!(columns.get(1), &Value::Integer(9));
drop(row);

let row = rows.next_row().unwrap().unwrap();
let columns = row.parse().unwrap();
assert_eq!(columns.len(), 2);
assert_eq!(columns.get(0), &Value::Integer(3));
assert_eq!(columns.get(1), &Value::Integer(9));
drop(row);

assert!(rows.next_row().unwrap().is_none());
}

#[test]
fn test_select_filter_ne() {
let file = create_sqlite_database(&[
"CREATE TABLE example(col1, col2, col3);",
"INSERT INTO example(col1, col2, col3) VALUES ('hello', 2.0, 3);",
// TODO: col2 = 2 integer?
"INSERT INTO example(col1, col2, col3) VALUES ('world', 2.0, 9);",
"INSERT INTO example(col1, col2, col3) VALUES ('hello', 5.0, 9);",
]);

let mut conn = Connection::open(file.path()).unwrap();
let mut stmt = conn
.prepare("SELECT rowid, col1 FROM example WHERE col1 != 'hello';")
.unwrap();
let mut rows = stmt.execute().unwrap();

let row = rows.next_row().unwrap().unwrap();
let columns = row.parse().unwrap();
assert_eq!(columns.len(), 2);
assert_eq!(columns.get(0), &Value::Integer(2));
assert_eq!(columns.get(1), &Value::Text(b"world"));
drop(row);

assert!(rows.next_row().unwrap().is_none());

let mut stmt = conn
.prepare("SELECT rowid, col2 FROM example WHERE col2 != 2.0;")
.unwrap();
let mut rows = stmt.execute().unwrap();

let row = rows.next_row().unwrap().unwrap();
let columns = row.parse().unwrap();
assert_eq!(columns.len(), 2);
assert_eq!(columns.get(0), &Value::Integer(3));
assert_eq!(columns.get(1), &Value::Real(5.0));
drop(row);

assert!(rows.next_row().unwrap().is_none());

let mut stmt = conn
.prepare("SELECT rowid, col3 FROM example WHERE col3 != 9;")
.unwrap();
let mut rows = stmt.execute().unwrap();

let row = rows.next_row().unwrap().unwrap();
let columns = row.parse().unwrap();
assert_eq!(columns.len(), 2);
assert_eq!(columns.get(0), &Value::Integer(1));
assert_eq!(columns.get(1), &Value::Integer(3));
drop(row);

assert!(rows.next_row().unwrap().is_none());
}

#[test]
fn test_select_filter_with_rowid() {
let file = create_sqlite_database(&[
Expand Down

0 comments on commit f2a23e1

Please sign in to comment.