risingwave_sqlsmith/sqlreduce/passes/mod.rs
1// Copyright 2025 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt;
16
17use risingwave_sqlparser::ast::{Query, Statement};
18
19pub mod pullup;
20pub mod remove;
21pub mod replace;
22
23pub type Ast = Statement;
24
25#[derive(Debug, Clone)]
26pub enum Strategy {
27 Single, // one-at-a-time
28 Aggressive, // all combinations
29 Consecutive(usize), // delete k consecutive elements
30}
31
32/// A transformation that can reduce parts of a SQL AST while preserving failure behavior.
33///
34/// A `Transform` operates by identifying **reduction points** in the AST—locations where
35/// a simplification or mutation can be safely attempted—and then applying those changes.
36///
37/// ### Reduction Points
38///
39/// A **reduction point** is an index identifying a target element (e.g., a SELECT item,
40/// a WHERE clause, or a binary operator) that can be removed, replaced, or mutated.
41///
42/// #### Example:
43///
44/// - For a `SELECT` list:
45/// ```sql
46/// SELECT a + b, c, d FROM t;
47/// ^ ^
48/// | └── reduction point 1 (c)
49/// └────── reduction point 0 (a + b)
50/// ```
51pub trait Transform: Send + Sync {
52 fn name(&self) -> String;
53
54 /// This function analyzes the given SQL AST and returns all the reduction points where
55 /// the transformation might be applicable.
56 ///
57 /// # Arguments
58 /// - `ast`: The SQL AST to analyze.
59 ///
60 /// # Returns
61 /// - A list of reduction points where the transformation might be applicable.
62 ///
63 /// Implementors should return a list of all applicable reduction indices for their transform.
64 fn get_reduction_points(&self, ast: Ast) -> Vec<usize>;
65
66 /// Applies the transformation to the AST at the given reduction points.
67 ///
68 /// # Arguments
69 /// - `ast`: The SQL AST to apply the transformation to.
70 /// - `reduction_points`: The list of reduction points to apply the transformation to.
71 ///
72 /// # Returns
73 /// - The modified AST.
74 fn apply_on(&self, ast: &mut Ast, reduction_points: Vec<usize>) -> Ast;
75
76 /// Applies the transformation to the AST at the given reduction points.
77 ///
78 /// # Arguments
79 /// - `ast`: The SQL AST to apply the transformation to.
80 /// - `idx`: The index of the reduction point to apply the transformation to.
81 /// - `strategy`: The strategy to use for applying the transformation.
82 fn transform(&self, ast: Ast, idx: usize, strategy: Strategy) -> Vec<(Ast, usize)> {
83 let reduction_points = self.get_reduction_points(ast.clone());
84
85 match strategy {
86 Strategy::Single => {
87 let mut results = Vec::new();
88 for (i, rp) in reduction_points.iter().enumerate() {
89 if i < idx {
90 continue;
91 }
92 let new_ast = self.apply_on(&mut ast.clone(), vec![*rp]);
93 results.push((new_ast, i));
94 }
95 results
96 }
97 Strategy::Aggressive => {
98 let mut results = Vec::new();
99 let new_ast = self.apply_on(&mut ast.clone(), reduction_points[idx..].to_vec());
100 results.push((new_ast, idx));
101 results
102 }
103 Strategy::Consecutive(k) => {
104 let mut results = Vec::new();
105 if reduction_points.len() >= k {
106 for i in 0..=reduction_points.len() - k {
107 let new_ast =
108 self.apply_on(&mut ast.clone(), reduction_points[i..i + k].to_vec());
109 results.push((new_ast, i));
110 }
111 }
112 results
113 }
114 }
115 }
116}
117
118impl fmt::Display for dyn Transform {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(f, "{}", self.name())
121 }
122}
123
124pub fn extract_query(stmt: &Statement) -> Option<&Query> {
125 match stmt {
126 Statement::Query(query) => Some(query),
127 Statement::CreateView { query, .. } => Some(query),
128 _ => None,
129 }
130}
131
132pub fn extract_query_mut(stmt: &mut Statement) -> Option<&mut Query> {
133 match stmt {
134 Statement::Query(query) => Some(query),
135 Statement::CreateView { query, .. } => Some(query),
136 _ => None,
137 }
138}