Coverage Report

Created: 2024-10-13 08:39

/Users/andrewlamb/Software/datafusion/datafusion/physical-expr/src/expressions/not.rs
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
//! Not expression
19
20
use std::any::Any;
21
use std::fmt;
22
use std::hash::{Hash, Hasher};
23
use std::sync::Arc;
24
25
use crate::physical_expr::down_cast_any_ref;
26
use crate::PhysicalExpr;
27
use arrow::datatypes::{DataType, Schema};
28
use arrow::record_batch::RecordBatch;
29
use datafusion_common::{cast::as_boolean_array, Result, ScalarValue};
30
use datafusion_expr::ColumnarValue;
31
32
/// Not expression
33
#[derive(Debug, Hash)]
34
pub struct NotExpr {
35
    /// Input expression
36
    arg: Arc<dyn PhysicalExpr>,
37
}
38
39
impl NotExpr {
40
    /// Create new not expression
41
0
    pub fn new(arg: Arc<dyn PhysicalExpr>) -> Self {
42
0
        Self { arg }
43
0
    }
44
45
    /// Get the input expression
46
0
    pub fn arg(&self) -> &Arc<dyn PhysicalExpr> {
47
0
        &self.arg
48
0
    }
49
}
50
51
impl fmt::Display for NotExpr {
52
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53
0
        write!(f, "NOT {}", self.arg)
54
0
    }
55
}
56
57
impl PhysicalExpr for NotExpr {
58
    /// Return a reference to Any that can be used for downcasting
59
0
    fn as_any(&self) -> &dyn Any {
60
0
        self
61
0
    }
62
63
0
    fn data_type(&self, _input_schema: &Schema) -> Result<DataType> {
64
0
        Ok(DataType::Boolean)
65
0
    }
66
67
0
    fn nullable(&self, input_schema: &Schema) -> Result<bool> {
68
0
        self.arg.nullable(input_schema)
69
0
    }
70
71
0
    fn evaluate(&self, batch: &RecordBatch) -> Result<ColumnarValue> {
72
0
        let evaluate_arg = self.arg.evaluate(batch)?;
73
0
        match evaluate_arg {
74
0
            ColumnarValue::Array(array) => {
75
0
                let array = as_boolean_array(&array)?;
76
                Ok(ColumnarValue::Array(Arc::new(
77
0
                    arrow::compute::kernels::boolean::not(array)?,
78
                )))
79
            }
80
0
            ColumnarValue::Scalar(scalar) => {
81
0
                if scalar.is_null() {
82
0
                    return Ok(ColumnarValue::Scalar(ScalarValue::Boolean(None)));
83
0
                }
84
0
                let bool_value: bool = scalar.try_into()?;
85
0
                Ok(ColumnarValue::Scalar(ScalarValue::Boolean(Some(
86
0
                    !bool_value,
87
0
                ))))
88
            }
89
        }
90
0
    }
91
92
0
    fn children(&self) -> Vec<&Arc<dyn PhysicalExpr>> {
93
0
        vec![&self.arg]
94
0
    }
95
96
0
    fn with_new_children(
97
0
        self: Arc<Self>,
98
0
        children: Vec<Arc<dyn PhysicalExpr>>,
99
0
    ) -> Result<Arc<dyn PhysicalExpr>> {
100
0
        Ok(Arc::new(NotExpr::new(Arc::clone(&children[0]))))
101
0
    }
102
103
0
    fn dyn_hash(&self, state: &mut dyn Hasher) {
104
0
        let mut s = state;
105
0
        self.hash(&mut s);
106
0
    }
107
}
108
109
impl PartialEq<dyn Any> for NotExpr {
110
0
    fn eq(&self, other: &dyn Any) -> bool {
111
0
        down_cast_any_ref(other)
112
0
            .downcast_ref::<Self>()
113
0
            .map(|x| self.arg.eq(&x.arg))
114
0
            .unwrap_or(false)
115
0
    }
116
}
117
118
/// Creates a unary expression NOT
119
0
pub fn not(arg: Arc<dyn PhysicalExpr>) -> Result<Arc<dyn PhysicalExpr>> {
120
0
    Ok(Arc::new(NotExpr::new(arg)))
121
0
}
122
123
#[cfg(test)]
124
mod tests {
125
    use super::*;
126
    use crate::expressions::col;
127
    use arrow::{array::BooleanArray, datatypes::*};
128
129
    #[test]
130
    fn neg_op() -> Result<()> {
131
        let schema = Schema::new(vec![Field::new("a", DataType::Boolean, true)]);
132
133
        let expr = not(col("a", &schema)?)?;
134
        assert_eq!(expr.data_type(&schema)?, DataType::Boolean);
135
        assert!(expr.nullable(&schema)?);
136
137
        let input = BooleanArray::from(vec![Some(true), None, Some(false)]);
138
        let expected = &BooleanArray::from(vec![Some(false), None, Some(true)]);
139
140
        let batch =
141
            RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(input)])?;
142
143
        let result = expr
144
            .evaluate(&batch)?
145
            .into_array(batch.num_rows())
146
            .expect("Failed to convert to array");
147
        let result =
148
            as_boolean_array(&result).expect("failed to downcast to BooleanArray");
149
        assert_eq!(result, expected);
150
151
        Ok(())
152
    }
153
}