mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-06 08:44:23 +01:00
refactor
This commit is contained in:
15
fuzz/Cargo.lock
generated
15
fuzz/Cargo.lock
generated
@@ -614,6 +614,7 @@ version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbb8270bb4060bd76c6e96f20c52d80620f1d82a3470885694e41e0f81ef6fe7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
@@ -642,7 +643,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "limbo_core"
|
||||
version = "0.0.16"
|
||||
version = "0.0.17"
|
||||
dependencies = [
|
||||
"built",
|
||||
"cfg_block",
|
||||
@@ -679,14 +680,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "limbo_ext"
|
||||
version = "0.0.16"
|
||||
version = "0.0.17"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"getrandom 0.3.1",
|
||||
"limbo_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "limbo_macros"
|
||||
version = "0.0.16"
|
||||
version = "0.0.17"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -695,7 +698,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "limbo_sqlite3_parser"
|
||||
version = "0.0.16"
|
||||
version = "0.0.17"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
@@ -714,7 +717,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "limbo_time"
|
||||
version = "0.0.16"
|
||||
version = "0.0.17"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"limbo_ext",
|
||||
@@ -726,7 +729,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "limbo_uuid"
|
||||
version = "0.0.16"
|
||||
version = "0.0.17"
|
||||
dependencies = [
|
||||
"limbo_ext",
|
||||
"mimalloc",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "limbo-fuzz"
|
||||
version = "0.0.0"
|
||||
authors = ["Automatically generated"]
|
||||
authors = ["the Limbo authors"]
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
@@ -12,7 +12,7 @@ cargo-fuzz = true
|
||||
libfuzzer-sys = "0.4"
|
||||
arbitrary = { version = "1.4.1", features = ["derive"] }
|
||||
limbo_core = { path = "../core" }
|
||||
rusqlite = "0.34.0"
|
||||
rusqlite = { version = "0.34.0", features = ["bundled"] }
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
|
||||
@@ -1,31 +1,67 @@
|
||||
#![no_main]
|
||||
use core::fmt;
|
||||
use std::{num::NonZero, sync::Arc};
|
||||
use std::{error::Error, num::NonZero, sync::Arc};
|
||||
|
||||
use arbitrary::Arbitrary;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use libfuzzer_sys::{fuzz_target, Corpus};
|
||||
use limbo_core::{OwnedValue, IO as _};
|
||||
|
||||
#[derive(Debug, Arbitrary)]
|
||||
enum Binary {
|
||||
Equal,
|
||||
NotEqual,
|
||||
macro_rules! str_enum {
|
||||
($vis:vis enum $name:ident { $($variant:ident => $value:literal),*, }) => {
|
||||
#[derive(PartialEq, Debug, Copy, Clone, Arbitrary)]
|
||||
$vis enum $name {
|
||||
$($variant),*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn to_str(self) -> &'static str {
|
||||
match self {
|
||||
$($name::$variant => $value),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for $name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.to_str())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl fmt::Display for Binary {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Binary::Equal => "=",
|
||||
Binary::NotEqual => "<>",
|
||||
}
|
||||
)
|
||||
str_enum! {
|
||||
enum Binary {
|
||||
Equal => "=",
|
||||
Is => "IS",
|
||||
NotEqual => "<>",
|
||||
GreaterThan => ">",
|
||||
GreaterThanOrEqual => ">=",
|
||||
LessThan => "<",
|
||||
LessThanOrEqual => "<=",
|
||||
RightShift => ">>",
|
||||
LeftShift => "<<",
|
||||
BitwiseAnd => "&",
|
||||
BitwiseOr => "|",
|
||||
And => "AND",
|
||||
Or => "OR",
|
||||
Add => "+",
|
||||
Subtract => "-",
|
||||
Multiply => "*",
|
||||
Divide => "/",
|
||||
Mod => "%",
|
||||
Concat => "||",
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Arbitrary, Clone)]
|
||||
str_enum! {
|
||||
enum Unary {
|
||||
Not => "~",
|
||||
Negative => "-",
|
||||
Positive => "+",
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Arbitrary, Debug, Clone)]
|
||||
enum Value {
|
||||
Null,
|
||||
Integer(i64),
|
||||
@@ -39,7 +75,13 @@ impl From<Value> for limbo_core::OwnedValue {
|
||||
match value {
|
||||
Value::Null => limbo_core::OwnedValue::Null,
|
||||
Value::Integer(v) => limbo_core::OwnedValue::Integer(v),
|
||||
Value::Real(v) => limbo_core::OwnedValue::Float(v),
|
||||
Value::Real(v) => {
|
||||
if v.is_nan() {
|
||||
limbo_core::OwnedValue::Null
|
||||
} else {
|
||||
limbo_core::OwnedValue::Float(v)
|
||||
}
|
||||
}
|
||||
Value::Text(v) => limbo_core::OwnedValue::from_text(&v),
|
||||
Value::Blob(v) => limbo_core::OwnedValue::from_blob(v.to_owned()),
|
||||
}
|
||||
@@ -78,48 +120,82 @@ impl rusqlite::types::FromSql for Value {
|
||||
enum Expr {
|
||||
Value(Value),
|
||||
Binary(Binary, Box<Expr>, Box<Expr>),
|
||||
Unary(Unary, Box<Expr>),
|
||||
}
|
||||
|
||||
fn to_sql(expr: &Expr) -> (String, Vec<Value>) {
|
||||
match expr {
|
||||
Expr::Value(value) => ("?".to_string(), vec![value.clone()]),
|
||||
Expr::Binary(op, lhs, rhs) => {
|
||||
let mut lhs = to_sql(lhs);
|
||||
let mut rhs = to_sql(rhs);
|
||||
lhs.1.append(&mut rhs.1);
|
||||
(format!("({}) {op} ({})", lhs.0, rhs.0), lhs.1)
|
||||
#[derive(Debug)]
|
||||
struct Output {
|
||||
query: String,
|
||||
parameters: Vec<Value>,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn lower(&self) -> Output {
|
||||
match self {
|
||||
Expr::Value(value) => Output {
|
||||
query: "?".to_string(),
|
||||
parameters: vec![value.clone()],
|
||||
depth: 0,
|
||||
},
|
||||
Expr::Unary(op, expr) => {
|
||||
let expr = expr.lower();
|
||||
Output {
|
||||
query: format!("{op} ({})", expr.query),
|
||||
parameters: expr.parameters,
|
||||
depth: expr.depth + 1,
|
||||
}
|
||||
}
|
||||
Expr::Binary(op, lhs, rhs) => {
|
||||
let mut lhs = lhs.lower();
|
||||
let mut rhs = rhs.lower();
|
||||
Output {
|
||||
query: format!("({}) {op} ({})", lhs.query, rhs.query),
|
||||
parameters: {
|
||||
lhs.parameters.append(&mut rhs.parameters);
|
||||
lhs.parameters
|
||||
},
|
||||
depth: lhs.depth.max(rhs.depth) + 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_fuzz(expr: Expr) {
|
||||
let (sql, params) = to_sql(&expr);
|
||||
let sql = format!("select {}", &sql);
|
||||
fn do_fuzz(expr: Expr) -> Result<Corpus, Box<dyn Error>> {
|
||||
let expr = expr.lower();
|
||||
let sql = format!("SELECT {}", expr.query);
|
||||
|
||||
let sqlite = rusqlite::Connection::open_in_memory().unwrap();
|
||||
let io = Arc::new(limbo_core::MemoryIO::new());
|
||||
let memory = limbo_core::Database::open_file(io.clone(), ":memory:", true).unwrap();
|
||||
let limbo = memory.connect().unwrap();
|
||||
|
||||
let expected = sqlite
|
||||
.query_row(&sql, rusqlite::params_from_iter(params.iter()), |row| {
|
||||
row.get::<_, Value>(0)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut stmt = limbo.prepare(sql).unwrap();
|
||||
for (idx, value) in params.into_iter().enumerate() {
|
||||
stmt.bind_at(NonZero::new(idx + 1).unwrap(), value.into())
|
||||
// FIX: `limbo_core::translate::expr::translate_expr` causes a overflow if this is any higher.
|
||||
if expr.depth > 153 {
|
||||
return Ok(Corpus::Reject);
|
||||
}
|
||||
|
||||
let value = 'value: {
|
||||
let expected = {
|
||||
let conn = rusqlite::Connection::open_in_memory()?;
|
||||
conn.query_row(
|
||||
&sql,
|
||||
rusqlite::params_from_iter(expr.parameters.iter()),
|
||||
|row| row.get::<_, Value>(0),
|
||||
)?
|
||||
};
|
||||
|
||||
let found = 'value: {
|
||||
let io = Arc::new(limbo_core::MemoryIO::new());
|
||||
let db = limbo_core::Database::open_file(io.clone(), ":memory:", true)?;
|
||||
let conn = db.connect()?;
|
||||
|
||||
let mut stmt = conn.prepare(sql)?;
|
||||
for (idx, value) in expr.parameters.iter().enumerate() {
|
||||
stmt.bind_at(NonZero::new(idx + 1).unwrap(), value.clone().into())
|
||||
}
|
||||
loop {
|
||||
use limbo_core::StepResult;
|
||||
match stmt.step().unwrap() {
|
||||
StepResult::IO => io.run_once().unwrap(),
|
||||
match stmt.step()? {
|
||||
StepResult::IO => io.run_once()?,
|
||||
StepResult::Row => {
|
||||
let row = stmt.row().unwrap();
|
||||
assert_eq!(row.count(), 1);
|
||||
assert_eq!(row.count(), 1, "expr: {:?}", expr);
|
||||
break 'value row.get_value(0).clone();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -127,7 +203,18 @@ fn do_fuzz(expr: Expr) {
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(OwnedValue::from(expected), value);
|
||||
assert_eq!(
|
||||
OwnedValue::from(expected.clone()),
|
||||
found.clone(),
|
||||
"with expression {:?} {}",
|
||||
expr,
|
||||
match (expected, found) {
|
||||
(Value::Real(a), OwnedValue::Float(b)) => format!("float diff: {:?}", (a - b).abs()),
|
||||
_ => "".to_string(),
|
||||
}
|
||||
);
|
||||
|
||||
Ok(Corpus::Keep)
|
||||
}
|
||||
|
||||
fuzz_target!(|expr: Expr| do_fuzz(expr));
|
||||
fuzz_target!(|expr: Expr| -> Corpus { do_fuzz(expr).unwrap_or(Corpus::Keep) });
|
||||
|
||||
Reference in New Issue
Block a user