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