risingwave_frontend/optimizer/plan_node/
stream_changelog.rs1use risingwave_pb::stream_plan::ChangeLogNode;
16use risingwave_pb::stream_plan::change_log_node::{Keyed, Mode};
17use risingwave_pb::stream_plan::stream_node::PbNodeBody;
18
19use super::expr_visitable::ExprVisitable;
20use super::stream::StreamPlanNodeMetadata;
21use super::stream::prelude::PhysicalPlanRef;
22use super::utils::impl_distill_by_unit;
23use super::{ExprRewritable, PlanBase, PlanTreeNodeUnary, Stream, StreamNode, generic};
24use crate::optimizer::StreamPlanRef as PlanRef;
25use crate::optimizer::property::{Distribution, MonotonicityMap, StreamKind};
26use crate::stream_fragmenter::BuildFragmentGraphState;
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub struct StreamChangeLog {
30 pub base: PlanBase<Stream>,
31 core: generic::ChangeLog<PlanRef>,
32 distribution_keys: Vec<u32>,
33}
34
35impl StreamChangeLog {
36 pub fn new_with_dist(
37 core: generic::ChangeLog<PlanRef>,
38 dist: Distribution,
39 distribution_keys: Vec<u32>,
40 ) -> Self {
41 let input = core.input.clone();
42 let base = PlanBase::new_stream_with_core(
43 &core,
44 dist,
45 StreamKind::AppendOnly,
47 input.emit_on_window_close(),
48 input.watermark_columns().clone(),
49 MonotonicityMap::new(), );
51 StreamChangeLog {
52 base,
53 core,
54 distribution_keys,
55 }
56 }
57}
58
59impl PlanTreeNodeUnary<Stream> for StreamChangeLog {
60 fn input(&self) -> PlanRef {
61 self.core.input.clone()
62 }
63
64 fn clone_with_input(&self, input: PlanRef) -> Self {
65 let mut core = self.core.clone();
66 core.input = input;
67 Self::new_with_dist(
68 core,
69 self.base.distribution().clone(),
70 self.distribution_keys.clone(),
71 )
72 }
73}
74
75impl_plan_tree_node_for_unary! { Stream, StreamChangeLog }
76impl_distill_by_unit!(StreamChangeLog, core, "StreamChangeLog");
77
78impl StreamNode for StreamChangeLog {
79 fn to_stream_prost_body(&self, _state: &mut BuildFragmentGraphState) -> PbNodeBody {
80 let mode = self.core.key_indices.as_ref().map(|key| {
81 Mode::Keyed(Keyed {
82 additional_key_columns: key
83 .iter()
84 .map(|&index| index as u32)
85 .filter(|index| !self.distribution_keys.contains(index))
86 .collect(),
87 })
88 });
89 PbNodeBody::Changelog(Box::new(ChangeLogNode {
90 need_op: self.core.need_op,
91 distribution_keys: self.distribution_keys.clone(),
92 mode,
93 stream_keys: self
94 .core
95 .input
96 .expect_stream_key()
97 .iter()
98 .map(|&index| index as u32)
99 .collect(),
100 }))
101 }
102}
103
104impl ExprRewritable<Stream> for StreamChangeLog {}
105
106impl ExprVisitable for StreamChangeLog {}
107
108#[cfg(test)]
109mod tests {
110 use risingwave_common::catalog::{Field, Schema};
111 use risingwave_common::types::DataType;
112
113 use super::*;
114 use crate::optimizer::optimizer_context::OptimizerContext;
115 use crate::optimizer::plan_node::{StreamExchange, StreamValues};
116
117 #[test]
118 fn test_changelog_separates_input_distribution_and_additional_keys() {
119 for (key_indices, distribution_keys, additional_key_columns) in [
120 (Some(vec![1, 2]), vec![1], Some(vec![2])),
121 (Some(vec![1, 2]), vec![2, 1], Some(vec![])),
122 (None, vec![1], None),
123 ] {
124 let values = StreamValues::new(generic::Values::new_with_stream_key(
125 vec![],
126 Schema::new(vec![
127 Field::with_name(DataType::Int32, "id"),
128 Field::with_name(DataType::Int32, "tenant"),
129 Field::with_name(DataType::Int32, "business_key"),
130 ]),
131 OptimizerContext::mock(),
132 vec![0],
133 ));
134 let input = StreamExchange::new(
135 values.into(),
136 Distribution::HashShard(distribution_keys.clone()),
137 );
138 let core = generic::ChangeLog::new(
139 input.into(),
140 key_indices,
141 Some(distribution_keys.clone()),
142 true,
143 true,
144 );
145 let distribution_keys = distribution_keys
146 .into_iter()
147 .map(|index| index as u32)
148 .collect::<Vec<_>>();
149 let changelog = StreamChangeLog::new_with_dist(
150 core,
151 Distribution::HashShard(vec![4]),
152 distribution_keys.clone(),
153 );
154 let PbNodeBody::Changelog(node) =
155 changelog.to_stream_prost_body(&mut BuildFragmentGraphState::default())
156 else {
157 panic!("expected a changelog node");
158 };
159
160 assert_eq!(node.stream_keys, vec![0]);
161 assert_eq!(node.distribution_keys, distribution_keys);
162 assert_eq!(
163 node.mode,
164 additional_key_columns.map(|additional_key_columns| Mode::Keyed(Keyed {
165 additional_key_columns,
166 }))
167 );
168 }
169 }
170}