generate self-inserts with nested subqueries

This commit is contained in:
Mikaël Francoeur
2025-11-10 19:27:58 -05:00
parent 23d6080531
commit d309e6ddb3
3 changed files with 49 additions and 16 deletions

View File

@@ -7,12 +7,12 @@ use serde::{Deserialize, Serialize};
use sql_generation::model::query::select::SelectTable; use sql_generation::model::query::select::SelectTable;
use sql_generation::model::{ use sql_generation::model::{
query::{ query::{
alter_table::{AlterTable, AlterTableType}, pragma::Pragma, select::{CompoundOperator, FromClause, ResultColumn, SelectInner}, transaction::{Begin, Commit, Rollback}, update::Update, Create, CreateIndex, Create, CreateIndex, Delete, Drop, DropIndex, Insert, Select,
Delete, alter_table::{AlterTable, AlterTableType},
Drop, pragma::Pragma,
DropIndex, select::{CompoundOperator, FromClause, ResultColumn, SelectInner},
Insert, transaction::{Begin, Commit, Rollback},
Select, update::Update,
}, },
table::{Index, JoinTable, JoinType, SimValue, Table, TableContext}, table::{Index, JoinTable, JoinType, SimValue, Table, TableContext},
}; };
@@ -323,6 +323,7 @@ impl Shadow for Drop {
impl Shadow for Insert { impl Shadow for Insert {
type Result = anyhow::Result<Vec<Vec<SimValue>>>; type Result = anyhow::Result<Vec<Vec<SimValue>>>;
//FIXME this doesn't handle type affinity
fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result { fn shadow(&self, tables: &mut ShadowTablesMut) -> Self::Result {
match self { match self {
Insert::Values { table, values } => { Insert::Values { table, values } => {
@@ -381,7 +382,6 @@ impl Shadow for FromClause {
} }
}; };
for join in &self.joins { for join in &self.joins {
let joined_table = tables let joined_table = tables
.iter() .iter()

View File

@@ -184,6 +184,7 @@ pub fn gen_random_text<R: Rng + ?Sized>(rng: &mut R) -> String {
} }
} }
//FIXME this can hang if count > items.len() or if there are duplicates
pub fn pick_unique<'a, T: PartialEq, R: Rng + ?Sized>( pub fn pick_unique<'a, T: PartialEq, R: Rng + ?Sized>(
items: &'a [T], items: &'a [T],
count: usize, count: usize,

View File

@@ -4,7 +4,10 @@ use crate::generation::{
}; };
use crate::model::query::alter_table::{AlterTable, AlterTableType, AlterTableTypeDiscriminants}; use crate::model::query::alter_table::{AlterTable, AlterTableType, AlterTableTypeDiscriminants};
use crate::model::query::predicate::Predicate; use crate::model::query::predicate::Predicate;
use crate::model::query::select::{CompoundOperator, CompoundSelect, Distinctness, FromClause, OrderBy, ResultColumn, SelectBody, SelectInner, SelectTable}; use crate::model::query::select::{
CompoundOperator, CompoundSelect, Distinctness, FromClause, OrderBy, ResultColumn, SelectBody,
SelectInner, SelectTable,
};
use crate::model::query::update::Update; use crate::model::query::update::Update;
use crate::model::query::{Create, CreateIndex, Delete, Drop, DropIndex, Insert, Select}; use crate::model::query::{Create, CreateIndex, Delete, Drop, DropIndex, Insert, Select};
use crate::model::table::{ use crate::model::table::{
@@ -81,7 +84,10 @@ impl Arbitrary for FromClause {
}) })
}) })
.collect(); .collect();
FromClause { table: SelectTable::Table(name), joins } FromClause {
table: SelectTable::Table(name),
joins,
}
} }
} }
@@ -146,6 +152,7 @@ impl Arbitrary for SelectInner {
} }
impl ArbitrarySized for SelectInner { impl ArbitrarySized for SelectInner {
//FIXME this can generate SELECT statements containing fewer columns than the num_result_columns parameter.
fn arbitrary_sized<R: Rng + ?Sized, C: GenerationContext>( fn arbitrary_sized<R: Rng + ?Sized, C: GenerationContext>(
rng: &mut R, rng: &mut R,
env: &C, env: &C,
@@ -282,13 +289,35 @@ impl Arbitrary for Insert {
}) })
}; };
let gen_self_select = |rng: &mut R| { let gen_nested_self_insert = |rng: &mut R| {
let table = pick(env.tables(), rng); let table = pick(env.tables(), rng);
if table.rows.is_empty() {
return None; const MAX_SELF_INSERT_DEPTH: i32 = 5;
let nesting_depth = rng.random_range(1..=MAX_SELF_INSERT_DEPTH);
let mut select = Select::simple(
table.name.clone(),
Predicate::arbitrary_from(rng, env, table),
);
for _ in 1..nesting_depth {
select = Select {
body: SelectBody {
select: Box::new(SelectInner {
distinctness: Distinctness::All,
columns: vec![ResultColumn::Star],
from: Some(FromClause {
table: SelectTable::Select(select),
joins: Vec::new(),
}),
where_clause: Predicate::true_(),
order_by: None,
}),
compounds: Vec::new(),
},
limit: None,
};
} }
let predicate = Predicate::true_();
let select = Select::simple(table.name.clone(), predicate);
Some(Insert::Select { Some(Insert::Select {
table: table.name.clone(), table: table.name.clone(),
@@ -297,10 +326,13 @@ impl Arbitrary for Insert {
}; };
backtrack( backtrack(
vec![(1, Box::new(gen_values)), (1, Box::new(gen_self_select))], vec![
(1, Box::new(gen_values)),
(1, Box::new(gen_nested_self_insert)),
],
rng, rng,
) )
.expect("backtrack with these arguments should not return None") .expect("backtrack with these arguments should not return None")
} }
} }