datafusion-physical-expr 55.0.0

Physical expression implementation for DataFusion query engine
Documentation
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Result building helpers for InList operations.
//!
//! This module provides unified logic for building BooleanArray results
//! from IN list membership tests, handling null propagation correctly
//! according to SQL three-valued logic.

use arrow::array::BooleanArray;
use arrow::buffer::{BooleanBuffer, NullBuffer};

// Truth table for (needle_nulls, haystack_has_nulls, negated):
// (Some, true,  false) => values: valid & contains,  nulls: valid & contains
// (None, true,  false) => values: contains,          nulls: contains
// (Some, true,  true)  => values: valid & !contains, nulls: valid & contains
// (None, true,  true)  => values: !contains,         nulls: contains
// (Some, false, false) => values: valid & contains,  nulls: valid
// (Some, false, true)  => values: valid & !contains, nulls: valid
// (None, false, false) => values: contains,          nulls: none
// (None, false, true)  => values: !contains,         nulls: none

/// Builds a BooleanArray result for IN list operations.
///
/// This function handles the null propagation logic for SQL IN lists:
/// - If the needle value is null, the result is null
/// - If the needle is not in the set and the haystack has nulls, the result is null
/// - Otherwise, the result is true/false based on membership and negation
///
/// This version computes contains for all positions, including nulls, then applies
/// null masking via bitmap operations.
#[inline]
pub(crate) fn build_in_list_result<C>(
    len: usize,
    needle_nulls: Option<&NullBuffer>,
    haystack_has_nulls: bool,
    negated: bool,
    contains: C,
) -> BooleanArray
where
    C: FnMut(usize) -> bool,
{
    let contains_buf = BooleanBuffer::collect_bool(len, contains);
    build_result_from_contains(needle_nulls, haystack_has_nulls, negated, contains_buf)
}

/// Builds a BooleanArray result from a pre-computed contains buffer.
///
/// This version does not assume contains_buf is pre-masked at null positions.
/// It handles nulls using bitmap operations.
#[inline]
pub(crate) fn build_result_from_contains(
    needle_nulls: Option<&NullBuffer>,
    haystack_has_nulls: bool,
    negated: bool,
    contains_buf: BooleanBuffer,
) -> BooleanArray {
    match (needle_nulls, haystack_has_nulls, negated) {
        // Haystack has nulls: result is null unless value is found.
        (Some(v), true, false) => {
            // values: valid & contains, nulls: valid & contains
            let values = v.inner() & &contains_buf;
            BooleanArray::new(values.clone(), Some(NullBuffer::new(values)))
        }
        (None, true, false) => {
            BooleanArray::new(contains_buf.clone(), Some(NullBuffer::new(contains_buf)))
        }
        (Some(v), true, true) => {
            // NOT IN with nulls: false if found, null if not found or needle null.
            // values: valid & !contains, nulls: valid & contains
            let valid = v.inner();
            let values = valid & &(!&contains_buf);
            let nulls = valid & &contains_buf;
            BooleanArray::new(values, Some(NullBuffer::new(nulls)))
        }
        (None, true, true) => {
            BooleanArray::new(!&contains_buf, Some(NullBuffer::new(contains_buf)))
        }
        // Haystack has no nulls: result validity follows needle validity.
        (Some(v), false, false) => {
            // values: valid & contains, nulls: valid
            BooleanArray::new(v.inner() & &contains_buf, Some(v.clone()))
        }
        (Some(v), false, true) => {
            // values: valid & !contains, nulls: valid
            BooleanArray::new(v.inner() & &(!&contains_buf), Some(v.clone()))
        }
        (None, false, false) => BooleanArray::new(contains_buf, None),
        (None, false, true) => BooleanArray::new(!&contains_buf, None),
    }
}