1use std::collections::HashMap;
16use std::sync::Arc;
17
18use risingwave_common::array::{ArrayRef, DataChunk};
19use risingwave_common::bail;
20use risingwave_common::row::{OwnedRow, Row};
21use risingwave_common::types::{DataType, Datum, ScalarImpl};
22use risingwave_expr::expr::{
23 AsyncExpression, AsyncExpressionBoxExt, BoxedExpression, ExpressionInfo, SyncExpression,
24 SyncExpressionBoxExt, try_convert_all,
25};
26use risingwave_expr::{Result, build_function};
27
28#[derive(Debug)]
29struct WhenClause<E> {
30 when: E,
31 then: E,
32}
33
34#[derive(Debug)]
35struct CaseExpression<E> {
36 return_type: DataType,
37 when_clauses: Vec<WhenClause<E>>,
38 else_clause: Option<E>,
39}
40
41impl<E> CaseExpression<E> {
42 fn new(
43 return_type: DataType,
44 when_clauses: Vec<WhenClause<E>>,
45 else_clause: Option<E>,
46 ) -> Self {
47 Self {
48 return_type,
49 when_clauses,
50 else_clause,
51 }
52 }
53}
54
55impl<E: ExpressionInfo> ExpressionInfo for CaseExpression<E> {
56 fn return_type(&self) -> DataType {
57 self.return_type.clone()
58 }
59}
60
61macro_rules! eval_case {
62 ($mode:ident, $this:expr, $input:expr) => {{
63 let mut input = $input.clone();
64 let input_len = input.capacity();
65 let mut selection = vec![None; input_len];
66 let when_len = $this.when_clauses.len();
67 let mut result_array = Vec::with_capacity(when_len + 1);
68 for (when_idx, WhenClause { when, then }) in $this.when_clauses.iter().enumerate() {
69 let input_vis = input.visibility().clone();
70 let calc_then_vis = risingwave_expr::forward!($mode, when, eval(&input))?
73 .as_bool()
74 .to_bitmap()
75 & &input_vis;
76 input.set_visibility(calc_then_vis.clone());
77 let then_res = risingwave_expr::forward!($mode, then, eval(&input))?;
78 calc_then_vis
79 .iter_ones()
80 .for_each(|pos| selection[pos] = Some(when_idx));
81 input.set_visibility(&input_vis & (!calc_then_vis));
82 result_array.push(then_res);
83 }
84 if let Some(ref else_expr) = $this.else_clause {
85 let else_res = risingwave_expr::forward!($mode, else_expr, eval(&input))?;
86 input
87 .visibility()
88 .iter_ones()
89 .for_each(|pos| selection[pos] = Some(when_len));
90 result_array.push(else_res);
91 }
92 let mut builder = $this.return_type().create_array_builder(input.capacity());
93 for (i, sel) in selection.into_iter().enumerate() {
94 if let Some(when_idx) = sel {
95 builder.append(result_array[when_idx].value_at(i));
96 } else {
97 builder.append_null();
98 }
99 }
100 Ok(Arc::new(builder.finish()))
101 }};
102}
103
104macro_rules! eval_row_case {
105 ($mode:ident, $this:expr, $input:expr) => {{
106 for WhenClause { when, then } in &$this.when_clauses {
107 if risingwave_expr::forward!($mode, when, eval_row($input))?
108 .is_some_and(|w| w.into_bool())
109 {
110 return risingwave_expr::forward!($mode, then, eval_row($input));
111 }
112 }
113 if let Some(ref else_expr) = $this.else_clause {
114 risingwave_expr::forward!($mode, else_expr, eval_row($input))
115 } else {
116 Ok(None)
117 }
118 }};
119}
120
121impl<E: SyncExpression> SyncExpression for CaseExpression<E> {
122 fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
123 eval_case!(sync, self, input)
124 }
125
126 fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
127 eval_row_case!(sync, self, input)
128 }
129}
130
131impl<E: AsyncExpression> AsyncExpression for CaseExpression<E> {
132 async fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
133 eval_case!(async, self, input)
134 }
135
136 async fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
137 eval_row_case!(async, self, input)
138 }
139}
140
141#[derive(Debug)]
145struct ConstantLookupExpression<E> {
146 return_type: DataType,
147 arms: HashMap<ScalarImpl, E>,
148 fallback: Option<E>,
149 operand: E,
151}
152
153impl<E> ConstantLookupExpression<E> {
154 fn new(
155 return_type: DataType,
156 arms: HashMap<ScalarImpl, E>,
157 fallback: Option<E>,
158 operand: E,
159 ) -> Self {
160 Self {
161 return_type,
162 arms,
163 fallback,
164 operand,
165 }
166 }
167}
168
169impl<E: ExpressionInfo> ExpressionInfo for ConstantLookupExpression<E> {
170 fn return_type(&self) -> DataType {
171 self.return_type.clone()
172 }
173}
174
175macro_rules! eval_fallback_lookup {
176 ($mode:ident, $this:expr, $input:expr) => {{
177 if let Some(ref fallback) = $this.fallback {
178 let Ok(res) = risingwave_expr::forward!($mode, fallback, eval_row($input)) else {
179 bail!("failed to evaluate the input for fallback arm");
180 };
181 Ok::<Datum, risingwave_expr::ExprError>(res)
182 } else {
183 Ok::<Datum, risingwave_expr::ExprError>(None)
184 }
185 }};
186}
187
188macro_rules! lookup {
189 ($mode:ident, $this:expr, $datum:expr, $input:expr) => {{
190 match $datum.as_ref() {
191 Some(datum) => {
192 if let Some(expr) = $this.arms.get(datum) {
193 let Ok(res) = risingwave_expr::forward!($mode, expr, eval_row($input)) else {
194 bail!("failed to evaluate the input for normal arm");
195 };
196 Ok(res)
197 } else {
198 eval_fallback_lookup!($mode, $this, $input)
199 }
200 }
201 None => eval_fallback_lookup!($mode, $this, $input),
202 }
203 }};
204}
205
206macro_rules! eval_constant_lookup {
207 ($mode:ident, $this:expr, $input:expr) => {{
208 let input_len = $input.capacity();
209 let mut builder = $this.return_type().create_array_builder(input_len);
210
211 let eval_result = risingwave_expr::forward!($mode, $this.operand, eval($input))?;
213
214 for i in 0..input_len {
215 let datum = eval_result.datum_at(i);
216 let (row, vis) = $input.row_at(i);
217
218 if !vis {
220 builder.append_null();
221 continue;
222 }
223
224 let owned_row = row.into_owned_row();
227
228 if let Ok(datum) = lookup!($mode, $this, datum, &owned_row) {
230 builder.append(datum.as_ref());
231 } else {
232 bail!("failed to lookup and evaluate the expression in `eval`");
233 }
234 }
235
236 Ok(Arc::new(builder.finish()))
237 }};
238}
239
240impl<E: SyncExpression> SyncExpression for ConstantLookupExpression<E> {
241 fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
242 eval_constant_lookup!(sync, self, input)
243 }
244
245 fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
246 let datum = self.operand.eval_row(input)?;
247 lookup!(sync, self, datum, input)
248 }
249}
250
251impl<E: AsyncExpression> AsyncExpression for ConstantLookupExpression<E> {
252 async fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
253 eval_constant_lookup!(async, self, input)
254 }
255
256 async fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
257 let datum = self.operand.eval_row(input).await?;
258 lookup!(async, self, datum, input)
259 }
260}
261
262#[build_function("constant_lookup(...) -> any", type_infer = "unreachable")]
263fn build_constant_lookup_expr(
264 return_type: DataType,
265 children: Vec<BoxedExpression>,
266) -> Result<BoxedExpression> {
267 if children.is_empty() {
268 bail!("children expression must not be empty for constant lookup expression");
269 }
270
271 let mut children = children;
272
273 let operand = children.remove(0);
274
275 let mut arms = HashMap::new();
276
277 let mut iter = children.into_iter().array_chunks();
279 for [when, then] in iter.by_ref() {
280 let Ok(Some(s)) = when.eval_const() else {
281 bail!("expect when expression to be const");
282 };
283 arms.insert(s, then);
284 }
285
286 let fallback = if let Some(else_clause) = iter.into_remainder().next() {
287 if else_clause.return_type() != return_type {
288 bail!("Type mismatched between else and case.");
289 }
290 Some(else_clause)
291 } else {
292 None
293 };
294
295 let BoxedExpression::Sync(operand) = operand else {
296 return Ok(ConstantLookupExpression::new(return_type, arms, fallback, operand).boxed());
297 };
298 let arms: Vec<_> = arms.into_iter().collect();
299 let sync_arms: HashMap<ScalarImpl, Arc<dyn SyncExpression>> = match try_convert_all(
300 arms,
301 |(key, expr)| match expr {
302 BoxedExpression::Sync(expr) => Ok((key, expr)),
303 expr @ BoxedExpression::Async(_) => Err((key, expr)),
304 },
305 |(key, expr)| (key, BoxedExpression::Sync(expr)),
306 ) {
307 Ok(arms) => arms.into_iter().collect(),
308 Err(arms) => {
309 return Ok(ConstantLookupExpression::new(
310 return_type,
311 arms.into_iter().collect(),
312 fallback,
313 BoxedExpression::Sync(operand),
314 )
315 .boxed());
316 }
317 };
318 let fallback = match fallback {
319 Some(BoxedExpression::Sync(expr)) => Some(expr),
320 Some(expr @ BoxedExpression::Async(_)) => {
321 let arms = sync_arms
322 .into_iter()
323 .map(|(key, expr)| (key, BoxedExpression::Sync(expr)))
324 .collect();
325 return Ok(ConstantLookupExpression::new(
326 return_type,
327 arms,
328 Some(expr),
329 BoxedExpression::Sync(operand),
330 )
331 .boxed());
332 }
333 None => None,
334 };
335 Ok(ConstantLookupExpression::new(return_type, sync_arms, fallback, operand).boxed())
336}
337
338#[build_function("case(...) -> any", type_infer = "unreachable")]
339fn build_case_expr(
340 return_type: DataType,
341 children: Vec<BoxedExpression>,
342) -> Result<BoxedExpression> {
343 let len = children.len();
345 let mut when_clauses = Vec::with_capacity(len / 2);
346 let mut iter = children.into_iter().array_chunks();
347 for [when, then] in iter.by_ref() {
348 if when.return_type() != DataType::Boolean {
349 bail!("Type mismatched between when clause and condition");
350 }
351 if then.return_type() != return_type {
352 bail!("Type mismatched between then clause and case");
353 }
354 when_clauses.push(WhenClause { when, then });
355 }
356 let else_clause = if let Some(else_clause) = iter.into_remainder().next() {
357 if else_clause.return_type() != return_type {
358 bail!("Type mismatched between else and case.");
359 }
360 Some(else_clause)
361 } else {
362 None
363 };
364
365 let sync_when_clauses = match try_convert_all(
366 when_clauses,
367 |WhenClause { when, then }| match (when, then) {
368 (BoxedExpression::Sync(when), BoxedExpression::Sync(then)) => {
369 Ok(WhenClause { when, then })
370 }
371 (when, then) => Err(WhenClause { when, then }),
372 },
373 |WhenClause { when, then }| WhenClause {
374 when: BoxedExpression::Sync(when),
375 then: BoxedExpression::Sync(then),
376 },
377 ) {
378 Ok(when_clauses) => when_clauses,
379 Err(when_clauses) => {
380 return Ok(CaseExpression::new(return_type, when_clauses, else_clause).boxed());
381 }
382 };
383 let else_clause = match else_clause {
384 Some(BoxedExpression::Sync(expr)) => Some(expr),
385 Some(expr @ BoxedExpression::Async(_)) => {
386 let when_clauses = sync_when_clauses
387 .into_iter()
388 .map(|WhenClause { when, then }| WhenClause {
389 when: BoxedExpression::Sync(when),
390 then: BoxedExpression::Sync(then),
391 })
392 .collect();
393 return Ok(CaseExpression::new(return_type, when_clauses, Some(expr)).boxed());
394 }
395 None => None,
396 };
397 Ok(CaseExpression::new(return_type, sync_when_clauses, else_clause).boxed())
398}
399
400#[cfg(test)]
401mod tests {
402 use risingwave_common::test_prelude::DataChunkTestExt;
403 use risingwave_common::types::ToOwnedDatum;
404 use risingwave_common::util::iter_util::ZipEqDebug;
405 use risingwave_expr::expr::build_from_pretty;
406
407 use super::*;
408
409 #[tokio::test]
410 async fn test_eval_searched_case() {
411 let case = build_from_pretty("(case:int4 $0:boolean 1:int4 2:int4)");
413 let (input, expected) = DataChunk::from_pretty(
414 "B i
415 t 1
416 f 2
417 t 1
418 t 1
419 f 2",
420 )
421 .split_column_at(1);
422
423 let output = case.eval(&input).await.unwrap();
425 assert_eq!(&output, expected.column_at(0));
426
427 for (row, expected) in input.rows().zip_eq_debug(expected.rows()) {
429 let result = case.eval_row(&row.to_owned_row()).await.unwrap();
430 assert_eq!(result, expected.datum_at(0).to_owned_datum());
431 }
432 }
433
434 #[tokio::test]
435 async fn test_eval_without_else() {
436 let case = build_from_pretty("(case:int4 $0:boolean 1:int4 $1:boolean 2:int4)");
438 let (input, expected) = DataChunk::from_pretty(
439 "B B i
440 f f .
441 f t 2
442 t f 1
443 t t 1",
444 )
445 .split_column_at(2);
446
447 let output = case.eval(&input).await.unwrap();
449 assert_eq!(&output, expected.column_at(0));
450
451 for (row, expected) in input.rows().zip_eq_debug(expected.rows()) {
453 let result = case.eval_row(&row.to_owned_row()).await.unwrap();
454 assert_eq!(result, expected.datum_at(0).to_owned_datum());
455 }
456 }
457}