risingwave_frontend/optimizer/rule/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// Copyright 2024 RisingWave Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Define all [`Rule`]

use std::convert::Infallible;
use std::ops::FromResidual;

use thiserror_ext::AsReport;

use super::PlanRef;
use crate::error::RwError;

/// Result when applying a [`Rule`] to a [`PlanNode`](super::plan_node::PlanNode).
pub enum ApplyResult<T = PlanRef> {
    /// Successfully applied the rule and returned a new plan.
    Ok(T),
    /// The current rule is not applicable to the input.
    /// The optimizer may try another rule.
    NotApplicable,
    /// An unrecoverable error occurred while applying the rule.
    /// The optimizer should stop applying other rules and report the error to the user.
    Err(RwError),
}

impl ApplyResult {
    /// Unwrap the result, panicking if it's not `Ok`.
    pub fn unwrap(self) -> PlanRef {
        match self {
            ApplyResult::Ok(plan) => plan,
            ApplyResult::NotApplicable => panic!("unwrap ApplyResult::NotApplicable"),
            ApplyResult::Err(e) => panic!("unwrap ApplyResult::Err, error: {:?}", e.as_report()),
        }
    }
}

/// Allow calling `?` on an `Option` in a function returning `ApplyResult`.
impl<T> FromResidual<Option<Infallible>> for ApplyResult<T> {
    fn from_residual(residual: Option<Infallible>) -> Self {
        match residual {
            Some(i) => match i {},
            None => Self::NotApplicable,
        }
    }
}

/// Allow calling `?` on a `Result` in a function returning `ApplyResult`.
impl<T, E> FromResidual<Result<Infallible, E>> for ApplyResult<T>
where
    E: Into<RwError>,
{
    fn from_residual(residual: Result<Infallible, E>) -> Self {
        match residual {
            Ok(i) => match i {},
            Err(e) => Self::Err(e.into()),
        }
    }
}

/// An one-to-one transform for the [`PlanNode`](super::plan_node::PlanNode).
///
/// It's a convenient trait to implement [`FallibleRule`], thus made available only within this module.
trait InfallibleRule: Send + Sync + Description {
    /// Apply the rule to the plan node.
    ///
    /// - Returns `Some` if the apply is successful.
    /// - Returns `None` if it's not applicable. The optimizer may try other rules.
    fn apply(&self, plan: PlanRef) -> Option<PlanRef>;
}
use InfallibleRule as Rule;

/// An one-to-one transform for the [`PlanNode`](super::plan_node::PlanNode) that may return an
/// unrecoverable error that stops further optimization.
///
/// An [`InfallibleRule`] is always a [`FallibleRule`].
pub trait FallibleRule: Send + Sync + Description {
    /// Apply the rule to the plan node, which may return an unrecoverable error.
    ///
    /// - Returns `ApplyResult::Ok` if the apply is successful.
    /// - Returns `ApplyResult::NotApplicable` if it's not applicable. The optimizer may try other rules.
    /// - Returns `ApplyResult::Err` if an unrecoverable error occurred. The optimizer should stop applying
    ///   other rules and report the error to the user.
    fn apply(&self, plan: PlanRef) -> ApplyResult;
}

impl<T> FallibleRule for T
where
    T: InfallibleRule,
{
    fn apply(&self, plan: PlanRef) -> ApplyResult {
        match InfallibleRule::apply(self, plan) {
            Some(plan) => ApplyResult::Ok(plan),
            None => ApplyResult::NotApplicable,
        }
    }
}

pub trait Description {
    fn description(&self) -> &str;
}

pub(super) type BoxedRule = Box<dyn FallibleRule>;

