risingwave_frontend/optimizer/plan_node/
convert.rs1use std::collections::HashMap;
16
17use risingwave_common::catalog::FieldDisplay;
18use risingwave_pb::stream_plan::StreamScanType;
19
20use super::*;
21use crate::optimizer::property::RequiredDist;
22
23pub trait ToStream {
34 fn logical_rewrite_for_stream(
42 &self,
43 ctx: &mut RewriteStreamContext,
44 ) -> Result<(LogicalPlanRef, ColIndexMapping)>;
45
46 fn to_stream(&self, ctx: &mut ToStreamContext) -> Result<StreamPlanRef>;
48
49 fn to_stream_with_dist_required(
51 &self,
52 required_dist: &RequiredDist,
53 ctx: &mut ToStreamContext,
54 ) -> Result<StreamPlanRef> {
55 let ret = self.to_stream(ctx)?;
56 required_dist.streaming_enforce_if_not_satisfies(ret)
57 }
58
59 fn try_better_locality(&self, _columns: &[usize]) -> Option<LogicalPlanRef> {
60 None
61 }
62}
63
64pub fn try_enforce_locality_requirement(
70 plan: LogicalPlanRef,
71 columns: &[usize],
72 locality_backfill_enabled: bool,
73) -> LogicalPlanRef {
74 assert!(!columns.is_empty());
75 if let Some(better_plan) = plan.try_better_locality(columns) {
76 better_plan
77 } else if locality_backfill_enabled {
78 LogicalLocalityProvider::new(plan, columns.to_owned()).into()
79 } else {
80 plan
81 }
82}
83
84pub fn stream_enforce_eowc_requirement(
85 ctx: OptimizerContextRef,
86 plan: StreamPlanRef,
87 emit_on_window_close: bool,
88) -> Result<StreamPlanRef> {
89 if emit_on_window_close && !plan.emit_on_window_close() {
90 let watermark_groups = plan.watermark_columns().grouped();
91 let n_watermark_groups = watermark_groups.len();
92 if n_watermark_groups == 0 {
93 Err(ErrorCode::NotSupported(
94 "The query cannot be executed in Emit-On-Window-Close mode.".to_owned(),
95 "Try define a watermark column in the source, or avoid aggregation without GROUP BY".to_owned(),
96 )
97 .into())
98 } else {
99 let first_watermark_group = watermark_groups.values().next().unwrap();
100 let watermark_col_idx = first_watermark_group.indices().next().unwrap();
101 if n_watermark_groups > 1 {
102 ctx.warn_to_user(format!(
103 "There are multiple unrelated watermark columns in the query, the first one `{}` is used.",
104 FieldDisplay(&plan.schema()[watermark_col_idx])
105 ));
106 }
107 Ok(StreamEowcSort::new(plan, watermark_col_idx).into())
108 }
109 } else {
110 Ok(plan)
111 }
112}
113
114#[derive(Debug, Clone)]
115pub struct RewriteStreamContext {
116 share_rewrite_map: HashMap<PlanNodeId, (LogicalPlanRef, ColIndexMapping)>,
117 backfill_type: BackfillType,
121 locality_backfill_enabled: bool,
122}
123
124impl RewriteStreamContext {
125 pub fn new_with_backfill_type(
126 backfill_type: BackfillType,
127 locality_backfill_enabled: bool,
128 ) -> Self {
129 Self {
130 share_rewrite_map: HashMap::new(),
131 backfill_type,
132 locality_backfill_enabled,
133 }
134 }
135
136 pub fn backfill_type(&self) -> BackfillType {
137 self.backfill_type
138 }
139
140 pub fn locality_backfill_enabled(&self) -> bool {
141 self.locality_backfill_enabled
142 }
143
144 pub fn add_rewrite_result(
145 &mut self,
146 plan_node_id: PlanNodeId,
147 plan_ref: LogicalPlanRef,
148 col_change: ColIndexMapping,
149 ) {
150 let prev = self
151 .share_rewrite_map
152 .insert(plan_node_id, (plan_ref, col_change));
153 assert!(prev.is_none());
154 }
155
156 pub fn get_rewrite_result(
157 &self,
158 plan_node_id: PlanNodeId,
159 ) -> Option<&(LogicalPlanRef, ColIndexMapping)> {
160 self.share_rewrite_map.get(&plan_node_id)
161 }
162}
163
164#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
165pub enum BackfillType {
166 Replicated,
167 UpstreamOnlySink,
170 ArrangementBackfill,
171 SnapshotBackfill,
172 SnapshotBackfillSinceTimestamp,
176}
177
178impl BackfillType {
179 pub fn without_snapshot(self) -> bool {
180 matches!(
181 self,
182 BackfillType::UpstreamOnlySink | BackfillType::SnapshotBackfillSinceTimestamp
183 )
184 }
185
186 pub fn is_snapshot_backfill(self) -> bool {
187 matches!(
188 self,
189 BackfillType::SnapshotBackfill | BackfillType::SnapshotBackfillSinceTimestamp
190 )
191 }
192
193 pub fn to_stream_scan_type(self, is_cross_db: bool) -> StreamScanType {
194 if is_cross_db {
195 return StreamScanType::CrossDbSnapshotBackfill;
196 }
197
198 match self {
199 BackfillType::Replicated | BackfillType::UpstreamOnlySink => {
200 StreamScanType::UpstreamOnly
201 }
202 BackfillType::ArrangementBackfill => StreamScanType::ArrangementBackfill,
203 BackfillType::SnapshotBackfill | BackfillType::SnapshotBackfillSinceTimestamp => {
204 StreamScanType::SnapshotBackfill
205 }
206 }
207 }
208}
209
210#[derive(Debug, Clone)]
211pub struct ToStreamContext {
212 share_to_stream_map: HashMap<PlanNodeId, StreamPlanRef>,
213 emit_on_window_close: bool,
214 backfill_type: BackfillType,
215}
216
217impl ToStreamContext {
218 pub fn new_with_backfill_type(emit_on_window_close: bool, backfill_type: BackfillType) -> Self {
219 Self {
220 share_to_stream_map: HashMap::new(),
221 emit_on_window_close,
222 backfill_type,
223 }
224 }
225
226 pub fn backfill_type(&self) -> BackfillType {
227 self.backfill_type
228 }
229
230 pub fn add_to_stream_result(&mut self, plan_node_id: PlanNodeId, plan_ref: StreamPlanRef) {
231 self.share_to_stream_map
232 .try_insert(plan_node_id, plan_ref)
233 .unwrap();
234 }
235
236 pub fn get_to_stream_result(&self, plan_node_id: PlanNodeId) -> Option<&StreamPlanRef> {
237 self.share_to_stream_map.get(&plan_node_id)
238 }
239
240 pub fn emit_on_window_close(&self) -> bool {
241 self.emit_on_window_close
242 }
243}
244
245pub trait ToBatch {
257 fn to_batch(&self) -> Result<BatchPlanRef>;
259 fn to_batch_with_order_required(&self, required_order: &Order) -> Result<BatchPlanRef> {
261 let ret = self.to_batch()?;
262 required_order.enforce_if_not_satisfies(ret)
263 }
264}
265
266pub trait ToLocalBatch {
271 fn to_local(&self) -> Result<BatchPlanRef>;
272
273 fn to_local_with_order_required(&self, required_order: &Order) -> Result<BatchPlanRef> {
275 let ret = self.to_local()?;
276 required_order.enforce_if_not_satisfies(ret)
277 }
278}
279
280pub trait ToDistributedBatch {
290 fn to_distributed(&self) -> Result<BatchPlanRef>;
293 fn to_distributed_with_required(
295 &self,
296 required_order: &Order,
297 required_dist: &RequiredDist,
298 ) -> Result<BatchPlanRef> {
299 let ret = self.to_distributed()?;
300 let ret = required_order.enforce_if_not_satisfies(ret)?;
301 required_dist.batch_enforce_if_not_satisfies(ret, required_order)
302 }
303}