Skip to main content

risingwave_frontend/optimizer/rule/
mod.rs

1// Copyright 2022 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
15//! Define all [`Rule`]
16
17use std::convert::Infallible;
18use std::ops::FromResidual;
19
20use thiserror_ext::AsReport;
21
22use super::PlanRef;
23use crate::error::RwError;
24
25/// Result when applying a [`Rule`] to a `PlanNode`
26pub enum ApplyResult<T> {
27    /// Successfully applied the rule and returned a new plan.
28    Ok(T),
29    /// The current rule is not applicable to the input.
30    /// The optimizer may try another rule.
31    NotApplicable,
32    /// An unrecoverable error occurred while applying the rule.
33    /// The optimizer should stop applying other rules and report the error to the user.
34    Err(RwError),
35}
36
37impl<T> ApplyResult<T> {
38    /// Unwrap the result, panicking if it's not `Ok`.
39    pub fn unwrap(self) -> T {
40        match self {
41            ApplyResult::Ok(plan) => plan,
42            ApplyResult::NotApplicable => panic!("unwrap ApplyResult::NotApplicable"),
43            ApplyResult::Err(e) => panic!("unwrap ApplyResult::Err, error: {:?}", e.as_report()),
44        }
45    }
46}
47
48/// Allow calling `?` on an `Option` in a function returning `ApplyResult`.
49impl<T> FromResidual<Option<Infallible>> for ApplyResult<T> {
50    fn from_residual(residual: Option<Infallible>) -> Self {
51        match residual {
52            Some(i) => match i {},
53            None => Self::NotApplicable,
54        }
55    }
56}
57
58/// Allow calling `?` on a `Result` in a function returning `ApplyResult`.
59impl<T, E> FromResidual<Result<Infallible, E>> for ApplyResult<T>
60where
61    E: Into<RwError>,
62{
63    fn from_residual(residual: Result<Infallible, E>) -> Self {
64        match residual {
65            Ok(i) => match i {},
66            Err(e) => Self::Err(e.into()),
67        }
68    }
69}
70
71/// An one-to-one transform for the `PlanNode`.
72///
73/// It's a convenient trait to implement [`FallibleRule`], thus made available only within this module.
74trait InfallibleRule<C: ConventionMarker>: Send + Sync + Description {
75    /// Apply the rule to the plan node.
76    ///
77    /// - Returns `Some` if the apply is successful.
78    /// - Returns `None` if it's not applicable. The optimizer may try other rules.
79    fn apply(&self, plan: PlanRef<C>) -> Option<PlanRef<C>>;
80}
81
82use InfallibleRule as Rule;
83
84/// An one-to-one transform for the `PlanNode` that may return an
85/// unrecoverable error that stops further optimization.
86///
87/// An [`InfallibleRule`] is always a [`FallibleRule`].
88pub trait FallibleRule<C: ConventionMarker>: Send + Sync + Description {
89    /// Apply the rule to the plan node, which may return an unrecoverable error.
90    ///
91    /// - Returns `ApplyResult::Ok` if the apply is successful.
92    /// - Returns `ApplyResult::NotApplicable` if it's not applicable. The optimizer may try other rules.
93    /// - Returns `ApplyResult::Err` if an unrecoverable error occurred. The optimizer should stop applying
94    ///   other rules and report the error to the user.
95    fn apply(&self, plan: PlanRef<C>) -> ApplyResult<PlanRef<C>>;
96}
97
98impl<C: ConventionMarker, R> FallibleRule<C> for R
99where
100    R: InfallibleRule<C>,
101{
102    fn apply(&self, plan: PlanRef<C>) -> ApplyResult<PlanRef<C>> {
103        match InfallibleRule::apply(self, plan) {
104            Some(plan) => ApplyResult::Ok(plan),
105            None => ApplyResult::NotApplicable,
106        }
107    }
108}
109
110pub trait Description {
111    fn description(&self) -> &str;
112}
113
114pub(super) type BoxedRule<C> = Box<dyn FallibleRule<C>>;
115
116mod correlated_expr_rewriter;
117mod logical_filter_expression_simplify_rule;
118pub use logical_filter_expression_simplify_rule::*;
119mod mv_selection_rule;
120pub use mv_selection_rule::*;
121mod over_window_merge_rule;
122pub use over_window_merge_rule::*;
123mod project_join_merge_rule;
124pub use project_join_merge_rule::*;
125mod project_eliminate_rule;
126pub use project_eliminate_rule::*;
127mod project_merge_rule;
128pub use project_merge_rule::*;
129mod project_top_n_transpose_rule;
130pub use project_top_n_transpose_rule::*;
131mod top_n_project_transpose_rule;
132pub use top_n_project_transpose_rule::*;
133mod pull_up_correlated_predicate_rule;
134pub use pull_up_correlated_predicate_rule::*;
135mod pull_up_correlated_project_value_rule;
136pub use pull_up_correlated_project_value_rule::*;
137mod index_delta_join_rule;
138pub use index_delta_join_rule::*;
139mod left_deep_tree_join_ordering_rule;
140pub use left_deep_tree_join_ordering_rule::*;
141mod apply_agg_transpose_rule;
142pub use apply_agg_transpose_rule::*;
143mod apply_filter_transpose_rule;
144pub use apply_filter_transpose_rule::*;
145mod apply_project_transpose_rule;
146pub use apply_project_transpose_rule::*;
147mod apply_eliminate_rule;
148pub use apply_eliminate_rule::*;
149mod translate_apply_rule;
150pub use translate_apply_rule::*;
151mod merge_multijoin_rule;
152pub use merge_multijoin_rule::*;
153mod max_one_row_eliminate_rule;
154pub use max_one_row_eliminate_rule::*;
155mod apply_join_transpose_rule;
156pub use apply_join_transpose_rule::*;
157mod apply_to_join_rule;
158pub use apply_to_join_rule::*;
159mod distinct_agg_rule;
160pub use distinct_agg_rule::*;
161mod index_selection_rule;
162pub use index_selection_rule::*;
163mod streaming_index_selection_rule;
164pub use streaming_index_selection_rule::StreamingIndexSelectionRule;
165mod push_calculation_of_join_rule;
166pub use push_calculation_of_join_rule::*;
167mod join_commute_rule;
168mod over_window_to_agg_and_join_rule;
169pub use over_window_to_agg_and_join_rule::*;
170mod over_window_split_rule;
171pub use over_window_split_rule::*;
172mod over_window_to_topn_rule;
173pub use join_commute_rule::*;
174pub use over_window_to_topn_rule::*;
175mod union_to_distinct_rule;
176pub use union_to_distinct_rule::*;
177mod agg_project_merge_rule;
178pub use agg_project_merge_rule::*;
179mod union_merge_rule;
180pub use union_merge_rule::*;
181mod dag_to_tree_rule;
182pub use dag_to_tree_rule::*;
183mod apply_share_eliminate_rule;
184pub use apply_share_eliminate_rule::*;
185mod top_n_on_index_rule;
186pub use top_n_on_index_rule::*;
187mod stream;
188pub use stream::bushy_tree_join_ordering_rule::*;
189pub use stream::filter_with_now_to_join_rule::*;
190pub use stream::generate_series_with_now_rule::*;
191pub use stream::separate_consecutive_join_rule::*;
192pub use stream::split_now_and_rule::*;
193pub use stream::split_now_or_rule::*;
194pub use stream::stream_project_merge_rule::*;
195mod trivial_project_to_values_rule;
196pub use trivial_project_to_values_rule::*;
197mod union_input_values_merge_rule;
198pub use union_input_values_merge_rule::*;
199mod rewrite_like_expr_rule;
200pub use rewrite_like_expr_rule::*;
201mod min_max_on_index_rule;
202pub use min_max_on_index_rule::*;
203mod always_false_filter_rule;
204pub use always_false_filter_rule::*;
205mod join_project_transpose_rule;
206pub use join_project_transpose_rule::*;
207mod limit_push_down_rule;
208pub use limit_push_down_rule::*;
209mod pull_up_hop_rule;
210pub use pull_up_hop_rule::*;
211mod apply_offset_rewriter;
212use apply_offset_rewriter::ApplyOffsetRewriter;
213mod intersect_to_semi_join_rule;
214pub use intersect_to_semi_join_rule::*;
215mod except_to_anti_join_rule;
216pub use except_to_anti_join_rule::*;
217mod intersect_merge_rule;
218pub use intersect_merge_rule::*;
219mod except_merge_rule;
220pub use except_merge_rule::*;
221mod apply_union_transpose_rule;
222pub use apply_union_transpose_rule::*;
223mod apply_dedup_transpose_rule;
224pub use apply_dedup_transpose_rule::*;
225mod project_join_separate_rule;
226pub use project_join_separate_rule::*;
227mod grouping_sets_to_expand_rule;
228pub use grouping_sets_to_expand_rule::*;
229mod apply_project_set_transpose_rule;
230pub use apply_project_set_transpose_rule::*;
231mod apply_table_function_to_project_set_rule;
232pub use apply_table_function_to_project_set_rule::*;
233mod cross_join_eliminate_rule;
234pub use cross_join_eliminate_rule::*;
235mod table_function_to_project_set_rule;
236
237pub use table_function_to_project_set_rule::*;
238mod apply_topn_transpose_rule;
239pub use apply_topn_transpose_rule::*;
240mod apply_limit_transpose_rule;
241pub use apply_limit_transpose_rule::*;
242mod batch;
243pub use batch::batch_project_merge_rule::*;
244mod common_sub_expr_extract_rule;
245pub use common_sub_expr_extract_rule::*;
246mod apply_over_window_transpose_rule;
247pub use apply_over_window_transpose_rule::*;
248mod apply_expand_transpose_rule;
249pub use apply_expand_transpose_rule::*;
250mod expand_to_project_rule;
251pub use expand_to_project_rule::*;
252mod agg_group_by_simplify_rule;
253pub use agg_group_by_simplify_rule::*;
254mod apply_hop_window_transpose_rule;
255pub use apply_hop_window_transpose_rule::*;
256mod agg_call_merge_rule;
257pub use agg_call_merge_rule::*;
258mod unify_first_last_value_rule;
259pub use unify_first_last_value_rule::*;
260mod empty_agg_remove_rule;
261pub use empty_agg_remove_rule::*;
262mod add_logstore_rule;
263mod correlated_topn_to_vector_search;
264mod iceberg_count_star_rule;
265mod iceberg_engine_storage_selection_rule;
266mod iceberg_intermediate_scan_rule;
267mod pull_up_correlated_predicate_agg_rule;
268mod source_to_kafka_scan_rule;
269mod table_function_to_file_scan_rule;
270mod table_function_to_internal_backfill_progress;
271mod table_function_to_internal_get_channel_delta_stats;
272mod table_function_to_internal_source_backfill_progress;
273mod table_function_to_mysql_query_rule;
274mod table_function_to_postgres_query_rule;
275mod top_n_to_vector_search_rule;
276mod values_extract_project_rule;
277pub use add_logstore_rule::*;
278pub use batch::batch_push_limit_to_scan_rule::*;
279pub use correlated_topn_to_vector_search::*;
280pub use iceberg_count_star_rule::IcebergCountStarRule;
281pub use iceberg_engine_storage_selection_rule::*;
282pub use iceberg_intermediate_scan_rule::*;
283pub use pull_up_correlated_predicate_agg_rule::*;
284pub use source_to_kafka_scan_rule::*;
285pub use table_function_to_file_scan_rule::*;
286pub use table_function_to_internal_backfill_progress::*;
287pub use table_function_to_internal_get_channel_delta_stats::*;
288pub use table_function_to_internal_source_backfill_progress::*;
289pub use table_function_to_mysql_query_rule::*;
290pub use table_function_to_postgres_query_rule::*;
291pub use top_n_to_vector_search_rule::*;
292pub use values_extract_project_rule::*;
293
294use crate::optimizer::plan_node::ConventionMarker;
295
296#[macro_export]
297macro_rules! for_all_rules {
298    ($macro:ident) => {
299        $macro! {
300              { ApplyAggTransposeRule }
301            , { ApplyFilterTransposeRule }
302            , { ApplyProjectTransposeRule }
303            , { ApplyProjectSetTransposeRule }
304            , { ApplyTableFunctionToProjectSetRule }
305            , { ApplyEliminateRule }
306            , { ApplyJoinTransposeRule }
307            , { ApplyShareEliminateRule }
308            , { ApplyToJoinRule }
309            , { MaxOneRowEliminateRule }
310            , { DistinctAggRule }
311            , { IndexDeltaJoinRule }
312            , { MergeMultiJoinRule }
313            , { ProjectEliminateRule }
314            , { ProjectJoinMergeRule }
315            , { ProjectMergeRule }
316            , { PullUpCorrelatedPredicateRule }
317            , { PullUpCorrelatedProjectValueRule }
318            , { LeftDeepTreeJoinOrderingRule }
319            , { TranslateApplyRule }
320            , { PushCalculationOfJoinRule }
321            , { IndexSelectionRule }
322            , { OverWindowToTopNRule }
323            , { OverWindowToAggAndJoinRule }
324            , { OverWindowSplitRule }
325            , { OverWindowMergeRule }
326            , { JoinCommuteRule }
327            , { UnionToDistinctRule }
328            , { AggProjectMergeRule }
329            , { UnionMergeRule }
330            , { DagToTreeRule }
331            , { SplitNowAndRule }
332            , { SplitNowOrRule }
333            , { FilterWithNowToJoinRule }
334            , { GenerateSeriesWithNowRule }
335            , { ProjectTopNTransposeRule }
336            , { TopNProjectTransposeRule }
337            , { TopNOnIndexRule }
338            , { TrivialProjectToValuesRule }
339            , { UnionInputValuesMergeRule }
340            , { RewriteLikeExprRule }
341            , { MinMaxOnIndexRule }
342            , { AlwaysFalseFilterRule }
343            , { BushyTreeJoinOrderingRule }
344            , { StreamProjectMergeRule }
345            , { SeparateConsecutiveJoinRule }
346            , { LogicalFilterExpressionSimplifyRule }
347            , { JoinProjectTransposeRule }
348            , { LimitPushDownRule }
349            , { PullUpHopRule }
350            , { IntersectToSemiJoinRule }
351            , { ExceptToAntiJoinRule }
352            , { IntersectMergeRule }
353            , { ExceptMergeRule }
354            , { ApplyUnionTransposeRule }
355            , { ApplyDedupTransposeRule }
356            , { ProjectJoinSeparateRule }
357            , { GroupingSetsToExpandRule }
358            , { CrossJoinEliminateRule }
359            , { ApplyTopNTransposeRule }
360            , { TableFunctionToProjectSetRule }
361            , { TableFunctionToFileScanRule }
362            , { TableFunctionToPostgresQueryRule }
363            , { TableFunctionToMySqlQueryRule }
364            , { TableFunctionToInternalBackfillProgressRule }
365            , { TableFunctionToInternalGetChannelDeltaStatsRule }
366            , { TableFunctionToInternalSourceBackfillProgressRule }
367            , { ApplyLimitTransposeRule }
368            , { CommonSubExprExtractRule }
369            , { BatchProjectMergeRule }
370            , { ApplyOverWindowTransposeRule }
371            , { ApplyExpandTransposeRule }
372            , { ExpandToProjectRule }
373            , { AggGroupBySimplifyRule }
374            , { ApplyHopWindowTransposeRule }
375            , { AggCallMergeRule }
376            , { UnifyFirstLastValueRule }
377            , { ValuesExtractProjectRule }
378            , { BatchPushLimitToScanRule }
379            , { PullUpCorrelatedPredicateAggRule }
380            , { SourceToKafkaScanRule }
381            , { IcebergEngineStorageSelectionRule }
382            , { IcebergCountStarRule}
383            , { IcebergIntermediateScanRule }
384            , { AddLogstoreRule }
385            , { EmptyAggRemoveRule }
386            , { TopNToVectorSearchRule }
387            , { CorrelatedTopNToVectorSearchRule }
388            , { MvSelectionRule }
389        }
390    };
391}
392
393macro_rules! impl_description {
394    ($( { $name:ident }),*) => {
395        paste::paste!{
396            $(impl Description for [<$name>] {
397                fn description(&self) -> &str {
398                    stringify!([<$name>])
399                }
400            })*
401        }
402    }
403}
404
405for_all_rules! {impl_description}
406
407mod prelude {
408    pub(super) use crate::optimizer::plan_node::{Logical, LogicalPlanRef as PlanRef};
409    pub(super) use crate::optimizer::rule::Rule;
410
411    pub(super) type BoxedRule = crate::optimizer::rule::BoxedRule<Logical>;
412}