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