risingwave_expr_impl/scalar/
in_.rs1use std::collections::HashSet;
16use std::fmt::Debug;
17use std::sync::Arc;
18
19use risingwave_common::array::{ArrayBuilder, ArrayRef, BoolArrayBuilder, DataChunk};
20use risingwave_common::bail;
21use risingwave_common::row::OwnedRow;
22use risingwave_common::types::{DataType, Datum, Scalar, ToOwnedDatum};
23use risingwave_common::util::iter_util::ZipEqFast;
24use risingwave_expr::expr::{
25 AsyncExpression, AsyncExpressionBoxExt, BoxedExpression, ExpressionInfo, SyncExpression,
26 SyncExpressionBoxExt,
27};
28use risingwave_expr::{Result, build_function};
29
30#[derive(Debug)]
31pub struct InExpression<E> {
32 left: E,
33 set: HashSet<Datum>,
34 return_type: DataType,
35}
36
37impl<E> InExpression<E> {
38 pub fn new(left: E, data: impl Iterator<Item = Datum>, return_type: DataType) -> Self {
39 Self {
40 left,
41 set: data.collect(),
42 return_type,
43 }
44 }
45
46 fn exists(&self, datum: &Datum) -> Option<bool> {
49 if datum.is_none() {
50 None
51 } else if self.set.contains(datum) {
52 Some(true)
53 } else if self.set.contains(&None) {
54 None
55 } else {
56 Some(false)
57 }
58 }
59}
60
61impl<E: ExpressionInfo> ExpressionInfo for InExpression<E> {
62 fn return_type(&self) -> DataType {
63 self.return_type.clone()
64 }
65}
66
67macro_rules! eval_in {
68 ($mode:ident, $this:expr, $input:expr) => {{
69 let input_array = risingwave_expr::forward!($mode, $this.left, eval($input))?;
70 let mut output_array = BoolArrayBuilder::new(input_array.len());
71 for (data, vis) in input_array.iter().zip_eq_fast($input.visibility().iter()) {
72 if vis {
73 let ret = $this.exists(&data.to_owned_datum());
75 output_array.append(ret);
76 } else {
77 output_array.append(None);
78 }
79 }
80 Ok(Arc::new(output_array.finish().into()))
81 }};
82}
83
84macro_rules! eval_row_in {
85 ($mode:ident, $this:expr, $input:expr) => {{
86 let data = risingwave_expr::forward!($mode, $this.left, eval_row($input))?;
87 let ret = $this.exists(&data);
88 Ok(ret.map(|b| b.to_scalar_value()))
89 }};
90}
91
92impl<E: SyncExpression> SyncExpression for InExpression<E> {
93 fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
94 eval_in!(sync, self, input)
95 }
96
97 fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
98 eval_row_in!(sync, self, input)
99 }
100}
101
102impl<E: AsyncExpression> AsyncExpression for InExpression<E> {
103 async fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
104 eval_in!(async, self, input)
105 }
106
107 async fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
108 eval_row_in!(async, self, input)
109 }
110}
111
112#[build_function("in(any, ...) -> boolean")]
113fn build(return_type: DataType, children: Vec<BoxedExpression>) -> Result<BoxedExpression> {
114 let mut iter = children.into_iter();
115 let left_expr = iter.next().unwrap();
116 let mut data = Vec::with_capacity(iter.size_hint().0);
117 let data_chunk = DataChunk::new_dummy(1);
118 for child in iter {
119 let BoxedExpression::Sync(child) = child else {
120 bail!("IN set expression must be sync")
121 };
122 let array = child.eval(&data_chunk)?;
123 let datum = array.value_at(0).to_owned_datum();
124 data.push(datum);
125 }
126 Ok(match left_expr {
127 BoxedExpression::Sync(left_expr) => {
128 InExpression::new(left_expr, data.into_iter(), return_type).boxed()
129 }
130 left_expr @ BoxedExpression::Async(_) => {
131 InExpression::new(left_expr, data.into_iter(), return_type).boxed()
132 }
133 })
134}
135
136#[cfg(test)]
137mod tests {
138 use risingwave_common::array::DataChunk;
139 use risingwave_common::row::Row;
140 use risingwave_common::test_prelude::DataChunkTestExt;
141 use risingwave_common::types::ToOwnedDatum;
142 use risingwave_common::util::iter_util::ZipEqDebug;
143 use risingwave_expr::expr::build_from_pretty;
144
145 #[tokio::test]
146 async fn test_in_expr() {
147 let expr = build_from_pretty("(in:boolean $0:varchar abc:varchar def:varchar)");
148 let (input, expected) = DataChunk::from_pretty(
149 "T B
150 abc t
151 a f
152 def t
153 abc t
154 . .",
155 )
156 .split_column_at(1);
157
158 let output = expr.eval(&input).await.unwrap();
160 assert_eq!(&output, expected.column_at(0));
161
162 for (row, expected) in input.rows().zip_eq_debug(expected.rows()) {
164 let result = expr.eval_row(&row.to_owned_row()).await.unwrap();
165 assert_eq!(result, expected.datum_at(0).to_owned_datum());
166 }
167 }
168
169 #[tokio::test]
170 async fn test_in_expr_null() {
171 let expr = build_from_pretty("(in:boolean $0:varchar abc:varchar null:varchar)");
172 let (input, expected) = DataChunk::from_pretty(
173 "T B
174 abc t
175 a .
176 . .",
177 )
178 .split_column_at(1);
179
180 let output = expr.eval(&input).await.unwrap();
182 assert_eq!(&output, expected.column_at(0));
183
184 for (row, expected) in input.rows().zip_eq_debug(expected.rows()) {
186 let result = expr.eval_row(&row.to_owned_row()).await.unwrap();
187 assert_eq!(result, expected.datum_at(0).to_owned_datum());
188 }
189 }
190}