mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-11 11:14:21 +01:00
filter for the tables that the failing query depended on + second pass after query clear
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::{fmt::Display, path::Path, rc::Rc, vec};
|
||||
use std::{collections::HashSet, fmt::Display, path::Path, rc::Rc, vec};
|
||||
|
||||
use limbo_core::{Connection, Result, StepResult, IO};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -97,7 +97,7 @@ pub(crate) struct InteractionPlanState {
|
||||
pub(crate) secondary_pointer: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) enum Interactions {
|
||||
Property(Property),
|
||||
Query(Query),
|
||||
@@ -123,13 +123,13 @@ impl Interactions {
|
||||
}
|
||||
|
||||
impl Interactions {
|
||||
pub(crate) fn dependencies(&self) -> Vec<String> {
|
||||
pub(crate) fn dependencies(&self) -> HashSet<String> {
|
||||
match self {
|
||||
Interactions::Property(property) => {
|
||||
property
|
||||
.interactions()
|
||||
.iter()
|
||||
.fold(vec![], |mut acc, i| match i {
|
||||
.fold(HashSet::new(), |mut acc, i| match i {
|
||||
Interaction::Query(q) => {
|
||||
acc.extend(q.dependencies());
|
||||
acc
|
||||
@@ -138,7 +138,7 @@ impl Interactions {
|
||||
})
|
||||
}
|
||||
Interactions::Query(query) => query.dependencies(),
|
||||
Interactions::Fault(_) => vec![],
|
||||
Interactions::Fault(_) => HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ use super::{
|
||||
|
||||
/// Properties are representations of executable specifications
|
||||
/// about the database behavior.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) enum Property {
|
||||
/// Insert-Select is a property in which the inserted row
|
||||
/// must be in the resulting rows of a select query that has a
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::fmt::Display;
|
||||
use std::{collections::HashSet, fmt::Display};
|
||||
|
||||
pub(crate) use create::Create;
|
||||
pub(crate) use create_index::CreateIndex;
|
||||
@@ -32,16 +32,18 @@ pub(crate) enum Query {
|
||||
}
|
||||
|
||||
impl Query {
|
||||
pub(crate) fn dependencies(&self) -> Vec<String> {
|
||||
pub(crate) fn dependencies(&self) -> HashSet<String> {
|
||||
match self {
|
||||
Query::Create(_) => vec![],
|
||||
Query::Create(_) => HashSet::new(),
|
||||
Query::Select(Select { table, .. })
|
||||
| Query::Insert(Insert::Select { table, .. })
|
||||
| Query::Insert(Insert::Values { table, .. })
|
||||
| Query::Delete(Delete { table, .. })
|
||||
| Query::Update(Update { table, .. })
|
||||
| Query::Drop(Drop { table, .. }) => vec![table.clone()],
|
||||
Query::CreateIndex(CreateIndex { table_name, .. }) => vec![table_name.clone()],
|
||||
| Query::Drop(Drop { table, .. }) => HashSet::from_iter([table.clone()]),
|
||||
Query::CreateIndex(CreateIndex { table_name, .. }) => {
|
||||
HashSet::from_iter([table_name.clone()])
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) fn uses(&self) -> Vec<String> {
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::{
|
||||
},
|
||||
model::query::Query,
|
||||
runner::execution::Execution,
|
||||
Interaction,
|
||||
};
|
||||
|
||||
impl InteractionPlan {
|
||||
@@ -13,36 +14,68 @@ impl InteractionPlan {
|
||||
// todo: this is a very naive implementation, next steps are;
|
||||
// - Shrink to multiple values by removing random interactions
|
||||
// - Shrink properties by removing their extensions, or shrinking their values
|
||||
|
||||
let mut plan = self.clone();
|
||||
let failing_property = &self.plan[failing_execution.interaction_index];
|
||||
let depending_tables = failing_property.dependencies();
|
||||
let mut depending_tables = failing_property.dependencies();
|
||||
|
||||
let interactions = failing_property.interactions();
|
||||
|
||||
{
|
||||
let mut idx = failing_execution.secondary_index;
|
||||
loop {
|
||||
match &interactions[idx] {
|
||||
Interaction::Query(query) => {
|
||||
depending_tables = query.dependencies();
|
||||
break;
|
||||
}
|
||||
// Fault does not depend on
|
||||
Interaction::Fault(..) => break,
|
||||
_ => {
|
||||
// In principle we should never fail this checked_sub.
|
||||
// But if there is a bug in how we count the secondary index
|
||||
// we may panic if we do not use a checked_sub.
|
||||
if let Some(new_idx) = idx.checked_sub(1) {
|
||||
idx = new_idx;
|
||||
} else {
|
||||
tracing::warn!("failed to find error query");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let before = self.plan.len();
|
||||
|
||||
// Remove all properties after the failing one
|
||||
plan.plan.truncate(failing_execution.interaction_index + 1);
|
||||
// Remove all properties that do not use the failing tables
|
||||
plan.plan
|
||||
.retain(|p| p.uses().iter().any(|t| depending_tables.contains(t)));
|
||||
|
||||
// Remove the extensional parts of the properties
|
||||
for interaction in plan.plan.iter_mut() {
|
||||
if let Interactions::Property(p) = interaction {
|
||||
match p {
|
||||
Property::InsertValuesSelect { queries, .. }
|
||||
| Property::DoubleCreateFailure { queries, .. }
|
||||
| Property::DeleteSelect { queries, .. }
|
||||
| Property::DropSelect { queries, .. } => {
|
||||
queries.clear();
|
||||
plan.plan.retain_mut(|interactions| {
|
||||
let mut has_table = interactions
|
||||
.uses()
|
||||
.iter()
|
||||
.any(|t| depending_tables.contains(t));
|
||||
if has_table {
|
||||
// Remove the extensional parts of the properties
|
||||
if let Interactions::Property(p) = interactions {
|
||||
match p {
|
||||
Property::InsertValuesSelect { queries, .. }
|
||||
| Property::DoubleCreateFailure { queries, .. }
|
||||
| Property::DeleteSelect { queries, .. }
|
||||
| Property::DropSelect { queries, .. } => {
|
||||
queries.clear();
|
||||
}
|
||||
Property::SelectLimit { .. } | Property::SelectSelectOptimizer { .. } => {}
|
||||
}
|
||||
Property::SelectLimit { .. } | Property::SelectSelectOptimizer { .. } => {}
|
||||
}
|
||||
// Check again after query clear if the interactions still uses the failing table
|
||||
has_table = interactions
|
||||
.uses()
|
||||
.iter()
|
||||
.any(|t| depending_tables.contains(t));
|
||||
}
|
||||
}
|
||||
|
||||
plan.plan
|
||||
.retain(|p| !matches!(p, Interactions::Query(Query::Select(_))));
|
||||
has_table && !matches!(interactions, Interactions::Query(Query::Select(_)))
|
||||
});
|
||||
|
||||
let after = plan.plan.len();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user