risingwave_stream/from_proto/
temporal_join.rs1use std::sync::Arc;
16
17use risingwave_common::bitmap::Bitmap;
18use risingwave_common::catalog::ColumnId;
19use risingwave_common::hash::{HashKey, HashKeyDispatcher, VnodeCountCompat};
20use risingwave_common::types::DataType;
21use risingwave_common::util::value_encoding::BasicSerde;
22use risingwave_common::util::value_encoding::column_aware_row_encoding::ColumnAwareSerde;
23use risingwave_expr::expr::{NonStrictExpression, build_non_strict_from_prost};
24use risingwave_pb::plan_common::{JoinType as JoinTypeProto, StorageTableDesc};
25use risingwave_storage::row_serde::value_serde::ValueRowSerde;
26
27use super::*;
28use crate::common::table::state_table::{
29 ReplicatedStateTable, StateTable, StateTableBuilder, StateTableOpConsistencyLevel,
30};
31use crate::executor::monitor::StreamingMetrics;
32use crate::executor::{
33 ActorContextRef, JoinType, NestedLoopTemporalJoinExecutor, TemporalJoinExecutor,
34};
35use crate::task::AtomicU64Ref;
36
37pub struct TemporalJoinExecutorBuilder;
38
39impl_stream_node_body!(TemporalJoin(TemporalJoinNode) => TemporalJoinExecutorBuilder);
40
41impl ExecutorBuilder for TemporalJoinExecutorBuilder {
42 type Node = TemporalJoinNode;
43
44 async fn new_boxed_executor(
45 params: ExecutorParams,
46 node: &Self::Node,
47 store: impl StateStore,
48 ) -> StreamResult<Executor> {
49 let table_desc: &StorageTableDesc = node.get_table_desc()?;
50 let condition = match node.get_condition() {
51 Ok(cond_prost) => Some(build_non_strict_from_prost(
52 cond_prost,
53 params.eval_error_report,
54 )?),
55 Err(_) => None,
56 };
57
58 let table_output_indices = node
59 .get_table_output_indices()
60 .iter()
61 .map(|&x| x as usize)
62 .collect_vec();
63
64 let output_indices = node
65 .get_output_indices()
66 .iter()
67 .map(|&x| x as usize)
68 .collect_vec();
69 let [source_l, source_r]: [_; 2] = params.input.try_into().unwrap();
70
71 let versioned = table_desc.versioned;
72 let output_column_ids = table_output_indices
77 .iter()
78 .map(|&x| ColumnId::new(table_desc.columns[x].column_id))
79 .collect_vec();
80 let is_broadcast = node.is_broadcast;
81 let vnodes = if is_broadcast {
82 Some(Arc::new(Bitmap::ones(table_desc.vnode_count())))
83 } else {
84 Some(Arc::new(params.vnode_bitmap.clone()))
85 };
86
87 if node.get_is_nested_loop() {
88 assert!(
89 !is_broadcast,
90 "nested-loop temporal join cannot be broadcast"
91 );
92 macro_rules! build_nested_loop {
93 ($SD:ident) => {{
94 let right_table =
95 StateTableBuilder::<_, $SD, true, _>::new_from_storage_table_desc(
96 table_desc,
97 store.clone(),
98 vnodes.clone(),
99 params.fragment_id,
100 )
101 .with_op_consistency_level(StateTableOpConsistencyLevel::Inconsistent)
102 .with_output_column_ids(output_column_ids.clone())
103 .forbid_preload_all_rows()
104 .build()
105 .await;
106
107 let dispatcher_args = NestedLoopTemporalJoinExecutorDispatcherArgs {
108 ctx: params.actor_context,
109 info: params.info.clone(),
110 left: source_l,
111 right: source_r,
112 right_table,
113 condition,
114 output_indices,
115 chunk_size: params.config.developer.chunk_size,
116 metrics: params.executor_stats,
117 join_type_proto: node.get_join_type()?,
118 };
119 Ok((params.info, dispatcher_args.dispatch()?).into())
120 }};
121 }
122 if versioned {
123 build_nested_loop!(ColumnAwareSerde)
124 } else {
125 build_nested_loop!(BasicSerde)
126 }
127 } else {
128 let table_stream_key_indices = table_desc
132 .stream_key
133 .iter()
134 .map(|&k| {
135 table_output_indices
136 .iter()
137 .position(|&idx| idx == k as usize)
138 .expect("stream key should be included in table output")
139 })
140 .collect_vec();
141
142 let left_join_keys = node
143 .get_left_key()
144 .iter()
145 .map(|key| *key as usize)
146 .collect_vec();
147
148 let right_join_keys = node
149 .get_right_key()
150 .iter()
151 .map(|key| *key as usize)
152 .collect_vec();
153
154 let null_safe = node.get_null_safe().clone();
155
156 let join_key_data_types = left_join_keys
157 .iter()
158 .map(|idx| source_l.schema().fields[*idx].data_type())
159 .collect_vec();
160
161 let memo_table = node.get_memo_table();
162 let memo_table = match memo_table {
163 Ok(memo_table) => {
164 let vnodes = Some(Arc::new(params.vnode_bitmap.clone()));
165 Some(
166 StateTableBuilder::new(memo_table, store.clone(), vnodes)
167 .enable_preload_all_rows_by_config(¶ms.config)
168 .build()
169 .await,
170 )
171 }
172 Err(_) => None,
173 };
174 let append_only = memo_table.is_none();
175
176 macro_rules! build_hash {
177 ($SD:ident) => {{
178 let right_table =
179 StateTableBuilder::<_, $SD, true, _>::new_from_storage_table_desc(
180 table_desc,
181 store.clone(),
182 vnodes.clone(),
183 params.fragment_id,
184 )
185 .with_op_consistency_level(StateTableOpConsistencyLevel::Inconsistent)
186 .with_output_column_ids(output_column_ids.clone())
187 .forbid_preload_all_rows()
188 .build()
189 .await;
190
191 let dispatcher_args = TemporalJoinExecutorDispatcherArgs::<_, $SD> {
192 ctx: params.actor_context,
193 info: params.info.clone(),
194 left: source_l,
195 right: source_r,
196 right_table,
197 left_join_keys,
198 right_join_keys,
199 null_safe,
200 condition,
201 output_indices,
202 table_stream_key_indices,
203 watermark_epoch: params.watermark_epoch,
204 chunk_size: params.config.developer.chunk_size,
205 metrics: params.executor_stats,
206 join_type_proto: node.get_join_type()?,
207 join_key_data_types,
208 memo_table,
209 append_only,
210 is_broadcast,
211 };
212
213 Ok((params.info, dispatcher_args.dispatch()?).into())
214 }};
215 }
216 if versioned {
217 build_hash!(ColumnAwareSerde)
218 } else {
219 build_hash!(BasicSerde)
220 }
221 }
222 }
223}
224
225struct TemporalJoinExecutorDispatcherArgs<S: StateStore, SD: ValueRowSerde> {
226 ctx: ActorContextRef,
227 info: ExecutorInfo,
228 left: Executor,
229 right: Executor,
230 right_table: ReplicatedStateTable<S, SD>,
231 left_join_keys: Vec<usize>,
232 right_join_keys: Vec<usize>,
233 null_safe: Vec<bool>,
234 condition: Option<NonStrictExpression>,
235 output_indices: Vec<usize>,
236 table_stream_key_indices: Vec<usize>,
237 watermark_epoch: AtomicU64Ref,
238 chunk_size: usize,
239 metrics: Arc<StreamingMetrics>,
240 join_type_proto: JoinTypeProto,
241 join_key_data_types: Vec<DataType>,
242 memo_table: Option<StateTable<S>>,
243 append_only: bool,
244 is_broadcast: bool,
245}
246
247impl<S: StateStore, SD: ValueRowSerde> HashKeyDispatcher
248 for TemporalJoinExecutorDispatcherArgs<S, SD>
249{
250 type Output = StreamResult<Box<dyn Execute>>;
251
252 fn dispatch_impl<K: HashKey>(self) -> Self::Output {
253 macro_rules! build {
255 ($join_type:ident, $append_only:ident) => {
256 Ok(Box::new(TemporalJoinExecutor::<
257 K,
258 S,
259 SD,
260 { JoinType::$join_type },
261 { $append_only },
262 >::new(
263 self.ctx,
264 self.info,
265 self.left,
266 self.right,
267 self.right_table,
268 self.left_join_keys,
269 self.right_join_keys,
270 self.null_safe,
271 self.condition,
272 self.output_indices,
273 self.table_stream_key_indices,
274 self.watermark_epoch,
275 self.metrics,
276 self.chunk_size,
277 self.join_key_data_types,
278 self.memo_table,
279 self.is_broadcast,
280 )))
281 };
282 }
283 match self.join_type_proto {
284 JoinTypeProto::Inner => {
285 if self.append_only {
286 build!(Inner, true)
287 } else {
288 build!(Inner, false)
289 }
290 }
291 JoinTypeProto::LeftOuter => {
292 if self.append_only {
293 build!(LeftOuter, true)
294 } else {
295 build!(LeftOuter, false)
296 }
297 }
298 _ => unreachable!(),
299 }
300 }
301
302 fn data_types(&self) -> &[DataType] {
303 &self.join_key_data_types
304 }
305}
306
307struct NestedLoopTemporalJoinExecutorDispatcherArgs<S: StateStore, SD: ValueRowSerde> {
308 ctx: ActorContextRef,
309 info: ExecutorInfo,
310 left: Executor,
311 right: Executor,
312 right_table: ReplicatedStateTable<S, SD>,
313 condition: Option<NonStrictExpression>,
314 output_indices: Vec<usize>,
315 chunk_size: usize,
316 metrics: Arc<StreamingMetrics>,
317 join_type_proto: JoinTypeProto,
318}
319
320impl<S: StateStore, SD: ValueRowSerde> NestedLoopTemporalJoinExecutorDispatcherArgs<S, SD> {
321 fn dispatch(self) -> StreamResult<Box<dyn Execute>> {
322 macro_rules! build {
324 ($join_type:ident) => {
325 Ok(Box::new(NestedLoopTemporalJoinExecutor::<
326 S,
327 SD,
328 { JoinType::$join_type },
329 >::new(
330 self.ctx,
331 self.info,
332 self.left,
333 self.right,
334 self.right_table,
335 self.condition,
336 self.output_indices,
337 self.metrics,
338 self.chunk_size,
339 )))
340 };
341 }
342 match self.join_type_proto {
343 JoinTypeProto::Inner => {
344 build!(Inner)
345 }
346 JoinTypeProto::LeftOuter => {
347 build!(LeftOuter)
348 }
349 _ => unreachable!(),
350 }
351 }
352}