mod logical_filter_expression_simplify_rule;
pub use logical_filter_expression_simplify_rule::*;
mod over_window_merge_rule;
pub use over_window_merge_rule::*;
mod project_join_merge_rule;
pub use project_join_merge_rule::*;
mod project_eliminate_rule;
pub use project_eliminate_rule::*;
mod project_merge_rule;
pub use project_merge_rule::*;
mod pull_up_correlated_predicate_rule;
pub use pull_up_correlated_predicate_rule::*;
mod index_delta_join_rule;
pub use index_delta_join_rule::*;
mod left_deep_tree_join_ordering_rule;
pub use left_deep_tree_join_ordering_rule::*;
mod apply_agg_transpose_rule;
pub use apply_agg_transpose_rule::*;
mod apply_filter_transpose_rule;
pub use apply_filter_transpose_rule::*;
mod apply_project_transpose_rule;
pub use apply_project_transpose_rule::*;
mod apply_eliminate_rule;
pub use apply_eliminate_rule::*;
mod translate_apply_rule;
pub use translate_apply_rule::*;
mod merge_multijoin_rule;
pub use merge_multijoin_rule::*;
mod max_one_row_eliminate_rule;
pub use max_one_row_eliminate_rule::*;
mod apply_join_transpose_rule;
pub use apply_join_transpose_rule::*;
mod apply_to_join_rule;
pub use apply_to_join_rule::*;
mod distinct_agg_rule;
pub use distinct_agg_rule::*;
mod index_selection_rule;
pub use index_selection_rule::*;
mod push_calculation_of_join_rule;
pub use push_calculation_of_join_rule::*;
mod join_commute_rule;
mod over_window_to_agg_and_join_rule;
pub use over_window_to_agg_and_join_rule::*;
mod over_window_split_rule;
pub use over_window_split_rule::*;
mod over_window_to_topn_rule;
pub use join_commute_rule::*;
pub use over_window_to_topn_rule::*;
mod union_to_distinct_rule;
pub use union_to_distinct_rule::*;
mod agg_project_merge_rule;
pub use agg_project_merge_rule::*;
mod union_merge_rule;
pub use union_merge_rule::*;
mod dag_to_tree_rule;
pub use dag_to_tree_rule::*;
mod apply_share_eliminate_rule;
pub use apply_share_eliminate_rule::*;
mod top_n_on_index_rule;
pub use top_n_on_index_rule::*;
mod stream;
pub use stream::bushy_tree_join_ordering_rule::*;
pub use stream::filter_with_now_to_join_rule::*;
pub use stream::generate_series_with_now_rule::*;
pub use stream::split_now_and_rule::*;
pub use stream::split_now_or_rule::*;
pub use stream::stream_project_merge_rule::*;
mod trivial_project_to_values_rule;
pub use trivial_project_to_values_rule::*;
mod union_input_values_merge_rule;
pub use union_input_values_merge_rule::*;
mod rewrite_like_expr_rule;
pub use rewrite_like_expr_rule::*;
mod min_max_on_index_rule;
pub use min_max_on_index_rule::*;
mod always_false_filter_rule;
pub use always_false_filter_rule::*;
mod join_project_transpose_rule;
pub use join_project_transpose_rule::*;
mod limit_push_down_rule;
pub use limit_push_down_rule::*;
mod pull_up_hop_rule;
pub use pull_up_hop_rule::*;
mod apply_offset_rewriter;
use apply_offset_rewriter::ApplyOffsetRewriter;
mod intersect_to_semi_join_rule;
pub use intersect_to_semi_join_rule::*;
mod except_to_anti_join_rule;
pub use except_to_anti_join_rule::*;
mod intersect_merge_rule;
pub use intersect_merge_rule::*;
mod except_merge_rule;
pub use except_merge_rule::*;
mod apply_union_transpose_rule;
pub use apply_union_transpose_rule::*;
mod apply_dedup_transpose_rule;
pub use apply_dedup_transpose_rule::*;
mod project_join_separate_rule;
pub use project_join_separate_rule::*;
mod grouping_sets_to_expand_rule;
pub use grouping_sets_to_expand_rule::*;
mod apply_project_set_transpose_rule;
pub use apply_project_set_transpose_rule::*;
mod cross_join_eliminate_rule;
pub use cross_join_eliminate_rule::*;
mod table_function_to_project_set_rule;

pub use table_function_to_project_set_rule::*;
mod apply_topn_transpose_rule;
pub use apply_topn_transpose_rule::*;
mod apply_limit_transpose_rule;
pub use apply_limit_transpose_rule::*;
mod batch;
pub use batch::batch_project_merge_rule::*;
mod common_sub_expr_extract_rule;
pub use common_sub_expr_extract_rule::*;
mod apply_over_window_transpose_rule;
pub use apply_over_window_transpose_rule::*;
mod apply_expand_transpose_rule;
pub use apply_expand_transpose_rule::*;
mod expand_to_project_rule;
pub use expand_to_project_rule::*;
mod agg_group_by_simplify_rule;
pub use agg_group_by_simplify_rule::*;
mod apply_hop_window_transpose_rule;
pub use apply_hop_window_transpose_rule::*;
mod agg_call_merge_rule;
pub use agg_call_merge_rule::*;
mod pull_up_correlated_predicate_agg_rule;
mod source_to_iceberg_scan_rule;
mod source_to_kafka_scan_rule;
mod table_function_to_file_scan_rule;
mod table_function_to_mysql_query_rule;
mod table_function_to_postgres_query_rule;
mod values_extract_project_rule;

