Skip to main content

datafusion_functions/core/
getfield.rs

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
18use std::sync::{Arc, OnceLock};
19
20use arrow::array::{
21    Array, BooleanArray, Capacities, MutableArrayData, Scalar, cast::AsArray, make_array,
22    make_comparator,
23};
24use arrow::compute::SortOptions;
25use arrow::datatypes::{DataType, Field, FieldRef};
26use arrow_buffer::NullBuffer;
27
28use datafusion_common::cast::{as_map_array, as_struct_array};
29use datafusion_common::{
30    Result, ScalarValue, exec_datafusion_err, exec_err, internal_err, plan_datafusion_err,
31};
32use datafusion_expr::expr::ScalarFunction;
33use datafusion_expr::simplify::ExprSimplifyResult;
34use datafusion_expr::{
35    ColumnarValue, Documentation, Expr, ExpressionPlacement, ReturnFieldArgs,
36    ScalarFunctionArgs, ScalarUDF, ScalarUDFImpl, Signature, Volatility,
37};
38use datafusion_macros::user_doc;
39
40use super::named_struct::NamedStructFunc;
41use super::r#struct::StructFunc;
42
43#[user_doc(
44    doc_section(label = "Other Functions"),
45    description = r#"Returns a field within a map or a struct with the given key.
46    Supports nested field access by providing multiple field names.
47    Note: most users invoke `get_field` indirectly via field access
48    syntax such as `my_struct_col['field_name']` which results in a call to
49    `get_field(my_struct_col, 'field_name')`.
50    Nested access like `my_struct['a']['b']` is optimized to a single call:
51    `get_field(my_struct, 'a', 'b')`."#,
52    syntax_example = "get_field(expression, field_name[, field_name2, ...])",
53    sql_example = r#"```sql
54> -- Access a field from a struct column
55> create table test( struct_col) as values
56    ({name: 'Alice', age: 30}),
57    ({name: 'Bob', age: 25});
58> select struct_col from test;
59+-----------------------------+
60| struct_col                  |
61+-----------------------------+
62| {name: Alice, age: 30}      |
63| {name: Bob, age: 25}        |
64+-----------------------------+
65> select struct_col['name'] as name from test;
66+-------+
67| name  |
68+-------+
69| Alice |
70| Bob   |
71+-------+
72
73> -- Nested field access with multiple arguments
74> create table test(struct_col) as values
75    ({outer: {inner_val: 42}});
76> select struct_col['outer']['inner_val'] as result from test;
77+--------+
78| result |
79+--------+
80| 42     |
81+--------+
82```"#,
83    argument(
84        name = "expression",
85        description = "The map or struct to retrieve a field from."
86    ),
87    argument(
88        name = "field_name",
89        description = "The field name(s) to access, in order for nested access. Must evaluate to strings."
90    )
91)]
92#[derive(Debug, PartialEq, Eq, Hash)]
93pub struct GetFieldFunc {
94    signature: Signature,
95}
96
97impl Default for GetFieldFunc {
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103/// Process a map array by finding matching keys and extracting corresponding values.
104///
105/// This function handles both simple (scalar) and nested key types by using
106/// appropriate comparison strategies.
107fn process_map_array(
108    array: &dyn Array,
109    key_array: Arc<dyn Array>,
110) -> Result<ColumnarValue> {
111    let map_array = as_map_array(array)?;
112    let keys = if key_array.data_type().is_nested() {
113        let comparator = make_comparator(
114            map_array.keys().as_ref(),
115            key_array.as_ref(),
116            SortOptions::default(),
117        )?;
118        let len = map_array.keys().len().min(key_array.len());
119        let values = (0..len).map(|i| comparator(i, i).is_eq()).collect();
120        let nulls = NullBuffer::union(map_array.keys().nulls(), key_array.nulls());
121        BooleanArray::new(values, nulls)
122    } else {
123        let be_compared = Scalar::new(key_array);
124        arrow::compute::kernels::cmp::eq(&be_compared, map_array.keys())?
125    };
126
127    let original_data = map_array.entries().column(1).to_data();
128    let capacity = Capacities::Array(original_data.len());
129    let mut mutable =
130        MutableArrayData::with_capacities(vec![&original_data], true, capacity);
131
132    let offsets = map_array.value_offsets();
133    // Scan the comparison result in place: slicing it per entry would allocate
134    // a new array for every row of the map. Map keys are non-null by
135    // definition, so the comparison result carries no nulls to check here.
136    let matches = keys.values();
137
138    for entry in 0..map_array.len() {
139        let start = offsets[entry] as usize;
140        let end = offsets[entry + 1] as usize;
141
142        let matched = (start..end).find(|&i| matches.value(i));
143
144        match matched {
145            Some(i) => mutable.try_extend(0, i, i + 1)?,
146            None => mutable.try_extend_nulls(1)?,
147        }
148    }
149
150    let data = mutable.freeze();
151    let data = make_array(data);
152    Ok(ColumnarValue::Array(data))
153}
154
155/// Process a map array with a nested key type by iterating through entries
156/// and using a comparator for key matching.
157///
158/// This specialized version is used when the key type is nested (e.g., struct, list).
159fn process_map_with_nested_key(
160    array: &dyn Array,
161    key_array: &dyn Array,
162) -> Result<ColumnarValue> {
163    let map_array = as_map_array(array)?;
164
165    let comparator =
166        make_comparator(map_array.keys().as_ref(), key_array, SortOptions::default())?;
167
168    let original_data = map_array.entries().column(1).to_data();
169    let capacity = Capacities::Array(original_data.len());
170    let mut mutable =
171        MutableArrayData::with_capacities(vec![&original_data], true, capacity);
172
173    for entry in 0..map_array.len() {
174        let start = map_array.value_offsets()[entry] as usize;
175        let end = map_array.value_offsets()[entry + 1] as usize;
176
177        let mut found_match = false;
178        for i in start..end {
179            if comparator(i, 0).is_eq() {
180                mutable.try_extend(0, i, i + 1)?;
181                found_match = true;
182                break;
183            }
184        }
185
186        if !found_match {
187            mutable.try_extend_nulls(1)?;
188        }
189    }
190
191    let data = mutable.freeze();
192    let data = make_array(data);
193    Ok(ColumnarValue::Array(data))
194}
195
196/// Extract a single field from a struct or map array
197fn extract_single_field(base: ColumnarValue, name: ScalarValue) -> Result<ColumnarValue> {
198    let arrays = ColumnarValue::values_to_arrays(&[base])?;
199    let array = Arc::clone(&arrays[0]);
200
201    let string_value = name.try_as_str().flatten().map(|s| s.to_string());
202
203    match (array.data_type(), name, string_value) {
204        // Dictionary-encoded struct: extract the field from the dictionary's
205        // values (the deduplicated struct array) and rebuild a dictionary with
206        // the same keys. This preserves dictionary encoding without expanding.
207        (DataType::Dictionary(_, value_type), _, Some(field_name))
208            if matches!(value_type.as_ref(), DataType::Struct(_)) =>
209        {
210            let dict = array.as_any_dictionary();
211            let values_struct = dict.values().as_struct();
212            let field_col =
213                values_struct.column_by_name(&field_name).ok_or_else(|| {
214                    exec_datafusion_err!(
215                        "Field {field_name} not found in dictionary struct"
216                    )
217                })?;
218            Ok(ColumnarValue::Array(
219                dict.with_values(Arc::clone(field_col)),
220            ))
221        }
222        (DataType::Map(_, _), ScalarValue::List(arr), _) => {
223            let key_array: Arc<dyn Array> = arr;
224            process_map_array(&array, key_array)
225        }
226        (DataType::Map(_, _), ScalarValue::Struct(arr), _) => {
227            process_map_array(&array, arr as Arc<dyn Array>)
228        }
229        (DataType::Map(_, _), other, _) => {
230            let data_type = other.data_type();
231            if data_type.is_nested() {
232                process_map_with_nested_key(&array, &other.to_array()?)
233            } else {
234                process_map_array(&array, other.to_array()?)
235            }
236        }
237        (DataType::Struct(_), _, Some(k)) => {
238            let as_struct_array = as_struct_array(&array)?;
239            match as_struct_array.column_by_name(&k) {
240                None => exec_err!("Field {k} not found in struct"),
241                Some(col) => Ok(ColumnarValue::Array(Arc::clone(col))),
242            }
243        }
244        (DataType::Struct(_), name, _) => exec_err!(
245            "get_field is only possible on struct with utf8 indexes. \
246                         Received with {name:?} index"
247        ),
248        (DataType::Null, _, _) => Ok(ColumnarValue::Scalar(ScalarValue::Null)),
249        (dt, name, _) => exec_err!(
250            "get_field is only possible on maps or structs. Received {dt} with {name:?} index"
251        ),
252    }
253}
254
255/// The shared `get_field` UDF, reused whenever simplification needs to build a
256/// fresh `get_field` node (e.g. re-wrapping the remaining access path).
257fn get_field_udf() -> Arc<ScalarUDF> {
258    static GET_FIELD_UDF: OnceLock<Arc<ScalarUDF>> = OnceLock::new();
259    Arc::clone(
260        GET_FIELD_UDF
261            .get_or_init(|| Arc::new(ScalarUDF::new_from_impl(GetFieldFunc::new()))),
262    )
263}
264
265/// Try to simplify a `get_field` call whose base is an inline struct
266/// constructor by resolving the field access at plan time.
267///
268/// Handles both struct constructors:
269/// * `named_struct('a', x, 'b', y)` — fields are looked up by name.
270/// * `struct(x, y)` — fields are positional and named `c0`, `c1`, ...
271///
272/// For example:
273/// * `get_field(named_struct('min', a, 'max', b), 'max')` => `b`
274/// * `get_field(struct(a, b), 'c1')` => `b`
275///
276/// `args` is the (already flattened) argument list of the `get_field` call:
277/// `[base, field_name, rest_of_path...]`. When extra path elements remain
278/// after resolving the first one (`get_field(named_struct('s', inner), 's', 'k')`),
279/// the resolved value is re-wrapped in a `get_field` call for the remaining
280/// path so the simplifier can recurse into it on the next pass.
281///
282/// Returns `None` — leaving the expression untouched — whenever the rewrite
283/// cannot be proven safe, e.g. a non-literal field name, a `named_struct`
284/// with a non-literal field name (which might shadow the requested field at
285/// runtime), or a field the constructor does not produce.
286///
287/// Replacing the access with the selected field expression drops the
288/// expressions for the other (unaccessed) fields, so they are no longer
289/// evaluated — e.g. `get_field(named_struct('a', 1/0, 'b', x), 'b')` becomes
290/// `x` and the `1/0` is never evaluated. This is intentional and matches the
291/// optimizer's contract for immutable expressions: a simplification may drop
292/// sub-expressions whose value is not observed.
293fn simplify_get_field_over_struct_constructor(args: &[Expr]) -> Option<Expr> {
294    let [base, field_name, rest @ ..] = args else {
295        return None;
296    };
297
298    // The accessed field name must be a non-empty string literal.
299    let Expr::Literal(field_name, _) = field_name else {
300        return None;
301    };
302    let field_name = field_name
303        .try_as_str()
304        .flatten()
305        .filter(|s| !s.is_empty())?;
306
307    let Expr::ScalarFunction(ScalarFunction {
308        func,
309        args: ctor_args,
310    }) = base
311    else {
312        return None;
313    };
314
315    let value = if func.inner().is::<NamedStructFunc>() {
316        // named_struct(name1, value1, name2, value2, ...)
317        if !ctor_args.len().is_multiple_of(2) {
318            return None;
319        }
320        let mut matched = None;
321        for pair in ctor_args.chunks_exact(2) {
322            // Every name must be a literal string: a non-literal name appearing
323            // *before* the first match could evaluate to `field_name` at runtime
324            // and become the real first match (Arrow's `column_by_name` returns
325            // the first match), so we cannot resolve the access.
326            //
327            // We conservatively bail on *any* non-literal name. Once a literal
328            // match has been found, a later non-literal name is in fact harmless
329            // — it can never precede the first match — so bailing there is a
330            // deliberate approximation we accept to keep this check simple, not a
331            // correctness requirement.
332            let Expr::Literal(name, _) = &pair[0] else {
333                return None;
334            };
335            let name = name.try_as_str().flatten()?;
336            // `column_by_name` resolves to the first match, so do the same.
337            if matched.is_none() && name == field_name {
338                matched = Some(&pair[1]);
339            }
340        }
341        matched?.clone()
342    } else if func.inner().is::<StructFunc>() {
343        // struct(value0, value1, ...) produces fields named c0, c1, ...
344        let index: usize = field_name.strip_prefix('c')?.parse().ok()?;
345        // Reject non-canonical spellings (e.g. "c01") that name no real field.
346        if format!("c{index}") != field_name {
347            return None;
348        }
349        ctor_args.get(index)?.clone()
350    } else {
351        return None;
352    };
353
354    if rest.is_empty() {
355        return Some(value);
356    }
357
358    // Remaining path elements: re-wrap as get_field(value, rest...) and let
359    // the simplifier resolve the rest on a subsequent pass.
360    let mut new_args = Vec::with_capacity(rest.len() + 1);
361    new_args.push(value);
362    new_args.extend_from_slice(rest);
363    Some(Expr::ScalarFunction(ScalarFunction::new_udf(
364        get_field_udf(),
365        new_args,
366    )))
367}
368
369impl GetFieldFunc {
370    pub fn new() -> Self {
371        Self {
372            signature: Signature::user_defined(Volatility::Immutable),
373        }
374    }
375}
376
377// get_field(struct_array, field_name)
378impl ScalarUDFImpl for GetFieldFunc {
379    fn name(&self) -> &str {
380        "get_field"
381    }
382
383    fn display_name(&self, args: &[Expr]) -> Result<String> {
384        if args.len() < 2 {
385            return exec_err!(
386                "get_field requires at least 2 arguments, got {}",
387                args.len()
388            );
389        }
390
391        let base = &args[0];
392        let field_names: Vec<String> = args[1..]
393            .iter()
394            .map(|f| match f {
395                Expr::Literal(name, _) => name.to_string(),
396                other => other.schema_name().to_string(),
397            })
398            .collect();
399
400        Ok(format!("{}[{}]", base, field_names.join("][")))
401    }
402
403    fn schema_name(&self, args: &[Expr]) -> Result<String> {
404        if args.len() < 2 {
405            return exec_err!(
406                "get_field requires at least 2 arguments, got {}",
407                args.len()
408            );
409        }
410
411        let base = &args[0];
412        let field_names: Vec<String> = args[1..]
413            .iter()
414            .map(|f| match f {
415                Expr::Literal(name, _) => name.to_string(),
416                other => other.schema_name().to_string(),
417            })
418            .collect();
419
420        Ok(format!(
421            "{}[{}]",
422            base.schema_name(),
423            field_names.join("][")
424        ))
425    }
426
427    fn signature(&self) -> &Signature {
428        &self.signature
429    }
430
431    fn return_type(&self, _: &[DataType]) -> Result<DataType> {
432        internal_err!("return_field_from_args should be called instead")
433    }
434
435    fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
436        // Validate minimum 2 arguments: base expression + at least one field name
437        if args.scalar_arguments.len() < 2 {
438            return exec_err!(
439                "get_field requires at least 2 arguments, got {}",
440                args.scalar_arguments.len()
441            );
442        }
443
444        let mut current_field = Arc::clone(&args.arg_fields[0]);
445
446        // Iterate through each field name (starting from index 1)
447        for (i, sv) in args.scalar_arguments.iter().enumerate().skip(1) {
448            match current_field.data_type() {
449                DataType::Map(map_field, _) => {
450                    match map_field.data_type() {
451                        DataType::Struct(fields) if fields.len() == 2 => {
452                            // Arrow's MapArray is essentially a ListArray of structs with two columns. They are
453                            // often named "key", and "value", but we don't require any specific naming here;
454                            // instead, we assume that the second column is the "value" column both here and in
455                            // execution.
456                            let value_field = fields
457                                .get(1)
458                                .expect("fields should have exactly two members");
459
460                            current_field = Arc::new(
461                                value_field.as_ref().clone().with_nullable(true),
462                            );
463                        }
464                        _ => {
465                            return exec_err!(
466                                "Map fields must contain a Struct with exactly 2 fields"
467                            );
468                        }
469                    }
470                }
471                // Dictionary-encoded struct: resolve the child field from
472                // the underlying struct, then wrap the result back in the
473                // same Dictionary type so the promised type matches execution.
474                DataType::Dictionary(key_type, value_type)
475                    if matches!(value_type.as_ref(), DataType::Struct(_)) =>
476                {
477                    let DataType::Struct(fields) = value_type.as_ref() else {
478                        unreachable!()
479                    };
480                    let field_name = sv
481                        .as_ref()
482                        .and_then(|sv| {
483                            sv.try_as_str().flatten().filter(|s| !s.is_empty())
484                        })
485                        .ok_or_else(|| {
486                            exec_datafusion_err!("Field name must be a non-empty string")
487                        })?;
488
489                    let child_field = fields
490                        .iter()
491                        .find(|f| f.name() == field_name)
492                        .ok_or_else(|| {
493                            plan_datafusion_err!("Field {field_name} not found in struct")
494                        })?;
495
496                    let dict_type = DataType::Dictionary(
497                        key_type.clone(),
498                        Box::new(child_field.data_type().clone()),
499                    );
500                    let mut new_field =
501                        child_field.as_ref().clone().with_data_type(dict_type);
502                    if current_field.is_nullable() {
503                        new_field = new_field.with_nullable(true);
504                    }
505                    current_field = Arc::new(new_field);
506                }
507                DataType::Struct(fields) => {
508                    let field_name = sv
509                        .as_ref()
510                        .and_then(|sv| {
511                            sv.try_as_str().flatten().filter(|s| !s.is_empty())
512                        })
513                        .ok_or_else(|| {
514                            datafusion_common::DataFusionError::Execution(
515                                "Field name must be a non-empty string".to_string(),
516                            )
517                        })?;
518
519                    let child_field = fields
520                        .iter()
521                        .find(|f| f.name() == field_name)
522                        .ok_or_else(|| {
523                            plan_datafusion_err!("Field {field_name} not found in struct")
524                        })?;
525
526                    let mut new_field = child_field.as_ref().clone();
527
528                    // If the parent is nullable, then getting the child must be nullable
529                    if current_field.is_nullable() {
530                        new_field = new_field.with_nullable(true);
531                    }
532                    current_field = Arc::new(new_field);
533                }
534                DataType::Null => {
535                    return Ok(Field::new(self.name(), DataType::Null, true).into());
536                }
537                other => {
538                    return exec_err!(
539                        "Cannot access field at argument {}: type {} is not Struct, Map, or Null",
540                        i,
541                        other
542                    );
543                }
544            }
545        }
546
547        Ok(current_field)
548    }
549
550    fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
551        if args.args.len() < 2 {
552            return exec_err!(
553                "get_field requires at least 2 arguments, got {}",
554                args.args.len()
555            );
556        }
557
558        let mut current = args.args[0].clone();
559
560        // Early exit for null base
561        if current.data_type().is_null() {
562            return Ok(ColumnarValue::Scalar(ScalarValue::Null));
563        }
564
565        // Iterate through each field name
566        for field_name in args.args.iter().skip(1) {
567            let field_name_scalar = match field_name {
568                ColumnarValue::Scalar(name) => name.clone(),
569                _ => {
570                    return exec_err!(
571                        "get_field function requires all field_name arguments to be scalars"
572                    );
573                }
574            };
575
576            current = extract_single_field(current, field_name_scalar)?;
577
578            // Early exit if we hit null
579            if current.data_type().is_null() {
580                return Ok(ColumnarValue::Scalar(ScalarValue::Null));
581            }
582        }
583
584        Ok(current)
585    }
586
587    fn simplify(
588        &self,
589        args: Vec<Expr>,
590        _info: &datafusion_expr::simplify::SimplifyContext,
591    ) -> Result<ExprSimplifyResult> {
592        // Need at least 2 args (base + field)
593        if args.len() < 2 {
594            return Ok(ExprSimplifyResult::Original(args));
595        }
596
597        // Flatten all nested get_field calls in a single pass
598        // Pattern: get_field(get_field(get_field(base, a), b), c) => get_field(base, a, b, c)
599        //
600        // `path_args_stack` collects each level's field-name arguments,
601        // outermost first; it is reversed below to restore access order.
602        let mut path_args_stack = vec![&args[1..]];
603        let mut current_expr = &args[0];
604
605        // Walk down the chain of nested get_field calls
606        let base_expr = loop {
607            if let Expr::ScalarFunction(ScalarFunction {
608                func,
609                args: inner_args,
610            }) = current_expr
611                && func.inner().is::<GetFieldFunc>()
612            {
613                // Store this level's path arguments (all except the first, which is base/nested call)
614                path_args_stack.push(&inner_args[1..]);
615
616                // Move to the next level down
617                current_expr = &inner_args[0];
618                continue;
619            }
620            // Not a get_field call, this is the base expression
621            break current_expr;
622        };
623
624        // Whether any nested get_field calls were collapsed above.
625        let did_flatten = path_args_stack.len() > 1;
626
627        // Build merged args: [base, ...all path args in access order].
628        // The stack holds path slices outermost-first, so iterate in reverse.
629        let mut merged_args = vec![base_expr.clone()];
630        for path_slice in path_args_stack.iter().rev() {
631            merged_args.extend_from_slice(path_slice);
632        }
633
634        // Resolve field accesses against an inline struct constructor:
635        //   get_field(named_struct('min', a, 'max', b), 'max') => b
636        if let Some(simplified) = simplify_get_field_over_struct_constructor(&merged_args)
637        {
638            return Ok(ExprSimplifyResult::Simplified(simplified));
639        }
640
641        if did_flatten {
642            return Ok(ExprSimplifyResult::Simplified(Expr::ScalarFunction(
643                ScalarFunction::new_udf(get_field_udf(), merged_args),
644            )));
645        }
646
647        Ok(ExprSimplifyResult::Original(args))
648    }
649
650    fn coerce_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
651        if arg_types.len() < 2 {
652            return exec_err!(
653                "get_field requires at least 2 arguments, got {}",
654                arg_types.len()
655            );
656        }
657        // Accept types as-is, validation happens in return_field_from_args
658        Ok(arg_types.to_vec())
659    }
660
661    fn documentation(&self) -> Option<&Documentation> {
662        self.doc()
663    }
664
665    fn placement(&self, args: &[ExpressionPlacement]) -> ExpressionPlacement {
666        // get_field can be pushed to leaves if:
667        // 1. The base (first arg) is a column or already placeable at leaves
668        // 2. All field keys (remaining args) are literals
669        if args.is_empty() {
670            return ExpressionPlacement::KeepInPlace;
671        }
672
673        let base_placement = args[0];
674        let base_is_pushable = matches!(
675            base_placement,
676            ExpressionPlacement::Column | ExpressionPlacement::MoveTowardsLeafNodes
677        );
678
679        let all_keys_are_literals = args
680            .iter()
681            .skip(1)
682            .all(|p| *p == ExpressionPlacement::Literal);
683
684        if base_is_pushable && all_keys_are_literals {
685            ExpressionPlacement::MoveTowardsLeafNodes
686        } else {
687            ExpressionPlacement::KeepInPlace
688        }
689    }
690}
691
692#[cfg(test)]
693mod tests {
694    use super::*;
695    use arrow::array::{ArrayRef, Int32Array, StructArray};
696    use arrow::datatypes::Fields;
697
698    #[test]
699    fn test_get_field_utf8view_key() -> Result<()> {
700        // Create a struct array with fields "a" and "b"
701        let a_values = Int32Array::from(vec![Some(1), Some(2), Some(3)]);
702        let b_values = Int32Array::from(vec![Some(10), Some(20), Some(30)]);
703
704        let fields: Fields = vec![
705            Field::new("a", DataType::Int32, true),
706            Field::new("b", DataType::Int32, true),
707        ]
708        .into();
709
710        let struct_array = StructArray::new(
711            fields,
712            vec![
713                Arc::new(a_values) as ArrayRef,
714                Arc::new(b_values) as ArrayRef,
715            ],
716            None,
717        );
718
719        let base = ColumnarValue::Array(Arc::new(struct_array));
720
721        // Use Utf8View key to access field "a"
722        let key = ScalarValue::Utf8View(Some("a".to_string()));
723
724        let result = extract_single_field(base, key)?;
725
726        let result_array = result.into_array(3)?;
727        let expected = Int32Array::from(vec![Some(1), Some(2), Some(3)]);
728
729        assert_eq!(result_array.as_ref(), &expected as &dyn Array);
730
731        Ok(())
732    }
733
734    #[test]
735    fn test_get_field_dict_encoded_struct() -> Result<()> {
736        use arrow::array::{DictionaryArray, StringArray, UInt32Array};
737        use arrow::datatypes::UInt32Type;
738
739        let names = Arc::new(StringArray::from(vec!["main", "foo", "bar"])) as ArrayRef;
740        let ids = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
741
742        let struct_fields: Fields = vec![
743            Field::new("name", DataType::Utf8, false),
744            Field::new("id", DataType::Int32, false),
745        ]
746        .into();
747
748        let values_struct =
749            Arc::new(StructArray::new(struct_fields, vec![names, ids], None)) as ArrayRef;
750
751        let keys = UInt32Array::from(vec![0u32, 1, 2, 0, 1]);
752        let dict = DictionaryArray::<UInt32Type>::try_new(keys, values_struct)?;
753
754        let base = ColumnarValue::Array(Arc::new(dict));
755        let key = ScalarValue::Utf8(Some("name".to_string()));
756
757        let result = extract_single_field(base, key)?;
758        let result_array = result.into_array(5)?;
759
760        assert!(
761            matches!(result_array.data_type(), DataType::Dictionary(_, _)),
762            "expected dictionary output, got {:?}",
763            result_array.data_type()
764        );
765
766        let result_dict = result_array
767            .as_any()
768            .downcast_ref::<DictionaryArray<UInt32Type>>()
769            .unwrap();
770        assert_eq!(result_dict.values().len(), 3);
771        assert_eq!(result_dict.len(), 5);
772
773        let resolved = arrow::compute::cast(&result_array, &DataType::Utf8)?;
774        let string_arr = resolved.as_any().downcast_ref::<StringArray>().unwrap();
775        assert_eq!(string_arr.value(0), "main");
776        assert_eq!(string_arr.value(1), "foo");
777        assert_eq!(string_arr.value(2), "bar");
778        assert_eq!(string_arr.value(3), "main");
779        assert_eq!(string_arr.value(4), "foo");
780
781        Ok(())
782    }
783
784    #[test]
785    fn test_get_field_nested_dict_struct() -> Result<()> {
786        use arrow::array::{DictionaryArray, StringArray, UInt32Array};
787        use arrow::datatypes::UInt32Type;
788
789        let func_names = Arc::new(StringArray::from(vec!["main", "foo"])) as ArrayRef;
790        let func_files = Arc::new(StringArray::from(vec!["main.c", "foo.c"])) as ArrayRef;
791        let func_fields: Fields = vec![
792            Field::new("name", DataType::Utf8, false),
793            Field::new("file", DataType::Utf8, false),
794        ]
795        .into();
796        let func_struct = Arc::new(StructArray::new(
797            func_fields.clone(),
798            vec![func_names, func_files],
799            None,
800        )) as ArrayRef;
801        let func_dict = Arc::new(DictionaryArray::<UInt32Type>::try_new(
802            UInt32Array::from(vec![0u32, 1, 0]),
803            func_struct,
804        )?) as ArrayRef;
805
806        let line_nums = Arc::new(Int32Array::from(vec![10, 20, 30])) as ArrayRef;
807        let line_fields: Fields = vec![
808            Field::new("num", DataType::Int32, false),
809            Field::new(
810                "function",
811                DataType::Dictionary(
812                    Box::new(DataType::UInt32),
813                    Box::new(DataType::Struct(func_fields)),
814                ),
815                false,
816            ),
817        ]
818        .into();
819        let line_struct = StructArray::new(line_fields, vec![line_nums, func_dict], None);
820
821        let base = ColumnarValue::Array(Arc::new(line_struct));
822
823        let func_result =
824            extract_single_field(base, ScalarValue::Utf8(Some("function".to_string())))?;
825
826        let func_array = func_result.into_array(3)?;
827        assert!(
828            matches!(func_array.data_type(), DataType::Dictionary(_, _)),
829            "expected dictionary for function, got {:?}",
830            func_array.data_type()
831        );
832
833        let name_result = extract_single_field(
834            ColumnarValue::Array(func_array),
835            ScalarValue::Utf8(Some("name".to_string())),
836        )?;
837        let name_array = name_result.into_array(3)?;
838
839        assert!(
840            matches!(name_array.data_type(), DataType::Dictionary(_, _)),
841            "expected dictionary for name, got {:?}",
842            name_array.data_type()
843        );
844
845        let name_dict = name_array
846            .as_any()
847            .downcast_ref::<DictionaryArray<UInt32Type>>()
848            .unwrap();
849        assert_eq!(name_dict.values().len(), 2);
850        assert_eq!(name_dict.len(), 3);
851
852        let resolved = arrow::compute::cast(&name_array, &DataType::Utf8)?;
853        let strings = resolved.as_any().downcast_ref::<StringArray>().unwrap();
854        assert_eq!(strings.value(0), "main");
855        assert_eq!(strings.value(1), "foo");
856        assert_eq!(strings.value(2), "main");
857
858        Ok(())
859    }
860
861    #[test]
862    fn test_placement_literal_key() {
863        let func = GetFieldFunc::new();
864
865        // get_field(col, 'literal') -> leaf-pushable (static field access)
866        let args = vec![ExpressionPlacement::Column, ExpressionPlacement::Literal];
867        assert_eq!(
868            func.placement(&args),
869            ExpressionPlacement::MoveTowardsLeafNodes
870        );
871
872        // get_field(col, 'a', 'b') -> leaf-pushable (nested static field access)
873        let args = vec![
874            ExpressionPlacement::Column,
875            ExpressionPlacement::Literal,
876            ExpressionPlacement::Literal,
877        ];
878        assert_eq!(
879            func.placement(&args),
880            ExpressionPlacement::MoveTowardsLeafNodes
881        );
882
883        // get_field(get_field(col, 'a'), 'b') represented as MoveTowardsLeafNodes for base
884        let args = vec![
885            ExpressionPlacement::MoveTowardsLeafNodes,
886            ExpressionPlacement::Literal,
887        ];
888        assert_eq!(
889            func.placement(&args),
890            ExpressionPlacement::MoveTowardsLeafNodes
891        );
892    }
893
894    #[test]
895    fn test_placement_column_key() {
896        let func = GetFieldFunc::new();
897
898        // get_field(col, other_col) -> NOT leaf-pushable (dynamic per-row lookup)
899        let args = vec![ExpressionPlacement::Column, ExpressionPlacement::Column];
900        assert_eq!(func.placement(&args), ExpressionPlacement::KeepInPlace);
901
902        // get_field(col, 'a', other_col) -> NOT leaf-pushable (dynamic nested lookup)
903        let args = vec![
904            ExpressionPlacement::Column,
905            ExpressionPlacement::Literal,
906            ExpressionPlacement::Column,
907        ];
908        assert_eq!(func.placement(&args), ExpressionPlacement::KeepInPlace);
909    }
910
911    #[test]
912    fn test_placement_root() {
913        let func = GetFieldFunc::new();
914
915        // get_field(root_expr, 'literal') -> NOT leaf-pushable
916        let args = vec![
917            ExpressionPlacement::KeepInPlace,
918            ExpressionPlacement::Literal,
919        ];
920        assert_eq!(func.placement(&args), ExpressionPlacement::KeepInPlace);
921
922        // get_field(col, root_expr) -> NOT leaf-pushable
923        let args = vec![
924            ExpressionPlacement::Column,
925            ExpressionPlacement::KeepInPlace,
926        ];
927        assert_eq!(func.placement(&args), ExpressionPlacement::KeepInPlace);
928    }
929
930    #[test]
931    fn test_placement_edge_cases() {
932        let func = GetFieldFunc::new();
933
934        // Empty args -> NOT leaf-pushable
935        assert_eq!(func.placement(&[]), ExpressionPlacement::KeepInPlace);
936
937        // Just base, no key -> MoveTowardsLeafNodes (not a valid call but should handle gracefully)
938        let args = vec![ExpressionPlacement::Column];
939        assert_eq!(
940            func.placement(&args),
941            ExpressionPlacement::MoveTowardsLeafNodes
942        );
943
944        // Literal base with literal key -> NOT leaf-pushable (would be constant-folded)
945        let args = vec![ExpressionPlacement::Literal, ExpressionPlacement::Literal];
946        assert_eq!(func.placement(&args), ExpressionPlacement::KeepInPlace);
947    }
948
949    // --- get_field over struct constructor simplification --------------------
950
951    use datafusion_common::Column;
952    use datafusion_expr::simplify::SimplifyContext;
953
954    /// A non-empty string literal expression.
955    fn lit_str(s: &str) -> Expr {
956        Expr::Literal(ScalarValue::Utf8(Some(s.to_string())), None)
957    }
958
959    /// A column reference expression.
960    fn col(name: &str) -> Expr {
961        Expr::Column(Column::from_name(name))
962    }
963
964    fn scalar_fn(udf: ScalarUDF, args: Vec<Expr>) -> Expr {
965        Expr::ScalarFunction(ScalarFunction::new_udf(Arc::new(udf), args))
966    }
967
968    /// `named_struct(name1, value1, name2, value2, ...)`.
969    fn named_struct(pairs: Vec<(&str, Expr)>) -> Expr {
970        let args = pairs
971            .into_iter()
972            .flat_map(|(name, value)| [lit_str(name), value])
973            .collect();
974        scalar_fn(ScalarUDF::new_from_impl(NamedStructFunc::new()), args)
975    }
976
977    /// `struct(value0, value1, ...)`.
978    fn struct_fn(values: Vec<Expr>) -> Expr {
979        scalar_fn(ScalarUDF::new_from_impl(StructFunc::new()), values)
980    }
981
982    /// `get_field(args...)`.
983    fn get_field(args: Vec<Expr>) -> Expr {
984        scalar_fn(ScalarUDF::new_from_impl(GetFieldFunc::new()), args)
985    }
986
987    /// Run `GetFieldFunc::simplify` once and return the rewritten expression,
988    /// panicking if the input was left unchanged.
989    fn simplified(args: Vec<Expr>) -> Expr {
990        match GetFieldFunc::new()
991            .simplify(args, &SimplifyContext::default())
992            .unwrap()
993        {
994            ExprSimplifyResult::Simplified(expr) => expr,
995            ExprSimplifyResult::Original(args) => {
996                panic!("expected the expression to be simplified, got {args:?}")
997            }
998        }
999    }
1000
1001    /// Assert that `GetFieldFunc::simplify` leaves the arguments unchanged.
1002    fn assert_not_simplified(args: Vec<Expr>) {
1003        match GetFieldFunc::new()
1004            .simplify(args.clone(), &SimplifyContext::default())
1005            .unwrap()
1006        {
1007            ExprSimplifyResult::Original(unchanged) => assert_eq!(unchanged, args),
1008            ExprSimplifyResult::Simplified(expr) => {
1009                panic!("expected no simplification, got {expr:?}")
1010            }
1011        }
1012    }
1013
1014    #[test]
1015    fn simplify_get_field_named_struct_returns_matching_value() {
1016        // get_field(named_struct('min', a, 'max', b), 'max') => b
1017        let args = vec![
1018            named_struct(vec![("min", col("a")), ("max", col("b"))]),
1019            lit_str("max"),
1020        ];
1021        assert_eq!(simplified(args), col("b"));
1022    }
1023
1024    #[test]
1025    fn simplify_get_field_named_struct_first_field() {
1026        // get_field(named_struct('min', a, 'max', b), 'min') => a
1027        let args = vec![
1028            named_struct(vec![("min", col("a")), ("max", col("b"))]),
1029            lit_str("min"),
1030        ];
1031        assert_eq!(simplified(args), col("a"));
1032    }
1033
1034    #[test]
1035    fn simplify_get_field_named_struct_duplicate_names_picks_first() {
1036        // Arrow's `column_by_name` resolves to the first match; mirror that.
1037        let args = vec![
1038            named_struct(vec![("k", col("a")), ("k", col("b"))]),
1039            lit_str("k"),
1040        ];
1041        assert_eq!(simplified(args), col("a"));
1042    }
1043
1044    #[test]
1045    fn simplify_get_field_struct_positional() {
1046        // get_field(struct(a, b), 'c1') => b
1047        let args = vec![struct_fn(vec![col("a"), col("b")]), lit_str("c1")];
1048        assert_eq!(simplified(args), col("b"));
1049    }
1050
1051    #[test]
1052    fn simplify_get_field_nested_named_struct() {
1053        // get_field(named_struct('s', named_struct('k', x)), 's', 'k')
1054        //   => get_field(named_struct('k', x), 'k')   (first pass)
1055        //   => x                                      (second pass)
1056        let args = vec![
1057            named_struct(vec![("s", named_struct(vec![("k", col("x"))]))]),
1058            lit_str("s"),
1059            lit_str("k"),
1060        ];
1061        let first_pass = simplified(args);
1062        let Expr::ScalarFunction(ScalarFunction { args, .. }) = first_pass else {
1063            panic!("expected a get_field call after the first pass")
1064        };
1065        assert_eq!(simplified(args), col("x"));
1066    }
1067
1068    #[test]
1069    fn simplify_get_field_flattens_then_resolves_named_struct() {
1070        // get_field(get_field(named_struct('s', named_struct('k', x)), 's'), 'k')
1071        // flattens to get_field(named_struct(...), 's', 'k') and resolves 's'.
1072        let args = vec![
1073            get_field(vec![
1074                named_struct(vec![("s", named_struct(vec![("k", col("x"))]))]),
1075                lit_str("s"),
1076            ]),
1077            lit_str("k"),
1078        ];
1079        let expected = get_field(vec![named_struct(vec![("k", col("x"))]), lit_str("k")]);
1080        assert_eq!(simplified(args), expected);
1081    }
1082
1083    #[test]
1084    fn simplify_get_field_dynamic_field_name_left_alone() {
1085        // A non-literal field name cannot be resolved at plan time.
1086        let args = vec![named_struct(vec![("a", col("x"))]), col("field_name")];
1087        assert_not_simplified(args);
1088    }
1089
1090    #[test]
1091    fn simplify_get_field_null_field_name_left_alone() {
1092        // A NULL string literal field name resolves to no field, so the
1093        // `try_as_str().flatten()` guard must leave the expression untouched.
1094        let null_field_name = Expr::Literal(ScalarValue::Utf8(None), None);
1095        let args = vec![named_struct(vec![("a", col("x"))]), null_field_name];
1096        assert_not_simplified(args);
1097    }
1098
1099    #[test]
1100    fn simplify_get_field_dynamic_struct_name_left_alone() {
1101        // A non-literal name inside named_struct could shadow the requested
1102        // field at runtime, so the rewrite must bail out entirely.
1103        let named_struct_with_dynamic_name = scalar_fn(
1104            ScalarUDF::new_from_impl(NamedStructFunc::new()),
1105            vec![col("dynamic_name"), col("x")],
1106        );
1107        let args = vec![named_struct_with_dynamic_name, lit_str("a")];
1108        assert_not_simplified(args);
1109    }
1110
1111    #[test]
1112    fn simplify_get_field_missing_field_left_alone() {
1113        // The named_struct does not produce field 'missing'.
1114        let args = vec![named_struct(vec![("a", col("x"))]), lit_str("missing")];
1115        assert_not_simplified(args);
1116    }
1117
1118    #[test]
1119    fn simplify_get_field_non_canonical_struct_field_left_alone() {
1120        // 'c01' is not a real field name produced by `struct(...)`.
1121        let args = vec![struct_fn(vec![col("a"), col("b")]), lit_str("c01")];
1122        assert_not_simplified(args);
1123    }
1124
1125    #[test]
1126    fn simplify_get_field_column_base_left_alone() {
1127        // A plain column base is not a struct constructor.
1128        let args = vec![col("s"), lit_str("a")];
1129        assert_not_simplified(args);
1130    }
1131}