From 6b58c4a33f0666c50f24a8cfec52e2ff7d654c52 Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Thu, 5 Jun 2025 15:10:40 -0300 Subject: [PATCH] migrate to using `limbo_core::Value` inside Simulator --- simulator/Cargo.toml | 2 +- simulator/generation/expr.rs | 6 +- simulator/generation/plan.rs | 14 +- simulator/generation/predicate/binary.rs | 12 +- simulator/generation/predicate/mod.rs | 10 +- simulator/generation/predicate/unary.rs | 44 ++--- simulator/generation/property.rs | 8 +- simulator/generation/query.rs | 10 +- simulator/generation/table.rs | 107 ++++++------ simulator/model/query/create.rs | 4 +- simulator/model/query/create_index.rs | 2 +- simulator/model/query/delete.rs | 4 +- simulator/model/query/drop.rs | 4 +- simulator/model/query/insert.rs | 6 +- simulator/model/query/mod.rs | 4 +- simulator/model/query/predicate.rs | 8 +- simulator/model/query/select.rs | 4 +- simulator/model/query/update.rs | 6 +- simulator/model/table.rs | 211 +++++++---------------- simulator/runner/differential.rs | 21 ++- 20 files changed, 201 insertions(+), 286 deletions(-) diff --git a/simulator/Cargo.toml b/simulator/Cargo.toml index 76565fcbd..b15f32b56 100644 --- a/simulator/Cargo.toml +++ b/simulator/Cargo.toml @@ -15,7 +15,7 @@ name = "limbo_sim" path = "main.rs" [dependencies] -limbo_core = { path = "../core", features = ["fuzz"]} +limbo_core = { path = "../core", features = ["simulator"]} rand = "0.8.5" rand_chacha = "0.3.1" log = "0.4.20" diff --git a/simulator/generation/expr.rs b/simulator/generation/expr.rs index 04a5b7cdc..59d07c920 100644 --- a/simulator/generation/expr.rs +++ b/simulator/generation/expr.rs @@ -4,7 +4,7 @@ use limbo_sqlite3_parser::ast::{ use crate::{ generation::{gen_random_text, pick, pick_index, Arbitrary, ArbitraryFrom}, - model::table::Value, + model::table::SimValue, SimulatorEnv, }; @@ -251,8 +251,8 @@ impl ArbitraryFrom<&SimulatorEnv> for ast::Literal { } // Creates a litreal value -impl ArbitraryFrom<&Vec<&Value>> for ast::Expr { - fn arbitrary_from(rng: &mut R, values: &Vec<&Value>) -> Self { +impl ArbitraryFrom<&Vec<&SimValue>> for ast::Expr { + fn arbitrary_from(rng: &mut R, values: &Vec<&SimValue>) -> Self { if values.is_empty() { return Self::Literal(ast::Literal::Null); } diff --git a/simulator/generation/plan.rs b/simulator/generation/plan.rs index 49885517e..e6520ae84 100644 --- a/simulator/generation/plan.rs +++ b/simulator/generation/plan.rs @@ -11,7 +11,7 @@ use crate::{ update::Update, Create, CreateIndex, Delete, Drop, Insert, Query, Select, }, - table::Value, + table::SimValue, }, runner::{env::SimConnection, io::SimulatorIO}, SimulatorEnv, @@ -21,7 +21,7 @@ use crate::generation::{frequency, Arbitrary, ArbitraryFrom}; use super::property::{remaining, Property}; -pub(crate) type ResultSet = Result>>; +pub(crate) type ResultSet = Result>>; #[derive(Clone, Serialize, Deserialize)] pub(crate) struct InteractionPlan { @@ -483,7 +483,7 @@ impl ArbitraryFrom<&mut SimulatorEnv> for InteractionPlan { } impl Interaction { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { + pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { match self { Self::Query(query) => query.shadow(env), Self::Assumption(_) | Self::Assertion(_) | Self::Fault(_) => vec![], @@ -512,13 +512,7 @@ impl Interaction { let row = rows.row().unwrap(); let mut r = Vec::new(); for v in row.get_values() { - let v = match v { - limbo_core::Value::Null => Value::Null, - limbo_core::Value::Integer(i) => Value::Integer(*i), - limbo_core::Value::Float(f) => Value::Float(*f), - limbo_core::Value::Text(t) => Value::Text(t.as_str().to_string()), - limbo_core::Value::Blob(b) => Value::Blob(b.to_vec()), - }; + let v = v.into(); r.push(v); } out.push(r); diff --git a/simulator/generation/predicate/binary.rs b/simulator/generation/predicate/binary.rs index 426493d76..12b0e3279 100644 --- a/simulator/generation/predicate/binary.rs +++ b/simulator/generation/predicate/binary.rs @@ -11,7 +11,7 @@ use crate::{ }, model::{ query::predicate::Predicate, - table::{Table, Value}, + table::{SimValue, Table}, }, }; @@ -20,7 +20,7 @@ impl Predicate { pub fn from_column_binary( rng: &mut R, column_name: &str, - value: &Value, + value: &SimValue, ) -> Predicate { let expr = one_of( vec![ @@ -54,7 +54,7 @@ impl Predicate { } /// Produces a true [ast::Expr::Binary] [Predicate] that is true for the provided row in the given table - pub fn true_binary(rng: &mut R, t: &Table, row: &Vec) -> Predicate { + pub fn true_binary(rng: &mut R, t: &Table, row: &Vec) -> Predicate { // Pick a column let column_index = rng.gen_range(0..t.columns.len()); let column = &t.columns[column_index]; @@ -77,7 +77,7 @@ impl Predicate { ( 1, Box::new(|rng| { - let v = Value::arbitrary_from(rng, &column.column_type); + let v = SimValue::arbitrary_from(rng, &column.column_type); if &v == value { None } else { @@ -145,7 +145,7 @@ impl Predicate { } /// Produces an [ast::Expr::Binary] [Predicate] that is false for the provided row in the given table - pub fn false_binary(rng: &mut R, t: &Table, row: &Vec) -> Predicate { + pub fn false_binary(rng: &mut R, t: &Table, row: &Vec) -> Predicate { // Pick a column let column_index = rng.gen_range(0..t.columns.len()); let column = &t.columns[column_index]; @@ -164,7 +164,7 @@ impl Predicate { }), Box::new(|rng| { let v = loop { - let v = Value::arbitrary_from(rng, &column.column_type); + let v = SimValue::arbitrary_from(rng, &column.column_type); if &v != value { break v; } diff --git a/simulator/generation/predicate/mod.rs b/simulator/generation/predicate/mod.rs index c6d9cdca9..ae87e5462 100644 --- a/simulator/generation/predicate/mod.rs +++ b/simulator/generation/predicate/mod.rs @@ -3,7 +3,7 @@ use rand::{seq::SliceRandom as _, Rng}; use crate::model::{ query::predicate::Predicate, - table::{Table, Value}, + table::{SimValue, Table}, }; use super::{one_of, ArbitraryFrom}; @@ -48,14 +48,14 @@ impl ArbitraryFrom<&Table> for Predicate { } } -impl ArbitraryFrom<(&str, &Value)> for Predicate { - fn arbitrary_from(rng: &mut R, (column_name, value): (&str, &Value)) -> Self { +impl ArbitraryFrom<(&str, &SimValue)> for Predicate { + fn arbitrary_from(rng: &mut R, (column_name, value): (&str, &SimValue)) -> Self { Predicate::from_column_binary(rng, column_name, value) } } -impl ArbitraryFrom<(&Table, &Vec)> for Predicate { - fn arbitrary_from(rng: &mut R, (t, row): (&Table, &Vec)) -> Self { +impl ArbitraryFrom<(&Table, &Vec)> for Predicate { + fn arbitrary_from(rng: &mut R, (t, row): (&Table, &Vec)) -> Self { // We want to produce a predicate that is true for the row // We can do this by creating several predicates that // are true, some that are false, combiend them in ways that correspond to the creation of a true predicate diff --git a/simulator/generation/predicate/unary.rs b/simulator/generation/predicate/unary.rs index 5de1850a2..87502556e 100644 --- a/simulator/generation/predicate/unary.rs +++ b/simulator/generation/predicate/unary.rs @@ -8,14 +8,14 @@ use crate::{ generation::{backtrack, pick, predicate::SimplePredicate, ArbitraryFromMaybe}, model::{ query::predicate::Predicate, - table::{Table, Value}, + table::{SimValue, Table}, }, }; -pub struct TrueValue(pub Value); +pub struct TrueValue(pub SimValue); -impl ArbitraryFromMaybe<&Value> for TrueValue { - fn arbitrary_from_maybe(_rng: &mut R, value: &Value) -> Option +impl ArbitraryFromMaybe<&SimValue> for TrueValue { + fn arbitrary_from_maybe(_rng: &mut R, value: &SimValue) -> Option where Self: Sized, { @@ -24,13 +24,13 @@ impl ArbitraryFromMaybe<&Value> for TrueValue { } } -impl ArbitraryFromMaybe<&Vec<&Value>> for TrueValue { - fn arbitrary_from_maybe(rng: &mut R, values: &Vec<&Value>) -> Option +impl ArbitraryFromMaybe<&Vec<&SimValue>> for TrueValue { + fn arbitrary_from_maybe(rng: &mut R, values: &Vec<&SimValue>) -> Option where Self: Sized, { if values.is_empty() { - return Some(Self(Value::TRUE)); + return Some(Self(SimValue::TRUE)); } let value = pick(values, rng); @@ -38,10 +38,10 @@ impl ArbitraryFromMaybe<&Vec<&Value>> for TrueValue { } } -pub struct FalseValue(pub Value); +pub struct FalseValue(pub SimValue); -impl ArbitraryFromMaybe<&Value> for FalseValue { - fn arbitrary_from_maybe(_rng: &mut R, value: &Value) -> Option +impl ArbitraryFromMaybe<&SimValue> for FalseValue { + fn arbitrary_from_maybe(_rng: &mut R, value: &SimValue) -> Option where Self: Sized, { @@ -50,13 +50,13 @@ impl ArbitraryFromMaybe<&Value> for FalseValue { } } -impl ArbitraryFromMaybe<&Vec<&Value>> for FalseValue { - fn arbitrary_from_maybe(rng: &mut R, values: &Vec<&Value>) -> Option +impl ArbitraryFromMaybe<&Vec<&SimValue>> for FalseValue { + fn arbitrary_from_maybe(rng: &mut R, values: &Vec<&SimValue>) -> Option where Self: Sized, { if values.is_empty() { - return Some(Self(Value::FALSE)); + return Some(Self(SimValue::FALSE)); } let value = pick(values, rng); @@ -64,12 +64,12 @@ impl ArbitraryFromMaybe<&Vec<&Value>> for FalseValue { } } -pub struct BitNotValue(pub Value); +pub struct BitNotValue(pub SimValue); -impl ArbitraryFromMaybe<(&Value, bool)> for BitNotValue { +impl ArbitraryFromMaybe<(&SimValue, bool)> for BitNotValue { fn arbitrary_from_maybe( _rng: &mut R, - (value, predicate): (&Value, bool), + (value, predicate): (&SimValue, bool), ) -> Option where Self: Sized, @@ -80,10 +80,10 @@ impl ArbitraryFromMaybe<(&Value, bool)> for BitNotValue { } } -impl ArbitraryFromMaybe<(&Vec<&Value>, bool)> for BitNotValue { +impl ArbitraryFromMaybe<(&Vec<&SimValue>, bool)> for BitNotValue { fn arbitrary_from_maybe( rng: &mut R, - (values, predicate): (&Vec<&Value>, bool), + (values, predicate): (&Vec<&SimValue>, bool), ) -> Option where Self: Sized, @@ -151,7 +151,9 @@ impl SimplePredicate { rng, ); // If cannot generate a value - SimplePredicate(Predicate(expr.unwrap_or(Expr::Literal(Value::TRUE.into())))) + SimplePredicate(Predicate( + expr.unwrap_or(Expr::Literal(SimValue::TRUE.into())), + )) } /// Generates a false [ast::Expr::Unary] [SimplePredicate] from a [Table] @@ -207,6 +209,8 @@ impl SimplePredicate { rng, ); // If cannot generate a value - SimplePredicate(Predicate(expr.unwrap_or(Expr::Literal(Value::TRUE.into())))) + SimplePredicate(Predicate( + expr.unwrap_or(Expr::Literal(SimValue::TRUE.into())), + )) } } diff --git a/simulator/generation/property.rs b/simulator/generation/property.rs index adc8a581d..058f28682 100644 --- a/simulator/generation/property.rs +++ b/simulator/generation/property.rs @@ -1,4 +1,4 @@ -use limbo_core::LimboError; +use limbo_core::{LimboError, Value}; use serde::{Deserialize, Serialize}; use crate::{ @@ -8,7 +8,7 @@ use crate::{ select::{Distinctness, ResultColumn}, Create, Delete, Drop, Insert, Query, Select, }, - table::Value, + table::SimValue, }, runner::env::SimulatorEnv, }; @@ -418,7 +418,7 @@ impl Property { .iter() .filter(|vs| { let v = vs.first().unwrap(); - if let Value::Integer(i) = v { + if let Value::Integer(i) = &v.0 { *i == 1 } else { false @@ -496,7 +496,7 @@ fn property_insert_values_select( let table = pick(&env.tables, rng); // Generate rows to insert let rows = (0..rng.gen_range(1..=5)) - .map(|_| Vec::::arbitrary_from(rng, table)) + .map(|_| Vec::::arbitrary_from(rng, table)) .collect::>(); // Pick a random row to select diff --git a/simulator/generation/query.rs b/simulator/generation/query.rs index eaaeec071..09d0f6421 100644 --- a/simulator/generation/query.rs +++ b/simulator/generation/query.rs @@ -5,7 +5,7 @@ use crate::model::query::predicate::Predicate; use crate::model::query::select::{Distinctness, ResultColumn}; use crate::model::query::update::Update; use crate::model::query::{Create, Delete, Drop, Insert, Query, Select}; -use crate::model::table::{Table, Value}; +use crate::model::table::{SimValue, Table}; use crate::SimulatorEnv; use rand::Rng; @@ -38,12 +38,12 @@ impl ArbitraryFrom<&SimulatorEnv> for Insert { let gen_values = |rng: &mut R| { let table = pick(&env.tables, rng); let num_rows = rng.gen_range(1..10); - let values: Vec> = (0..num_rows) + let values: Vec> = (0..num_rows) .map(|_| { table .columns .iter() - .map(|c| Value::arbitrary_from(rng, &c.column_type)) + .map(|c| SimValue::arbitrary_from(rng, &c.column_type)) .collect() }) .collect(); @@ -141,7 +141,7 @@ impl ArbitraryFrom<&SimulatorEnv> for Update { let table = pick(&env.tables, rng); let mut seen = HashSet::new(); let num_cols = rng.gen_range(1..=table.columns.len()); - let set_values: Vec<(String, Value)> = (0..num_cols) + let set_values: Vec<(String, SimValue)> = (0..num_cols) .map(|_| { let column = loop { let column = pick(&table.columns, rng); @@ -153,7 +153,7 @@ impl ArbitraryFrom<&SimulatorEnv> for Update { seen.insert(column.name.clone()); ( column.name.clone(), - Value::arbitrary_from(rng, &column.column_type), + SimValue::arbitrary_from(rng, &column.column_type), ) }) .collect(); diff --git a/simulator/generation/table.rs b/simulator/generation/table.rs index 2858928b7..baac48236 100644 --- a/simulator/generation/table.rs +++ b/simulator/generation/table.rs @@ -1,7 +1,8 @@ +use limbo_core::Value; use rand::Rng; use crate::generation::{gen_random_text, pick, readable_name_custom, Arbitrary, ArbitraryFrom}; -use crate::model::table::{Column, ColumnType, Name, Table, Value}; +use crate::model::table::{Column, ColumnType, Name, SimValue, Table}; use super::ArbitraryFromMaybe; @@ -45,44 +46,45 @@ impl Arbitrary for ColumnType { } } -impl ArbitraryFrom<&Table> for Vec { +impl ArbitraryFrom<&Table> for Vec { fn arbitrary_from(rng: &mut R, table: &Table) -> Self { let mut row = Vec::new(); for column in table.columns.iter() { - let value = Value::arbitrary_from(rng, &column.column_type); + let value = SimValue::arbitrary_from(rng, &column.column_type); row.push(value); } row } } -impl ArbitraryFrom<&Vec<&Value>> for Value { +impl ArbitraryFrom<&Vec<&SimValue>> for SimValue { fn arbitrary_from(rng: &mut R, values: &Vec<&Self>) -> Self { if values.is_empty() { - return Self::Null; + return Self(Value::Null); } pick(values, rng).to_owned().clone() } } -impl ArbitraryFrom<&ColumnType> for Value { +impl ArbitraryFrom<&ColumnType> for SimValue { fn arbitrary_from(rng: &mut R, column_type: &ColumnType) -> Self { - match column_type { - ColumnType::Integer => Self::Integer(rng.gen_range(i64::MIN..i64::MAX)), - ColumnType::Float => Self::Float(rng.gen_range(-1e10..1e10)), - ColumnType::Text => Self::Text(gen_random_text(rng)), - ColumnType::Blob => Self::Blob(gen_random_text(rng).as_bytes().to_vec()), - } + let value = match column_type { + ColumnType::Integer => Value::Integer(rng.gen_range(i64::MIN..i64::MAX)), + ColumnType::Float => Value::Float(rng.gen_range(-1e10..1e10)), + ColumnType::Text => Value::build_text(gen_random_text(rng)), + ColumnType::Blob => Value::Blob(gen_random_text(rng).as_bytes().to_vec()), + }; + SimValue(value) } } -pub(crate) struct LTValue(pub(crate) Value); +pub(crate) struct LTValue(pub(crate) SimValue); -impl ArbitraryFrom<&Vec<&Value>> for LTValue { - fn arbitrary_from(rng: &mut R, values: &Vec<&Value>) -> Self { +impl ArbitraryFrom<&Vec<&SimValue>> for LTValue { + fn arbitrary_from(rng: &mut R, values: &Vec<&SimValue>) -> Self { if values.is_empty() { - return Self(Value::Null); + return Self(SimValue(Value::Null)); } let value = pick(values, rng); @@ -90,17 +92,17 @@ impl ArbitraryFrom<&Vec<&Value>> for LTValue { } } -impl ArbitraryFrom<&Value> for LTValue { - fn arbitrary_from(rng: &mut R, value: &Value) -> Self { - match value { - Value::Integer(i) => Self(Value::Integer(rng.gen_range(i64::MIN..*i - 1))), - Value::Float(f) => Self(Value::Float(f - rng.gen_range(0.0..1e10))), - Value::Text(t) => { +impl ArbitraryFrom<&SimValue> for LTValue { + fn arbitrary_from(rng: &mut R, value: &SimValue) -> Self { + let new_value = match &value.0 { + Value::Integer(i) => Value::Integer(rng.gen_range(i64::MIN..*i - 1)), + Value::Float(f) => Value::Float(f - rng.gen_range(0.0..1e10)), + value @ Value::Text(..) => { // Either shorten the string, or make at least one character smaller and mutate the rest - let mut t = t.clone(); + let mut t = value.to_string(); if rng.gen_bool(0.01) { t.pop(); - Self(Value::Text(t)) + Value::build_text(t) } else { let mut t = t.chars().map(|c| c as u32).collect::>(); let index = rng.gen_range(0..t.len()); @@ -113,7 +115,7 @@ impl ArbitraryFrom<&Value> for LTValue { .into_iter() .map(|c| char::from_u32(c).unwrap_or('z')) .collect::(); - Self(Value::Text(t)) + Value::build_text(t) } } Value::Blob(b) => { @@ -121,7 +123,7 @@ impl ArbitraryFrom<&Value> for LTValue { let mut b = b.clone(); if rng.gen_bool(0.01) { b.pop(); - Self(Value::Blob(b)) + Value::Blob(b) } else { let index = rng.gen_range(0..b.len()); b[index] -= 1; @@ -129,20 +131,21 @@ impl ArbitraryFrom<&Value> for LTValue { for i in (index + 1)..b.len() { b[i] = rng.gen_range(0..=255); } - Self(Value::Blob(b)) + Value::Blob(b) } } _ => unreachable!(), - } + }; + Self(SimValue(new_value)) } } -pub(crate) struct GTValue(pub(crate) Value); +pub(crate) struct GTValue(pub(crate) SimValue); -impl ArbitraryFrom<&Vec<&Value>> for GTValue { - fn arbitrary_from(rng: &mut R, values: &Vec<&Value>) -> Self { +impl ArbitraryFrom<&Vec<&SimValue>> for GTValue { + fn arbitrary_from(rng: &mut R, values: &Vec<&SimValue>) -> Self { if values.is_empty() { - return Self(Value::Null); + return Self(SimValue(Value::Null)); } let value = pick(values, rng); @@ -150,17 +153,17 @@ impl ArbitraryFrom<&Vec<&Value>> for GTValue { } } -impl ArbitraryFrom<&Value> for GTValue { - fn arbitrary_from(rng: &mut R, value: &Value) -> Self { - match value { - Value::Integer(i) => Self(Value::Integer(rng.gen_range(*i..i64::MAX))), - Value::Float(f) => Self(Value::Float(rng.gen_range(*f..1e10))), - Value::Text(t) => { +impl ArbitraryFrom<&SimValue> for GTValue { + fn arbitrary_from(rng: &mut R, value: &SimValue) -> Self { + let new_value = match &value.0 { + Value::Integer(i) => Value::Integer(rng.gen_range(*i..i64::MAX)), + Value::Float(f) => Value::Float(rng.gen_range(*f..1e10)), + value @ Value::Text(..) => { // Either lengthen the string, or make at least one character smaller and mutate the rest - let mut t = t.clone(); + let mut t = value.to_string(); if rng.gen_bool(0.01) { t.push(rng.gen_range(0..=255) as u8 as char); - Self(Value::Text(t)) + Value::build_text(t) } else { let mut t = t.chars().map(|c| c as u32).collect::>(); let index = rng.gen_range(0..t.len()); @@ -173,7 +176,7 @@ impl ArbitraryFrom<&Value> for GTValue { .into_iter() .map(|c| char::from_u32(c).unwrap_or('a')) .collect::(); - Self(Value::Text(t)) + Value::build_text(t) } } Value::Blob(b) => { @@ -181,7 +184,7 @@ impl ArbitraryFrom<&Value> for GTValue { let mut b = b.clone(); if rng.gen_bool(0.01) { b.push(rng.gen_range(0..=255)); - Self(Value::Blob(b)) + Value::Blob(b) } else { let index = rng.gen_range(0..b.len()); b[index] += 1; @@ -189,20 +192,22 @@ impl ArbitraryFrom<&Value> for GTValue { for i in (index + 1)..b.len() { b[i] = rng.gen_range(0..=255); } - Self(Value::Blob(b)) + Value::Blob(b) } } _ => unreachable!(), - } + }; + Self(SimValue(new_value)) } } -pub(crate) struct LikeValue(pub(crate) Value); +pub(crate) struct LikeValue(pub(crate) SimValue); -impl ArbitraryFromMaybe<&Value> for LikeValue { - fn arbitrary_from_maybe(rng: &mut R, value: &Value) -> Option { - match value { - Value::Text(t) => { +impl ArbitraryFromMaybe<&SimValue> for LikeValue { + fn arbitrary_from_maybe(rng: &mut R, value: &SimValue) -> Option { + match &value.0 { + value @ Value::Text(..) => { + let t = value.to_string(); let mut t = t.chars().collect::>(); // Remove a number of characters, either insert `_` for each character removed, or // insert one `%` for the whole substring @@ -221,7 +226,9 @@ impl ArbitraryFromMaybe<&Value> for LikeValue { } let index = rng.gen_range(0..t.len()); t.insert(index, '%'); - Some(Self(Value::Text(t.into_iter().collect()))) + Some(Self(SimValue(Value::build_text( + t.into_iter().collect::(), + )))) } _ => None, } diff --git a/simulator/model/query/create.rs b/simulator/model/query/create.rs index 172fd9c9d..eba5842ab 100644 --- a/simulator/model/query/create.rs +++ b/simulator/model/query/create.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; use crate::{ - model::table::{Table, Value}, + model::table::{SimValue, Table}, SimulatorEnv, }; @@ -13,7 +13,7 @@ pub(crate) struct Create { } impl Create { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { + pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { if !env.tables.iter().any(|t| t.name == self.table.name) { env.tables.push(self.table.clone()); } diff --git a/simulator/model/query/create_index.rs b/simulator/model/query/create_index.rs index ea003e7f4..25ea7e795 100644 --- a/simulator/model/query/create_index.rs +++ b/simulator/model/query/create_index.rs @@ -31,7 +31,7 @@ impl CreateIndex { pub(crate) fn shadow( &self, _env: &mut crate::runner::env::SimulatorEnv, - ) -> Vec> { + ) -> Vec> { // CREATE INDEX doesn't require any shadowing; we don't need to keep track // in the simulator what indexes exist. vec![] diff --git a/simulator/model/query/delete.rs b/simulator/model/query/delete.rs index 381de7f4d..e375a62ed 100644 --- a/simulator/model/query/delete.rs +++ b/simulator/model/query/delete.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{model::table::Value, SimulatorEnv}; +use crate::{model::table::SimValue, SimulatorEnv}; use super::predicate::Predicate; @@ -13,7 +13,7 @@ pub(crate) struct Delete { } impl Delete { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { + pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { let table = env .tables .iter_mut() diff --git a/simulator/model/query/drop.rs b/simulator/model/query/drop.rs index b38c9dc71..2731586da 100644 --- a/simulator/model/query/drop.rs +++ b/simulator/model/query/drop.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{model::table::Value, SimulatorEnv}; +use crate::{model::table::SimValue, SimulatorEnv}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub(crate) struct Drop { @@ -10,7 +10,7 @@ pub(crate) struct Drop { } impl Drop { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { + pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { env.tables.retain(|t| t.name != self.table); vec![] } diff --git a/simulator/model/query/insert.rs b/simulator/model/query/insert.rs index 109c9df3a..47ad98e47 100644 --- a/simulator/model/query/insert.rs +++ b/simulator/model/query/insert.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{model::table::Value, SimulatorEnv}; +use crate::{model::table::SimValue, SimulatorEnv}; use super::select::Select; @@ -10,7 +10,7 @@ use super::select::Select; pub(crate) enum Insert { Values { table: String, - values: Vec>, + values: Vec>, }, Select { table: String, @@ -19,7 +19,7 @@ pub(crate) enum Insert { } impl Insert { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { + pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { match self { Insert::Values { table, values } => { if let Some(t) = env.tables.iter_mut().find(|t| &t.name == table) { diff --git a/simulator/model/query/mod.rs b/simulator/model/query/mod.rs index f1b82834b..31c3d52ce 100644 --- a/simulator/model/query/mod.rs +++ b/simulator/model/query/mod.rs @@ -10,7 +10,7 @@ pub(crate) use select::Select; use serde::{Deserialize, Serialize}; use update::Update; -use crate::{model::table::Value, runner::env::SimulatorEnv}; +use crate::{model::table::SimValue, runner::env::SimulatorEnv}; pub mod create; pub mod create_index; @@ -61,7 +61,7 @@ impl Query { } } - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { + pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { match self { Query::Create(create) => create.shadow(env), Query::Insert(insert) => insert.shadow(env), diff --git a/simulator/model/query/predicate.rs b/simulator/model/query/predicate.rs index 198d04bc6..065853a27 100644 --- a/simulator/model/query/predicate.rs +++ b/simulator/model/query/predicate.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::model::{ query::EmptyContext, - table::{Table, Value}, + table::{SimValue, Table}, }; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -20,7 +20,7 @@ impl Predicate { Self(ast::Expr::Literal(ast::Literal::Numeric("0".to_string()))) } - pub(crate) fn test(&self, row: &[Value], table: &Table) -> bool { + pub(crate) fn test(&self, row: &[SimValue], table: &Table) -> bool { let value = expr_to_value(&self.0, row, table); value.map_or(false, |value| value.into_bool()) } @@ -30,7 +30,7 @@ impl Predicate { // This function attempts to convert an simpler easily computable expression into values // TODO: In the future, we can try to expand this computation if we want to support harder properties that require us // to already know more values before hand -fn expr_to_value(expr: &ast::Expr, row: &[Value], table: &Table) -> Option { +fn expr_to_value(expr: &ast::Expr, row: &[SimValue], table: &Table) -> Option { match expr { ast::Expr::DoublyQualified(_, _, ast::Name(col_name)) | ast::Expr::Qualified(_, ast::Name(col_name)) @@ -57,7 +57,7 @@ fn expr_to_value(expr: &ast::Expr, row: &[Value], table: &Table) -> Option { diff --git a/simulator/model/query/select.rs b/simulator/model/query/select.rs index 2f1de7b5a..f51db22ed 100644 --- a/simulator/model/query/select.rs +++ b/simulator/model/query/select.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{model::table::Value, SimulatorEnv}; +use crate::{model::table::SimValue, SimulatorEnv}; use super::predicate::Predicate; @@ -46,7 +46,7 @@ pub(crate) struct Select { } impl Select { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { + pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { let table = env.tables.iter().find(|t| t.name == self.table.as_str()); if let Some(table) = table { table diff --git a/simulator/model/query/update.rs b/simulator/model/query/update.rs index c88e7de5d..7bcff95e2 100644 --- a/simulator/model/query/update.rs +++ b/simulator/model/query/update.rs @@ -2,19 +2,19 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{model::table::Value, SimulatorEnv}; +use crate::{model::table::SimValue, SimulatorEnv}; use super::predicate::Predicate; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub(crate) struct Update { pub(crate) table: String, - pub(crate) set_values: Vec<(String, Value)>, // Pair of value for set expressions => SET name=value + pub(crate) set_values: Vec<(String, SimValue)>, // Pair of value for set expressions => SET name=value pub(crate) predicate: Predicate, } impl Update { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { + pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { let table = env .tables .iter_mut() diff --git a/simulator/model/table.rs b/simulator/model/table.rs index 02094bca4..5fb3e7dbb 100644 --- a/simulator/model/table.rs +++ b/simulator/model/table.rs @@ -1,6 +1,6 @@ use std::{fmt::Display, ops::Deref}; -use limbo_core::numeric::{nonnan::NonNan, NullableInteger, Numeric}; +use limbo_core::{numeric::Numeric, types}; use limbo_sqlite3_parser::ast; use regex::{Regex, RegexBuilder}; use serde::{Deserialize, Serialize}; @@ -17,7 +17,7 @@ impl Deref for Name { #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct Table { - pub(crate) rows: Vec>, + pub(crate) rows: Vec>, pub(crate) name: String, pub(crate) columns: Vec, } @@ -64,35 +64,8 @@ where s.parse().map_err(serde::de::Error::custom) } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub(crate) enum Value { - Null, - Integer(i64), - // we use custom serialization to preserve float precision - #[serde( - serialize_with = "float_to_string", - deserialize_with = "string_to_float" - )] - Float(f64), - Text(String), - Blob(Vec), -} - -impl PartialOrd for Value { - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - (Self::Null, Self::Null) => Some(std::cmp::Ordering::Equal), - (Self::Null, _) => Some(std::cmp::Ordering::Less), - (_, Self::Null) => Some(std::cmp::Ordering::Greater), - (Self::Integer(i1), Self::Integer(i2)) => i1.partial_cmp(i2), - (Self::Float(f1), Self::Float(f2)) => f1.partial_cmp(f2), - (Self::Text(t1), Self::Text(t2)) => t1.partial_cmp(t2), - (Self::Blob(b1), Self::Blob(b2)) => b1.partial_cmp(b2), - // todo: add type coercions here - _ => None, - } - } -} +#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] +pub(crate) struct SimValue(pub limbo_core::Value); fn to_sqlite_blob(bytes: &[u8]) -> String { format!( @@ -103,29 +76,29 @@ fn to_sqlite_blob(bytes: &[u8]) -> String { ) } -impl Display for Value { +impl Display for SimValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Null => write!(f, "NULL"), - Self::Integer(i) => write!(f, "{}", i), - Self::Float(fl) => write!(f, "{}", fl), - Self::Text(t) => write!(f, "'{}'", t), - Self::Blob(b) => write!(f, "{}", to_sqlite_blob(b)), + match &self.0 { + types::Value::Null => write!(f, "NULL"), + types::Value::Integer(i) => write!(f, "{}", i), + types::Value::Float(fl) => write!(f, "{}", fl), + value @ types::Value::Text(..) => write!(f, "'{}'", value.to_string()), + types::Value::Blob(b) => write!(f, "{}", to_sqlite_blob(b)), } } } -impl Value { - pub const FALSE: Self = Value::Integer(0); - pub const TRUE: Self = Value::Integer(1); +impl SimValue { + pub const FALSE: Self = SimValue(types::Value::Integer(0)); + pub const TRUE: Self = SimValue(types::Value::Integer(1)); pub fn into_bool(&self) -> bool { - Numeric::from(self).try_into_bool().unwrap_or_default() + Numeric::from(&self.0).try_into_bool().unwrap_or_default() } // TODO: support more predicates /// Returns a Value::TRUE or VALUE::FALSE - pub fn binary_compare(&self, other: &Self, operator: ast::Operator) -> Value { + pub fn binary_compare(&self, other: &Self, operator: ast::Operator) -> SimValue { match operator { ast::Operator::Add => todo!(), ast::Operator::And => self.into_bool() && other.into_bool(), @@ -167,13 +140,14 @@ impl Value { } } - pub fn unary_exec(&self, operator: ast::UnaryOperator) -> Value { - match operator { - ast::UnaryOperator::BitwiseNot => exec_bit_not(self), + pub fn unary_exec(&self, operator: ast::UnaryOperator) -> SimValue { + let new_value = match operator { + ast::UnaryOperator::BitwiseNot => self.0.exec_bit_not(), ast::UnaryOperator::Negative => todo!(), ast::UnaryOperator::Not => todo!(), ast::UnaryOperator::Positive => todo!(), - } + }; + Self(new_value) } } @@ -212,35 +186,19 @@ fn exec_like(pattern: &str, text: &str) -> bool { re.is_match(text) } -impl From<&ast::Literal> for Value { - fn from(value: &ast::Literal) -> Self { - match value { - ast::Literal::Null => Self::Null, - ast::Literal::Numeric(number) => Numeric::from(number).into(), - ast::Literal::String(string) => Self::Text(string.clone()), - ast::Literal::Blob(blob) => Self::Blob( - blob.as_bytes() - .chunks_exact(2) - .map(|pair| { - // We assume that sqlite3-parser has already validated that - // the input is valid hex string, thus unwrap is safe. - let hex_byte = std::str::from_utf8(pair).unwrap(); - u8::from_str_radix(hex_byte, 16).unwrap() - }) - .collect(), - ), - lit => unimplemented!("{:?}", lit), - } - } -} - -impl From for Value { +impl From for SimValue { fn from(value: ast::Literal) -> Self { - match value { - ast::Literal::Null => Self::Null, + Self::from(&value) + } +} + +impl From<&ast::Literal> for SimValue { + fn from(value: &ast::Literal) -> Self { + let new_value = match value { + ast::Literal::Null => types::Value::Null, ast::Literal::Numeric(number) => Numeric::from(number).into(), - ast::Literal::String(string) => Self::Text(string), - ast::Literal::Blob(blob) => Self::Blob( + ast::Literal::String(string) => types::Value::build_text(string), + ast::Literal::Blob(blob) => types::Value::Blob( blob.as_bytes() .chunks_exact(2) .map(|pair| { @@ -252,106 +210,55 @@ impl From for Value { .collect(), ), lit => unimplemented!("{:?}", lit), - } + }; + Self(new_value) } } -impl From for ast::Literal { - fn from(value: Value) -> Self { - match value { - Value::Null => Self::Null, - Value::Integer(i) => Self::Numeric(i.to_string()), - Value::Float(f) => Self::Numeric(f.to_string()), - Value::Text(string) => Self::String(format!("'{}'", string)), - Value::Blob(blob) => Self::Blob(hex::encode(blob)), - } - } -} - -impl From<&Value> for ast::Literal { - fn from(value: &Value) -> Self { - match value { - Value::Null => Self::Null, - Value::Integer(i) => Self::Numeric(i.to_string()), - Value::Float(f) => Self::Numeric(f.to_string()), - Value::Text(string) => Self::String(format!("'{}'", string)), - Value::Blob(blob) => Self::Blob(hex::encode(blob)), - } - } -} - -impl From for Value { - fn from(value: Numeric) -> Self { - match value { - Numeric::Null => Value::Null, - Numeric::Integer(i) => Value::Integer(i), - Numeric::Float(f) => Value::Float(f.into()), - } - } -} - -// Copied from numeric in Core -impl From for Numeric { - fn from(value: Value) -> Self { +impl From for ast::Literal { + fn from(value: SimValue) -> Self { Self::from(&value) } } -impl From<&Value> for Numeric { - fn from(value: &Value) -> Self { - match value { - Value::Null => Self::Null, - Value::Integer(v) => Self::Integer(*v), - Value::Float(v) => match NonNan::new(*v) { - Some(v) => Self::Float(v), - None => Self::Null, - }, - Value::Text(text) => Numeric::from(text.as_str()), - Value::Blob(blob) => { - let text = String::from_utf8_lossy(blob.as_slice()); - Numeric::from(&text) - } +impl From<&SimValue> for ast::Literal { + fn from(value: &SimValue) -> Self { + match &value.0 { + types::Value::Null => Self::Null, + types::Value::Integer(i) => Self::Numeric(i.to_string()), + types::Value::Float(f) => Self::Numeric(f.to_string()), + text @ types::Value::Text(..) => Self::String(format!("'{}'", text)), + types::Value::Blob(blob) => Self::Blob(hex::encode(blob)), } } } -impl From for Value { +impl From for SimValue { fn from(value: bool) -> Self { - value.then_some(Value::TRUE).unwrap_or(Value::FALSE) + value.then_some(SimValue::TRUE).unwrap_or(SimValue::FALSE) } } -// NullableInteger Code copied from Core. Ideally we should use Limbo's Core Value for everything to avoid code repetition -impl From for Value { - fn from(value: NullableInteger) -> Self { - match value { - NullableInteger::Null => Value::Null, - NullableInteger::Integer(v) => Value::Integer(v), - } +impl From for limbo_core::types::Value { + fn from(value: SimValue) -> Self { + value.0 } } -impl From for NullableInteger { - fn from(value: Value) -> Self { - Self::from(&value) +impl From<&SimValue> for limbo_core::types::Value { + fn from(value: &SimValue) -> Self { + value.0.clone() } } -impl From<&Value> for NullableInteger { - fn from(value: &Value) -> Self { - match value { - Value::Null => Self::Null, - Value::Integer(v) => Self::Integer(*v), - Value::Float(v) => Self::Integer(*v as i64), - Value::Text(text) => Self::from(text.as_str()), - Value::Blob(blob) => { - let text = String::from_utf8_lossy(blob.as_slice()); - Self::from(text) - } - } +impl From for SimValue { + fn from(value: limbo_core::types::Value) -> Self { + Self(value) } } -fn exec_bit_not(reg: &Value) -> Value { - (!NullableInteger::from(reg)).into() +impl From<&limbo_core::types::Value> for SimValue { + fn from(value: &limbo_core::types::Value) -> Self { + Self(value.clone()) + } } diff --git a/simulator/runner/differential.rs b/simulator/runner/differential.rs index 2da5d81ce..443b07675 100644 --- a/simulator/runner/differential.rs +++ b/simulator/runner/differential.rs @@ -1,11 +1,13 @@ use std::sync::{Arc, Mutex}; +use limbo_core::Value; + use crate::{ generation::{ pick_index, plan::{Interaction, InteractionPlanState, ResultSet}, }, - model::{query::Query, table::Value}, + model::{query::Query, table::SimValue}, runner::execution::ExecutionContinuation, InteractionPlan, }; @@ -60,7 +62,7 @@ pub(crate) fn run_simulation( fn execute_query_rusqlite( connection: &rusqlite::Connection, query: &Query, -) -> rusqlite::Result>> { +) -> rusqlite::Result>> { match query { Query::Create(create) => { connection.execute(create.to_string().as_str(), ())?; @@ -73,13 +75,14 @@ fn execute_query_rusqlite( let mut values = vec![]; for i in 0..columns { let value = row.get_unwrap(i); - match value { - rusqlite::types::Value::Null => values.push(Value::Null), - rusqlite::types::Value::Integer(i) => values.push(Value::Integer(i)), - rusqlite::types::Value::Real(f) => values.push(Value::Float(f)), - rusqlite::types::Value::Text(s) => values.push(Value::Text(s)), - rusqlite::types::Value::Blob(b) => values.push(Value::Blob(b)), - } + let value = match value { + rusqlite::types::Value::Null => Value::Null, + rusqlite::types::Value::Integer(i) => Value::Integer(i), + rusqlite::types::Value::Real(f) => Value::Float(f), + rusqlite::types::Value::Text(s) => Value::build_text(s), + rusqlite::types::Value::Blob(b) => Value::Blob(b), + }; + values.push(SimValue(value)); } Ok(values) })?;