Skip to main content

risingwave_frontend/optimizer/plan_node/generic/
share.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 std::fmt;
16use std::hash::{Hash, Hasher};
17
18use risingwave_common::catalog::Schema;
19
20use super::{GenericPlanNode, GenericPlanRef};
21use crate::optimizer::plan_node::PlanNodeId;
22use crate::optimizer::property::FunctionalDependencySet;
23use crate::optimizer::{OptimizerContextRef, ShareEntryRef, ShareId};
24
25/// Immutable handle to a registered shared subplan.
26#[derive(Clone)]
27pub struct Share<PlanRef> {
28    share_id: ShareId,
29    input: PlanRef,
30    /// Keeps the weak context registry entry alive without making the context own a plan that
31    /// points back to itself.
32    entry: ShareEntryRef<PlanRef>,
33}
34
35impl<PlanRef> fmt::Debug for Share<PlanRef> {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        f.debug_struct("Share")
38            .field("share_id", &self.share_id)
39            .finish_non_exhaustive()
40    }
41}
42
43/// Compare and hash the shared subplan *structurally* instead of by `share_id`: ids are
44/// allocated per registration, so two plans built independently (e.g. an MV selection
45/// candidate vs. the query plan) never agree on ids even when they are semantically
46/// identical. Structural comparison keeps `Eq`/`Hash` consistent with the behavior
47/// before the share input was moved into the context side table, which structural
48/// matchers like `MvSelectionRule` and common sub-plan sharing rely on.
49impl<PlanRef: PartialEq> PartialEq for Share<PlanRef> {
50    fn eq(&self, other: &Self) -> bool {
51        self.input == other.input
52    }
53}
54
55impl<PlanRef: Eq> Eq for Share<PlanRef> {}
56
57impl<PlanRef: Hash> Hash for Share<PlanRef> {
58    fn hash<H: Hasher>(&self, state: &mut H) {
59        self.input.hash(state);
60    }
61}
62
63impl<PlanRef: Clone> Share<PlanRef> {
64    pub(in crate::optimizer) fn new(
65        share_id: ShareId,
66        input: PlanRef,
67        entry: ShareEntryRef<PlanRef>,
68    ) -> Self {
69        Self {
70            share_id,
71            input,
72            entry,
73        }
74    }
75
76    pub(in crate::optimizer) fn with_input(&self, input: PlanRef) -> Self {
77        Self {
78            share_id: self.share_id,
79            input,
80            entry: self.entry.clone(),
81        }
82    }
83
84    pub fn share_id(&self) -> ShareId {
85        self.share_id
86    }
87
88    pub fn plan_node_id(&self) -> PlanNodeId {
89        self.entry.plan_node_id()
90    }
91
92    pub fn input(&self) -> PlanRef {
93        self.input.clone()
94    }
95}
96
97impl<PlanRef: GenericPlanRef + Clone> GenericPlanNode for Share<PlanRef> {
98    fn schema(&self) -> Schema {
99        self.input.schema().clone()
100    }
101
102    fn stream_key(&self) -> Option<Vec<usize>> {
103        Some(self.input.stream_key()?.to_vec())
104    }
105
106    fn ctx(&self) -> OptimizerContextRef {
107        self.input.ctx()
108    }
109
110    fn functional_dependency(&self) -> FunctionalDependencySet {
111        self.input.functional_dependency().clone()
112    }
113}