alamb commented on code in PR #19050:
URL: https://github.com/apache/datafusion/pull/19050#discussion_r2602363180


##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -226,31 +226,62 @@ impl StaticFilter for Int32StaticFilter {
     }
 
     fn contains(&self, v: &dyn Array, negated: bool) -> Result<BooleanArray> {
+        // Handle dictionary arrays by recursing on the values
+        downcast_dictionary_array! {
+            v => {
+                let values_contains = self.contains(v.values().as_ref(), 
negated)?;
+                let result = take(&values_contains, v.keys(), None)?;
+                return Ok(downcast_array(result.as_ref()))
+            }
+            _ => {}
+        }
+
         let v = v
             .as_primitive_opt::<Int32Type>()
             .ok_or_else(|| exec_datafusion_err!("Failed to downcast array"))?;
 
-        let result = match (v.null_count() > 0, negated) {
+        let haystack_has_nulls = self.null_count > 0;
+        let has_nulls = v.null_count() > 0 || haystack_has_nulls;
+
+        let result = match (has_nulls, negated) {
             (true, false) => {
-                // has nulls, not negated"
-                BooleanArray::from_iter(
-                    v.iter().map(|value| Some(self.values.contains(&value?))),
-                )
+                // Either needle or haystack has nulls, not negated
+                BooleanArray::from_iter(v.iter().map(|value| match value {
+                    None => None,
+                    Some(v) => {
+                        if self.values.contains(&v) {
+                            Some(true)
+                        } else if haystack_has_nulls {
+                            None
+                        } else {
+                            Some(false)
+                        }
+                    }
+                }))
             }
             (true, true) => {
-                // has nulls, negated
-                BooleanArray::from_iter(
-                    v.iter().map(|value| Some(!self.values.contains(&value?))),
-                )
+                // Either needle or haystack has nulls, negated

Review Comment:
   ```suggestion
                   // needle has nulls, negated
   ```



##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -2815,4 +2577,321 @@ mod tests {
 
         Ok(())
     }
+
+    #[test]
+    fn test_in_list_dictionary_int32() -> Result<()> {
+        // Create schema with dictionary-encoded Int32 column
+        let dict_type =
+            DataType::Dictionary(Box::new(DataType::Int8), 
Box::new(DataType::Int32));
+        let schema = Schema::new(vec![Field::new("a", dict_type.clone(), 
false)]);
+        let col_a = col("a", &schema)?;
+
+        // Create IN list with Int32 literals: (1, 2, 3)
+        let list = vec![lit(1i32), lit(2i32), lit(3i32)];
+
+        // Create InListExpr via in_list() - this uses Int32StaticFilter for 
Int32 lists
+        let expr = in_list(col_a, list, &false, &schema)?;
+
+        // Create dictionary-encoded batch with values [1, 2, 5]
+        // Dictionary: keys [0, 1, 2] -> values [1, 2, 5]
+        let keys = Int8Array::from(vec![0, 1, 2]);
+        let values = Int32Array::from(vec![1, 2, 5]);

Review Comment:
   I personally recommend using values that are clearly not the keys - for 
example
   
   ```suggestion
           let values = Int32Array::from(vec![100, 200, 500]);
   ```
   
   ( you would also have to change the literals above)



##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -226,31 +226,62 @@ impl StaticFilter for Int32StaticFilter {
     }
 
     fn contains(&self, v: &dyn Array, negated: bool) -> Result<BooleanArray> {
+        // Handle dictionary arrays by recursing on the values
+        downcast_dictionary_array! {
+            v => {
+                let values_contains = self.contains(v.values().as_ref(), 
negated)?;
+                let result = take(&values_contains, v.keys(), None)?;
+                return Ok(downcast_array(result.as_ref()))
+            }
+            _ => {}
+        }
+
         let v = v
             .as_primitive_opt::<Int32Type>()
             .ok_or_else(|| exec_datafusion_err!("Failed to downcast array"))?;
 
-        let result = match (v.null_count() > 0, negated) {
+        let haystack_has_nulls = self.null_count > 0;
+        let has_nulls = v.null_count() > 0 || haystack_has_nulls;
+
+        let result = match (has_nulls, negated) {
             (true, false) => {
-                // has nulls, not negated"
-                BooleanArray::from_iter(
-                    v.iter().map(|value| Some(self.values.contains(&value?))),
-                )
+                // Either needle or haystack has nulls, not negated
+                BooleanArray::from_iter(v.iter().map(|value| match value {
+                    None => None,
+                    Some(v) => {
+                        if self.values.contains(&v) {
+                            Some(true)
+                        } else if haystack_has_nulls {
+                            None
+                        } else {
+                            Some(false)
+                        }
+                    }
+                }))
             }
             (true, true) => {
-                // has nulls, negated
-                BooleanArray::from_iter(
-                    v.iter().map(|value| Some(!self.values.contains(&value?))),
-                )
+                // Either needle or haystack has nulls, negated
+                BooleanArray::from_iter(v.iter().map(|value| match value {
+                    None => None,
+                    Some(v) => {
+                        if self.values.contains(&v) {
+                            Some(false)
+                        } else if haystack_has_nulls {
+                            None
+                        } else {
+                            Some(true)
+                        }
+                    }
+                }))
             }
             (false, false) => {
-                //no null, not negated
+                // No nulls anywhere, not negated
                 BooleanArray::from_iter(
                     v.values().iter().map(|value| self.values.contains(value)),
                 )
             }
             (false, true) => {
-                // no null, negated
+                // No nulls anywhere, negated

Review Comment:
   according to code coverage, this branch is not covered by tests (see PR 
comments)



##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -226,31 +226,61 @@ impl StaticFilter for Int32StaticFilter {
     }
 
     fn contains(&self, v: &dyn Array, negated: bool) -> Result<BooleanArray> {
+        // Handle dictionary arrays by recursing on the values
+        downcast_dictionary_array! {
+            v => {
+                let values_contains = self.contains(v.values().as_ref(), 
negated)?;
+                let result = take(&values_contains, v.keys(), None)?;
+                return Ok(downcast_array(result.as_ref()))
+            }
+            _ => {}
+        }
+
         let v = v
             .as_primitive_opt::<Int32Type>()
             .ok_or_else(|| exec_datafusion_err!("Failed to downcast array"))?;
 
-        let result = match (v.null_count() > 0, negated) {
-            (true, false) => {
-                // has nulls, not negated"
-                BooleanArray::from_iter(
-                    v.iter().map(|value| Some(self.values.contains(&value?))),

Review Comment:
   i double checked with postgres and the new code looks right
   
   ```sql
   postgres=# select NULL IN (1);
    ?column?
   ----------
   
   (1 row)
   
   postgres=# select 1 IN (1, NULL);
    ?column?
   ----------
    t
   (1 row)
   
   postgres=# select 1 IN (2, NULL);
    ?column?
   ----------
   
   (1 row)
   
   postgres=# select 1 NOT IN (2, NULL);
    ?column?
   ----------
   
   (1 row)
   ```



##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -355,6 +386,57 @@ impl InListExpr {
             Some(instantiate_static_filter(array)?),
         ))
     }
+
+    /// Create a new InList expression with a static filter for constant list 
expressions.
+    ///
+    /// This validates data types, evaluates the list as constants, and uses 
specialized
+    /// StaticFilter implementations for better performance (e.g., 
Int32StaticFilter for Int32).
+    ///
+    /// Returns an error if data types don't match or if the list contains 
non-constant expressions.
+    pub fn try_from_static_filter(
+        expr: Arc<dyn PhysicalExpr>,
+        list: Vec<Arc<dyn PhysicalExpr>>,
+        negated: bool,
+        schema: &Schema,
+    ) -> Result<Self> {
+        // Check the data types match
+        let expr_data_type = expr.data_type(schema)?;
+        for list_expr in list.iter() {
+            let list_expr_data_type = list_expr.data_type(schema)?;
+            assert_or_internal_err!(
+                DFSchema::datatype_is_logically_equal(
+                    &expr_data_type,
+                    &list_expr_data_type
+                ),
+                "The data type inlist should be same, the value type is 
{expr_data_type}, one of list expr type is {list_expr_data_type}"
+            );
+        }
+
+        match try_evaluate_constant_list(&list, schema) {
+            Ok(in_array) => Ok(Self::new(
+                expr,
+                list,
+                negated,
+                Some(instantiate_static_filter(in_array)?),
+            )),
+            Err(_) => {

Review Comment:
   nit -- I think it would be clearer if this `try_evaluate_constant_list` 
evaluated to Result<Option<..>> rather than Result -- that way we avoid the 
string allocation on error, and could pass real errors .
   
   This code responds to all errors, even if the issue is something different 
than the types are not supported
   
   If you went this route, I suspect you wouldn't need the second error check 
for type matches (it would already be handled)



##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -698,562 +750,370 @@ mod tests {
         }};
     }
 
-    #[test]
-    fn in_list_utf8() -> Result<()> {
-        let schema = Schema::new(vec![Field::new("a", DataType::Utf8, true)]);
-        let a = StringArray::from(vec![Some("a"), Some("d"), None]);
-        let col_a = col("a", &schema)?;
-        let batch = RecordBatch::try_new(Arc::new(schema.clone()), 
vec![Arc::new(a)])?;
-
-        // expression: "a in ("a", "b")"
-        let list = vec![lit("a"), lit("b")];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), Some(false), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in ("a", "b")"
-        let list = vec![lit("a"), lit("b")];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), Some(true), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a in ("a", "b", null)"
-        let list = vec![lit("a"), lit("b"), lit(ScalarValue::Utf8(None))];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in ("a", "b", null)"
-        let list = vec![lit("a"), lit("b"), lit(ScalarValue::Utf8(None))];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        Ok(())
+    /// Test case for primitive types following the standard IN LIST pattern.
+    ///
+    /// Each test case represents a data type with:
+    /// - `value_in`: A value that appears in both the test array and the IN 
list (matches → true)
+    /// - `value_not_in`: A value that appears in the test array but NOT in 
the IN list (doesn't match → false)
+    /// - `value_in_list`: A value that appears in the IN list but not in the 
array (filler value)
+    /// - `null_value`: A null scalar value for NULL handling tests
+    struct InListPrimitiveTestCase {
+        name: &'static str,
+        value_in: ScalarValue,
+        value_not_in: ScalarValue,
+        value_in_list: ScalarValue,

Review Comment:
   The previous tests all had mutli-value lists, such as
   
   ```rust
           // expression: "a not in ("a", "b")"
   ```
   
   Maybe we could make this something like `values_in: Vec<ScalarValue>`
   
   However, I see there are multi-value tests below too, so maybe this is ok



##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -355,6 +386,57 @@ impl InListExpr {
             Some(instantiate_static_filter(array)?),
         ))
     }
+
+    /// Create a new InList expression with a static filter for constant list 
expressions.
+    ///
+    /// This validates data types, evaluates the list as constants, and uses 
specialized
+    /// StaticFilter implementations for better performance (e.g., 
Int32StaticFilter for Int32).
+    ///
+    /// Returns an error if data types don't match or if the list contains 
non-constant expressions.

Review Comment:
   I don't think this comment is correct -- the code appears to handle the case 
of non-constant expressions as well
   
   I think this might also be clearer if it were simply named `try_new()` 
rather than `try_from_static_filter`



##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -226,31 +226,62 @@ impl StaticFilter for Int32StaticFilter {
     }
 
     fn contains(&self, v: &dyn Array, negated: bool) -> Result<BooleanArray> {
+        // Handle dictionary arrays by recursing on the values
+        downcast_dictionary_array! {
+            v => {
+                let values_contains = self.contains(v.values().as_ref(), 
negated)?;
+                let result = take(&values_contains, v.keys(), None)?;
+                return Ok(downcast_array(result.as_ref()))
+            }
+            _ => {}
+        }
+
         let v = v
             .as_primitive_opt::<Int32Type>()
             .ok_or_else(|| exec_datafusion_err!("Failed to downcast array"))?;
 
-        let result = match (v.null_count() > 0, negated) {
+        let haystack_has_nulls = self.null_count > 0;
+        let has_nulls = v.null_count() > 0 || haystack_has_nulls;
+
+        let result = match (has_nulls, negated) {
             (true, false) => {
-                // has nulls, not negated"
-                BooleanArray::from_iter(
-                    v.iter().map(|value| Some(self.values.contains(&value?))),
-                )
+                // Either needle or haystack has nulls, not negated

Review Comment:
   ```suggestion
                   // needle has nulls, not negated
   ```



##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -698,562 +750,370 @@ mod tests {
         }};
     }
 
-    #[test]
-    fn in_list_utf8() -> Result<()> {
-        let schema = Schema::new(vec![Field::new("a", DataType::Utf8, true)]);
-        let a = StringArray::from(vec![Some("a"), Some("d"), None]);
-        let col_a = col("a", &schema)?;
-        let batch = RecordBatch::try_new(Arc::new(schema.clone()), 
vec![Arc::new(a)])?;
-
-        // expression: "a in ("a", "b")"
-        let list = vec![lit("a"), lit("b")];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), Some(false), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in ("a", "b")"
-        let list = vec![lit("a"), lit("b")];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), Some(true), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a in ("a", "b", null)"
-        let list = vec![lit("a"), lit("b"), lit(ScalarValue::Utf8(None))];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in ("a", "b", null)"
-        let list = vec![lit("a"), lit("b"), lit(ScalarValue::Utf8(None))];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        Ok(())
+    /// Test case for primitive types following the standard IN LIST pattern.
+    ///
+    /// Each test case represents a data type with:
+    /// - `value_in`: A value that appears in both the test array and the IN 
list (matches → true)
+    /// - `value_not_in`: A value that appears in the test array but NOT in 
the IN list (doesn't match → false)
+    /// - `value_in_list`: A value that appears in the IN list but not in the 
array (filler value)
+    /// - `null_value`: A null scalar value for NULL handling tests
+    struct InListPrimitiveTestCase {
+        name: &'static str,
+        value_in: ScalarValue,
+        value_not_in: ScalarValue,
+        value_in_list: ScalarValue,
+        null_value: ScalarValue,
     }
 
-    #[test]
-    fn in_list_binary() -> Result<()> {
-        let schema = Schema::new(vec![Field::new("a", DataType::Binary, 
true)]);
-        let a = BinaryArray::from(vec![
-            Some([1, 2, 3].as_slice()),
-            Some([1, 2, 2].as_slice()),
-            None,
-        ]);
-        let col_a = col("a", &schema)?;
-        let batch = RecordBatch::try_new(Arc::new(schema.clone()), 
vec![Arc::new(a)])?;
-
-        // expression: "a in ([1, 2, 3], [4, 5, 6])"
-        let list = vec![lit([1, 2, 3].as_slice()), lit([4, 5, 6].as_slice())];
-        in_list!(
-            batch,
-            list.clone(),
-            &false,
-            vec![Some(true), Some(false), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in ([1, 2, 3], [4, 5, 6])"
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), Some(true), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a in ([1, 2, 3], [4, 5, 6], null)"
-        let list = vec![
-            lit([1, 2, 3].as_slice()),
-            lit([4, 5, 6].as_slice()),
-            lit(ScalarValue::Binary(None)),
-        ];
-        in_list!(
-            batch,
-            list.clone(),
-            &false,
-            vec![Some(true), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a in ([1, 2, 3], [4, 5, 6], null)"
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        Ok(())
+    /// Generic test data struct for primitive types.
+    ///
+    /// Holds the three test values needed for IN LIST tests, allowing the data
+    /// to be declared explicitly and reused across multiple types.
+    #[derive(Clone)]
+    struct PrimitiveTestCaseData<T> {
+        value_in: T,
+        value_not_in: T,
+        value_in_list: T,
     }
 
-    #[test]
-    fn in_list_int64() -> Result<()> {
-        let schema = Schema::new(vec![Field::new("a", DataType::Int64, true)]);
-        let a = Int64Array::from(vec![Some(0), Some(2), None]);
-        let col_a = col("a", &schema)?;
-        let batch = RecordBatch::try_new(Arc::new(schema.clone()), 
vec![Arc::new(a)])?;
-
-        // expression: "a in (0, 1)"
-        let list = vec![lit(0i64), lit(1i64)];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), Some(false), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in (0, 1)"
-        let list = vec![lit(0i64), lit(1i64)];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), Some(true), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a in (0, 1, NULL)"
-        let list = vec![lit(0i64), lit(1i64), lit(ScalarValue::Null)];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in (0, 1, NULL)"
-        let list = vec![lit(0i64), lit(1i64), lit(ScalarValue::Null)];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        Ok(())
+    /// Helper to create test cases for any primitive type using generic data.
+    ///
+    /// Uses TryInto for flexible type conversion, allowing test data to be
+    /// declared in any convertible type (e.g., i32 for all integer types).
+    fn primitive_test_case<T, D, F>(
+        name: &'static str,
+        constructor: F,
+        data: PrimitiveTestCaseData<D>,
+    ) -> InListPrimitiveTestCase
+    where
+        D: TryInto<T>,
+        <D as TryInto<T>>::Error: Debug,
+        F: Fn(Option<T>) -> ScalarValue,
+        T: Clone,
+    {
+        InListPrimitiveTestCase {
+            name,
+            value_in: constructor(Some(data.value_in.try_into().unwrap())),
+            value_not_in: 
constructor(Some(data.value_not_in.try_into().unwrap())),
+            value_in_list: 
constructor(Some(data.value_in_list.try_into().unwrap())),
+            null_value: constructor(None),
+        }
     }
 
-    #[test]
-    fn in_list_float64() -> Result<()> {
-        let schema = Schema::new(vec![Field::new("a", DataType::Float64, 
true)]);
-        let a = Float64Array::from(vec![
-            Some(0.0),
-            Some(0.2),
-            None,
-            Some(f64::NAN),
-            Some(-f64::NAN),
-        ]);
-        let col_a = col("a", &schema)?;
-        let batch = RecordBatch::try_new(Arc::new(schema.clone()), 
vec![Arc::new(a)])?;
-
-        // expression: "a in (0.0, 0.1)"
-        let list = vec![lit(0.0f64), lit(0.1f64)];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), Some(false), None, Some(false), Some(false)],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in (0.0, 0.1)"
-        let list = vec![lit(0.0f64), lit(0.1f64)];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), Some(true), None, Some(true), Some(true)],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a in (0.0, 0.1, NULL)"
-        let list = vec![lit(0.0f64), lit(0.1f64), lit(ScalarValue::Null)];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), None, None, None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in (0.0, 0.1, NULL)"
-        let list = vec![lit(0.0f64), lit(0.1f64), lit(ScalarValue::Null)];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), None, None, None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
+    /// Runs test cases for multiple types, providing detailed SQL error 
messages on failure.
+    ///
+    /// For each test case, runs 4 standard IN LIST scenarios and provides 
context
+    /// about the test data and expected behavior when assertions fail.
+    fn run_test_cases(test_cases: Vec<InListPrimitiveTestCase>) -> Result<()> {
+        for test_case in test_cases {
+            let test_name = test_case.name;
+
+            // Get the data type from the scalar value
+            let data_type = test_case.value_in.data_type();
+            let schema = Schema::new(vec![Field::new("a", data_type.clone(), 
true)]);
+
+            // Create array from scalar values: [value_in, value_not_in, None]
+            let array = ScalarValue::iter_to_array(vec![
+                test_case.value_in.clone(),
+                test_case.value_not_in.clone(),
+                test_case.null_value.clone(),
+            ])?;
+
+            let col_a = col("a", &schema)?;
+            let batch =
+                RecordBatch::try_new(Arc::new(schema.clone()), 
vec![Arc::clone(&array)])?;
+
+            // Helper to format SQL-like representation for error messages
+            let _format_sql = |negated: bool, with_null: bool| -> String {

Review Comment:
   this helper seems unused (why is it named starting with `_` 🤔 )



##########
datafusion/physical-expr/src/expressions/in_list.rs:
##########
@@ -698,562 +750,370 @@ mod tests {
         }};
     }
 
-    #[test]
-    fn in_list_utf8() -> Result<()> {
-        let schema = Schema::new(vec![Field::new("a", DataType::Utf8, true)]);
-        let a = StringArray::from(vec![Some("a"), Some("d"), None]);
-        let col_a = col("a", &schema)?;
-        let batch = RecordBatch::try_new(Arc::new(schema.clone()), 
vec![Arc::new(a)])?;
-
-        // expression: "a in ("a", "b")"
-        let list = vec![lit("a"), lit("b")];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), Some(false), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in ("a", "b")"
-        let list = vec![lit("a"), lit("b")];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), Some(true), None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a in ("a", "b", null)"
-        let list = vec![lit("a"), lit("b"), lit(ScalarValue::Utf8(None))];
-        in_list!(
-            batch,
-            list,
-            &false,
-            vec![Some(true), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        // expression: "a not in ("a", "b", null)"
-        let list = vec![lit("a"), lit("b"), lit(ScalarValue::Utf8(None))];
-        in_list!(
-            batch,
-            list,
-            &true,
-            vec![Some(false), None, None],
-            Arc::clone(&col_a),
-            &schema
-        );
-
-        Ok(())
+    /// Test case for primitive types following the standard IN LIST pattern.
+    ///
+    /// Each test case represents a data type with:
+    /// - `value_in`: A value that appears in both the test array and the IN 
list (matches → true)
+    /// - `value_not_in`: A value that appears in the test array but NOT in 
the IN list (doesn't match → false)
+    /// - `value_in_list`: A value that appears in the IN list but not in the 
array (filler value)
+    /// - `null_value`: A null scalar value for NULL handling tests
+    struct InListPrimitiveTestCase {
+        name: &'static str,
+        value_in: ScalarValue,
+        value_not_in: ScalarValue,
+        value_in_list: ScalarValue,

Review Comment:
   my reading of these tests is that they always test a one element list -- as 
in the list does not have multiple values
   
   Do you think we need coverage for multi-value lists?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to