Skip to main content

risingwave_frontend/optimizer/plan_node/generic/
changelog.rs

1// Copyright 2024 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 pretty_xmlish::{Pretty, Str, XmlNode};
16use risingwave_common::catalog::{Field, FieldDisplay, Schema};
17use risingwave_common::util::column_index_mapping::ColIndexMapping;
18
19use super::{DistillUnit, GenericPlanNode};
20use crate::OptimizerContextRef;
21use crate::optimizer::plan_node::stream::prelude::GenericPlanRef;
22use crate::optimizer::plan_node::utils::childless_record;
23use crate::optimizer::property::FunctionalDependencySet;
24use crate::utils::ColIndexMappingRewriteExt;
25
26pub const CHANGELOG_OP: &str = "changelog_op";
27pub const _CHANGELOG_ROW_ID: &str = "_changelog_row_id";
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub struct ChangeLog<PlanRef> {
30    pub input: PlanRef,
31    /// Declared key column indices in the input schema.
32    pub key_indices: Option<Vec<usize>>,
33    // If there is no op in the output result, it is false, example 'create materialized view mv1 as with sub as changelog from t1 select v1 from sub;'
34    pub need_op: bool,
35    // Before rewrite. If there is no changelog_row_id in the output result, it is false.
36    // After rewrite. It is always true.
37    pub need_changelog_row_id: bool,
38}
39impl<PlanRef: GenericPlanRef> DistillUnit for ChangeLog<PlanRef> {
40    fn distill_with_name<'a>(&self, name: impl Into<Str<'a>>) -> XmlNode<'a> {
41        let mut fields = Vec::with_capacity(1);
42
43        if let Some(key_indices) = &self.key_indices {
44            let key = Pretty::Array(
45                key_indices
46                    .iter()
47                    .map(|&index| {
48                        let field = &self.input.schema()[index];
49
50                        Pretty::display(&FieldDisplay(field))
51                    })
52                    .collect(),
53            );
54
55            fields.push(("key", key));
56        }
57
58        childless_record(name, fields)
59    }
60}
61impl<PlanRef: GenericPlanRef> ChangeLog<PlanRef> {
62    pub fn new(
63        input: PlanRef,
64        key_indices: Option<Vec<usize>>,
65        need_op: bool,
66        need_changelog_row_id: bool,
67    ) -> Self {
68        ChangeLog {
69            input,
70            key_indices,
71            need_op,
72            need_changelog_row_id,
73        }
74    }
75
76    pub fn clone_with_input<OtherPlanRef>(&self, input: OtherPlanRef) -> ChangeLog<OtherPlanRef> {
77        ChangeLog {
78            input,
79            key_indices: self.key_indices.clone(),
80            need_op: self.need_op,
81            need_changelog_row_id: self.need_changelog_row_id,
82        }
83    }
84
85    pub fn i2o_col_mapping(&self) -> ColIndexMapping {
86        let mut map = vec![None; self.input.schema().len()];
87        (0..self.input.schema().len()).for_each(|i| map[i] = Some(i));
88        ColIndexMapping::new(map, self.schema().len())
89    }
90}
91impl<PlanRef: GenericPlanRef> GenericPlanNode for ChangeLog<PlanRef> {
92    fn schema(&self) -> Schema {
93        let mut fields = self.input.schema().fields.clone();
94        if self.need_op {
95            fields.push(Field::with_name(
96                risingwave_common::types::DataType::Int16,
97                CHANGELOG_OP,
98            ));
99        }
100        if self.need_changelog_row_id {
101            fields.push(Field::with_name(
102                risingwave_common::types::DataType::Serial,
103                _CHANGELOG_ROW_ID,
104            ));
105        }
106        Schema::new(fields)
107    }
108
109    fn stream_key(&self) -> Option<Vec<usize>> {
110        if self.need_changelog_row_id {
111            let keys = vec![self.schema().len() - 1];
112            Some(keys)
113        } else {
114            None
115        }
116    }
117
118    fn ctx(&self) -> OptimizerContextRef {
119        self.input.ctx()
120    }
121
122    fn functional_dependency(&self) -> FunctionalDependencySet {
123        let i2o = self.i2o_col_mapping();
124        i2o.rewrite_functional_dependency_set(self.input.functional_dependency().clone())
125    }
126}