mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 18:18:03 +00:00
add insert into <table> <select> generation
This commit is contained in:
parent
f5139f086e
commit
48d091e112
6 changed files with 242 additions and 136 deletions
|
@ -1,4 +1,7 @@
|
|||
use std::{iter::Sum, ops::SubAssign};
|
||||
use std::{
|
||||
iter::Sum,
|
||||
ops::SubAssign,
|
||||
};
|
||||
|
||||
use anarchist_readable_name_generator_lib::readable_name_custom;
|
||||
use rand::{distributions::uniform::SampleUniform, Rng};
|
||||
|
@ -60,6 +63,36 @@ pub(crate) fn one_of<'a, T, R: Rng>(choices: Vec<Box<dyn Fn(&mut R) -> T + 'a>>,
|
|||
choices[index](rng)
|
||||
}
|
||||
|
||||
/// backtrack is a helper function for composing different "failable" generators.
|
||||
/// The function takes a list of functions that return an Option<T>, along with number of retries
|
||||
/// to make before giving up.
|
||||
pub(crate) fn backtrack<'a, T, R: Rng>(
|
||||
mut choices: Vec<(u32, Box<dyn Fn(&mut R) -> Option<T> + 'a>)>,
|
||||
rng: &mut R,
|
||||
) -> T {
|
||||
loop {
|
||||
// If there are no more choices left, we give up
|
||||
let choices_ = choices
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, (retries, _))| *retries > 0)
|
||||
.collect::<Vec<_>>();
|
||||
if choices_.is_empty() {
|
||||
panic!("backtrack: no more choices left");
|
||||
}
|
||||
// Run a one_of on the remaining choices
|
||||
let (choice_index, choice) = pick(&choices_, rng);
|
||||
let choice_index = *choice_index;
|
||||
// If the choice returns None, we decrement the number of retries and try again
|
||||
let result = choice.1(rng);
|
||||
if let Some(result) = result {
|
||||
return result;
|
||||
} else {
|
||||
choices[choice_index].0 -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// pick is a helper function for uniformly picking a random element from a slice
|
||||
pub(crate) fn pick<'a, T, R: Rng>(choices: &'a [T], rng: &mut R) -> &'a T {
|
||||
let index = rng.gen_range(0..choices.len());
|
||||
|
|
|
@ -14,10 +14,7 @@ use crate::{
|
|||
|
||||
use crate::generation::{frequency, Arbitrary, ArbitraryFrom};
|
||||
|
||||
use super::{
|
||||
pick,
|
||||
property::{remaining, Property},
|
||||
};
|
||||
use super::property::{remaining, Property};
|
||||
|
||||
pub(crate) type ResultSet = Result<Vec<Vec<Value>>>;
|
||||
|
||||
|
@ -261,7 +258,7 @@ impl Interactions {
|
|||
match self {
|
||||
Interactions::Property(property) => {
|
||||
match property {
|
||||
Property::InsertSelect {
|
||||
Property::InsertValuesSelect {
|
||||
insert,
|
||||
row_index: _,
|
||||
queries,
|
||||
|
@ -284,7 +281,7 @@ impl Interactions {
|
|||
}
|
||||
Property::SelectLimit { select } => {
|
||||
select.shadow(env);
|
||||
},
|
||||
}
|
||||
}
|
||||
for interaction in property.interactions() {
|
||||
match interaction {
|
||||
|
@ -295,12 +292,16 @@ impl Interactions {
|
|||
}
|
||||
}
|
||||
Query::Insert(insert) => {
|
||||
let values = match &insert {
|
||||
Insert::Values { values, .. } => values.clone(),
|
||||
Insert::Select { select, .. } => select.shadow(env),
|
||||
};
|
||||
let table = env
|
||||
.tables
|
||||
.iter_mut()
|
||||
.find(|t| t.name == insert.table)
|
||||
.find(|t| t.name == insert.table())
|
||||
.unwrap();
|
||||
table.rows.extend(insert.values.clone());
|
||||
table.rows.extend(values);
|
||||
}
|
||||
Query::Delete(_) => todo!(),
|
||||
Query::Select(_) => {}
|
||||
|
@ -311,7 +312,9 @@ impl Interactions {
|
|||
}
|
||||
}
|
||||
}
|
||||
Interactions::Query(query) => query.shadow(env),
|
||||
Interactions::Query(query) => {
|
||||
query.shadow(env);
|
||||
}
|
||||
Interactions::Fault(_) => {}
|
||||
}
|
||||
}
|
||||
|
@ -392,12 +395,10 @@ impl ArbitraryFrom<&mut SimulatorEnv> for InteractionPlan {
|
|||
}
|
||||
|
||||
impl Interaction {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
match self {
|
||||
Self::Query(query) => query.shadow(env),
|
||||
Self::Assumption(_) => {}
|
||||
Self::Assertion(_) => {}
|
||||
Self::Fault(_) => {}
|
||||
Self::Assumption(_) | Self::Assertion(_) | Self::Fault(_) => vec![],
|
||||
}
|
||||
}
|
||||
pub(crate) fn execute_query(&self, conn: &mut Rc<Connection>) -> ResultSet {
|
||||
|
@ -548,12 +549,11 @@ fn create_table<R: rand::Rng>(rng: &mut R, _env: &SimulatorEnv) -> Interactions
|
|||
}
|
||||
|
||||
fn random_read<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Interactions {
|
||||
Interactions::Query(Query::Select(Select::arbitrary_from(rng, &env.tables)))
|
||||
Interactions::Query(Query::Select(Select::arbitrary_from(rng, env)))
|
||||
}
|
||||
|
||||
fn random_write<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Interactions {
|
||||
let table = pick(&env.tables, rng);
|
||||
let insert_query = Query::Insert(Insert::arbitrary_from(rng, table));
|
||||
let insert_query = Query::Insert(Insert::arbitrary_from(rng, env));
|
||||
Interactions::Query(insert_query)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::{
|
||||
model::{
|
||||
query::{Create, Delete, Insert, Predicate, Query, Select},
|
||||
query::{Create, Delete, Distinctness, Insert, Predicate, Query, Select},
|
||||
table::Value,
|
||||
},
|
||||
runner::env::SimulatorEnv,
|
||||
|
@ -34,7 +34,7 @@ pub(crate) enum Property {
|
|||
/// - The inserted row will not be deleted.
|
||||
/// - The inserted row will not be updated.
|
||||
/// - The table `t` will not be renamed, dropped, or altered.
|
||||
InsertSelect {
|
||||
InsertValuesSelect {
|
||||
/// The insert query
|
||||
insert: Insert,
|
||||
/// Selected row index
|
||||
|
@ -78,7 +78,7 @@ pub(crate) enum Property {
|
|||
impl Property {
|
||||
pub(crate) fn name(&self) -> String {
|
||||
match self {
|
||||
Property::InsertSelect { .. } => "Insert-Select".to_string(),
|
||||
Property::InsertValuesSelect { .. } => "Insert-Values-Select".to_string(),
|
||||
Property::DoubleCreateFailure { .. } => "Double-Create-Failure".to_string(),
|
||||
Property::SelectLimit { .. } => "Select-Limit".to_string(),
|
||||
}
|
||||
|
@ -88,26 +88,33 @@ impl Property {
|
|||
/// and `interaction` cannot be serialized directly.
|
||||
pub(crate) fn interactions(&self) -> Vec<Interaction> {
|
||||
match self {
|
||||
Property::InsertSelect {
|
||||
Property::InsertValuesSelect {
|
||||
insert,
|
||||
row_index,
|
||||
queries,
|
||||
select,
|
||||
} => {
|
||||
let (table, values) = if let Insert::Values { table, values } = insert {
|
||||
(table, values)
|
||||
} else {
|
||||
unreachable!(
|
||||
"insert query should be Insert::Values for Insert-Values-Select property"
|
||||
)
|
||||
};
|
||||
// Check that the insert query has at least 1 value
|
||||
assert!(
|
||||
!insert.values.is_empty(),
|
||||
!values.is_empty(),
|
||||
"insert query should have at least 1 value"
|
||||
);
|
||||
|
||||
// Pick a random row within the insert values
|
||||
let row = insert.values[*row_index].clone();
|
||||
let row = values[*row_index].clone();
|
||||
|
||||
// Assume that the table exists
|
||||
let assumption = Interaction::Assumption(Assertion {
|
||||
message: format!("table {} exists", insert.table),
|
||||
message: format!("table {} exists", insert.table()),
|
||||
func: Box::new({
|
||||
let table_name = insert.table.clone();
|
||||
let table_name = table.clone();
|
||||
move |_: &Vec<ResultSet>, env: &SimulatorEnv| {
|
||||
Ok(env.tables.iter().any(|t| t.name == table_name))
|
||||
}
|
||||
|
@ -118,7 +125,7 @@ impl Property {
|
|||
message: format!(
|
||||
"row [{:?}] not found in table {}",
|
||||
row.iter().map(|v| v.to_string()).collect::<Vec<String>>(),
|
||||
insert.table,
|
||||
insert.table(),
|
||||
),
|
||||
func: Box::new(move |stack: &Vec<ResultSet>, _: &SimulatorEnv| {
|
||||
let rows = stack.last().unwrap();
|
||||
|
@ -189,7 +196,7 @@ impl Property {
|
|||
}),
|
||||
});
|
||||
|
||||
let limit = select.limit.clone().unwrap_or(0);
|
||||
let limit = select.limit.unwrap_or(0);
|
||||
|
||||
let assertion = Interaction::Assertion(Assertion {
|
||||
message: "select query should respect the limit clause".to_string(),
|
||||
|
@ -212,6 +219,7 @@ impl Property {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Remaining {
|
||||
pub(crate) read: f64,
|
||||
pub(crate) write: f64,
|
||||
|
@ -236,7 +244,7 @@ pub(crate) fn remaining(env: &SimulatorEnv, stats: &InteractionStats) -> Remaini
|
|||
}
|
||||
}
|
||||
|
||||
fn property_insert_select<R: rand::Rng>(
|
||||
fn property_insert_values_select<R: rand::Rng>(
|
||||
rng: &mut R,
|
||||
env: &SimulatorEnv,
|
||||
remaining: &Remaining,
|
||||
|
@ -253,7 +261,7 @@ fn property_insert_select<R: rand::Rng>(
|
|||
let row = rows[row_index].clone();
|
||||
|
||||
// Insert the rows
|
||||
let insert_query = Insert {
|
||||
let insert_query = Insert::Values {
|
||||
table: table.name.clone(),
|
||||
values: rows,
|
||||
};
|
||||
|
@ -265,7 +273,7 @@ fn property_insert_select<R: rand::Rng>(
|
|||
// - [ ] The inserted row will not be updated. (todo: add this constraint once UPDATE is implemented)
|
||||
// - [ ] The table `t` will not be renamed, dropped, or altered. (todo: add this constraint once ALTER or DROP is implemented)
|
||||
for _ in 0..rng.gen_range(0..3) {
|
||||
let query = Query::arbitrary_from(rng, (table, remaining));
|
||||
let query = Query::arbitrary_from(rng, (env, remaining));
|
||||
match &query {
|
||||
Query::Delete(Delete {
|
||||
table: t,
|
||||
|
@ -293,9 +301,10 @@ fn property_insert_select<R: rand::Rng>(
|
|||
table: table.name.clone(),
|
||||
predicate: Predicate::arbitrary_from(rng, (table, &row)),
|
||||
limit: None,
|
||||
distinct: Distinctness::All,
|
||||
};
|
||||
|
||||
Property::InsertSelect {
|
||||
Property::InsertValuesSelect {
|
||||
insert: insert_query,
|
||||
row_index,
|
||||
queries,
|
||||
|
@ -303,10 +312,7 @@ fn property_insert_select<R: rand::Rng>(
|
|||
}
|
||||
}
|
||||
|
||||
fn property_select_limit<R: rand::Rng>(
|
||||
rng: &mut R,
|
||||
env: &SimulatorEnv,
|
||||
) -> Property {
|
||||
fn property_select_limit<R: rand::Rng>(rng: &mut R, env: &SimulatorEnv) -> Property {
|
||||
// Get a random table
|
||||
let table = pick(&env.tables, rng);
|
||||
// Select the table
|
||||
|
@ -314,6 +320,7 @@ fn property_select_limit<R: rand::Rng>(
|
|||
table: table.name.clone(),
|
||||
predicate: Predicate::arbitrary_from(rng, table),
|
||||
limit: Some(rng.gen_range(1..=5)),
|
||||
distinct: Distinctness::All,
|
||||
};
|
||||
Property::SelectLimit { select }
|
||||
}
|
||||
|
@ -336,7 +343,7 @@ fn property_double_create_failure<R: rand::Rng>(
|
|||
// - [x] There will be no errors in the middle interactions.(best effort)
|
||||
// - [ ] Table `t` will not be renamed or dropped.(todo: add this constraint once ALTER or DROP is implemented)
|
||||
for _ in 0..rng.gen_range(0..3) {
|
||||
let query = Query::arbitrary_from(rng, (table, remaining));
|
||||
let query = Query::arbitrary_from(rng, (env, remaining));
|
||||
match &query {
|
||||
Query::Create(Create { table: t }) => {
|
||||
// There will be no errors in the middle interactions.
|
||||
|
@ -366,7 +373,7 @@ impl ArbitraryFrom<(&SimulatorEnv, &InteractionStats)> for Property {
|
|||
vec![
|
||||
(
|
||||
f64::min(remaining_.read, remaining_.write),
|
||||
Box::new(|rng: &mut R| property_insert_select(rng, env, &remaining_)),
|
||||
Box::new(|rng: &mut R| property_insert_values_select(rng, env, &remaining_)),
|
||||
),
|
||||
(
|
||||
remaining_.create / 2.0,
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use crate::generation::table::{GTValue, LTValue};
|
||||
use crate::generation::{one_of, Arbitrary, ArbitraryFrom};
|
||||
|
||||
use crate::model::query::{Create, Delete, Insert, Predicate, Query, Select};
|
||||
use crate::model::query::{Create, Delete, Distinctness, Insert, Predicate, Query, Select};
|
||||
use crate::model::table::{Table, Value};
|
||||
use crate::SimulatorEnv;
|
||||
use rand::seq::SliceRandom as _;
|
||||
use rand::Rng;
|
||||
|
||||
use super::property::Remaining;
|
||||
use super::{frequency, pick};
|
||||
use super::{backtrack, frequency, pick};
|
||||
|
||||
impl Arbitrary for Create {
|
||||
fn arbitrary<R: Rng>(rng: &mut R) -> Self {
|
||||
|
@ -17,81 +18,85 @@ impl Arbitrary for Create {
|
|||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<&Vec<Table>> for Select {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, tables: &Vec<Table>) -> Self {
|
||||
let table = pick(tables, rng);
|
||||
impl ArbitraryFrom<&SimulatorEnv> for Select {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, env: &SimulatorEnv) -> Self {
|
||||
let table = pick(&env.tables, rng);
|
||||
Self {
|
||||
table: table.name.clone(),
|
||||
predicate: Predicate::arbitrary_from(rng, table),
|
||||
limit: Some(rng.gen_range(0..=1000)),
|
||||
distinct: Distinctness::All,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<&Vec<&Table>> for Select {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, tables: &Vec<&Table>) -> Self {
|
||||
let table = pick(tables, rng);
|
||||
Self {
|
||||
table: table.name.clone(),
|
||||
predicate: Predicate::arbitrary_from(rng, *table),
|
||||
limit: Some(rng.gen_range(0..=1000)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<&Table> for Insert {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, table: &Table) -> Self {
|
||||
let num_rows = rng.gen_range(1..10);
|
||||
let values: Vec<Vec<Value>> = (0..num_rows)
|
||||
.map(|_| {
|
||||
table
|
||||
.columns
|
||||
.iter()
|
||||
.map(|c| Value::arbitrary_from(rng, &c.column_type))
|
||||
.collect()
|
||||
impl ArbitraryFrom<&SimulatorEnv> for Insert {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, env: &SimulatorEnv) -> Self {
|
||||
let gen_values = |rng: &mut R| {
|
||||
let table = pick(&env.tables, rng);
|
||||
let num_rows = rng.gen_range(1..10);
|
||||
let values: Vec<Vec<Value>> = (0..num_rows)
|
||||
.map(|_| {
|
||||
table
|
||||
.columns
|
||||
.iter()
|
||||
.map(|c| Value::arbitrary_from(rng, &c.column_type))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
Some(Insert::Values {
|
||||
table: table.name.clone(),
|
||||
values,
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
table: table.name.clone(),
|
||||
values,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
impl ArbitraryFrom<&Table> for Delete {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, table: &Table) -> Self {
|
||||
Self {
|
||||
table: table.name.clone(),
|
||||
predicate: Predicate::arbitrary_from(rng, table),
|
||||
}
|
||||
}
|
||||
}
|
||||
let _gen_select = |rng: &mut R| {
|
||||
// Find a non-empty table
|
||||
let table = env.tables.iter().find(|t| !t.rows.is_empty());
|
||||
if table.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<&Table> for Query {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, table: &Table) -> Self {
|
||||
frequency(
|
||||
let select_table = table.unwrap();
|
||||
let row = pick(&select_table.rows, rng);
|
||||
let predicate = Predicate::arbitrary_from(rng, (select_table, row));
|
||||
// Pick another table to insert into
|
||||
let select = Select {
|
||||
table: select_table.name.clone(),
|
||||
predicate,
|
||||
limit: None,
|
||||
distinct: Distinctness::All,
|
||||
};
|
||||
let table = pick(&env.tables, rng);
|
||||
Some(Insert::Select {
|
||||
table: table.name.clone(),
|
||||
select: Box::new(select),
|
||||
})
|
||||
};
|
||||
|
||||
backtrack(
|
||||
vec![
|
||||
(1, Box::new(|rng| Self::Create(Create::arbitrary(rng)))),
|
||||
(
|
||||
100,
|
||||
Box::new(|rng| Self::Select(Select::arbitrary_from(rng, &vec![table]))),
|
||||
),
|
||||
(
|
||||
100,
|
||||
Box::new(|rng| Self::Insert(Insert::arbitrary_from(rng, table))),
|
||||
),
|
||||
(
|
||||
0,
|
||||
Box::new(|rng| Self::Delete(Delete::arbitrary_from(rng, table))),
|
||||
),
|
||||
(1, Box::new(|rng| gen_values(rng))),
|
||||
// todo: test and enable this once `INSERT INTO <table> SELECT * FROM <table>` is supported
|
||||
// (1, Box::new(|rng| gen_select(rng))),
|
||||
],
|
||||
rng,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<(&Table, &Remaining)> for Query {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, (table, remaining): (&Table, &Remaining)) -> Self {
|
||||
impl ArbitraryFrom<&SimulatorEnv> for Delete {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, env: &SimulatorEnv) -> Self {
|
||||
let table = pick(&env.tables, rng);
|
||||
Self {
|
||||
table: table.name.clone(),
|
||||
predicate: Predicate::arbitrary_from(rng, table),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArbitraryFrom<(&SimulatorEnv, &Remaining)> for Query {
|
||||
fn arbitrary_from<R: Rng>(rng: &mut R, (env, remaining): (&SimulatorEnv, &Remaining)) -> Self {
|
||||
frequency(
|
||||
vec![
|
||||
(
|
||||
|
@ -100,15 +105,15 @@ impl ArbitraryFrom<(&Table, &Remaining)> for Query {
|
|||
),
|
||||
(
|
||||
remaining.read,
|
||||
Box::new(|rng| Self::Select(Select::arbitrary_from(rng, &vec![table]))),
|
||||
Box::new(|rng| Self::Select(Select::arbitrary_from(rng, env))),
|
||||
),
|
||||
(
|
||||
remaining.write,
|
||||
Box::new(|rng| Self::Insert(Insert::arbitrary_from(rng, table))),
|
||||
Box::new(|rng| Self::Insert(Insert::arbitrary_from(rng, env))),
|
||||
),
|
||||
(
|
||||
0.0,
|
||||
Box::new(|rng| Self::Delete(Delete::arbitrary_from(rng, table))),
|
||||
Box::new(|rng| Self::Delete(Delete::arbitrary_from(rng, env))),
|
||||
),
|
||||
],
|
||||
rng,
|
||||
|
|
|
@ -101,7 +101,8 @@ impl Query {
|
|||
match self {
|
||||
Query::Create(_) => vec![],
|
||||
Query::Select(Select { table, .. })
|
||||
| Query::Insert(Insert { table, .. })
|
||||
| Query::Insert(Insert::Select { table, .. })
|
||||
| Query::Insert(Insert::Values { table, .. })
|
||||
| Query::Delete(Delete { table, .. }) => vec![table.clone()],
|
||||
}
|
||||
}
|
||||
|
@ -109,12 +110,13 @@ impl Query {
|
|||
match self {
|
||||
Query::Create(Create { table }) => vec![table.name.clone()],
|
||||
Query::Select(Select { table, .. })
|
||||
| Query::Insert(Insert { table, .. })
|
||||
| Query::Insert(Insert::Select { table, .. })
|
||||
| Query::Insert(Insert::Values { table, .. })
|
||||
| Query::Delete(Delete { table, .. }) => vec![table.clone()],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
match self {
|
||||
Query::Create(create) => create.shadow(env),
|
||||
Query::Insert(insert) => insert.shadow(env),
|
||||
|
@ -129,34 +131,110 @@ pub(crate) struct Create {
|
|||
}
|
||||
|
||||
impl Create {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
if !env.tables.iter().any(|t| t.name == self.table.name) {
|
||||
env.tables.push(self.table.clone());
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Create {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "CREATE TABLE {} (", self.table.name)?;
|
||||
|
||||
for (i, column) in self.table.columns.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ",")?;
|
||||
}
|
||||
write!(f, "{} {}", column.name, column.column_type)?;
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
/// `SELECT` distinctness
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Distinctness {
|
||||
/// `DISTINCT`
|
||||
Distinct,
|
||||
/// `ALL`
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) struct Select {
|
||||
pub(crate) table: String,
|
||||
pub(crate) predicate: Predicate,
|
||||
pub(crate) distinct: Distinctness,
|
||||
pub(crate) limit: Option<usize>,
|
||||
}
|
||||
|
||||
impl Select {
|
||||
pub(crate) fn shadow(&self, _env: &mut SimulatorEnv) {}
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
let table = env.tables.iter().find(|t| t.name == self.table.as_str());
|
||||
if let Some(table) = table {
|
||||
table
|
||||
.rows
|
||||
.iter()
|
||||
.filter(|row| self.predicate.test(row, table))
|
||||
.cloned()
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Select {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"SELECT * FROM {} WHERE {}{}",
|
||||
self.table,
|
||||
self.predicate,
|
||||
self.limit
|
||||
.map_or("".to_string(), |l| format!(" LIMIT {}", l))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) struct Insert {
|
||||
pub(crate) table: String,
|
||||
pub(crate) values: Vec<Vec<Value>>,
|
||||
pub(crate) enum Insert {
|
||||
Values {
|
||||
table: String,
|
||||
values: Vec<Vec<Value>>,
|
||||
},
|
||||
Select {
|
||||
table: String,
|
||||
select: Box<Select>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Insert {
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) {
|
||||
if let Some(t) = env.tables.iter_mut().find(|t| t.name == self.table) {
|
||||
t.rows.extend(self.values.clone());
|
||||
pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
match self {
|
||||
Insert::Values { table, values } => {
|
||||
if let Some(t) = env.tables.iter_mut().find(|t| &t.name == table) {
|
||||
t.rows.extend(values.clone());
|
||||
}
|
||||
}
|
||||
Insert::Select { table, select } => {
|
||||
let rows = select.shadow(env);
|
||||
if let Some(t) = env.tables.iter_mut().find(|t| &t.name == table) {
|
||||
t.rows.extend(rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub(crate) fn table(&self) -> &str {
|
||||
match self {
|
||||
Insert::Values { table, .. } | Insert::Select { table, .. } => table,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +246,7 @@ pub(crate) struct Delete {
|
|||
}
|
||||
|
||||
impl Delete {
|
||||
pub(crate) fn shadow(&self, _env: &mut SimulatorEnv) {
|
||||
pub(crate) fn shadow(&self, _env: &mut SimulatorEnv) -> Vec<Vec<Value>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
@ -176,30 +254,9 @@ impl Delete {
|
|||
impl Display for Query {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Create(Create { table }) => {
|
||||
write!(f, "CREATE TABLE {} (", table.name)?;
|
||||
|
||||
for (i, column) in table.columns.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ",")?;
|
||||
}
|
||||
write!(f, "{} {}", column.name, column.column_type)?;
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
Self::Select(Select {
|
||||
table,
|
||||
predicate: guard,
|
||||
limit,
|
||||
}) => write!(
|
||||
f,
|
||||
"SELECT * FROM {} WHERE {}{}",
|
||||
table,
|
||||
guard,
|
||||
limit.map_or("".to_string(), |l| format!(" LIMIT {}", l))
|
||||
),
|
||||
Self::Insert(Insert { table, values }) => {
|
||||
Self::Create(create) => write!(f, "{}", create),
|
||||
Self::Select(select) => write!(f, "{}", select),
|
||||
Self::Insert(Insert::Values { table, values }) => {
|
||||
write!(f, "INSERT INTO {} VALUES ", table)?;
|
||||
for (i, row) in values.iter().enumerate() {
|
||||
if i != 0 {
|
||||
|
@ -216,6 +273,10 @@ impl Display for Query {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
Self::Insert(Insert::Select { table, select }) => {
|
||||
write!(f, "INSERT INTO {} ", table)?;
|
||||
write!(f, "{}", select)
|
||||
}
|
||||
Self::Delete(Delete {
|
||||
table,
|
||||
predicate: guard,
|
||||
|
|
|
@ -30,7 +30,7 @@ impl InteractionPlan {
|
|||
for interaction in plan.plan.iter_mut() {
|
||||
if let Interactions::Property(p) = interaction {
|
||||
match p {
|
||||
Property::InsertSelect { queries, .. }
|
||||
Property::InsertValuesSelect { queries, .. }
|
||||
| Property::DoubleCreateFailure { queries, .. } => {
|
||||
queries.clear();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue