Skip to main content

risingwave_frontend/optimizer/plan_node/
plan_base.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
15use educe::Educe;
16
17use super::generic::GenericPlanNode;
18use super::*;
19use crate::optimizer::property::{Distribution, StreamKind, WatermarkColumns};
20
21/// No extra fields for logical plan nodes.
22#[derive(Clone, Debug, PartialEq, Eq, Hash)]
23pub struct NoExtra;
24
25// Make them public types in a private module to allow using them as public trait bounds,
26// while still keeping them private to the super module.
27mod physical_common {
28    use super::*;
29
30    /// Common extra fields for physical plan nodes.
31    #[derive(Clone, Debug, PartialEq, Eq, Hash)]
32    pub struct PhysicalCommonExtra {
33        /// The distribution property of the `PlanNode`'s output, store an `Distribution::any()` here
34        /// will not affect correctness, but insert unnecessary exchange in plan
35        pub dist: Distribution,
36    }
37
38    /// A helper trait to reuse code for accessing the common physical fields of batch and stream
39    /// plan bases.
40    pub trait GetPhysicalCommon {
41        fn physical(&self) -> &PhysicalCommonExtra;
42        fn physical_mut(&mut self) -> &mut PhysicalCommonExtra;
43    }
44}
45
46use physical_common::*;
47
48/// Extra fields for stream plan nodes.
49#[derive(Clone, Debug, PartialEq, Eq, Hash)]
50pub struct StreamExtra {
51    /// Common fields for physical plan nodes.
52    physical: PhysicalCommonExtra,
53
54    /// Whether the `PlanNode`'s output is append-only, retract, or upsert.
55    stream_kind: StreamKind,
56
57    /// Whether the output is emitted on window close.
58    emit_on_window_close: bool,
59    /// The watermark column indices of the `PlanNode`'s output. There could be watermark output from
60    /// this stream operator.
61    watermark_columns: WatermarkColumns,
62    /// The monotonicity of columns in the output.
63    columns_monotonicity: MonotonicityMap,
64}
65
66impl GetPhysicalCommon for StreamExtra {
67    fn physical(&self) -> &PhysicalCommonExtra {
68        &self.physical
69    }
70
71    fn physical_mut(&mut self) -> &mut PhysicalCommonExtra {
72        &mut self.physical
73    }
74}
75
76/// Extra fields for batch plan nodes.
77#[derive(Clone, Debug, PartialEq, Eq, Hash)]
78pub struct BatchExtra {
79    /// Common fields for physical plan nodes.
80    physical: PhysicalCommonExtra,
81
82    /// Equivalent order properties of the `PlanNode`'s output.
83    ///
84    /// The first item is canonical and returned by `order()`. Additional items represent
85    /// equivalent orders that can also satisfy required order checks.
86    ///
87    /// Example (`BatchSeqScan`):
88    /// - Base table order: `(a, b, c, d)`.
89    /// - Scan range fixes `a = const` and `b = const` (`eq_prefix_len = 2`).
90    /// - The scan can provide these equivalent orders:
91    ///   `(a, b, c, d)`, `(b, c, d)`, `(c, d)`.
92    ///
93    /// This lets optimization rules avoid unnecessary `BatchSort` when required order is a suffix
94    /// after fixed equality prefixes.
95    orders: Vec<Order>,
96}
97
98impl GetPhysicalCommon for BatchExtra {
99    fn physical(&self) -> &PhysicalCommonExtra {
100        &self.physical
101    }
102
103    fn physical_mut(&mut self) -> &mut PhysicalCommonExtra {
104        &mut self.physical
105    }
106}
107
108/// The common fields of all plan nodes with different conventions.
109///
110/// Please make a field named `base` in every planNode and correctly value
111/// it when construct the planNode.
112///
113/// All fields are intentionally made private and immutable, as they should
114/// normally be the same as the given [`GenericPlanNode`] when constructing.
115///
116/// - To access them, use traits including [`GenericPlanRef`],
117///   [`PhysicalPlanRef`], [`StreamPlanNodeMetadata`] and [`BatchPlanNodeMetadata`] with
118///   compile-time checks.
119/// - To mutate them, use methods like `new_*` or `clone_with_*`.
120#[derive(Educe)]
121#[educe(PartialEq, Eq, Hash, Clone, Debug)]
122pub struct PlanBase<C: ConventionMarker> {
123    // -- common fields --
124    #[educe(PartialEq(ignore), Hash(ignore))]
125    id: PlanNodeId,
126    #[educe(PartialEq(ignore), Hash(ignore))]
127    ctx: OptimizerContextRef,
128
129    schema: Schema,
130    /// the pk indices of the `PlanNode`'s output, a empty stream key vec means there is no stream key
131    // TODO: this is actually a logical and stream only property.
132    // - For logical nodes, this is `None` in most time expect for the phase after `logical_rewrite_for_stream`.
133    // - For stream nodes, this is always `Some`.
134    stream_key: Option<Vec<usize>>,
135    functional_dependency: FunctionalDependencySet,
136
137    /// Extra fields for different conventions.
138    extra: C::Extra,
139}
140
141impl<C: ConventionMarker> generic::GenericPlanRef for PlanBase<C> {
142    fn id(&self) -> PlanNodeId {
143        self.id
144    }
145
146    fn schema(&self) -> &Schema {
147        &self.schema
148    }
149
150    fn stream_key(&self) -> Option<&[usize]> {
151        self.stream_key.as_deref()
152    }
153
154    fn ctx(&self) -> OptimizerContextRef {
155        self.ctx.clone()
156    }
157
158    fn functional_dependency(&self) -> &FunctionalDependencySet {
159        &self.functional_dependency
160    }
161}
162
163impl<C: ConventionMarker> generic::PhysicalPlanRef for PlanBase<C>
164where
165    C::Extra: GetPhysicalCommon,
166{
167    fn distribution(&self) -> &Distribution {
168        &self.extra.physical().dist
169    }
170}
171
172impl stream::StreamPlanNodeMetadata for PlanBase<Stream> {
173    fn stream_kind(&self) -> StreamKind {
174        self.extra.stream_kind
175    }
176
177    fn emit_on_window_close(&self) -> bool {
178        self.extra.emit_on_window_close
179    }
180
181    fn watermark_columns(&self) -> &WatermarkColumns {
182        &self.extra.watermark_columns
183    }
184
185    fn columns_monotonicity(&self) -> &MonotonicityMap {
186        &self.extra.columns_monotonicity
187    }
188}
189
190impl batch::BatchPlanNodeMetadata for PlanBase<Batch> {
191    fn order(&self) -> &Order {
192        self.extra
193            .orders
194            .first()
195            .expect("batch plan node should always have at least one order")
196    }
197
198    fn orders(&self) -> Vec<Order> {
199        self.extra.orders.clone()
200    }
201}
202
203impl<C: ConventionMarker> PlanBase<C> {
204    pub fn clone_with_new_plan_id(&self) -> Self {
205        let mut new = self.clone();
206        new.id = self.ctx().next_plan_node_id();
207        new
208    }
209}
210
211impl PlanBase<Logical> {
212    pub fn new_logical(
213        ctx: OptimizerContextRef,
214        schema: Schema,
215        stream_key: Option<Vec<usize>>,
216        functional_dependency: FunctionalDependencySet,
217    ) -> Self {
218        let id = ctx.next_plan_node_id();
219        Self::new_logical_with_id(ctx, id, schema, stream_key, functional_dependency)
220    }
221
222    fn new_logical_with_id(
223        ctx: OptimizerContextRef,
224        id: PlanNodeId,
225        schema: Schema,
226        stream_key: Option<Vec<usize>>,
227        functional_dependency: FunctionalDependencySet,
228    ) -> Self {
229        Self {
230            id,
231            ctx,
232            schema,
233            stream_key,
234            functional_dependency,
235            extra: NoExtra,
236        }
237    }
238
239    pub fn new_logical_with_core(core: &impl GenericPlanNode) -> Self {
240        Self::new_logical(
241            core.ctx(),
242            core.schema(),
243            core.stream_key(),
244            core.functional_dependency(),
245        )
246    }
247
248    pub fn new_logical_share(core: &generic::Share<LogicalPlanRef>) -> Self {
249        Self::new_logical_with_id(
250            core.ctx(),
251            core.plan_node_id(),
252            core.schema(),
253            core.stream_key(),
254            core.functional_dependency(),
255        )
256    }
257}
258
259impl PlanBase<Stream> {
260    pub fn new_stream(
261        ctx: OptimizerContextRef,
262        schema: Schema,
263        stream_key: Option<Vec<usize>>,
264        functional_dependency: FunctionalDependencySet,
265        dist: Distribution,
266        stream_kind: StreamKind,
267        emit_on_window_close: bool,
268        watermark_columns: WatermarkColumns,
269        columns_monotonicity: MonotonicityMap,
270    ) -> Self {
271        let id = ctx.next_plan_node_id();
272        Self::new_stream_with_id(
273            ctx,
274            id,
275            schema,
276            stream_key,
277            functional_dependency,
278            dist,
279            stream_kind,
280            emit_on_window_close,
281            watermark_columns,
282            columns_monotonicity,
283        )
284    }
285
286    fn new_stream_with_id(
287        ctx: OptimizerContextRef,
288        id: PlanNodeId,
289        schema: Schema,
290        stream_key: Option<Vec<usize>>,
291        functional_dependency: FunctionalDependencySet,
292        dist: Distribution,
293        stream_kind: StreamKind,
294        emit_on_window_close: bool,
295        watermark_columns: WatermarkColumns,
296        columns_monotonicity: MonotonicityMap,
297    ) -> Self {
298        Self {
299            id,
300            ctx,
301            schema,
302            stream_key,
303            functional_dependency,
304            extra: StreamExtra {
305                physical: PhysicalCommonExtra { dist },
306                stream_kind,
307                emit_on_window_close,
308                watermark_columns,
309                columns_monotonicity,
310            },
311        }
312    }
313
314    pub fn new_stream_with_core(
315        core: &impl GenericPlanNode,
316        dist: Distribution,
317        stream_kind: StreamKind,
318        emit_on_window_close: bool,
319        watermark_columns: WatermarkColumns,
320        columns_monotonicity: MonotonicityMap,
321    ) -> Self {
322        Self::new_stream(
323            core.ctx(),
324            core.schema(),
325            core.stream_key(),
326            core.functional_dependency(),
327            dist,
328            stream_kind,
329            emit_on_window_close,
330            watermark_columns,
331            columns_monotonicity,
332        )
333    }
334
335    pub fn new_stream_share(
336        core: &generic::Share<StreamPlanRef>,
337        dist: Distribution,
338        stream_kind: StreamKind,
339        emit_on_window_close: bool,
340        watermark_columns: WatermarkColumns,
341        columns_monotonicity: MonotonicityMap,
342    ) -> Self {
343        Self::new_stream_with_id(
344            core.ctx(),
345            core.plan_node_id(),
346            core.schema(),
347            core.stream_key(),
348            core.functional_dependency(),
349            dist,
350            stream_kind,
351            emit_on_window_close,
352            watermark_columns,
353            columns_monotonicity,
354        )
355    }
356}
357
358impl PlanBase<Batch> {
359    pub fn new_batch(
360        ctx: OptimizerContextRef,
361        schema: Schema,
362        dist: Distribution,
363        order: Order,
364    ) -> Self {
365        Self::new_batch_with_orders(ctx, schema, dist, vec![order])
366    }
367
368    pub fn new_batch_with_orders(
369        ctx: OptimizerContextRef,
370        schema: Schema,
371        dist: Distribution,
372        orders: Vec<Order>,
373    ) -> Self {
374        assert!(
375            !orders.is_empty(),
376            "batch plan node should always have at least one order"
377        );
378        let id = ctx.next_plan_node_id();
379        let functional_dependency = FunctionalDependencySet::new(schema.len());
380        Self {
381            id,
382            ctx,
383            schema,
384            stream_key: None,
385            functional_dependency,
386            extra: BatchExtra {
387                physical: PhysicalCommonExtra { dist },
388                orders,
389            },
390        }
391    }
392
393    pub fn new_batch_with_core(
394        core: &impl GenericPlanNode,
395        dist: Distribution,
396        order: Order,
397    ) -> Self {
398        Self::new_batch(core.ctx(), core.schema(), dist, order)
399    }
400
401    pub fn new_batch_with_core_and_orders(
402        core: &impl GenericPlanNode,
403        dist: Distribution,
404        orders: Vec<Order>,
405    ) -> Self {
406        Self::new_batch_with_orders(core.ctx(), core.schema(), dist, orders)
407    }
408}
409
410impl<C: ConventionMarker> PlanBase<C>
411where
412    C::Extra: GetPhysicalCommon,
413{
414    /// Clone the plan node with a new distribution.
415    ///
416    /// Panics if the plan node is not physical.
417    pub fn clone_with_new_distribution(&self, dist: Distribution) -> Self {
418        let mut new = self.clone();
419        new.extra.physical_mut().dist = dist;
420        new
421    }
422}
423
424// Mutators for testing only.
425#[cfg(test)]
426impl<C: ConventionMarker> PlanBase<C> {
427    pub fn functional_dependency_mut(&mut self) -> &mut FunctionalDependencySet {
428        &mut self.functional_dependency
429    }
430}