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