pub use batch::batch_iceberg_predicate_pushdown::*;
pub use batch::batch_push_limit_to_scan_rule::*;
pub use pull_up_correlated_predicate_agg_rule::*;
pub use source_to_iceberg_scan_rule::*;
pub use source_to_kafka_scan_rule::*;
pub use table_function_to_file_scan_rule::*;
pub use table_function_to_mysql_query_rule::*;
pub use table_function_to_postgres_query_rule::*;
pub use values_extract_project_rule::*;

#[macro_export]
macro_rules! for_all_rules {
    ($macro:ident) => {
        $macro! {
              { ApplyAggTransposeRule }
            , { ApplyFilterTransposeRule }
            , { ApplyProjectTransposeRule }
            , { ApplyProjectSetTransposeRule }
            , { ApplyEliminateRule }
            , { ApplyJoinTransposeRule }
            , { ApplyShareEliminateRule }
            , { ApplyToJoinRule }
            , { MaxOneRowEliminateRule }
            , { DistinctAggRule }
            , { IndexDeltaJoinRule }
            , { MergeMultiJoinRule }
            , { ProjectEliminateRule }
            , { ProjectJoinMergeRule }
            , { ProjectMergeRule }
            , { PullUpCorrelatedPredicateRule }
            , { LeftDeepTreeJoinOrderingRule }
            , { TranslateApplyRule }
            , { PushCalculationOfJoinRule }
            , { IndexSelectionRule }
            , { OverWindowToTopNRule }
            , { OverWindowToAggAndJoinRule }
            , { OverWindowSplitRule }
            , { OverWindowMergeRule }
            , { JoinCommuteRule }
            , { UnionToDistinctRule }
            , { AggProjectMergeRule }
            , { UnionMergeRule }
            , { DagToTreeRule }
            , { SplitNowAndRule }
            , { SplitNowOrRule }
            , { FilterWithNowToJoinRule }
            , { GenerateSeriesWithNowRule }
            , { TopNOnIndexRule }
            , { TrivialProjectToValuesRule }
            , { UnionInputValuesMergeRule }
            , { RewriteLikeExprRule }
            , { MinMaxOnIndexRule }
            , { AlwaysFalseFilterRule }
            , { BushyTreeJoinOrderingRule }
            , { StreamProjectMergeRule }
            , { LogicalFilterExpressionSimplifyRule }
            , { JoinProjectTransposeRule }
            , { LimitPushDownRule }
            , { PullUpHopRule }
            , { IntersectToSemiJoinRule }
            , { ExceptToAntiJoinRule }
            , { IntersectMergeRule }
            , { ExceptMergeRule }
            , { ApplyUnionTransposeRule }
            , { ApplyDedupTransposeRule }
            , { ProjectJoinSeparateRule }
            , { GroupingSetsToExpandRule }
            , { CrossJoinEliminateRule }
            , { ApplyTopNTransposeRule }
            , { TableFunctionToProjectSetRule }
            , { TableFunctionToFileScanRule }
            , { TableFunctionToPostgresQueryRule }
            , { TableFunctionToMySqlQueryRule }
            , { ApplyLimitTransposeRule }
            , { CommonSubExprExtractRule }
            , { BatchProjectMergeRule }
            , { ApplyOverWindowTransposeRule }
            , { ApplyExpandTransposeRule }
            , { ExpandToProjectRule }
            , { AggGroupBySimplifyRule }
            , { ApplyHopWindowTransposeRule }
            , { AggCallMergeRule }
            , { ValuesExtractProjectRule }
            , { BatchPushLimitToScanRule }
            , { BatchIcebergPredicatePushDownRule }
            , { PullUpCorrelatedPredicateAggRule }
            , { SourceToKafkaScanRule }
            , { SourceToIcebergScanRule }
        }
    };
}

macro_rules! impl_description {
    ($( { $name:ident }),*) => {
        paste::paste!{
            $(impl Description for [<$name>] {
                fn description(&self) -> &str {
                    stringify!([<$name>])
                }
            })*
        }
    }
}

for_all_rules! {impl_description}