From d67a9f03fdf9fd4f6b452eb10b583df42b6873c3 Mon Sep 17 00:00:00 2001 From: Jussi Saurio Date: Fri, 18 Jul 2025 10:42:44 +0300 Subject: [PATCH] sim: add order by to some queries --- simulator/generation/property.rs | 3 +++ simulator/generation/query.rs | 45 ++++++++++++++++++++++++++++++-- simulator/model/query/select.rs | 25 ++++++++++++++++-- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/simulator/generation/property.rs b/simulator/generation/property.rs index 8e009e068..d83a60fd1 100644 --- a/simulator/generation/property.rs +++ b/simulator/generation/property.rs @@ -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, }), }, ], diff --git a/simulator/generation/query.rs b/simulator/generation/query.rs index e66798d9a..d55a47c50 100644 --- a/simulator/generation/query.rs +++ b/simulator/generation/query.rs @@ -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::>(); + let cuml_col_count = join_table.columns.len(); + let order_by_col_count = + (rng.gen::() * rng.gen::() * (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, } } } diff --git a/simulator/model/query/select.rs b/simulator/model/query/select.rs index 9cc9fc4b8..1c23a0281 100644 --- a/simulator/model/query/select.rs +++ b/simulator/model/query/select.rs @@ -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, } +#[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, /// `WHERE` clause pub where_clause: Predicate, + /// `ORDER BY` clause + pub order_by: Option, } #[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())),