## Coverage Report

#### Created: 2025-11-22 06:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/datafusion/datafusion/physical-expr/src/expressions/literal.rs

    Line
    Count
    Source
    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
    //! Literal expressions for physical operations
    19
    20
    use std::any::Any;
    21
    use std::hash::Hash;
    22
    use std::sync::Arc;
    23
    24
    use crate::physical_expr::PhysicalExpr;
    25
    26
    use arrow::datatypes::{Field, FieldRef};
    27
    use arrow::{
    28
        datatypes::{DataType, Schema},
    29
        record_batch::RecordBatch,
    30
    };
    31
    use datafusion_common::metadata::FieldMetadata;
    32
    use datafusion_common::{Result, ScalarValue};
    33
    use datafusion_expr::Expr;
    34
    use datafusion_expr_common::columnar_value::ColumnarValue;
    35
    use datafusion_expr_common::interval_arithmetic::Interval;
    36
    use datafusion_expr_common::sort_properties::{ExprProperties, SortProperties};
    37
    38
    /// Represents a literal value
    39
    #[derive(Debug, PartialEq, Eq, Clone)]
    40
    pub struct Literal {
    41
        value: ScalarValue,
    42
        field: FieldRef,
    43
    }
    44
    45
    impl Hash for Literal {
    46
    3
        fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
    47
    3
            self.value.hash(state);
    48
    3
            let metadata = self.field.metadata();
    49
    3
            let mut keys = metadata.keys().collect::<Vec<_>>();
    50
    3
            keys.sort();
    51
    3
            for key0 in keys {
    52
    0
                key.hash(state);
    53
    0
                metadata.get(key).unwrap().hash(state);
    54
    0
            }
    55
    3
        }
    56
    }
    57
    58
    impl Literal {
    59
        /// Create a literal value expression
    60
    552
        pub fn new(value: ScalarValue) -> Self {
    61
    552
            Self::new_with_metadata(value, None)
    62
    552
        }
    63
    64
        /// Create a literal value expression
    65
    552
        pub fn new_with_metadata(
    66
    552
            value: ScalarValue,
    67
    552
            metadata: Option<FieldMetadata>,
    68
    552
        ) -> Self {
    69
    552
            let mut field = Field::new("lit".to_string(), value.data_type(), value.is_null());
    70
    71
    552
            if let Some(metadata0) = metadata {
    72
    0
                field = metadata.add_to_field(field);
    73
    552
            }
    74
    75
    552
            Self {
    76
    552
                value,
    77
    552
                field: field.into(),
    78
    552
            }
    79
    552
        }
    80
    81
        /// Get the scalar value
    82
    0
        pub fn value(&self) -> &ScalarValue {
    83
    0
            &self.value
    84
    0
        }
    85
    }
    86
    87
    impl std::fmt::Display for Literal {
    88
    22
        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    89
    22
            write!(f, "{}", self.value)
    90
    22
        }
    91
    }
    92
    93
    impl PhysicalExpr for Literal {
    94
        /// Return a reference to Any that can be used for downcasting
    95
    2
        fn as_any(&self) -> &dyn Any {
    96
    2
            self
    97
    2
        }
    98
    99
    1.60k
        fn data_type(&self, _input_schema: &Schema) -> Result<DataType> {
    100
    1.60k
            Ok(self.value.data_type())
    101
    1.60k
        }
    102
    103
    0
        fn nullable(&self, _input_schema: &Schema) -> Result<bool> {
    104
    0
            Ok(self.value.is_null())
    105
    0
        }
    106
    107
    0
        fn return_field(&self, _input_schema: &Schema) -> Result<FieldRef> {
    108
    0
            Ok(Arc::clone(&self.field))
    109
    0
        }
    110
    111
    805
        fn evaluate(&self, _batch: &RecordBatch) -> Result<ColumnarValue> {
    112
    805
            Ok(ColumnarValue::Scalar(self.value.clone()))
    113
    805
        }
    114
    115
    1
        fn children(&self) -> Vec<&Arc<dyn PhysicalExpr>> {
    116
    1
            vec![]
    117
    1
        }
    118
    119
    0
        fn with_new_children(
    120
    0
            self: Arc<Self>,
    121
    0
            _children: Vec<Arc<dyn PhysicalExpr>>,
    122
    0
        ) -> Result<Arc<dyn PhysicalExpr>> {
    123
    0
            Ok(self)
    124
    0
        }
    125
    126
    0
        fn get_properties(&self, _children: &[ExprProperties]) -> Result<ExprProperties> {
    127
            Ok(ExprProperties {
    128
    0
                sort_properties: SortProperties::Singleton,
    129
    0
                range: Interval::try_new(self.value().clone(), self.value().clone())?,
    130
                preserves_lex_ordering: true,
    131
            })
    132
    0
        }
    133
    134
    10
        fn fmt_sql(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    135
    10
            std::fmt::Display::fmt(self, f)
    136
    10
        }
    137
    }
    138
    139
    /// Create a literal expression
    140
    #[expect(clippy::needless_pass_by_value)]
    141
    552
    pub fn lit<T: datafusion_expr::Literal>(value: T) -> Arc<dyn PhysicalExpr> {
    142
    552
        match value.lit() {
    143
    552
            Expr::Literal(v, _) => Arc::new(Literal::new(v)),
    144
    0
            _ => unreachable!(),
    145
        }
    146
    552
    }
    147
    148
    #[cfg(test)]
    149
    mod tests {
    150
        use super::*;
    151
    152
        use arrow::array::Int32Array;
    153
        use arrow::datatypes::Field;
    154
        use datafusion_common::cast::as_int32_array;
    155
        use datafusion_physical_expr_common::physical_expr::fmt_sql;
    156
    157
        #[test]
    158
    0
        fn literal_i32() -> Result<()> {
    159
            // create an arbitrary record batch
    160
    0
            let schema = Schema::new(vec![Field::new("a", DataType::Int32, true)]);
    161
    0
            let a = Int32Array::from(vec![Some(1), None, Some(3), Some(4), Some(5)]);
    162
    0
            let batch = RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)])?;
    163
    164
            // create and evaluate a literal expression
    165
    0
            let literal_expr = lit(42i32);
    166
    0
            assert_eq!("42", format!("{literal_expr}"));
    167
    168
    0
            let literal_array = literal_expr
    169
    0
                .evaluate(&batch)?
    170
    0
                .into_array(batch.num_rows())
    171
    0
                .expect("Failed to convert to array");
    172
    0
            let literal_array = as_int32_array(&literal_array)?;
    173
    174
            // note that the contents of the literal array are unrelated to the batch contents except for the length of the array
    175
    0
            assert_eq!(literal_array.len(), 5); // 5 rows in the batch
    176
    0
            for i in 0..literal_array.len() {
    177
    0
                assert_eq!(literal_array.value(i), 42);
    178
            }
    179
    180
    0
            Ok(())
    181
    0
        }
    182
    183
        #[test]
    184
    0
        fn test_fmt_sql() -> Result<()> {
    185
            // create and evaluate a literal expression
    186
    0
            let expr = lit(42i32);
    187
    0
            let display_string = expr.to_string();
    188
    0
            assert_eq!(display_string, "42");
    189
    0
            let sql_string = fmt_sql(expr.as_ref()).to_string();
    190
    0
            assert_eq!(sql_string, "42");
    191
    192
    0
            Ok(())
    193
    0
        }
    194
    }