From 65fe60ba57a1771b464c9ae934a949bdb11dcdc8 Mon Sep 17 00:00:00 2001 From: alpaylan Date: Fri, 11 Jul 2025 02:04:14 -0400 Subject: [PATCH] fix the merge conflicts --- simulator/generation/mod.rs | 4 +- simulator/generation/plan.rs | 14 ++-- simulator/generation/property.rs | 70 +++++++++---------- simulator/generation/query.rs | 16 +++-- simulator/model/query/create.rs | 4 +- simulator/model/query/create_index.rs | 6 +- simulator/model/query/delete.rs | 6 +- simulator/model/query/drop.rs | 6 +- simulator/model/query/insert.rs | 8 +-- simulator/model/query/mod.rs | 16 ++--- simulator/model/query/select.rs | 10 +-- simulator/model/query/transaction.rs | 29 +++++--- simulator/model/query/update.rs | 6 +- simulator/runner/env.rs | 39 +++++++++-- vendored/sqlite3-parser/src/parser/ast/mod.rs | 1 + 15 files changed, 137 insertions(+), 98 deletions(-) diff --git a/simulator/generation/mod.rs b/simulator/generation/mod.rs index 7120dc5d5..e51b4414d 100644 --- a/simulator/generation/mod.rs +++ b/simulator/generation/mod.rs @@ -3,7 +3,7 @@ use std::{iter::Sum, ops::SubAssign}; use anarchist_readable_name_generator_lib::readable_name_custom; use rand::{distributions::uniform::SampleUniform, Rng}; -use crate::model::table::Table; +use crate::runner::env::SimulatorTables; mod expr; pub mod plan; @@ -50,7 +50,7 @@ pub trait ArbitraryFromMaybe { /// might return a vector of rows that were inserted into the table. pub(crate) trait Shadow { type Result; - fn shadow(&self, tables: &mut Vec) -> Self::Result; + fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result; } /// Frequency is a helper function for composing different generators with different frequency diff --git a/simulator/generation/plan.rs b/simulator/generation/plan.rs index 8570363cb..2e608bc70 100644 --- a/simulator/generation/plan.rs +++ b/simulator/generation/plan.rs @@ -8,16 +8,16 @@ use std::{ use serde::{Deserialize, Serialize}; -use turso_core::{Connection, Result, StepResult, IO}; +use turso_core::{Connection, Result, StepResult}; use crate::{ generation::Shadow, model::{ query::{update::Update, Create, CreateIndex, Delete, Drop, Insert, Query, Select}, - table::{SimValue, Table}, + table::SimValue, }, runner::{ - env::{SimConnection, SimulationType}, + env::{SimConnection, SimulationType, SimulatorTables}, io::SimulatorIO, }, SimulatorEnv, @@ -114,7 +114,7 @@ pub(crate) enum Interactions { impl Shadow for Interactions { type Result = (); - fn shadow(&self, tables: &mut Vec
) { + fn shadow(&self, tables: &mut SimulatorTables) { match self { Interactions::Property(property) => { let initial_tables = tables.clone(); @@ -423,7 +423,7 @@ impl ArbitraryFrom<&mut SimulatorEnv> for InteractionPlan { impl Shadow for Interaction { type Result = anyhow::Result>>; - fn shadow(&self, env: &mut Vec
) -> Self::Result { + fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { match self { Self::Query(query) => query.shadow(env), Self::FsyncQuery(query) => { @@ -438,7 +438,7 @@ impl Shadow for Interaction { } } impl Interaction { - pub(crate) fn execute_query(&self, conn: &mut Arc, io: &SimulatorIO) -> ResultSet { + pub(crate) fn execute_query(&self, conn: &mut Arc, _io: &SimulatorIO) -> ResultSet { if let Self::Query(query) = self { let query_str = query.to_string(); let rows = conn.query(&query_str); @@ -737,7 +737,7 @@ fn random_create(rng: &mut R, _env: &SimulatorEnv) -> Interactions } fn random_read(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.tables.tables))) } fn random_write(rng: &mut R, env: &SimulatorEnv) -> Interactions { diff --git a/simulator/generation/property.rs b/simulator/generation/property.rs index e355ba630..78e999280 100644 --- a/simulator/generation/property.rs +++ b/simulator/generation/property.rs @@ -3,22 +3,16 @@ use turso_core::LimboError; use turso_sqlite3_parser::ast::{self}; use crate::{ - model::{ + generation::Shadow as _, model::{ query::{ - predicate::Predicate, - select::{ + predicate::Predicate, select::{ CompoundOperator, CompoundSelect, Distinctness, ResultColumn, SelectBody, SelectInner, - }, - select::{Distinctness, ResultColumn}, - transaction::{Begin, Commit, Rollback}, - update::Update, - Create, Delete, Drop, Insert, Query, Select, + }, transaction::{Begin, Commit, Rollback}, update::Update, Create, Delete, Drop, Insert, Query, Select }, table::SimValue, FAULT_ERROR_MSG, - }, - runner::env::SimulatorEnv, + }, runner::env::SimulatorEnv }; use super::{ @@ -285,17 +279,17 @@ impl Property { let table_name = create.table.name.clone(); let assertion = Interaction::Assertion(Assertion { - message: - "creating two tables with the name should result in a failure for the second query" - .to_string(), - func: Box::new(move |stack: &Vec, _: &SimulatorEnv| { - let last = stack.last().unwrap(); - match last { - Ok(_) => Ok(false), - Err(e) => Ok(e.to_string().to_lowercase().contains(&format!("table {table_name} already exists"))), - } - }), - }); + message: + "creating two tables with the name should result in a failure for the second query" + .to_string(), + func: Box::new(move |stack: &Vec, _| { + let last = stack.last().unwrap(); + match last { + Ok(_) => Ok(false), + Err(e) => Ok(e.to_string().to_lowercase().contains(&format!("table {table_name} already exists"))), + } + }), + }); let mut interactions = Vec::new(); interactions.push(assumption); @@ -318,7 +312,7 @@ impl Property { ), func: Box::new({ let table_name = select.dependencies(); - move |_: &Vec, env: &SimulatorEnv| { + move |_: &Vec, env: &mut SimulatorEnv| { Ok(table_name .iter() .all(|table| env.tables.iter().any(|t| t.name == *table))) @@ -373,8 +367,8 @@ impl Property { ))); let assertion = Interaction::Assertion(Assertion { - message: format!("`{select}` should return no values for table `{table}`",), - func: Box::new(move |stack: &Vec, _: &SimulatorEnv| { + message: format!("`{}` should return no values for table `{}`", select, table,), + func: Box::new(move |stack: &Vec, _| { let rows = stack.last().unwrap(); match rows { Ok(rows) => Ok(rows.is_empty()), @@ -410,8 +404,11 @@ impl Property { let table_name = table.clone(); let assertion = Interaction::Assertion(Assertion { - message: format!("select query should result in an error for table '{table}'"), - func: Box::new(move |stack: &Vec, _: &SimulatorEnv| { + message: format!( + "select query should result in an error for table '{}'", + table + ), + func: Box::new(move |stack: &Vec, _| { let last = stack.last().unwrap(); match last { Ok(_) => Ok(false), @@ -471,8 +468,8 @@ impl Property { // If rows1 results have more than 1 column, there is a problem if rows1.iter().any(|vs| vs.len() > 1) { return Err(LimboError::InternalError( - "Select query without the star should return only one column".to_string(), - )); + "Select query without the star should return only one column".to_string(), + )); } // Count the 1s in the select query without the star let rows1_count = rows1 @@ -517,11 +514,11 @@ impl Property { // then when IO is called the fault triggers. It may happen that a fault is injected // but no IO happens right after it message: "fault occured".to_string(), - func: Box::new(move |stack, env| { + func: Box::new(move |stack, env: &mut SimulatorEnv| { let last = stack.last().unwrap(); match last { Ok(_) => { - query_clone.shadow(env); + let _ = query_clone.shadow(&mut env.tables); Ok(true) } Err(err) => { @@ -554,7 +551,7 @@ impl Property { ), func: Box::new({ let tables = select.dependencies(); - move |_: &Vec, env: &SimulatorEnv| { + move |_: &Vec, env: &mut SimulatorEnv| { Ok(tables .iter() .all(|table| env.tables.iter().any(|t| t.name == *table))) @@ -612,7 +609,7 @@ impl Property { // select and select_tlp should return the same rows let assertion = Interaction::Assertion(Assertion { message: "select and select_tlp should return the same rows".to_string(), - func: Box::new(move |stack: &Vec, _: &SimulatorEnv| { + func: Box::new(move |stack: &Vec, _: &mut SimulatorEnv| { if stack.len() < 2 { return Err(LimboError::InternalError( "Not enough result sets on the stack".to_string(), @@ -681,7 +678,7 @@ impl Property { Interaction::Query(Query::Select(s3.clone())), Interaction::Assertion(Assertion { message: "UNION ALL should preserve cardinality".to_string(), - func: Box::new(move |stack: &Vec, _: &SimulatorEnv| { + func: Box::new(move |stack: &Vec, _: &mut SimulatorEnv| { if stack.len() < 3 { return Err(LimboError::InternalError( "Not enough result sets on the stack".to_string(), @@ -720,15 +717,14 @@ fn assert_all_table_values(tables: &[String]) -> impl Iterator, env: &mut SimulatorEnv| { let table = env.tables.iter().find(|t| t.name == table).ok_or_else(|| { LimboError::InternalError(format!( - "table {table} should exist in simulator env", + "table {} should exist in simulator env", + table )) })?; let last = stack.last().unwrap(); diff --git a/simulator/generation/query.rs b/simulator/generation/query.rs index 39e6acc53..1f0f2578e 100644 --- a/simulator/generation/query.rs +++ b/simulator/generation/query.rs @@ -7,6 +7,7 @@ use crate::model::query::select::{ use crate::model::query::update::Update; use crate::model::query::{Create, Delete, Drop, Insert, Query, Select}; use crate::model::table::{SimValue, Table}; +use crate::runner::env::SimulatorTables; use crate::SimulatorEnv; use itertools::Itertools; use rand::Rng; @@ -77,7 +78,10 @@ impl ArbitraryFrom<&Vec
> for FromClause { impl ArbitraryFrom<&Vec
> for SelectInner { fn arbitrary_from(rng: &mut R, tables: &Vec
) -> Self { let from = FromClause::arbitrary_from(rng, tables); - let mut tables = tables.clone(); + let mut tables = SimulatorTables { + tables: tables.clone(), + snapshot: None, + }; // todo: this is a temporary hack because env is not separated from the tables let join_table = from .shadow(&mut tables) @@ -180,7 +184,10 @@ impl ArbitraryFrom<&SimulatorEnv> for Insert { // Backtrack here cannot return None backtrack( - vec![(1, Box::new(gen_values)), (1, Box::new(gen_select))], + vec![ + (1, Box::new(gen_values)), + (1, Box::new(|rng| gen_select(rng))), + ], rng, ) .unwrap() @@ -259,14 +266,15 @@ impl ArbitraryFrom<&SimulatorEnv> for Update { #[cfg(test)] mod query_generation_tests { - + use rand::RngCore; use turso_core::Value; use turso_sqlite3_parser::to_sql_string::ToSqlString; use super::*; - + use crate::model::query::predicate::Predicate; use crate::model::query::EmptyContext; use crate::model::table::{Column, ColumnType}; + use crate::SimulatorEnv; #[test] fn test_select_query_generation() { diff --git a/simulator/model/query/create.rs b/simulator/model/query/create.rs index 777e53173..fe8604d2c 100644 --- a/simulator/model/query/create.rs +++ b/simulator/model/query/create.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{ generation::Shadow, - model::table::{SimValue, Table}, + model::table::{SimValue, Table}, runner::env::SimulatorTables, }; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -15,7 +15,7 @@ pub(crate) struct Create { impl Shadow for Create { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut Vec
) -> Self::Result { + fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { if !tables.iter().any(|t| t.name == self.table.name) { tables.push(self.table.clone()); Ok(vec![]) diff --git a/simulator/model/query/create_index.rs b/simulator/model/query/create_index.rs index 922f0a5b2..57715131e 100644 --- a/simulator/model/query/create_index.rs +++ b/simulator/model/query/create_index.rs @@ -1,7 +1,7 @@ use crate::{ generation::{gen_random_text, pick, pick_n_unique, ArbitraryFrom, Shadow}, - model::table::{SimValue, Table}, - runner::env::SimulatorEnv, + model::table::SimValue, + runner::env::{SimulatorEnv, SimulatorTables}, }; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -30,7 +30,7 @@ pub(crate) struct CreateIndex { impl Shadow for CreateIndex { type Result = Vec>; - fn shadow(&self, _env: &mut Vec
) -> Vec> { + fn shadow(&self, _env: &mut SimulatorTables) -> 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 bd54f7a44..70e4c890b 100644 --- a/simulator/model/query/delete.rs +++ b/simulator/model/query/delete.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{ generation::Shadow, - model::table::{SimValue, Table}, + model::table::SimValue, runner::env::SimulatorTables, }; use super::predicate::Predicate; @@ -18,8 +18,8 @@ pub(crate) struct Delete { impl Shadow for Delete { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut Vec
) -> Self::Result { - let table = tables.iter_mut().find(|t| t.name == self.table); + fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + let table = tables.tables.iter_mut().find(|t| t.name == self.table); if let Some(table) = table { // If the table exists, we can delete from it diff --git a/simulator/model/query/drop.rs b/simulator/model/query/drop.rs index 261de0cdc..d5979a9f4 100644 --- a/simulator/model/query/drop.rs +++ b/simulator/model/query/drop.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{ generation::Shadow, - model::table::{SimValue, Table}, + model::table::SimValue, runner::env::SimulatorTables, }; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -15,7 +15,7 @@ pub(crate) struct Drop { impl Shadow for Drop { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut Vec
) -> Self::Result { + fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { if !tables.iter().any(|t| t.name == self.table) { // If the table does not exist, we return an error return Err(anyhow::anyhow!( @@ -24,7 +24,7 @@ impl Shadow for Drop { )); } - tables.retain(|t| t.name != self.table); + tables.tables.retain(|t| t.name != self.table); Ok(vec![]) } diff --git a/simulator/model/query/insert.rs b/simulator/model/query/insert.rs index 8bdd375d7..ef7f68bb4 100644 --- a/simulator/model/query/insert.rs +++ b/simulator/model/query/insert.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{ generation::Shadow, - model::table::{SimValue, Table}, + model::table::SimValue, runner::env::SimulatorTables, }; use super::select::Select; @@ -24,10 +24,10 @@ pub(crate) enum Insert { impl Shadow for Insert { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut Vec
) -> Self::Result { + fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { match self { Insert::Values { table, values } => { - if let Some(t) = tables.iter_mut().find(|t| &t.name == table) { + if let Some(t) = tables.tables.iter_mut().find(|t| &t.name == table) { t.rows.extend(values.clone()); } else { return Err(anyhow::anyhow!( @@ -38,7 +38,7 @@ impl Shadow for Insert { } Insert::Select { table, select } => { let rows = select.shadow(tables)?; - if let Some(t) = tables.iter_mut().find(|t| &t.name == table) { + if let Some(t) = tables.tables.iter_mut().find(|t| &t.name == table) { t.rows.extend(rows); } else { return Err(anyhow::anyhow!( diff --git a/simulator/model/query/mod.rs b/simulator/model/query/mod.rs index d3e6fd45f..ee90a26a1 100644 --- a/simulator/model/query/mod.rs +++ b/simulator/model/query/mod.rs @@ -12,12 +12,8 @@ use update::Update; use crate::{ generation::Shadow, - model::table::{SimValue, Table}, - model::{ - query::transaction::{Begin, Commit, Rollback}, - table::SimValue, - }, - runner::env::SimulatorEnv, + model::{query::transaction::{Begin, Commit, Rollback}, table::SimValue}, + runner::env::SimulatorTables, }; pub mod create; @@ -79,7 +75,7 @@ impl Query { impl Shadow for Query { type Result = anyhow::Result>>; - fn shadow(&self, env: &mut Vec
) -> Self::Result { + fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { match self { Query::Create(create) => create.shadow(env), Query::Insert(insert) => insert.shadow(env), @@ -88,9 +84,9 @@ impl Shadow for Query { Query::Update(update) => update.shadow(env), Query::Drop(drop) => drop.shadow(env), Query::CreateIndex(create_index) => Ok(create_index.shadow(env)), - Query::Begin(begin) => begin.shadow(env), - Query::Commit(commit) => commit.shadow(env), - Query::Rollback(rollback) => rollback.shadow(env), + Query::Begin(begin) => Ok(begin.shadow(env)), + Query::Commit(commit) => Ok(commit.shadow(env)), + Query::Rollback(rollback) => Ok(rollback.shadow(env)), } } } diff --git a/simulator/model/query/select.rs b/simulator/model/query/select.rs index 862cc99fb..18939370b 100644 --- a/simulator/model/query/select.rs +++ b/simulator/model/query/select.rs @@ -11,7 +11,7 @@ use crate::{ model::{ query::EmptyContext, table::{SimValue, Table}, - }, + }, runner::env::SimulatorTables, }; use super::predicate::Predicate; @@ -253,7 +253,9 @@ impl JoinTable { impl Shadow for FromClause { type Result = anyhow::Result; - fn shadow(&self, tables: &mut Vec
) -> Self::Result { + fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { + let tables = &mut env.tables; + let first_table = tables .iter() .find(|t| t.name == self.table) @@ -309,7 +311,7 @@ impl Shadow for FromClause { impl Shadow for SelectInner { type Result = anyhow::Result; - fn shadow(&self, env: &mut Vec
) -> Self::Result { + fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { let mut join_table = self.from.shadow(env)?; let as_table = join_table.clone().into_table(); for row in &mut join_table.rows { @@ -336,7 +338,7 @@ impl Shadow for SelectInner { impl Shadow for Select { type Result = anyhow::Result>>; - fn shadow(&self, env: &mut Vec
) -> Self::Result { + fn shadow(&self, env: &mut SimulatorTables) -> Self::Result { let first_result = self.body.select.shadow(env)?; let mut rows = first_result.into_table().rows; diff --git a/simulator/model/query/transaction.rs b/simulator/model/query/transaction.rs index 22a390383..2d47280af 100644 --- a/simulator/model/query/transaction.rs +++ b/simulator/model/query/transaction.rs @@ -2,7 +2,11 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use crate::{model::table::SimValue, runner::env::SimulatorEnv}; +use crate::{ + generation::Shadow, + model::table::SimValue, + runner::env::SimulatorTables, +}; #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct Begin { @@ -15,24 +19,27 @@ pub(crate) struct Commit; #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct Rollback; -impl Begin { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { - env.tables_snapshot = Some(env.tables.clone()); +impl Shadow for Begin { + type Result = Vec>; + fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + tables.snapshot = Some(tables.tables.clone()); vec![] } } -impl Commit { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { - env.tables_snapshot = None; +impl Shadow for Commit { + type Result = Vec>; + fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + tables.snapshot = None; vec![] } } -impl Rollback { - pub(crate) fn shadow(&self, env: &mut SimulatorEnv) -> Vec> { - if let Some(tables) = env.tables_snapshot.take() { - env.tables = tables; +impl Shadow for Rollback { + type Result = Vec>; + fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + if let Some(tables_) = tables.snapshot.take() { + tables.tables = tables_; } vec![] } diff --git a/simulator/model/query/update.rs b/simulator/model/query/update.rs index 15d8a2945..09fce2370 100644 --- a/simulator/model/query/update.rs +++ b/simulator/model/query/update.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{ generation::Shadow, - model::table::{SimValue, Table}, + model::table::SimValue, runner::env::SimulatorTables, }; use super::predicate::Predicate; @@ -19,8 +19,8 @@ pub(crate) struct Update { impl Shadow for Update { type Result = anyhow::Result>>; - fn shadow(&self, tables: &mut Vec
) -> Self::Result { - let table = tables.iter_mut().find(|t| t.name == self.table); + fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + let table = tables.tables.iter_mut().find(|t| t.name == self.table); let table = if let Some(table) = table { table diff --git a/simulator/runner/env.rs b/simulator/runner/env.rs index aa1927cd7..4629d3ffb 100644 --- a/simulator/runner/env.rs +++ b/simulator/runner/env.rs @@ -1,5 +1,6 @@ use std::fmt::Display; use std::mem; +use std::ops::Deref; use std::panic::UnwindSafe; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -27,9 +28,39 @@ pub(crate) enum SimulationPhase { Shrink, } + +#[derive(Debug, Clone)] +pub(crate) struct SimulatorTables { + pub(crate) tables: Vec
, + pub(crate) snapshot: Option>, +} +impl SimulatorTables { + pub(crate) fn new() -> Self { + Self { + tables: Vec::new(), + snapshot: None, + } + } + + pub(crate) fn clear(&mut self) { + self.tables.clear(); + self.snapshot = None; + } + + pub(crate) fn push(&mut self, table: Table) { + self.tables.push(table); + } +} +impl Deref for SimulatorTables { + type Target = Vec
; + + fn deref(&self) -> &Self::Target { + &self.tables + } +} + pub(crate) struct SimulatorEnv { pub(crate) opts: SimulatorOpts, - pub(crate) tables: Vec
, pub(crate) connections: Vec, pub(crate) io: Arc, pub(crate) db: Arc, @@ -37,7 +68,7 @@ pub(crate) struct SimulatorEnv { pub(crate) paths: Paths, pub(crate) type_: SimulationType, pub(crate) phase: SimulationPhase, - pub tables_snapshot: Option>, + pub(crate) tables: SimulatorTables, } impl UnwindSafe for SimulatorEnv {} @@ -56,7 +87,6 @@ impl SimulatorEnv { paths: self.paths.clone(), type_: self.type_, phase: self.phase, - tables_snapshot: None, } } @@ -246,7 +276,7 @@ impl SimulatorEnv { SimulatorEnv { opts, - tables: Vec::new(), + tables: SimulatorTables::new(), connections, paths, rng, @@ -254,7 +284,6 @@ impl SimulatorEnv { db, type_: simulation_type, phase: SimulationPhase::Test, - tables_snapshot: None, } } diff --git a/vendored/sqlite3-parser/src/parser/ast/mod.rs b/vendored/sqlite3-parser/src/parser/ast/mod.rs index f635a5556..e23551a24 100644 --- a/vendored/sqlite3-parser/src/parser/ast/mod.rs +++ b/vendored/sqlite3-parser/src/parser/ast/mod.rs @@ -896,6 +896,7 @@ pub struct FromClause { pub select: Option>, // FIXME mandatory /// `JOIN`ed tabled pub joins: Option>, + /// A default join operator pub op: Option, // FIXME transient } impl FromClause {