risingwave_frontend/utils/
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
15mod pretty_serde;
16pub use pretty_serde::PrettySerde;
17mod column_index_mapping;
18use std::any::Any;
19use std::hash::{Hash, Hasher};
20use std::sync::LazyLock;
21
22pub use column_index_mapping::*;
23mod condition;
24pub mod data_type;
25pub use condition::*;
26mod connected_components;
27pub(crate) use connected_components::*;
28mod stream_graph_formatter;
29pub use stream_graph_formatter::*;
30mod with_options;
31use tokio::runtime::Runtime;
32pub use with_options::*;
33mod rewrite_index;
34pub use rewrite_index::*;
35mod index_set;
36pub use index_set::*;
37pub(crate) mod group_by;
38pub mod overwrite_options;
39pub use group_by::*;
40pub use overwrite_options::*;
41mod iceberg_predicate;
42pub use iceberg_predicate::*;
43
44use crate::expr::{Expr, ExprImpl, ExprRewriter, InputRef};
45
46pub static FRONTEND_RUNTIME: LazyLock<Runtime> = LazyLock::new(|| {
47    tokio::runtime::Builder::new_multi_thread()
48        .thread_name("rw-frontend")
49        .enable_all()
50        .build()
51        .expect("failed to build frontend runtime")
52});
53
54/// Substitute `InputRef` with corresponding `ExprImpl`.
55pub struct Substitute {
56    pub mapping: Vec<ExprImpl>,
57}
58
59impl ExprRewriter for Substitute {
60    fn rewrite_input_ref(&mut self, input_ref: InputRef) -> ExprImpl {
61        assert!(
62            input_ref
63                .return_type()
64                .equals_datatype(&self.mapping[input_ref.index()].return_type()),
65            "Type mismatch when substituting {:?} of {:?} with {:?} of {:?}",
66            input_ref,
67            input_ref.return_type(),
68            self.mapping[input_ref.index()],
69            self.mapping[input_ref.index()].return_type()
70        );
71        self.mapping[input_ref.index()].clone()
72    }
73}
74
75// Traits for easy manipulation of recursive structures
76
77/// A `Layer` is a container with subcomponents of type `Sub`.
78/// We usually use `Layer` to represents one layer of a tree-like structure,
79/// where the subcomponents are the recursive subtrees.
80/// But in general, the subcomponent can be of different type than the `Layer`.
81/// Such structural relation between `Sub` and `Layer`
82/// allows us to lift transformation on `Sub` to that on `Layer.`
83/// A related and even more general notion is `Functor`,
84/// which might also be helpful to define in the future.
85pub trait Layer: Sized {
86    type Sub;
87
88    /// Given a transformation `f : Sub -> Sub`,
89    /// we can derive a transformation on the entire `Layer` by acting `f` on all subcomponents.
90    fn map<F>(self, f: F) -> Self
91    where
92        F: FnMut(Self::Sub) -> Self::Sub;
93
94    /// Given a traversal `f : Sub -> ()`,
95    /// we can derive a traversal on the entire `Layer`
96    /// by sequentially visiting the subcomponents with `f`.
97    fn descent<F>(&self, f: F)
98    where
99        F: FnMut(&Self::Sub);
100}
101
102/// A tree-like structure is a `Layer` where the subcomponents are recursively trees.
103pub trait Tree = Layer<Sub = Self>;
104
105/// Given a tree-like structure `T`,
106/// we usually can specify a transformation `T -> T`
107/// by providing a pre-order transformation `pre : T -> T`
108/// and a post-order transformation `post : T -> T`.
109/// Specifically, the derived transformation `apply : T -> T` first applies `pre`,
110/// then maps itself over the subtrees, and finally applies `post`.
111/// This allows us to obtain a global transformation acting recursively on all levels
112/// by specifying simpler transformations at acts locally.
113pub trait Endo<T: Tree> {
114    fn pre(&mut self, t: T) -> T {
115        t
116    }
117
118    fn post(&mut self, t: T) -> T {
119        t
120    }
121
122    /// The real application function is left undefined.
123    /// If we want the derived transformation
124    /// we can simply call `tree_apply` in the implementation.
125    /// But for more complicated requirements,
126    /// e.g. skipping over certain subtrees, custom logic can be added.
127    fn apply(&mut self, t: T) -> T;
128
129    /// The derived transformation based on `pre` and `post`.
130    fn tree_apply(&mut self, t: T) -> T {
131        let t = self.pre(t).map(|s| self.apply(s));
132        self.post(t)
133    }
134}
135
136/// A similar trait to generate traversal over tree-like structure.
137/// See `Endo` for more details.
138#[allow(unused_variables)]
139pub trait Visit<T: Tree> {
140    fn pre(&mut self, t: &T) {}
141
142    fn post(&mut self, t: &T) {}
143
144    fn visit(&mut self, t: &T);
145
146    fn tree_visit(&mut self, t: &T) {
147        self.pre(t);
148        t.descent(|i| self.visit(i));
149        self.post(t);
150    }
151}
152
153// Workaround object safety rules for Eq and Hash, adopted from
154// https://github.com/bevyengine/bevy/blob/f7fbfaf9c72035e98c6b6cec0c7d26ff9f5b1c82/crates/bevy_utils/src/label.rs
155
156/// An object safe version of [`Eq`]. This trait is automatically implemented
157/// for any `'static` type that implements `Eq`.
158pub trait DynEq: Any {
159    fn as_any(&self) -> &dyn Any;
160    fn dyn_eq(&self, other: &dyn DynEq) -> bool;
161}
162
163impl<T: Any + Eq> DynEq for T {
164    fn as_any(&self) -> &dyn Any {
165        self
166    }
167
168    fn dyn_eq(&self, other: &dyn DynEq) -> bool {
169        let other = other.as_any().downcast_ref::<T>();
170        other.is_some_and(|other| self == other)
171    }
172}
173
174impl PartialEq<dyn DynEq + 'static> for dyn DynEq {
175    fn eq(&self, other: &Self) -> bool {
176        self.dyn_eq(other)
177    }
178}
179
180impl Eq for dyn DynEq {
181    fn assert_receiver_is_total_eq(&self) {}
182}
183
184/// An object safe version of [`Hash`]. This trait is automatically implemented
185/// for any `'static` type that implements `Hash`.
186pub trait DynHash: DynEq {
187    fn as_dyn_eq(&self) -> &dyn DynEq;
188    fn dyn_hash(&self, state: &mut dyn Hasher);
189}
190
191impl<T: DynEq + Hash> DynHash for T {
192    fn as_dyn_eq(&self) -> &dyn DynEq {
193        self
194    }
195
196    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
197        T::hash(self, &mut state);
198        self.type_id().hash(&mut state);
199    }
200}
201
202impl Hash for dyn DynHash {
203    fn hash<H: Hasher>(&self, state: &mut H) {
204        self.dyn_hash(state);
205    }
206}
207
208pub fn ordinal(i: usize) -> String {
209    let s = i.to_string();
210    let suffix = if s.ends_with('1') && !s.ends_with("11") {
211        "st"
212    } else if s.ends_with('2') && !s.ends_with("12") {
213        "nd"
214    } else if s.ends_with('3') && !s.ends_with("13") {
215        "rd"
216    } else {
217        "th"
218    };
219    s + suffix
220}