mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-27 04:54:21 +01:00
Merge 'Simulator: Drop Index' from Pedro Muniz
Added the ability for us to generate `Drop Index` queries in the simulator. Most of the code is just boilerplate and some checks to make sure we do not generate `Drop Index` when we have no indexes to drop Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com> Reviewed-by: bit-aloo (@Shourya742) Closes #3713
This commit is contained in:
@@ -235,6 +235,7 @@ impl InteractionPlan {
|
||||
Query::Commit(_) => stats.commit_count += 1,
|
||||
Query::Rollback(_) => stats.rollback_count += 1,
|
||||
Query::AlterTable(_) => stats.alter_table_count += 1,
|
||||
Query::DropIndex(_) => stats.drop_index_count += 1,
|
||||
Query::Placeholder => {}
|
||||
}
|
||||
}
|
||||
@@ -472,11 +473,14 @@ impl<'a, R: rand::Rng> PlanGenerator<'a, R> {
|
||||
if let InteractionType::Query(Query::Placeholder) = &interaction.interaction {
|
||||
let stats = self.plan.stats();
|
||||
|
||||
let conn_ctx = env.connection_context(interaction.connection_index);
|
||||
|
||||
let remaining_ = remaining(
|
||||
env.opts.max_interactions,
|
||||
&env.profile.query,
|
||||
&stats,
|
||||
env.profile.experimental_mvcc,
|
||||
&conn_ctx,
|
||||
);
|
||||
|
||||
let InteractionsType::Property(property) =
|
||||
@@ -485,8 +489,6 @@ impl<'a, R: rand::Rng> PlanGenerator<'a, R> {
|
||||
unreachable!("only properties have extensional queries");
|
||||
};
|
||||
|
||||
let conn_ctx = env.connection_context(interaction.connection_index);
|
||||
|
||||
let queries = possible_queries(conn_ctx.tables());
|
||||
let query_distr = QueryDistribution::new(queries, &remaining_);
|
||||
|
||||
@@ -768,13 +770,14 @@ pub(crate) struct InteractionStats {
|
||||
pub commit_count: u32,
|
||||
pub rollback_count: u32,
|
||||
pub alter_table_count: u32,
|
||||
pub drop_index_count: u32,
|
||||
}
|
||||
|
||||
impl Display for InteractionStats {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Read: {}, Insert: {}, Delete: {}, Update: {}, Create: {}, CreateIndex: {}, Drop: {}, Begin: {}, Commit: {}, Rollback: {}, Alter Table: {}",
|
||||
"Read: {}, Insert: {}, Delete: {}, Update: {}, Create: {}, CreateIndex: {}, Drop: {}, Begin: {}, Commit: {}, Rollback: {}, Alter Table: {}, Drop Index: {}",
|
||||
self.select_count,
|
||||
self.insert_count,
|
||||
self.delete_count,
|
||||
@@ -786,6 +789,7 @@ impl Display for InteractionStats {
|
||||
self.commit_count,
|
||||
self.rollback_count,
|
||||
self.alter_table_count,
|
||||
self.drop_index_count,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1272,12 +1276,13 @@ impl ArbitraryFrom<(&SimulatorEnv, InteractionStats, usize)> for Interactions {
|
||||
&env.profile.query,
|
||||
&stats,
|
||||
env.profile.experimental_mvcc,
|
||||
conn_ctx,
|
||||
);
|
||||
|
||||
let queries = possible_queries(conn_ctx.tables());
|
||||
let query_distr = QueryDistribution::new(queries, &remaining_);
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[expect(clippy::type_complexity)]
|
||||
let mut choices: Vec<(u32, Box<dyn Fn(&mut R) -> Interactions>)> = vec![
|
||||
(
|
||||
query_distr.weights().total_weight(),
|
||||
|
||||
@@ -1380,6 +1380,7 @@ pub(super) struct Remaining {
|
||||
pub update: u32,
|
||||
pub drop: u32,
|
||||
pub alter_table: u32,
|
||||
pub drop_index: u32,
|
||||
}
|
||||
|
||||
pub(super) fn remaining(
|
||||
@@ -1387,6 +1388,7 @@ pub(super) fn remaining(
|
||||
opts: &QueryProfile,
|
||||
stats: &InteractionStats,
|
||||
mvcc: bool,
|
||||
context: &impl GenerationContext,
|
||||
) -> Remaining {
|
||||
let total_weight = opts.total_weight();
|
||||
|
||||
@@ -1398,6 +1400,7 @@ pub(super) fn remaining(
|
||||
let total_update = (max_interactions * opts.update_weight) / total_weight;
|
||||
let total_drop = (max_interactions * opts.drop_table_weight) / total_weight;
|
||||
let total_alter_table = (max_interactions * opts.alter_table_weight) / total_weight;
|
||||
let total_drop_index = (max_interactions * opts.drop_index) / total_weight;
|
||||
|
||||
let remaining_select = total_select
|
||||
.checked_sub(stats.select_count)
|
||||
@@ -1423,9 +1426,23 @@ pub(super) fn remaining(
|
||||
.checked_sub(stats.alter_table_count)
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut remaining_drop_index = total_drop_index
|
||||
.checked_sub(stats.alter_table_count)
|
||||
.unwrap_or_default();
|
||||
|
||||
if mvcc {
|
||||
// TODO: index not supported yet for mvcc
|
||||
remaining_create_index = 0;
|
||||
remaining_drop_index = 0;
|
||||
}
|
||||
|
||||
// if there are no indexes do not allow creation of drop_index
|
||||
if !context
|
||||
.tables()
|
||||
.iter()
|
||||
.any(|table| !table.indexes.is_empty())
|
||||
{
|
||||
remaining_drop_index = 0;
|
||||
}
|
||||
|
||||
Remaining {
|
||||
@@ -1437,6 +1454,7 @@ pub(super) fn remaining(
|
||||
drop: remaining_drop,
|
||||
update: remaining_update,
|
||||
alter_table: remaining_alter_table,
|
||||
drop_index: remaining_drop_index,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ use sql_generation::{
|
||||
generation::{Arbitrary, ArbitraryFrom, GenerationContext, query::SelectFree},
|
||||
model::{
|
||||
query::{
|
||||
Create, CreateIndex, Delete, Insert, Select, alter_table::AlterTable, update::Update,
|
||||
Create, CreateIndex, Delete, DropIndex, Insert, Select, alter_table::AlterTable,
|
||||
update::Update,
|
||||
},
|
||||
table::Table,
|
||||
},
|
||||
@@ -89,6 +90,19 @@ fn random_alter_table<R: rand::Rng + ?Sized>(
|
||||
Query::AlterTable(AlterTable::arbitrary(rng, conn_ctx))
|
||||
}
|
||||
|
||||
fn random_drop_index<R: rand::Rng + ?Sized>(
|
||||
rng: &mut R,
|
||||
conn_ctx: &impl GenerationContext,
|
||||
) -> Query {
|
||||
assert!(
|
||||
conn_ctx
|
||||
.tables()
|
||||
.iter()
|
||||
.any(|table| !table.indexes.is_empty())
|
||||
);
|
||||
Query::DropIndex(DropIndex::arbitrary(rng, conn_ctx))
|
||||
}
|
||||
|
||||
/// Possible queries that can be generated given the table state
|
||||
///
|
||||
/// Does not take into account transactional statements
|
||||
@@ -117,6 +131,7 @@ impl QueryDiscriminants {
|
||||
QueryDiscriminants::Drop => random_drop,
|
||||
QueryDiscriminants::CreateIndex => random_create_index,
|
||||
QueryDiscriminants::AlterTable => random_alter_table,
|
||||
QueryDiscriminants::DropIndex => random_drop_index,
|
||||
QueryDiscriminants::Begin
|
||||
| QueryDiscriminants::Commit
|
||||
| QueryDiscriminants::Rollback => {
|
||||
@@ -140,6 +155,7 @@ impl QueryDiscriminants {
|
||||
QueryDiscriminants::Drop => remaining.drop,
|
||||
QueryDiscriminants::CreateIndex => remaining.create_index,
|
||||
QueryDiscriminants::AlterTable => remaining.alter_table,
|
||||
QueryDiscriminants::DropIndex => remaining.drop_index,
|
||||
QueryDiscriminants::Begin
|
||||
| QueryDiscriminants::Commit
|
||||
| QueryDiscriminants::Rollback => {
|
||||
|
||||
@@ -7,7 +7,7 @@ use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sql_generation::model::{
|
||||
query::{
|
||||
Create, CreateIndex, Delete, Drop, Insert, Select,
|
||||
Create, CreateIndex, Delete, Drop, DropIndex, Insert, Select,
|
||||
alter_table::{AlterTable, AlterTableType},
|
||||
select::{CompoundOperator, FromClause, ResultColumn, SelectInner},
|
||||
transaction::{Begin, Commit, Rollback},
|
||||
@@ -30,6 +30,7 @@ pub enum Query {
|
||||
Drop(Drop),
|
||||
CreateIndex(CreateIndex),
|
||||
AlterTable(AlterTable),
|
||||
DropIndex(DropIndex),
|
||||
Begin(Begin),
|
||||
Commit(Commit),
|
||||
Rollback(Rollback),
|
||||
@@ -76,6 +77,9 @@ impl Query {
|
||||
})
|
||||
| Query::AlterTable(AlterTable {
|
||||
table_name: table, ..
|
||||
})
|
||||
| Query::DropIndex(DropIndex {
|
||||
table_name: table, ..
|
||||
}) => IndexSet::from_iter([table.clone()]),
|
||||
Query::Begin(_) | Query::Commit(_) | Query::Rollback(_) => IndexSet::new(),
|
||||
Query::Placeholder => IndexSet::new(),
|
||||
@@ -97,6 +101,9 @@ impl Query {
|
||||
})
|
||||
| Query::AlterTable(AlterTable {
|
||||
table_name: table, ..
|
||||
})
|
||||
| Query::DropIndex(DropIndex {
|
||||
table_name: table, ..
|
||||
}) => vec![table.clone()],
|
||||
Query::Begin(..) | Query::Commit(..) | Query::Rollback(..) => vec![],
|
||||
Query::Placeholder => vec![],
|
||||
@@ -115,7 +122,11 @@ impl Query {
|
||||
pub fn is_ddl(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Create(..) | Self::CreateIndex(..) | Self::Drop(..) | Self::AlterTable(..)
|
||||
Self::Create(..)
|
||||
| Self::CreateIndex(..)
|
||||
| Self::Drop(..)
|
||||
| Self::AlterTable(..)
|
||||
| Self::DropIndex(..)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -131,6 +142,7 @@ impl Display for Query {
|
||||
Self::Drop(drop) => write!(f, "{drop}"),
|
||||
Self::CreateIndex(create_index) => write!(f, "{create_index}"),
|
||||
Self::AlterTable(alter_table) => write!(f, "{alter_table}"),
|
||||
Self::DropIndex(drop_index) => write!(f, "{drop_index}"),
|
||||
Self::Begin(begin) => write!(f, "{begin}"),
|
||||
Self::Commit(commit) => write!(f, "{commit}"),
|
||||
Self::Rollback(rollback) => write!(f, "{rollback}"),
|
||||
@@ -152,6 +164,7 @@ impl Shadow for Query {
|
||||
Query::Drop(drop) => drop.shadow(env),
|
||||
Query::CreateIndex(create_index) => Ok(create_index.shadow(env)),
|
||||
Query::AlterTable(alter_table) => alter_table.shadow(env),
|
||||
Query::DropIndex(drop_index) => drop_index.shadow(env),
|
||||
Query::Begin(begin) => Ok(begin.shadow(env)),
|
||||
Query::Commit(commit) => Ok(commit.shadow(env)),
|
||||
Query::Rollback(rollback) => Ok(rollback.shadow(env)),
|
||||
@@ -170,6 +183,7 @@ bitflags! {
|
||||
const DROP = 1 << 5;
|
||||
const CREATE_INDEX = 1 << 6;
|
||||
const ALTER_TABLE = 1 << 7;
|
||||
const DROP_INDEX = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +213,7 @@ impl From<QueryDiscriminants> for QueryCapabilities {
|
||||
QueryDiscriminants::Drop => Self::DROP,
|
||||
QueryDiscriminants::CreateIndex => Self::CREATE_INDEX,
|
||||
QueryDiscriminants::AlterTable => Self::ALTER_TABLE,
|
||||
QueryDiscriminants::DropIndex => Self::DROP_INDEX,
|
||||
QueryDiscriminants::Begin
|
||||
| QueryDiscriminants::Commit
|
||||
| QueryDiscriminants::Rollback => {
|
||||
@@ -221,6 +236,7 @@ impl QueryDiscriminants {
|
||||
QueryDiscriminants::Drop,
|
||||
QueryDiscriminants::CreateIndex,
|
||||
QueryDiscriminants::AlterTable,
|
||||
QueryDiscriminants::DropIndex,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -583,3 +599,19 @@ impl Shadow for AlterTable {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl Shadow for DropIndex {
|
||||
type Result = anyhow::Result<Vec<Vec<SimValue>>>;
|
||||
|
||||
fn shadow(&self, tables: &mut ShadowTablesMut<'_>) -> Self::Result {
|
||||
let table = tables
|
||||
.iter_mut()
|
||||
.find(|t| t.name == self.table_name)
|
||||
.ok_or_else(|| anyhow::anyhow!("Table {} does not exist", self.table_name))?;
|
||||
|
||||
table
|
||||
.indexes
|
||||
.retain(|index| index.index_name != self.index_name);
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ pub struct QueryProfile {
|
||||
pub drop_table_weight: u32,
|
||||
#[garde(skip)]
|
||||
pub alter_table_weight: u32,
|
||||
#[garde(skip)]
|
||||
pub drop_index: u32,
|
||||
}
|
||||
|
||||
impl Default for QueryProfile {
|
||||
@@ -38,6 +40,7 @@ impl Default for QueryProfile {
|
||||
delete_weight: 20,
|
||||
drop_table_weight: 2,
|
||||
alter_table_weight: 2,
|
||||
drop_index: 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::model::query::select::{
|
||||
SelectInner,
|
||||
};
|
||||
use crate::model::query::update::Update;
|
||||
use crate::model::query::{Create, CreateIndex, Delete, Drop, Insert, Select};
|
||||
use crate::model::query::{Create, CreateIndex, Delete, Drop, DropIndex, Insert, Select};
|
||||
use crate::model::table::{
|
||||
Column, Index, JoinTable, JoinType, JoinedTable, Name, SimValue, Table, TableContext,
|
||||
};
|
||||
@@ -535,3 +535,22 @@ impl Arbitrary for AlterTable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for DropIndex {
|
||||
fn arbitrary<R: Rng + ?Sized, C: GenerationContext>(rng: &mut R, context: &C) -> Self {
|
||||
let tables_with_indexes = context
|
||||
.tables()
|
||||
.iter()
|
||||
.filter(|table| !table.indexes.is_empty())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Cannot DROP INDEX if there is no index to drop
|
||||
assert!(!tables_with_indexes.is_empty());
|
||||
let table = tables_with_indexes.choose(rng).unwrap();
|
||||
let index = table.indexes.choose(rng).unwrap();
|
||||
Self {
|
||||
index_name: index.index_name.clone(),
|
||||
table_name: table.name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub struct DropIndex {
|
||||
pub index_name: String,
|
||||
pub table_name: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DropIndex {
|
||||
|
||||
@@ -68,7 +68,7 @@ struct SimulatorFiber {
|
||||
struct SimulatorContext {
|
||||
fibers: Vec<SimulatorFiber>,
|
||||
tables: Vec<Table>,
|
||||
indexes: Vec<String>,
|
||||
indexes: Vec<(String, String)>,
|
||||
opts: Opts,
|
||||
stats: Stats,
|
||||
disable_indexes: bool,
|
||||
@@ -210,7 +210,10 @@ fn main() -> anyhow::Result<()> {
|
||||
let mut context = SimulatorContext {
|
||||
fibers,
|
||||
tables,
|
||||
indexes: indexes.iter().map(|idx| idx.index_name.clone()).collect(),
|
||||
indexes: indexes
|
||||
.iter()
|
||||
.map(|idx| (idx.table_name.clone(), idx.index_name.clone()))
|
||||
.collect(),
|
||||
opts: Opts::default(),
|
||||
stats: Stats::default(),
|
||||
disable_indexes: args.disable_indexes,
|
||||
@@ -567,7 +570,10 @@ fn perform_work(
|
||||
let sql = create_index.to_string();
|
||||
if let Ok(stmt) = context.fibers[fiber_idx].connection.prepare(&sql) {
|
||||
context.fibers[fiber_idx].statement.replace(Some(stmt));
|
||||
context.indexes.push(create_index.index_name.clone());
|
||||
context.indexes.push((
|
||||
create_index.index.table_name.clone(),
|
||||
create_index.index_name.clone(),
|
||||
));
|
||||
}
|
||||
trace!("{} CREATE INDEX: {}", fiber_idx, sql);
|
||||
}
|
||||
@@ -576,8 +582,11 @@ fn perform_work(
|
||||
// DROP INDEX (2%)
|
||||
if !context.disable_indexes && !context.indexes.is_empty() {
|
||||
let index_idx = rng.random_range(0..context.indexes.len());
|
||||
let index_name = context.indexes.remove(index_idx);
|
||||
let drop_index = DropIndex { index_name };
|
||||
let (table_name, index_name) = context.indexes.remove(index_idx);
|
||||
let drop_index = DropIndex {
|
||||
table_name,
|
||||
index_name,
|
||||
};
|
||||
let sql = drop_index.to_string();
|
||||
if let Ok(stmt) = context.fibers[fiber_idx].connection.prepare(&sql) {
|
||||
context.fibers[fiber_idx].statement.replace(Some(stmt));
|
||||
|
||||
Reference in New Issue
Block a user