sim: add order by to some queries

This commit is contained in:
Jussi Saurio
2025-07-18 10:42:44 +03:00
parent 2f2ecb3576
commit d67a9f03fd
3 changed files with 69 additions and 4 deletions

View File

@@ -688,6 +688,7 @@ impl Property {
columns: select.body.select.columns.clone(),
from: select.body.select.from.clone(),
where_clause: p_true,
order_by: None,
}),
compounds: vec![
CompoundSelect {
@@ -697,6 +698,7 @@ impl Property {
columns: select.body.select.columns.clone(),
from: select.body.select.from.clone(),
where_clause: p_false,
order_by: None,
}),
},
CompoundSelect {
@@ -706,6 +708,7 @@ impl Property {
columns: select.body.select.columns.clone(),
from: select.body.select.from.clone(),
where_clause: p_null,
order_by: None,
}),
},
],

View File

@@ -2,7 +2,7 @@ use crate::generation::{Arbitrary, ArbitraryFrom, ArbitrarySizedFrom, Shadow};
use crate::model::query::predicate::Predicate;
use crate::model::query::select::{
CompoundOperator, CompoundSelect, Distinctness, FromClause, JoinTable, JoinType, JoinedTable,
ResultColumn, SelectBody, SelectInner,
OrderBy, ResultColumn, SelectBody, SelectInner,
};
use crate::model::query::update::Update;
use crate::model::query::{Create, Delete, Drop, Insert, Query, Select};
@@ -10,7 +10,7 @@ use crate::model::table::{SimValue, Table};
use crate::SimulatorEnv;
use itertools::Itertools;
use rand::Rng;
use turso_sqlite3_parser::ast::Expr;
use turso_sqlite3_parser::ast::{Expr, SortOrder};
use super::property::Remaining;
use super::{backtrack, frequency, pick};
@@ -85,6 +85,46 @@ impl ArbitraryFrom<&SimulatorEnv> for SelectInner {
.expect("Failed to shadow FromClause")
.into_table();
let order_by = 'order_by: {
if rng.gen_bool(0.3) {
let order_by_table_candidates = from
.joins
.iter()
.map(|j| j.table.clone())
.chain(std::iter::once(from.table.clone()))
.collect::<Vec<_>>();
let cuml_col_count = join_table.columns.len();
let order_by_col_count =
(rng.gen::<f64>() * rng.gen::<f64>() * (cuml_col_count as f64)) as usize; // skew towards 0
if order_by_col_count == 0 {
break 'order_by None;
}
let mut col_names = std::collections::HashSet::new();
let mut order_by_cols = Vec::new();
while order_by_cols.len() < order_by_col_count {
let table = pick(&order_by_table_candidates, rng);
let table = tables.iter().find(|t| t.name == *table).unwrap();
let col = pick(&table.columns, rng);
let col_name = format!("{}.{}", table.name, col.name);
if col_names.insert(col_name.clone()) {
order_by_cols.push((
col_name,
if rng.gen_bool(0.5) {
SortOrder::Asc
} else {
SortOrder::Desc
},
));
}
}
Some(OrderBy {
columns: order_by_cols,
})
} else {
None
}
};
SelectInner {
distinctness: if env.opts.experimental_indexes {
Distinctness::arbitrary(rng)
@@ -94,6 +134,7 @@ impl ArbitraryFrom<&SimulatorEnv> for SelectInner {
columns: vec![ResultColumn::Star],
from: Some(from),
where_clause: Predicate::arbitrary_from(rng, &join_table),
order_by,
}
}
}

View File

@@ -4,7 +4,7 @@ use anyhow::Context;
pub use ast::Distinctness;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use turso_sqlite3_parser::ast::{self, fmt::ToTokens};
use turso_sqlite3_parser::ast::{self, fmt::ToTokens, SortOrder};
use crate::{
generation::Shadow,
@@ -63,6 +63,7 @@ impl Select {
columns: vec![ResultColumn::Expr(expr)],
from: None,
where_clause: Predicate::true_(),
order_by: None,
}),
compounds: Vec::new(),
},
@@ -87,6 +88,7 @@ impl Select {
joins: Vec::new(),
}),
where_clause,
order_by: None,
}),
compounds: Vec::new(),
},
@@ -140,6 +142,11 @@ pub struct SelectBody {
pub compounds: Vec<CompoundSelect>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct OrderBy {
pub columns: Vec<(String, SortOrder)>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SelectInner {
/// `DISTINCT`
@@ -150,6 +157,8 @@ pub struct SelectInner {
pub from: Option<FromClause>,
/// `WHERE` clause
pub where_clause: Predicate,
/// `ORDER BY` clause
pub order_by: Option<OrderBy>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -496,7 +505,19 @@ impl Select {
.collect(),
),
},
order_by: None,
order_by: self.body.select.order_by.as_ref().map(|o| {
o.columns
.iter()
.map(|(name, order)| ast::SortedColumn {
expr: ast::Expr::Id(ast::Id(name.clone())),
order: match order {
SortOrder::Asc => Some(ast::SortOrder::Asc),
SortOrder::Desc => Some(ast::SortOrder::Desc),
},
nulls: None,
})
.collect()
}),
limit: self.limit.map(|l| {
Box::new(ast::Limit {
expr: ast::Expr::Literal(ast::Literal::Numeric(l.to_string())),