Skip to main content

datafusion_expr/logical_plan/
plan.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
18//! Logical plan types
19
20use std::cmp::Ordering;
21use std::collections::{HashMap, HashSet};
22use std::fmt::{self, Debug, Display, Formatter};
23use std::hash::{Hash, Hasher};
24use std::sync::{Arc, LazyLock};
25
26use super::DdlStatement;
27use super::dml::CopyTo;
28use super::invariants::{
29    InvariantLevel, assert_always_invariants_at_current_node,
30    assert_executable_invariants,
31};
32use crate::builder::{unique_field_aliases, unnest_with_options};
33use crate::expr::{
34    Alias, Placeholder, Sort as SortExpr, WindowFunction, WindowFunctionParams,
35    intersect_metadata_for_union,
36};
37use crate::expr_rewriter::{
38    NamePreserver, create_col_from_scalar_expr, normalize_cols, normalize_sorts,
39};
40use crate::logical_plan::display::{GraphvizVisitor, IndentVisitor};
41use crate::logical_plan::extension::UserDefinedLogicalNode;
42use crate::logical_plan::{DmlStatement, Statement};
43use crate::utils::{
44    enumerate_grouping_sets, exprlist_to_fields, find_out_reference_exprs,
45    grouping_set_expr_count, grouping_set_to_exprlist, merge_schema, split_conjunction,
46};
47use crate::{
48    BinaryExpr, CreateMemoryTable, CreateView, Execute, Expr, ExprSchemable, GroupingSet,
49    LogicalPlanBuilder, Operator, Prepare, TableProviderFilterPushDown, TableSource,
50    WindowFunctionDefinition, build_join_schema, expr_vec_fmt, requalify_sides_if_needed,
51};
52
53use arrow::datatypes::{DataType, Field, FieldRef, Schema, SchemaRef};
54use datafusion_common::cse::{NormalizeEq, Normalizeable};
55use datafusion_common::format::ExplainFormat;
56use datafusion_common::metadata::check_metadata_with_storage_equal;
57use datafusion_common::tree_node::{
58    Transformed, TreeNode, TreeNodeContainer, TreeNodeRecursion,
59};
60use datafusion_common::{
61    Column, Constraints, DFSchema, DFSchemaRef, DataFusionError, Dependency,
62    FunctionalDependence, FunctionalDependencies, NullEquality, ParamValues, Result,
63    ScalarValue, Spans, TableReference, UnnestOptions, aggregate_functional_dependencies,
64    assert_eq_or_internal_err, assert_or_internal_err, internal_err, plan_err,
65};
66use indexmap::IndexSet;
67
68// backwards compatibility
69use crate::display::PgJsonVisitor;
70pub use datafusion_common::display::{PlanType, StringifiedPlan, ToStringifiedPlan};
71pub use datafusion_common::{JoinConstraint, JoinType};
72
73/// A `LogicalPlan` is a node in a tree of relational operators (such as
74/// Projection or Filter).
75///
76/// Represents transforming an input relation (table) to an output relation
77/// (table) with a potentially different schema. Plans form a dataflow tree
78/// where data flows from leaves up to the root to produce the query result.
79///
80/// `LogicalPlan`s can be created by the SQL query planner, the DataFrame API,
81/// or programmatically (for example custom query languages).
82///
83/// # See also:
84/// * [`Expr`]: For the expressions that are evaluated by the plan
85/// * [`LogicalPlanBuilder`]: For building `LogicalPlan`s
86/// * [`tree_node`]: To inspect and rewrite `LogicalPlan`s
87///
88/// [`tree_node`]: crate::logical_plan::tree_node
89///
90/// # Examples
91///
92/// ## Creating a LogicalPlan from SQL:
93///
94/// See [`SessionContext::sql`](https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.sql)
95///
96/// ## Creating a LogicalPlan from the DataFrame API:
97///
98/// See [`DataFrame::logical_plan`](https://docs.rs/datafusion/latest/datafusion/dataframe/struct.DataFrame.html#method.logical_plan)
99///
100/// ## Creating a LogicalPlan programmatically:
101///
102/// See [`LogicalPlanBuilder`]
103///
104/// # Visiting and Rewriting `LogicalPlan`s
105///
106/// Using the [`tree_node`] API, you can recursively walk all nodes in a
107/// `LogicalPlan`. For example, to find all column references in a plan:
108///
109/// ```
110/// # use std::collections::HashSet;
111/// # use arrow::datatypes::{DataType, Field, Schema};
112/// # use datafusion_expr::{Expr, col, lit, LogicalPlan, LogicalPlanBuilder, table_scan};
113/// # use datafusion_common::tree_node::{TreeNodeRecursion, TreeNode};
114/// # use datafusion_common::{Column, Result};
115/// # fn employee_schema() -> Schema {
116/// #    Schema::new(vec![
117/// #           Field::new("name", DataType::Utf8, false),
118/// #           Field::new("salary", DataType::Int32, false),
119/// #       ])
120/// #   }
121/// // Projection(name, salary)
122/// //   Filter(salary > 1000)
123/// //     TableScan(employee)
124/// # fn main() -> Result<()> {
125/// let plan = table_scan(Some("employee"), &employee_schema(), None)?
126///  .filter(col("salary").gt(lit(1000)))?
127///  .project(vec![col("name")])?
128///  .build()?;
129///
130/// // use apply to walk the plan and collect all expressions
131/// let mut expressions = HashSet::new();
132/// plan.apply(|node| {
133///   // collect all expressions in the plan
134///   node.apply_expressions(|expr| {
135///    expressions.insert(expr.clone());
136///    Ok(TreeNodeRecursion::Continue) // control walk of expressions
137///   })?;
138///   Ok(TreeNodeRecursion::Continue) // control walk of plan nodes
139/// }).unwrap();
140///
141/// // we found the expression in projection and filter
142/// assert_eq!(expressions.len(), 2);
143/// println!("Found expressions: {:?}", expressions);
144/// // found predicate in the Filter: employee.salary > 1000
145/// let salary = Expr::Column(Column::new(Some("employee"), "salary"));
146/// assert!(expressions.contains(&salary.gt(lit(1000))));
147/// // found projection in the Projection: employee.name
148/// let name = Expr::Column(Column::new(Some("employee"), "name"));
149/// assert!(expressions.contains(&name));
150/// # Ok(())
151/// # }
152/// ```
153///
154/// You can also rewrite plans using the [`tree_node`] API. For example, to
155/// replace the filter predicate in a plan:
156///
157/// ```
158/// # use std::collections::HashSet;
159/// # use arrow::datatypes::{DataType, Field, Schema};
160/// # use datafusion_expr::{Expr, col, lit, LogicalPlan, LogicalPlanBuilder, table_scan};
161/// # use datafusion_common::tree_node::{TreeNodeRecursion, TreeNode};
162/// # use datafusion_common::{Column, Result};
163/// # fn employee_schema() -> Schema {
164/// #    Schema::new(vec![
165/// #           Field::new("name", DataType::Utf8, false),
166/// #           Field::new("salary", DataType::Int32, false),
167/// #       ])
168/// #   }
169/// // Projection(name, salary)
170/// //   Filter(salary > 1000)
171/// //     TableScan(employee)
172/// # fn main() -> Result<()> {
173/// use datafusion_common::tree_node::Transformed;
174/// let plan = table_scan(Some("employee"), &employee_schema(), None)?
175///  .filter(col("salary").gt(lit(1000)))?
176///  .project(vec![col("name")])?
177///  .build()?;
178///
179/// // use transform to rewrite the plan
180/// let transformed_result = plan.transform(|node| {
181///   // when we see the filter node
182///   if let LogicalPlan::Filter(mut filter) = node {
183///     // replace predicate with salary < 2000
184///     filter.predicate = Expr::Column(Column::new(Some("employee"), "salary")).lt(lit(2000));
185///     let new_plan = LogicalPlan::Filter(filter);
186///     return Ok(Transformed::yes(new_plan)); // communicate the node was changed
187///   }
188///   // return the node unchanged
189///   Ok(Transformed::no(node))
190/// }).unwrap();
191///
192/// // Transformed result contains rewritten plan and information about
193/// // whether the plan was changed
194/// assert!(transformed_result.transformed);
195/// let rewritten_plan = transformed_result.data;
196///
197/// // we found the filter
198/// assert_eq!(rewritten_plan.display_indent().to_string(),
199/// "Projection: employee.name\
200/// \n  Filter: employee.salary < Int32(2000)\
201/// \n    TableScan: employee");
202/// # Ok(())
203/// # }
204/// ```
205#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
206pub enum LogicalPlan {
207    /// Evaluates an arbitrary list of expressions (essentially a
208    /// SELECT with an expression list) on its input.
209    Projection(Projection),
210    /// Filters rows from its input that do not match an
211    /// expression (essentially a WHERE clause with a predicate
212    /// expression).
213    ///
214    /// Semantically, `<predicate>` is evaluated for each row of the
215    /// input; If the value of `<predicate>` is true, the input row is
216    /// passed to the output. If the value of `<predicate>` is false
217    /// (or null), the row is discarded.
218    Filter(Filter),
219    /// Windows input based on a set of window spec and window
220    /// function (e.g. SUM or RANK).  This is used to implement SQL
221    /// window functions, and the `OVER` clause.
222    ///
223    /// See [`Window`] for more details
224    Window(Window),
225    /// Aggregates its input based on a set of grouping and aggregate
226    /// expressions (e.g. SUM). This is used to implement SQL aggregates
227    /// and `GROUP BY`.
228    ///
229    /// See [`Aggregate`] for more details
230    Aggregate(Aggregate),
231    /// Sorts its input according to a list of sort expressions. This
232    /// is used to implement SQL `ORDER BY`
233    Sort(Sort),
234    /// Join two logical plans on one or more join columns.
235    /// This is used to implement SQL `JOIN`
236    Join(Join),
237    /// Repartitions the input based on a partitioning scheme. This is
238    /// used to add parallelism and is sometimes referred to as an
239    /// "exchange" operator in other systems
240    Repartition(Repartition),
241    /// Union multiple inputs with the same schema into a single
242    /// output stream. This is used to implement SQL `UNION [ALL]` and
243    /// `INTERSECT [ALL]`.
244    Union(Union),
245    /// Produces rows from a [`TableSource`], used to implement SQL
246    /// `FROM` tables or views.
247    TableScan(TableScan),
248    /// Produces no rows: An empty relation with an empty schema that
249    /// produces 0 or 1 row. This is used to implement SQL `SELECT`
250    /// that has no values in the `FROM` clause.
251    EmptyRelation(EmptyRelation),
252    /// Produces the output of running another query.  This is used to
253    /// implement SQL subqueries
254    Subquery(Subquery),
255    /// Aliased relation provides, or changes, the name of a relation.
256    SubqueryAlias(SubqueryAlias),
257    /// Skip some number of rows, and then fetch some number of rows.
258    Limit(Limit),
259    /// A DataFusion [`Statement`] such as `SET VARIABLE` or `START TRANSACTION`
260    Statement(Statement),
261    /// Values expression. See
262    /// [Postgres VALUES](https://www.postgresql.org/docs/current/queries-values.html)
263    /// documentation for more details. This is used to implement SQL such as
264    /// `VALUES (1, 2), (3, 4)`
265    Values(Values),
266    /// Produces a relation with string representations of
267    /// various parts of the plan. This is used to implement SQL `EXPLAIN`.
268    Explain(Explain),
269    /// Runs the input, and prints annotated physical plan as a string
270    /// with execution metric. This is used to implement SQL
271    /// `EXPLAIN ANALYZE`.
272    Analyze(Analyze),
273    /// Extension operator defined outside of DataFusion. This is used
274    /// to extend DataFusion with custom relational operations that
275    Extension(Extension),
276    /// Remove duplicate rows from the input. This is used to
277    /// implement SQL `SELECT DISTINCT ...`.
278    Distinct(Distinct),
279    /// Data Manipulation Language (DML): Insert / Update / Delete
280    Dml(DmlStatement),
281    /// Data Definition Language (DDL): CREATE / DROP TABLES / VIEWS / SCHEMAS
282    Ddl(DdlStatement),
283    /// `COPY TO` for writing plan results to files
284    Copy(CopyTo),
285    /// Describe the schema of the table. This is used to implement the
286    /// SQL `DESCRIBE` command from MySQL.
287    DescribeTable(DescribeTable),
288    /// Unnest a column that contains a nested list type such as an
289    /// ARRAY. This is used to implement SQL `UNNEST`
290    Unnest(Unnest),
291    /// A variadic query (e.g. "Recursive CTEs")
292    RecursiveQuery(RecursiveQuery),
293}
294
295impl Default for LogicalPlan {
296    fn default() -> Self {
297        // `Default` is used as a transient placeholder on hot paths (e.g.
298        // `Box`/`Arc` `map_elements`), so use a shared empty schema to avoid
299        // allocating.
300        LogicalPlan::EmptyRelation(EmptyRelation {
301            produce_one_row: false,
302            schema: Arc::clone(DFSchema::empty_ref()),
303        })
304    }
305}
306
307impl<'a> TreeNodeContainer<'a, Self> for LogicalPlan {
308    fn apply_elements<F: FnMut(&'a Self) -> Result<TreeNodeRecursion>>(
309        &'a self,
310        mut f: F,
311    ) -> Result<TreeNodeRecursion> {
312        f(self)
313    }
314
315    fn map_elements<F: FnMut(Self) -> Result<Transformed<Self>>>(
316        self,
317        mut f: F,
318    ) -> Result<Transformed<Self>> {
319        f(self)
320    }
321}
322
323impl LogicalPlan {
324    /// Get a reference to the logical plan's schema
325    pub fn schema(&self) -> &DFSchemaRef {
326        match self {
327            LogicalPlan::EmptyRelation(EmptyRelation { schema, .. }) => schema,
328            LogicalPlan::Values(Values { schema, .. }) => schema,
329            LogicalPlan::TableScan(TableScan {
330                projected_schema, ..
331            }) => projected_schema,
332            LogicalPlan::Projection(Projection { schema, .. }) => schema,
333            LogicalPlan::Filter(Filter { input, .. }) => input.schema(),
334            LogicalPlan::Distinct(Distinct::All(input)) => input.schema(),
335            LogicalPlan::Distinct(Distinct::On(DistinctOn { schema, .. })) => schema,
336            LogicalPlan::Window(Window { schema, .. }) => schema,
337            LogicalPlan::Aggregate(Aggregate { schema, .. }) => schema,
338            LogicalPlan::Sort(Sort { input, .. }) => input.schema(),
339            LogicalPlan::Join(Join { schema, .. }) => schema,
340            LogicalPlan::Repartition(Repartition { input, .. }) => input.schema(),
341            LogicalPlan::Limit(Limit { input, .. }) => input.schema(),
342            LogicalPlan::Statement(statement) => statement.schema(),
343            LogicalPlan::Subquery(Subquery { subquery, .. }) => subquery.schema(),
344            LogicalPlan::SubqueryAlias(SubqueryAlias { schema, .. }) => schema,
345            LogicalPlan::Explain(explain) => &explain.schema,
346            LogicalPlan::Analyze(analyze) => &analyze.schema,
347            LogicalPlan::Extension(extension) => extension.node.schema(),
348            LogicalPlan::Union(Union { schema, .. }) => schema,
349            LogicalPlan::DescribeTable(DescribeTable { output_schema, .. }) => {
350                output_schema
351            }
352            LogicalPlan::Dml(DmlStatement { output_schema, .. }) => output_schema,
353            LogicalPlan::Copy(CopyTo { output_schema, .. }) => output_schema,
354            LogicalPlan::Ddl(ddl) => ddl.schema(),
355            LogicalPlan::Unnest(Unnest { schema, .. }) => schema,
356            LogicalPlan::RecursiveQuery(RecursiveQuery { schema, .. }) => schema,
357        }
358    }
359
360    /// Used for normalizing columns, as the fallback schemas to the main schema
361    /// of the plan.
362    pub fn fallback_normalize_schemas(&self) -> Vec<&DFSchema> {
363        match self {
364            LogicalPlan::Window(_)
365            | LogicalPlan::Projection(_)
366            | LogicalPlan::Aggregate(_)
367            | LogicalPlan::Unnest(_)
368            | LogicalPlan::Join(_) => self
369                .inputs()
370                .iter()
371                .map(|input| input.schema().as_ref())
372                .collect(),
373            _ => vec![],
374        }
375    }
376
377    /// Returns the (fixed) output schema for explain plans
378    pub fn explain_schema() -> SchemaRef {
379        SchemaRef::new(Schema::new(vec![
380            Field::new("plan_type", DataType::Utf8, false),
381            Field::new("plan", DataType::Utf8, false),
382        ]))
383    }
384
385    /// Returns the (fixed) output schema for `DESCRIBE` plans
386    pub fn describe_schema() -> Schema {
387        Schema::new(vec![
388            Field::new("column_name", DataType::Utf8, false),
389            Field::new("data_type", DataType::Utf8, false),
390            Field::new("is_nullable", DataType::Utf8, false),
391        ])
392    }
393
394    /// Returns all expressions (non-recursively) evaluated by the current
395    /// logical plan node. This does not include expressions in any children.
396    ///
397    /// Note this method `clone`s all the expressions. When possible, the
398    /// [`tree_node`] API should be used instead of this API.
399    ///
400    /// The returned expressions do not necessarily represent or even
401    /// contributed to the output schema of this node. For example,
402    /// `LogicalPlan::Filter` returns the filter expression even though the
403    /// output of a Filter has the same columns as the input.
404    ///
405    /// The expressions do contain all the columns that are used by this plan,
406    /// so if there are columns not referenced by these expressions then
407    /// DataFusion's optimizer attempts to optimize them away.
408    ///
409    /// [`tree_node`]: crate::logical_plan::tree_node
410    pub fn expressions(self: &LogicalPlan) -> Vec<Expr> {
411        let mut exprs = vec![];
412        self.apply_expressions(|e| {
413            exprs.push(e.clone());
414            Ok(TreeNodeRecursion::Continue)
415        })
416        // closure always returns OK
417        .unwrap();
418        exprs
419    }
420
421    /// Returns all the out reference(correlated) expressions (recursively) in the current
422    /// logical plan nodes and all its descendant nodes.
423    pub fn all_out_ref_exprs(self: &LogicalPlan) -> Vec<Expr> {
424        let mut exprs = vec![];
425        self.apply_expressions(|e| {
426            find_out_reference_exprs(e).into_iter().for_each(|e| {
427                if !exprs.contains(&e) {
428                    exprs.push(e)
429                }
430            });
431            Ok(TreeNodeRecursion::Continue)
432        })
433        // closure always returns OK
434        .unwrap();
435        self.inputs()
436            .into_iter()
437            .flat_map(|child| child.all_out_ref_exprs())
438            .for_each(|e| {
439                if !exprs.contains(&e) {
440                    exprs.push(e)
441                }
442            });
443        exprs
444    }
445
446    /// Returns all inputs / children of this `LogicalPlan` node.
447    ///
448    /// Note does not include inputs to inputs, or subqueries.
449    pub fn inputs(&self) -> Vec<&LogicalPlan> {
450        match self {
451            LogicalPlan::Projection(Projection { input, .. }) => vec![input],
452            LogicalPlan::Filter(Filter { input, .. }) => vec![input],
453            LogicalPlan::Repartition(Repartition { input, .. }) => vec![input],
454            LogicalPlan::Window(Window { input, .. }) => vec![input],
455            LogicalPlan::Aggregate(Aggregate { input, .. }) => vec![input],
456            LogicalPlan::Sort(Sort { input, .. }) => vec![input],
457            LogicalPlan::Join(Join { left, right, .. }) => vec![left, right],
458            LogicalPlan::Limit(Limit { input, .. }) => vec![input],
459            LogicalPlan::Subquery(Subquery { subquery, .. }) => vec![subquery],
460            LogicalPlan::SubqueryAlias(SubqueryAlias { input, .. }) => vec![input],
461            LogicalPlan::Extension(extension) => extension.node.inputs(),
462            LogicalPlan::Union(Union { inputs, .. }) => {
463                inputs.iter().map(|arc| arc.as_ref()).collect()
464            }
465            LogicalPlan::Distinct(
466                Distinct::All(input) | Distinct::On(DistinctOn { input, .. }),
467            ) => vec![input],
468            LogicalPlan::Explain(explain) => vec![&explain.plan],
469            LogicalPlan::Analyze(analyze) => vec![&analyze.input],
470            LogicalPlan::Dml(write) => vec![&write.input],
471            LogicalPlan::Copy(copy) => vec![&copy.input],
472            LogicalPlan::Ddl(ddl) => ddl.inputs(),
473            LogicalPlan::Unnest(Unnest { input, .. }) => vec![input],
474            LogicalPlan::RecursiveQuery(RecursiveQuery {
475                static_term,
476                recursive_term,
477                ..
478            }) => vec![static_term, recursive_term],
479            LogicalPlan::Statement(stmt) => stmt.inputs(),
480            // plans without inputs
481            LogicalPlan::TableScan { .. }
482            | LogicalPlan::EmptyRelation { .. }
483            | LogicalPlan::Values { .. }
484            | LogicalPlan::DescribeTable(_) => vec![],
485        }
486    }
487
488    /// returns all `Using` join columns in a logical plan
489    pub fn using_columns(&self) -> Result<Vec<HashSet<Column>>, DataFusionError> {
490        let mut using_columns: Vec<HashSet<Column>> = vec![];
491
492        self.apply_with_subqueries(|plan| {
493            if let LogicalPlan::Join(Join {
494                join_constraint: JoinConstraint::Using,
495                on,
496                ..
497            }) = plan
498            {
499                // The join keys in using-join must be columns.
500                let columns =
501                    on.iter().try_fold(HashSet::new(), |mut accumu, (l, r)| {
502                        let Some(l) = l.get_as_join_column() else {
503                            return internal_err!(
504                                "Invalid join key. Expected column, found {l:?}"
505                            );
506                        };
507                        let Some(r) = r.get_as_join_column() else {
508                            return internal_err!(
509                                "Invalid join key. Expected column, found {r:?}"
510                            );
511                        };
512                        accumu.insert(l.to_owned());
513                        accumu.insert(r.to_owned());
514                        Result::<_, DataFusionError>::Ok(accumu)
515                    })?;
516                using_columns.push(columns);
517            }
518            Ok(TreeNodeRecursion::Continue)
519        })?;
520
521        Ok(using_columns)
522    }
523
524    /// returns the first output expression of this `LogicalPlan` node.
525    pub fn head_output_expr(&self) -> Result<Option<Expr>> {
526        match self {
527            LogicalPlan::Projection(projection) => {
528                Ok(Some(projection.expr.as_slice()[0].clone()))
529            }
530            LogicalPlan::Aggregate(agg) => {
531                if agg.group_expr.is_empty() {
532                    Ok(Some(agg.aggr_expr.as_slice()[0].clone()))
533                } else {
534                    Ok(Some(agg.group_expr.as_slice()[0].clone()))
535                }
536            }
537            LogicalPlan::Distinct(Distinct::On(DistinctOn { select_expr, .. })) => {
538                Ok(Some(select_expr[0].clone()))
539            }
540            LogicalPlan::Filter(Filter { input, .. })
541            | LogicalPlan::Distinct(Distinct::All(input))
542            | LogicalPlan::Sort(Sort { input, .. })
543            | LogicalPlan::Limit(Limit { input, .. })
544            | LogicalPlan::Repartition(Repartition { input, .. })
545            | LogicalPlan::Window(Window { input, .. }) => input.head_output_expr(),
546            LogicalPlan::Join(Join {
547                left,
548                right,
549                join_type,
550                ..
551            }) => match join_type {
552                JoinType::Inner | JoinType::Left | JoinType::Right | JoinType::Full => {
553                    if left.schema().fields().is_empty() {
554                        right.head_output_expr()
555                    } else {
556                        left.head_output_expr()
557                    }
558                }
559                JoinType::LeftSemi | JoinType::LeftAnti | JoinType::LeftMark => {
560                    left.head_output_expr()
561                }
562                JoinType::RightSemi | JoinType::RightAnti | JoinType::RightMark => {
563                    right.head_output_expr()
564                }
565            },
566            LogicalPlan::RecursiveQuery(RecursiveQuery { static_term, .. }) => {
567                static_term.head_output_expr()
568            }
569            LogicalPlan::Union(union) => Ok(Some(Expr::Column(Column::from(
570                union.schema.qualified_field(0),
571            )))),
572            LogicalPlan::TableScan(table) => Ok(Some(Expr::Column(Column::from(
573                table.projected_schema.qualified_field(0),
574            )))),
575            LogicalPlan::SubqueryAlias(subquery_alias) => {
576                let expr_opt = subquery_alias.input.head_output_expr()?;
577                expr_opt
578                    .map(|expr| {
579                        Ok(Expr::Column(create_col_from_scalar_expr(
580                            &expr,
581                            subquery_alias.alias.to_string(),
582                        )?))
583                    })
584                    .map_or(Ok(None), |v| v.map(Some))
585            }
586            LogicalPlan::Subquery(_) => Ok(None),
587            LogicalPlan::EmptyRelation(_)
588            | LogicalPlan::Statement(_)
589            | LogicalPlan::Values(_)
590            | LogicalPlan::Explain(_)
591            | LogicalPlan::Analyze(_)
592            | LogicalPlan::Extension(_)
593            | LogicalPlan::Dml(_)
594            | LogicalPlan::Copy(_)
595            | LogicalPlan::Ddl(_)
596            | LogicalPlan::DescribeTable(_)
597            | LogicalPlan::Unnest(_) => Ok(None),
598        }
599    }
600
601    /// Recomputes schema and type information for this LogicalPlan if needed.
602    ///
603    /// Some `LogicalPlan`s may need to recompute their schema if the number or
604    /// type of expressions have been changed (for example due to type
605    /// coercion). For example [`LogicalPlan::Projection`]s schema depends on
606    /// its expressions.
607    ///
608    /// Some `LogicalPlan`s schema is unaffected by any changes to their
609    /// expressions. For example [`LogicalPlan::Filter`] schema is always the
610    /// same as its input schema.
611    ///
612    /// This is useful after modifying a plans `Expr`s (or input plans) via
613    /// methods such as [Self::map_children] and [Self::map_expressions]. Unlike
614    /// [Self::with_new_exprs], this method does not require a new set of
615    /// expressions or inputs plans.
616    ///
617    /// # Return value
618    /// Returns an error if there is some issue recomputing the schema.
619    ///
620    /// # Notes
621    ///
622    /// * Does not recursively recompute schema for input (child) plans.
623    pub fn recompute_schema(self) -> Result<Self> {
624        match self {
625            // Since expr may be different than the previous expr, schema of the projection
626            // may change. We need to use try_new method instead of try_new_with_schema method.
627            LogicalPlan::Projection(Projection {
628                expr,
629                input,
630                schema: _,
631            }) => Projection::try_new(expr, input).map(LogicalPlan::Projection),
632            LogicalPlan::Dml(_) => Ok(self),
633            LogicalPlan::Copy(_) => Ok(self),
634            LogicalPlan::Values(Values { schema, values }) => {
635                // todo it isn't clear why the schema is not recomputed here
636                Ok(LogicalPlan::Values(Values { schema, values }))
637            }
638            LogicalPlan::Filter(Filter { predicate, input }) => {
639                Filter::try_new(predicate, input).map(LogicalPlan::Filter)
640            }
641            LogicalPlan::Repartition(_) => Ok(self),
642            LogicalPlan::Window(Window {
643                input,
644                window_expr,
645                schema: _,
646            }) => Window::try_new(window_expr, input).map(LogicalPlan::Window),
647            LogicalPlan::Aggregate(Aggregate {
648                input,
649                group_expr,
650                aggr_expr,
651                schema: _,
652            }) => Aggregate::try_new(input, group_expr, aggr_expr)
653                .map(LogicalPlan::Aggregate),
654            LogicalPlan::Sort(_) => Ok(self),
655            LogicalPlan::Join(Join {
656                left,
657                right,
658                filter,
659                join_type,
660                join_constraint,
661                on,
662                schema: _,
663                null_equality,
664                null_aware,
665            }) => {
666                let schema =
667                    build_join_schema(left.schema(), right.schema(), &join_type)?;
668
669                let new_on: Vec<_> = on
670                    .into_iter()
671                    .map(|equi_expr| {
672                        // SimplifyExpression rule may add alias to the equi_expr.
673                        (equi_expr.0.unalias(), equi_expr.1.unalias())
674                    })
675                    .collect();
676
677                Ok(LogicalPlan::Join(Join {
678                    left,
679                    right,
680                    join_type,
681                    join_constraint,
682                    on: new_on,
683                    filter,
684                    schema: DFSchemaRef::new(schema),
685                    null_equality,
686                    null_aware,
687                }))
688            }
689            LogicalPlan::Subquery(_) => Ok(self),
690            LogicalPlan::SubqueryAlias(SubqueryAlias {
691                input,
692                alias,
693                schema: _,
694            }) => SubqueryAlias::try_new(input, alias).map(LogicalPlan::SubqueryAlias),
695            LogicalPlan::Limit(_) => Ok(self),
696            LogicalPlan::Ddl(_) => Ok(self),
697            LogicalPlan::Extension(Extension { node }) => {
698                // todo make an API that does not require cloning
699                // This requires a copy of the extension nodes expressions and inputs
700                let expr = node.expressions();
701                let inputs: Vec<_> = node.inputs().into_iter().cloned().collect();
702                Ok(LogicalPlan::Extension(Extension {
703                    node: node.with_exprs_and_inputs(expr, inputs)?,
704                }))
705            }
706            LogicalPlan::Union(Union { inputs, schema }) => {
707                let first_input_schema = inputs[0].schema();
708                if schema.fields().len() == first_input_schema.fields().len() {
709                    // If inputs are not pruned do not change schema
710                    Ok(LogicalPlan::Union(Union { inputs, schema }))
711                } else {
712                    // A note on `Union`s constructed via `try_new_by_name`:
713                    //
714                    // At this point, the schema for each input should have
715                    // the same width. Thus, we do not need to save whether a
716                    // `Union` was created `BY NAME`, and can safely rely on the
717                    // `try_new` initializer to derive the new schema based on
718                    // column positions.
719                    Ok(LogicalPlan::Union(Union::try_new(inputs)?))
720                }
721            }
722            LogicalPlan::Distinct(distinct) => {
723                let distinct = match distinct {
724                    Distinct::All(input) => Distinct::All(input),
725                    Distinct::On(DistinctOn {
726                        on_expr,
727                        select_expr,
728                        sort_expr,
729                        input,
730                        schema: _,
731                    }) => Distinct::On(DistinctOn::try_new(
732                        on_expr,
733                        select_expr,
734                        sort_expr,
735                        input,
736                    )?),
737                };
738                Ok(LogicalPlan::Distinct(distinct))
739            }
740            LogicalPlan::RecursiveQuery(RecursiveQuery {
741                name,
742                static_term,
743                recursive_term,
744                is_distinct,
745                schema: _,
746            }) => RecursiveQuery::try_new(name, static_term, recursive_term, is_distinct)
747                .map(LogicalPlan::RecursiveQuery),
748            LogicalPlan::Analyze(_) => Ok(self),
749            LogicalPlan::Explain(_) => Ok(self),
750            LogicalPlan::TableScan(_) => Ok(self),
751            LogicalPlan::EmptyRelation(_) => Ok(self),
752            LogicalPlan::Statement(_) => Ok(self),
753            LogicalPlan::DescribeTable(_) => Ok(self),
754            LogicalPlan::Unnest(Unnest {
755                input,
756                exec_columns,
757                options,
758                ..
759            }) => {
760                // Update schema with unnested column type.
761                unnest_with_options(Arc::unwrap_or_clone(input), exec_columns, options)
762            }
763        }
764    }
765
766    /// Returns a new `LogicalPlan` based on `self` with inputs and
767    /// expressions replaced.
768    ///
769    /// Note this method creates an entirely new node, which requires a large
770    /// amount of clone'ing. When possible, the [`tree_node`] API should be used
771    /// instead of this API.
772    ///
773    /// The exprs correspond to the same order of expressions returned
774    /// by [`Self::expressions`]. This function is used by optimizers
775    /// to rewrite plans using the following pattern:
776    ///
777    /// [`tree_node`]: crate::logical_plan::tree_node
778    ///
779    /// ```text
780    /// let new_inputs = optimize_children(..., plan, props);
781    ///
782    /// // get the plans expressions to optimize
783    /// let exprs = plan.expressions();
784    ///
785    /// // potentially rewrite plan expressions
786    /// let rewritten_exprs = rewrite_exprs(exprs);
787    ///
788    /// // create new plan using rewritten_exprs in same position
789    /// let new_plan = plan.new_with_exprs(rewritten_exprs, new_inputs);
790    /// ```
791    pub fn with_new_exprs(
792        &self,
793        mut expr: Vec<Expr>,
794        inputs: Vec<LogicalPlan>,
795    ) -> Result<LogicalPlan> {
796        match self {
797            // Since expr may be different than the previous expr, schema of the projection
798            // may change. We need to use try_new method instead of try_new_with_schema method.
799            LogicalPlan::Projection(Projection { .. }) => {
800                let input = self.only_input(inputs)?;
801                Projection::try_new(expr, Arc::new(input)).map(LogicalPlan::Projection)
802            }
803            LogicalPlan::Dml(DmlStatement {
804                table_name,
805                target,
806                op,
807                ..
808            }) => {
809                self.assert_no_expressions(expr)?;
810                let input = self.only_input(inputs)?;
811                Ok(LogicalPlan::Dml(DmlStatement::new(
812                    table_name.clone(),
813                    Arc::clone(target),
814                    op.clone(),
815                    Arc::new(input),
816                )))
817            }
818            LogicalPlan::Copy(CopyTo {
819                input: _,
820                output_url,
821                file_type,
822                options,
823                partition_by,
824                output_schema: _,
825            }) => {
826                self.assert_no_expressions(expr)?;
827                let input = self.only_input(inputs)?;
828                Ok(LogicalPlan::Copy(CopyTo::new(
829                    Arc::new(input),
830                    output_url.clone(),
831                    partition_by.clone(),
832                    Arc::clone(file_type),
833                    options.clone(),
834                )))
835            }
836            LogicalPlan::Values(Values { schema, .. }) => {
837                self.assert_no_inputs(inputs)?;
838                Ok(LogicalPlan::Values(Values {
839                    schema: Arc::clone(schema),
840                    values: expr
841                        .chunks_exact(schema.fields().len())
842                        .map(|s| s.to_vec())
843                        .collect(),
844                }))
845            }
846            LogicalPlan::Filter { .. } => {
847                let predicate = self.only_expr(expr)?;
848                let input = self.only_input(inputs)?;
849
850                Filter::try_new(predicate, Arc::new(input)).map(LogicalPlan::Filter)
851            }
852            LogicalPlan::Repartition(Repartition {
853                partitioning_scheme,
854                ..
855            }) => match partitioning_scheme {
856                Partitioning::RoundRobinBatch(n) => {
857                    self.assert_no_expressions(expr)?;
858                    let input = self.only_input(inputs)?;
859                    Ok(LogicalPlan::Repartition(Repartition {
860                        partitioning_scheme: Partitioning::RoundRobinBatch(*n),
861                        input: Arc::new(input),
862                    }))
863                }
864                Partitioning::Hash(_, n) => {
865                    let input = self.only_input(inputs)?;
866                    Ok(LogicalPlan::Repartition(Repartition {
867                        partitioning_scheme: Partitioning::Hash(expr, *n),
868                        input: Arc::new(input),
869                    }))
870                }
871                Partitioning::DistributeBy(_) => {
872                    let input = self.only_input(inputs)?;
873                    Ok(LogicalPlan::Repartition(Repartition {
874                        partitioning_scheme: Partitioning::DistributeBy(expr),
875                        input: Arc::new(input),
876                    }))
877                }
878            },
879            LogicalPlan::Window(Window { window_expr, .. }) => {
880                assert_eq!(window_expr.len(), expr.len());
881                let input = self.only_input(inputs)?;
882                Window::try_new(expr, Arc::new(input)).map(LogicalPlan::Window)
883            }
884            LogicalPlan::Aggregate(Aggregate { group_expr, .. }) => {
885                let input = self.only_input(inputs)?;
886                // group exprs are the first expressions
887                let agg_expr = expr.split_off(group_expr.len());
888
889                Aggregate::try_new(Arc::new(input), expr, agg_expr)
890                    .map(LogicalPlan::Aggregate)
891            }
892            LogicalPlan::Sort(Sort {
893                expr: sort_expr,
894                fetch,
895                ..
896            }) => {
897                let input = self.only_input(inputs)?;
898                Ok(LogicalPlan::Sort(Sort {
899                    expr: expr
900                        .into_iter()
901                        .zip(sort_expr.iter())
902                        .map(|(expr, sort)| sort.with_expr(expr))
903                        .collect(),
904                    input: Arc::new(input),
905                    fetch: *fetch,
906                }))
907            }
908            LogicalPlan::Join(Join {
909                join_type,
910                join_constraint,
911                on,
912                null_equality,
913                null_aware,
914                ..
915            }) => {
916                let (left, right) = self.only_two_inputs(inputs)?;
917                let schema = build_join_schema(left.schema(), right.schema(), join_type)?;
918
919                let equi_expr_count = on.len() * 2;
920                assert!(expr.len() >= equi_expr_count);
921
922                // Assume that the last expr, if any,
923                // is the filter_expr (non equality predicate from ON clause)
924                let filter_expr = if expr.len() > equi_expr_count {
925                    expr.pop()
926                } else {
927                    None
928                };
929
930                // The first part of expr is equi-exprs,
931                // and the struct of each equi-expr is like `left-expr = right-expr`.
932                assert_eq!(expr.len(), equi_expr_count);
933                let mut new_on = Vec::with_capacity(on.len());
934                let mut iter = expr.into_iter();
935                while let Some(left) = iter.next() {
936                    let Some(right) = iter.next() else {
937                        internal_err!(
938                            "Expected a pair of expressions to construct the join on expression"
939                        )?
940                    };
941
942                    // SimplifyExpression rule may add alias to the equi_expr.
943                    new_on.push((left.unalias(), right.unalias()));
944                }
945
946                Ok(LogicalPlan::Join(Join {
947                    left: Arc::new(left),
948                    right: Arc::new(right),
949                    join_type: *join_type,
950                    join_constraint: *join_constraint,
951                    on: new_on,
952                    filter: filter_expr,
953                    schema: DFSchemaRef::new(schema),
954                    null_equality: *null_equality,
955                    null_aware: *null_aware,
956                }))
957            }
958            LogicalPlan::Subquery(Subquery {
959                outer_ref_columns,
960                spans,
961                ..
962            }) => {
963                self.assert_no_expressions(expr)?;
964                let input = self.only_input(inputs)?;
965                let subquery = LogicalPlanBuilder::from(input).build()?;
966                Ok(LogicalPlan::Subquery(Subquery {
967                    subquery: Arc::new(subquery),
968                    outer_ref_columns: outer_ref_columns.clone(),
969                    spans: spans.clone(),
970                }))
971            }
972            LogicalPlan::SubqueryAlias(SubqueryAlias { alias, .. }) => {
973                self.assert_no_expressions(expr)?;
974                let input = self.only_input(inputs)?;
975                SubqueryAlias::try_new(Arc::new(input), alias.clone())
976                    .map(LogicalPlan::SubqueryAlias)
977            }
978            LogicalPlan::Limit(Limit { skip, fetch, .. }) => {
979                let old_expr_len = skip.iter().chain(fetch.iter()).count();
980                assert_eq_or_internal_err!(
981                    old_expr_len,
982                    expr.len(),
983                    "Invalid number of new Limit expressions: expected {}, got {}",
984                    old_expr_len,
985                    expr.len()
986                );
987                // `LogicalPlan::expressions()` returns in [skip, fetch] order, so we can pop from the end.
988                let new_fetch = fetch.as_ref().and_then(|_| expr.pop());
989                let new_skip = skip.as_ref().and_then(|_| expr.pop());
990                let input = self.only_input(inputs)?;
991                Ok(LogicalPlan::Limit(Limit {
992                    skip: new_skip.map(Box::new),
993                    fetch: new_fetch.map(Box::new),
994                    input: Arc::new(input),
995                }))
996            }
997            LogicalPlan::Ddl(DdlStatement::CreateMemoryTable(CreateMemoryTable {
998                name,
999                if_not_exists,
1000                or_replace,
1001                column_defaults,
1002                temporary,
1003                ..
1004            })) => {
1005                self.assert_no_expressions(expr)?;
1006                let input = self.only_input(inputs)?;
1007                Ok(LogicalPlan::Ddl(DdlStatement::CreateMemoryTable(
1008                    CreateMemoryTable {
1009                        input: Arc::new(input),
1010                        constraints: Constraints::default(),
1011                        name: name.clone(),
1012                        if_not_exists: *if_not_exists,
1013                        or_replace: *or_replace,
1014                        column_defaults: column_defaults.clone(),
1015                        temporary: *temporary,
1016                    },
1017                )))
1018            }
1019            LogicalPlan::Ddl(DdlStatement::CreateView(CreateView {
1020                name,
1021                or_replace,
1022                definition,
1023                temporary,
1024                ..
1025            })) => {
1026                self.assert_no_expressions(expr)?;
1027                let input = self.only_input(inputs)?;
1028                Ok(LogicalPlan::Ddl(DdlStatement::CreateView(CreateView {
1029                    input: Arc::new(input),
1030                    name: name.clone(),
1031                    or_replace: *or_replace,
1032                    temporary: *temporary,
1033                    definition: definition.clone(),
1034                })))
1035            }
1036            LogicalPlan::Extension(e) => Ok(LogicalPlan::Extension(Extension {
1037                node: e.node.with_exprs_and_inputs(expr, inputs)?,
1038            })),
1039            LogicalPlan::Union(Union { schema, .. }) => {
1040                self.assert_no_expressions(expr)?;
1041                let input_schema = inputs[0].schema();
1042                // If inputs are not pruned do not change schema.
1043                let schema = if schema.fields().len() == input_schema.fields().len() {
1044                    Arc::clone(schema)
1045                } else {
1046                    Arc::clone(input_schema)
1047                };
1048                Ok(LogicalPlan::Union(Union {
1049                    inputs: inputs.into_iter().map(Arc::new).collect(),
1050                    schema,
1051                }))
1052            }
1053            LogicalPlan::Distinct(distinct) => {
1054                let distinct = match distinct {
1055                    Distinct::All(_) => {
1056                        self.assert_no_expressions(expr)?;
1057                        let input = self.only_input(inputs)?;
1058                        Distinct::All(Arc::new(input))
1059                    }
1060                    Distinct::On(DistinctOn {
1061                        on_expr,
1062                        select_expr,
1063                        ..
1064                    }) => {
1065                        let input = self.only_input(inputs)?;
1066                        let sort_expr = expr.split_off(on_expr.len() + select_expr.len());
1067                        let select_expr = expr.split_off(on_expr.len());
1068                        assert!(
1069                            sort_expr.is_empty(),
1070                            "with_new_exprs for Distinct does not support sort expressions"
1071                        );
1072                        Distinct::On(DistinctOn::try_new(
1073                            expr,
1074                            select_expr,
1075                            None, // no sort expressions accepted
1076                            Arc::new(input),
1077                        )?)
1078                    }
1079                };
1080                Ok(LogicalPlan::Distinct(distinct))
1081            }
1082            LogicalPlan::RecursiveQuery(RecursiveQuery {
1083                name, is_distinct, ..
1084            }) => {
1085                self.assert_no_expressions(expr)?;
1086                let (static_term, recursive_term) = self.only_two_inputs(inputs)?;
1087                RecursiveQuery::try_new(
1088                    name.clone(),
1089                    Arc::new(static_term),
1090                    Arc::new(recursive_term),
1091                    *is_distinct,
1092                )
1093                .map(LogicalPlan::RecursiveQuery)
1094            }
1095            LogicalPlan::Analyze(a) => {
1096                self.assert_no_expressions(expr)?;
1097                let input = self.only_input(inputs)?;
1098                Ok(LogicalPlan::Analyze(Analyze {
1099                    verbose: a.verbose,
1100                    schema: Arc::clone(&a.schema),
1101                    input: Arc::new(input),
1102                }))
1103            }
1104            LogicalPlan::Explain(e) => {
1105                self.assert_no_expressions(expr)?;
1106                let input = self.only_input(inputs)?;
1107                Ok(LogicalPlan::Explain(Explain {
1108                    verbose: e.verbose,
1109                    plan: Arc::new(input),
1110                    explain_format: e.explain_format.clone(),
1111                    stringified_plans: e.stringified_plans.clone(),
1112                    schema: Arc::clone(&e.schema),
1113                    logical_optimization_succeeded: e.logical_optimization_succeeded,
1114                }))
1115            }
1116            LogicalPlan::Statement(Statement::Prepare(Prepare {
1117                name, fields, ..
1118            })) => {
1119                self.assert_no_expressions(expr)?;
1120                let input = self.only_input(inputs)?;
1121                Ok(LogicalPlan::Statement(Statement::Prepare(Prepare {
1122                    name: name.clone(),
1123                    fields: fields.clone(),
1124                    input: Arc::new(input),
1125                })))
1126            }
1127            LogicalPlan::Statement(Statement::Execute(Execute { name, .. })) => {
1128                self.assert_no_inputs(inputs)?;
1129                Ok(LogicalPlan::Statement(Statement::Execute(Execute {
1130                    name: name.clone(),
1131                    parameters: expr,
1132                })))
1133            }
1134            LogicalPlan::TableScan(ts) => {
1135                self.assert_no_inputs(inputs)?;
1136                Ok(LogicalPlan::TableScan(TableScan {
1137                    filters: expr,
1138                    ..ts.clone()
1139                }))
1140            }
1141            LogicalPlan::EmptyRelation(_)
1142            | LogicalPlan::Ddl(_)
1143            | LogicalPlan::Statement(_)
1144            | LogicalPlan::DescribeTable(_) => {
1145                // All of these plan types have no inputs / exprs so should not be called
1146                self.assert_no_expressions(expr)?;
1147                self.assert_no_inputs(inputs)?;
1148                Ok(self.clone())
1149            }
1150            LogicalPlan::Unnest(Unnest {
1151                exec_columns: columns,
1152                options,
1153                ..
1154            }) => {
1155                self.assert_no_expressions(expr)?;
1156                let input = self.only_input(inputs)?;
1157                // Update schema with unnested column type.
1158                let new_plan =
1159                    unnest_with_options(input, columns.clone(), options.clone())?;
1160                Ok(new_plan)
1161            }
1162        }
1163    }
1164
1165    /// checks that the plan conforms to the listed invariant level, returning an Error if not
1166    pub fn check_invariants(&self, check: InvariantLevel) -> Result<()> {
1167        match check {
1168            InvariantLevel::Always => assert_always_invariants_at_current_node(self),
1169            InvariantLevel::Executable => assert_executable_invariants(self),
1170        }
1171    }
1172
1173    /// Helper for [Self::with_new_exprs] to use when no expressions are expected.
1174    #[inline]
1175    #[expect(clippy::needless_pass_by_value)] // expr is moved intentionally to ensure it's not used again
1176    fn assert_no_expressions(&self, expr: Vec<Expr>) -> Result<()> {
1177        assert_or_internal_err!(
1178            expr.is_empty(),
1179            "{self:?} should have no exprs, got {:?}",
1180            expr
1181        );
1182        Ok(())
1183    }
1184
1185    /// Helper for [Self::with_new_exprs] to use when no inputs are expected.
1186    #[inline]
1187    #[expect(clippy::needless_pass_by_value)] // inputs is moved intentionally to ensure it's not used again
1188    fn assert_no_inputs(&self, inputs: Vec<LogicalPlan>) -> Result<()> {
1189        assert_or_internal_err!(
1190            inputs.is_empty(),
1191            "{self:?} should have no inputs, got: {:?}",
1192            inputs
1193        );
1194        Ok(())
1195    }
1196
1197    /// Helper for [Self::with_new_exprs] to use when exactly one expression is expected.
1198    #[inline]
1199    fn only_expr(&self, mut expr: Vec<Expr>) -> Result<Expr> {
1200        assert_eq_or_internal_err!(
1201            expr.len(),
1202            1,
1203            "{self:?} should have exactly one expr, got {:?}",
1204            &expr
1205        );
1206        Ok(expr.remove(0))
1207    }
1208
1209    /// Helper for [Self::with_new_exprs] to use when exactly one input is expected.
1210    #[inline]
1211    fn only_input(&self, mut inputs: Vec<LogicalPlan>) -> Result<LogicalPlan> {
1212        assert_eq_or_internal_err!(
1213            inputs.len(),
1214            1,
1215            "{self:?} should have exactly one input, got {:?}",
1216            &inputs
1217        );
1218        Ok(inputs.remove(0))
1219    }
1220
1221    /// Helper for [Self::with_new_exprs] to use when exactly two inputs are expected.
1222    #[inline]
1223    fn only_two_inputs(
1224        &self,
1225        mut inputs: Vec<LogicalPlan>,
1226    ) -> Result<(LogicalPlan, LogicalPlan)> {
1227        assert_eq_or_internal_err!(
1228            inputs.len(),
1229            2,
1230            "{self:?} should have exactly two inputs, got {:?}",
1231            &inputs
1232        );
1233        let right = inputs.remove(1);
1234        let left = inputs.remove(0);
1235        Ok((left, right))
1236    }
1237
1238    /// Replaces placeholder param values (like `$1`, `$2`) in [`LogicalPlan`]
1239    /// with the specified `param_values`.
1240    ///
1241    /// [`Prepare`] statements are converted to
1242    /// their inner logical plan for execution.
1243    ///
1244    /// # Example
1245    /// ```
1246    /// # use arrow::datatypes::{Field, Schema, DataType};
1247    /// use datafusion_common::ScalarValue;
1248    /// # use datafusion_expr::{lit, col, LogicalPlanBuilder, logical_plan::table_scan, placeholder};
1249    /// # let schema = Schema::new(vec![
1250    /// #     Field::new("id", DataType::Int32, false),
1251    /// # ]);
1252    /// // Build SELECT * FROM t1 WHERE id = $1
1253    /// let plan = table_scan(Some("t1"), &schema, None).unwrap()
1254    ///     .filter(col("id").eq(placeholder("$1"))).unwrap()
1255    ///     .build().unwrap();
1256    ///
1257    /// assert_eq!(
1258    ///   "Filter: t1.id = $1\
1259    ///   \n  TableScan: t1",
1260    ///   plan.display_indent().to_string()
1261    /// );
1262    ///
1263    /// // Fill in the parameter $1 with a literal 3
1264    /// let plan = plan.with_param_values(vec![
1265    ///   ScalarValue::from(3i32) // value at index 0 --> $1
1266    /// ]).unwrap();
1267    ///
1268    /// assert_eq!(
1269    ///    "Filter: t1.id = Int32(3)\
1270    ///    \n  TableScan: t1",
1271    ///    plan.display_indent().to_string()
1272    ///  );
1273    ///
1274    /// // Note you can also used named parameters
1275    /// // Build SELECT * FROM t1 WHERE id = $my_param
1276    /// let plan = table_scan(Some("t1"), &schema, None).unwrap()
1277    ///     .filter(col("id").eq(placeholder("$my_param"))).unwrap()
1278    ///     .build().unwrap()
1279    ///     // Fill in the parameter $my_param with a literal 3
1280    ///     .with_param_values(vec![
1281    ///       ("my_param", ScalarValue::from(3i32)),
1282    ///     ]).unwrap();
1283    ///
1284    /// assert_eq!(
1285    ///    "Filter: t1.id = Int32(3)\
1286    ///    \n  TableScan: t1",
1287    ///    plan.display_indent().to_string()
1288    ///  );
1289    /// ```
1290    pub fn with_param_values(
1291        self,
1292        param_values: impl Into<ParamValues>,
1293    ) -> Result<LogicalPlan> {
1294        let param_values = param_values.into();
1295        let plan_with_values = self.replace_params_with_values(&param_values)?;
1296
1297        // unwrap Prepare
1298        Ok(
1299            if let LogicalPlan::Statement(Statement::Prepare(prepare_lp)) =
1300                plan_with_values
1301            {
1302                param_values.verify_fields(&prepare_lp.fields)?;
1303                // try and take ownership of the input if is not shared, clone otherwise
1304                Arc::unwrap_or_clone(prepare_lp.input)
1305            } else {
1306                plan_with_values
1307            },
1308        )
1309    }
1310
1311    /// Returns the maximum number of rows that this plan can output, if known.
1312    ///
1313    /// If `None`, the plan can return any number of rows.
1314    /// If `Some(n)` then the plan can return at most `n` rows but may return fewer.
1315    pub fn max_rows(self: &LogicalPlan) -> Option<usize> {
1316        match self {
1317            LogicalPlan::Projection(Projection { input, .. }) => input.max_rows(),
1318            LogicalPlan::Filter(filter) => {
1319                if filter.is_scalar() {
1320                    Some(1)
1321                } else {
1322                    filter.input.max_rows()
1323                }
1324            }
1325            LogicalPlan::Window(Window { input, .. }) => input.max_rows(),
1326            LogicalPlan::Aggregate(Aggregate {
1327                input, group_expr, ..
1328            }) => {
1329                // Empty group_expr will return Some(1)
1330                if group_expr
1331                    .iter()
1332                    .all(|expr| matches!(expr, Expr::Literal(_, _)))
1333                {
1334                    Some(1)
1335                } else {
1336                    input.max_rows()
1337                }
1338            }
1339            LogicalPlan::Sort(Sort { input, fetch, .. }) => {
1340                match (fetch, input.max_rows()) {
1341                    (Some(fetch_limit), Some(input_max)) => {
1342                        Some(input_max.min(*fetch_limit))
1343                    }
1344                    (Some(fetch_limit), None) => Some(*fetch_limit),
1345                    (None, Some(input_max)) => Some(input_max),
1346                    (None, None) => None,
1347                }
1348            }
1349            LogicalPlan::Join(Join {
1350                left,
1351                right,
1352                join_type,
1353                ..
1354            }) => match join_type {
1355                JoinType::Inner => Some(left.max_rows()? * right.max_rows()?),
1356                JoinType::Left | JoinType::Right | JoinType::Full => {
1357                    match (left.max_rows()?, right.max_rows()?, join_type) {
1358                        (0, 0, _) => Some(0),
1359                        (max_rows, 0, JoinType::Left | JoinType::Full) => Some(max_rows),
1360                        (0, max_rows, JoinType::Right | JoinType::Full) => Some(max_rows),
1361                        (left_max, right_max, _) => Some(left_max * right_max),
1362                    }
1363                }
1364                JoinType::LeftSemi | JoinType::LeftAnti | JoinType::LeftMark => {
1365                    left.max_rows()
1366                }
1367                JoinType::RightSemi | JoinType::RightAnti | JoinType::RightMark => {
1368                    right.max_rows()
1369                }
1370            },
1371            LogicalPlan::Repartition(Repartition { input, .. }) => input.max_rows(),
1372            LogicalPlan::Union(Union { inputs, .. }) => {
1373                inputs.iter().try_fold(0usize, |mut acc, plan| {
1374                    acc += plan.max_rows()?;
1375                    Some(acc)
1376                })
1377            }
1378            LogicalPlan::TableScan(TableScan { fetch, .. }) => *fetch,
1379            LogicalPlan::EmptyRelation(_) => Some(0),
1380            LogicalPlan::RecursiveQuery(_) => None,
1381            LogicalPlan::Subquery(_) => None,
1382            LogicalPlan::SubqueryAlias(SubqueryAlias { input, .. }) => input.max_rows(),
1383            LogicalPlan::Limit(limit) => match limit.get_fetch_type() {
1384                Ok(FetchType::Literal(s)) => s,
1385                _ => None,
1386            },
1387            LogicalPlan::Distinct(
1388                Distinct::All(input) | Distinct::On(DistinctOn { input, .. }),
1389            ) => input.max_rows(),
1390            LogicalPlan::Values(v) => Some(v.values.len()),
1391            LogicalPlan::Unnest(_) => None,
1392            LogicalPlan::Ddl(_)
1393            | LogicalPlan::Explain(_)
1394            | LogicalPlan::Analyze(_)
1395            | LogicalPlan::Dml(_)
1396            | LogicalPlan::Copy(_)
1397            | LogicalPlan::DescribeTable(_)
1398            | LogicalPlan::Statement(_)
1399            | LogicalPlan::Extension(_) => None,
1400        }
1401    }
1402
1403    /// Returns the skip (offset) of this plan node, if it has one.
1404    ///
1405    /// Only [`LogicalPlan::Limit`] carries a skip value; all other variants
1406    /// return `Ok(None)`. Returns `Ok(None)` for a zero skip.
1407    pub fn skip(&self) -> Result<Option<usize>> {
1408        match self {
1409            LogicalPlan::Limit(limit) => match limit.get_skip_type()? {
1410                SkipType::Literal(0) => Ok(None),
1411                SkipType::Literal(n) => Ok(Some(n)),
1412                SkipType::UnsupportedExpr => Ok(None),
1413            },
1414            LogicalPlan::Sort(_) => Ok(None),
1415            LogicalPlan::TableScan(_) => Ok(None),
1416            LogicalPlan::Projection(_) => Ok(None),
1417            LogicalPlan::Filter(_) => Ok(None),
1418            LogicalPlan::Window(_) => Ok(None),
1419            LogicalPlan::Aggregate(_) => Ok(None),
1420            LogicalPlan::Join(_) => Ok(None),
1421            LogicalPlan::Repartition(_) => Ok(None),
1422            LogicalPlan::Union(_) => Ok(None),
1423            LogicalPlan::EmptyRelation(_) => Ok(None),
1424            LogicalPlan::Subquery(_) => Ok(None),
1425            LogicalPlan::SubqueryAlias(_) => Ok(None),
1426            LogicalPlan::Statement(_) => Ok(None),
1427            LogicalPlan::Values(_) => Ok(None),
1428            LogicalPlan::Explain(_) => Ok(None),
1429            LogicalPlan::Analyze(_) => Ok(None),
1430            LogicalPlan::Extension(_) => Ok(None),
1431            LogicalPlan::Distinct(_) => Ok(None),
1432            LogicalPlan::Dml(_) => Ok(None),
1433            LogicalPlan::Ddl(_) => Ok(None),
1434            LogicalPlan::Copy(_) => Ok(None),
1435            LogicalPlan::DescribeTable(_) => Ok(None),
1436            LogicalPlan::Unnest(_) => Ok(None),
1437            LogicalPlan::RecursiveQuery(_) => Ok(None),
1438        }
1439    }
1440
1441    /// Returns the fetch (limit) of this plan node, if it has one.
1442    ///
1443    /// [`LogicalPlan::Sort`], [`LogicalPlan::TableScan`], and
1444    /// [`LogicalPlan::Limit`] may carry a fetch value; all other variants
1445    /// return `Ok(None)`.
1446    pub fn fetch(&self) -> Result<Option<usize>> {
1447        match self {
1448            LogicalPlan::Sort(Sort { fetch, .. }) => Ok(*fetch),
1449            LogicalPlan::TableScan(TableScan { fetch, .. }) => Ok(*fetch),
1450            LogicalPlan::Limit(limit) => match limit.get_fetch_type()? {
1451                FetchType::Literal(s) => Ok(s),
1452                FetchType::UnsupportedExpr => Ok(None),
1453            },
1454            LogicalPlan::Projection(_) => Ok(None),
1455            LogicalPlan::Filter(_) => Ok(None),
1456            LogicalPlan::Window(_) => Ok(None),
1457            LogicalPlan::Aggregate(_) => Ok(None),
1458            LogicalPlan::Join(_) => Ok(None),
1459            LogicalPlan::Repartition(_) => Ok(None),
1460            LogicalPlan::Union(_) => Ok(None),
1461            LogicalPlan::EmptyRelation(_) => Ok(None),
1462            LogicalPlan::Subquery(_) => Ok(None),
1463            LogicalPlan::SubqueryAlias(_) => Ok(None),
1464            LogicalPlan::Statement(_) => Ok(None),
1465            LogicalPlan::Values(_) => Ok(None),
1466            LogicalPlan::Explain(_) => Ok(None),
1467            LogicalPlan::Analyze(_) => Ok(None),
1468            LogicalPlan::Extension(_) => Ok(None),
1469            LogicalPlan::Distinct(_) => Ok(None),
1470            LogicalPlan::Dml(_) => Ok(None),
1471            LogicalPlan::Ddl(_) => Ok(None),
1472            LogicalPlan::Copy(_) => Ok(None),
1473            LogicalPlan::DescribeTable(_) => Ok(None),
1474            LogicalPlan::Unnest(_) => Ok(None),
1475            LogicalPlan::RecursiveQuery(_) => Ok(None),
1476        }
1477    }
1478
1479    /// If this node's expressions contains any references to an outer subquery
1480    pub fn contains_outer_reference(&self) -> bool {
1481        let mut contains = false;
1482        self.apply_expressions(|expr| {
1483            Ok(if expr.contains_outer() {
1484                contains = true;
1485                TreeNodeRecursion::Stop
1486            } else {
1487                TreeNodeRecursion::Continue
1488            })
1489        })
1490        .unwrap();
1491        contains
1492    }
1493
1494    /// Get the output expressions and their corresponding columns.
1495    ///
1496    /// The parent node may reference the output columns of the plan by expressions, such as
1497    /// projection over aggregate or window functions. This method helps to convert the
1498    /// referenced expressions into columns.
1499    ///
1500    /// See also: [`crate::utils::columnize_expr`]
1501    pub fn columnized_output_exprs(&self) -> Result<Vec<(&Expr, Column)>> {
1502        match self {
1503            LogicalPlan::Aggregate(aggregate) => Ok(aggregate
1504                .output_expressions()?
1505                .into_iter()
1506                .zip(self.schema().columns())
1507                .collect()),
1508            LogicalPlan::Window(Window {
1509                window_expr,
1510                input,
1511                schema,
1512            }) => {
1513                // The input could be another Window, so the result should also include the input's. For Example:
1514                // `EXPLAIN SELECT RANK() OVER (PARTITION BY a ORDER BY b), SUM(b) OVER (PARTITION BY a) FROM t`
1515                // Its plan is:
1516                // Projection: RANK() PARTITION BY [t.a] ORDER BY [t.b ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, SUM(t.b) PARTITION BY [t.a] ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
1517                //   WindowAggr: windowExpr=[[SUM(CAST(t.b AS Int64)) PARTITION BY [t.a] ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING]]
1518                //     WindowAggr: windowExpr=[[RANK() PARTITION BY [t.a] ORDER BY [t.b ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]/
1519                //       TableScan: t projection=[a, b]
1520                let mut output_exprs = input.columnized_output_exprs()?;
1521                let input_len = input.schema().fields().len();
1522                output_exprs.extend(
1523                    window_expr
1524                        .iter()
1525                        .zip(schema.columns().into_iter().skip(input_len)),
1526                );
1527                Ok(output_exprs)
1528            }
1529            _ => Ok(vec![]),
1530        }
1531    }
1532}
1533
1534impl LogicalPlan {
1535    /// Return a `LogicalPlan` with all placeholders (e.g $1 $2,
1536    /// ...) replaced with corresponding values provided in
1537    /// `params_values`
1538    ///
1539    /// See [`Self::with_param_values`] for examples and usage with an owned
1540    /// `ParamValues`
1541    pub fn replace_params_with_values(
1542        self,
1543        param_values: &ParamValues,
1544    ) -> Result<LogicalPlan> {
1545        self.transform_up_with_subqueries(|plan| {
1546            let schema = Arc::clone(plan.schema());
1547            let name_preserver = NamePreserver::new(&plan);
1548            plan.map_expressions(|e| {
1549                let (e, has_placeholder) = e.infer_placeholder_types(&schema)?;
1550                if !has_placeholder {
1551                    // Performance optimization:
1552                    // avoid NamePreserver copy and second pass over expression
1553                    // if no placeholders.
1554                    Ok(Transformed::no(e))
1555                } else {
1556                    let original_name = name_preserver.save(&e);
1557                    let transformed_expr = e.transform_up(|e| {
1558                        if let Expr::Placeholder(Placeholder { id, .. }) = e {
1559                            let (value, metadata) = param_values
1560                                .get_placeholders_with_values(&id)?
1561                                .into_inner();
1562                            Ok(Transformed::yes(Expr::Literal(value, metadata)))
1563                        } else {
1564                            Ok(Transformed::no(e))
1565                        }
1566                    })?;
1567                    // Preserve name to avoid breaking column references to this expression
1568                    Ok(transformed_expr.update_data(|expr| original_name.restore(expr)))
1569                }
1570            })?
1571            .map_data(|plan| plan.update_schema_data_type())
1572        })
1573        .map(|res| res.data)
1574    }
1575
1576    /// Recompute schema fields' data type after replacing params, ensuring fields data type can be
1577    /// updated according to the new parameters.
1578    ///
1579    /// Unlike `recompute_schema()`, this method rebuilds VALUES plans entirely to properly infer
1580    /// types types from literal values after placeholder substitution.
1581    fn update_schema_data_type(self) -> Result<LogicalPlan> {
1582        match self {
1583            // Build `LogicalPlan::Values` from the values for type inference.
1584            // We can't use `recompute_schema` because it skips recomputing for
1585            // `LogicalPlan::Values`.
1586            LogicalPlan::Values(Values { values, schema: _ }) => {
1587                LogicalPlanBuilder::values(values)?.build()
1588            }
1589            // other plans can just use `recompute_schema` directly.
1590            plan => plan.recompute_schema(),
1591        }
1592    }
1593
1594    /// Walk the logical plan, find any `Placeholder` tokens, and return a set of their names.
1595    pub fn get_parameter_names(&self) -> Result<HashSet<String>> {
1596        let mut param_names = HashSet::new();
1597        self.apply_with_subqueries(|plan| {
1598            plan.apply_expressions(|expr| {
1599                expr.apply(|expr| {
1600                    if let Expr::Placeholder(Placeholder { id, .. }) = expr {
1601                        param_names.insert(id.clone());
1602                    }
1603                    Ok(TreeNodeRecursion::Continue)
1604                })
1605            })
1606        })
1607        .map(|_| param_names)
1608    }
1609
1610    /// Walk the logical plan, find any `Placeholder` tokens, and return a map of their IDs and DataTypes
1611    ///
1612    /// Note that this will drop any extension or field metadata attached to parameters. Use
1613    /// [`LogicalPlan::get_parameter_fields`] to keep extension metadata.
1614    pub fn get_parameter_types(
1615        &self,
1616    ) -> Result<HashMap<String, Option<DataType>>, DataFusionError> {
1617        let mut parameter_fields = self.get_parameter_fields()?;
1618        Ok(parameter_fields
1619            .drain()
1620            .map(|(name, maybe_field)| {
1621                (name, maybe_field.map(|field| field.data_type().clone()))
1622            })
1623            .collect())
1624    }
1625
1626    /// Walk the logical plan, find any `Placeholder` tokens, and return a map of their IDs and FieldRefs
1627    pub fn get_parameter_fields(
1628        &self,
1629    ) -> Result<HashMap<String, Option<FieldRef>>, DataFusionError> {
1630        let mut param_types: HashMap<String, Option<FieldRef>> = HashMap::new();
1631
1632        self.apply_with_subqueries(|plan| {
1633            plan.apply_expressions(|expr| {
1634                expr.apply(|expr| {
1635                    if let Expr::Placeholder(Placeholder { id, field }) = expr {
1636                        let prev = param_types.get(id);
1637                        match (prev, field) {
1638                            (Some(Some(prev)), Some(field)) => {
1639                                check_metadata_with_storage_equal(
1640                                    (field.data_type(), Some(field.metadata())),
1641                                    (prev.data_type(), Some(prev.metadata())),
1642                                    "parameter",
1643                                    &format!(": Conflicting types for id {id}"),
1644                                )?;
1645                            }
1646                            (_, Some(field)) => {
1647                                param_types.insert(id.clone(), Some(Arc::clone(field)));
1648                            }
1649                            _ => {
1650                                param_types.insert(id.clone(), None);
1651                            }
1652                        }
1653                    }
1654                    Ok(TreeNodeRecursion::Continue)
1655                })
1656            })
1657        })
1658        .map(|_| param_types)
1659    }
1660
1661    // ------------
1662    // Various implementations for printing out LogicalPlans
1663    // ------------
1664
1665    /// Return a `format`able structure that produces a single line
1666    /// per node.
1667    ///
1668    /// # Example
1669    ///
1670    /// ```text
1671    /// Projection: employee.id
1672    ///    Filter: employee.state Eq Utf8(\"CO\")\
1673    ///       CsvScan: employee projection=Some([0, 3])
1674    /// ```
1675    ///
1676    /// ```
1677    /// use arrow::datatypes::{DataType, Field, Schema};
1678    /// use datafusion_expr::{col, lit, logical_plan::table_scan, LogicalPlanBuilder};
1679    /// let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
1680    /// let plan = table_scan(Some("t1"), &schema, None)
1681    ///     .unwrap()
1682    ///     .filter(col("id").eq(lit(5)))
1683    ///     .unwrap()
1684    ///     .build()
1685    ///     .unwrap();
1686    ///
1687    /// // Format using display_indent
1688    /// let display_string = format!("{}", plan.display_indent());
1689    ///
1690    /// assert_eq!("Filter: t1.id = Int32(5)\n  TableScan: t1", display_string);
1691    /// ```
1692    pub fn display_indent(&self) -> impl Display + '_ {
1693        // Boilerplate structure to wrap LogicalPlan with something
1694        // that that can be formatted
1695        struct Wrapper<'a>(&'a LogicalPlan);
1696        impl Display for Wrapper<'_> {
1697            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1698                let with_schema = false;
1699                let mut visitor = IndentVisitor::new(f, with_schema);
1700                match self.0.visit_with_subqueries(&mut visitor) {
1701                    Ok(_) => Ok(()),
1702                    Err(_) => Err(fmt::Error),
1703                }
1704            }
1705        }
1706        Wrapper(self)
1707    }
1708
1709    /// Return a `format`able structure that produces a single line
1710    /// per node that includes the output schema. For example:
1711    ///
1712    /// ```text
1713    /// Projection: employee.id [id:Int32]\
1714    ///    Filter: employee.state = Utf8(\"CO\") [id:Int32, state:Utf8]\
1715    ///      TableScan: employee projection=[0, 3] [id:Int32, state:Utf8]";
1716    /// ```
1717    ///
1718    /// ```
1719    /// use arrow::datatypes::{DataType, Field, Schema};
1720    /// use datafusion_expr::{col, lit, logical_plan::table_scan, LogicalPlanBuilder};
1721    /// let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
1722    /// let plan = table_scan(Some("t1"), &schema, None)
1723    ///     .unwrap()
1724    ///     .filter(col("id").eq(lit(5)))
1725    ///     .unwrap()
1726    ///     .build()
1727    ///     .unwrap();
1728    ///
1729    /// // Format using display_indent_schema
1730    /// let display_string = format!("{}", plan.display_indent_schema());
1731    ///
1732    /// assert_eq!(
1733    ///     "Filter: t1.id = Int32(5) [id:Int32]\
1734    ///             \n  TableScan: t1 [id:Int32]",
1735    ///     display_string
1736    /// );
1737    /// ```
1738    pub fn display_indent_schema(&self) -> impl Display + '_ {
1739        // Boilerplate structure to wrap LogicalPlan with something
1740        // that that can be formatted
1741        struct Wrapper<'a>(&'a LogicalPlan);
1742        impl Display for Wrapper<'_> {
1743            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1744                let with_schema = true;
1745                let mut visitor = IndentVisitor::new(f, with_schema);
1746                match self.0.visit_with_subqueries(&mut visitor) {
1747                    Ok(_) => Ok(()),
1748                    Err(_) => Err(fmt::Error),
1749                }
1750            }
1751        }
1752        Wrapper(self)
1753    }
1754
1755    /// Return a displayable structure that produces plan in postgresql JSON format.
1756    ///
1757    /// Users can use this format to visualize the plan in existing plan visualization tools, for example [dalibo](https://explain.dalibo.com/)
1758    pub fn display_pg_json(&self) -> impl Display + '_ {
1759        // Boilerplate structure to wrap LogicalPlan with something
1760        // that that can be formatted
1761        struct Wrapper<'a>(&'a LogicalPlan);
1762        impl Display for Wrapper<'_> {
1763            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1764                let mut visitor = PgJsonVisitor::new(f);
1765                visitor.with_schema(true);
1766                match self.0.visit_with_subqueries(&mut visitor) {
1767                    Ok(_) => Ok(()),
1768                    Err(_) => Err(fmt::Error),
1769                }
1770            }
1771        }
1772        Wrapper(self)
1773    }
1774
1775    /// Return a `format`able structure that produces lines meant for
1776    /// graphical display using the `DOT` language. This format can be
1777    /// visualized using software from
1778    /// [`graphviz`](https://graphviz.org/)
1779    ///
1780    /// This currently produces two graphs -- one with the basic
1781    /// structure, and one with additional details such as schema.
1782    ///
1783    /// ```
1784    /// use arrow::datatypes::{DataType, Field, Schema};
1785    /// use datafusion_expr::{col, lit, logical_plan::table_scan, LogicalPlanBuilder};
1786    /// let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
1787    /// let plan = table_scan(Some("t1"), &schema, None)
1788    ///     .unwrap()
1789    ///     .filter(col("id").eq(lit(5)))
1790    ///     .unwrap()
1791    ///     .build()
1792    ///     .unwrap();
1793    ///
1794    /// // Format using display_graphviz
1795    /// let graphviz_string = format!("{}", plan.display_graphviz());
1796    /// ```
1797    ///
1798    /// If graphviz string is saved to a file such as `/tmp/example.dot`, the following
1799    /// commands can be used to render it as a pdf:
1800    ///
1801    /// ```bash
1802    ///   dot -Tpdf < /tmp/example.dot  > /tmp/example.pdf
1803    /// ```
1804    pub fn display_graphviz(&self) -> impl Display + '_ {
1805        // Boilerplate structure to wrap LogicalPlan with something
1806        // that that can be formatted
1807        struct Wrapper<'a>(&'a LogicalPlan);
1808        impl Display for Wrapper<'_> {
1809            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1810                let mut visitor = GraphvizVisitor::new(f);
1811
1812                visitor.start_graph()?;
1813
1814                visitor.pre_visit_plan("LogicalPlan")?;
1815                self.0
1816                    .visit_with_subqueries(&mut visitor)
1817                    .map_err(|_| fmt::Error)?;
1818                visitor.post_visit_plan()?;
1819
1820                visitor.set_with_schema(true);
1821                visitor.pre_visit_plan("Detailed LogicalPlan")?;
1822                self.0
1823                    .visit_with_subqueries(&mut visitor)
1824                    .map_err(|_| fmt::Error)?;
1825                visitor.post_visit_plan()?;
1826
1827                visitor.end_graph()?;
1828                Ok(())
1829            }
1830        }
1831        Wrapper(self)
1832    }
1833
1834    /// Return a `format`able structure with the a human readable
1835    /// description of this LogicalPlan node per node, not including
1836    /// children. For example:
1837    ///
1838    /// ```text
1839    /// Projection: id
1840    /// ```
1841    /// ```
1842    /// use arrow::datatypes::{DataType, Field, Schema};
1843    /// use datafusion_expr::{col, lit, logical_plan::table_scan, LogicalPlanBuilder};
1844    /// let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
1845    /// let plan = table_scan(Some("t1"), &schema, None)
1846    ///     .unwrap()
1847    ///     .build()
1848    ///     .unwrap();
1849    ///
1850    /// // Format using display
1851    /// let display_string = format!("{}", plan.display());
1852    ///
1853    /// assert_eq!("TableScan: t1", display_string);
1854    /// ```
1855    pub fn display(&self) -> impl Display + '_ {
1856        // Boilerplate structure to wrap LogicalPlan with something
1857        // that that can be formatted
1858        struct Wrapper<'a>(&'a LogicalPlan);
1859        impl Display for Wrapper<'_> {
1860            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1861                match self.0 {
1862                    LogicalPlan::EmptyRelation(EmptyRelation {
1863                        produce_one_row,
1864                        schema: _,
1865                    }) => {
1866                        let rows = if *produce_one_row { 1 } else { 0 };
1867                        write!(f, "EmptyRelation: rows={rows}")
1868                    }
1869                    LogicalPlan::RecursiveQuery(RecursiveQuery {
1870                        is_distinct, ..
1871                    }) => {
1872                        write!(f, "RecursiveQuery: is_distinct={is_distinct}")
1873                    }
1874                    LogicalPlan::Values(Values { values, .. }) => {
1875                        let str_values: Vec<_> = values
1876                            .iter()
1877                            // limit to only 5 values to avoid horrible display
1878                            .take(5)
1879                            .map(|row| {
1880                                let item = row
1881                                    .iter()
1882                                    .map(|expr| expr.to_string())
1883                                    .collect::<Vec<_>>()
1884                                    .join(", ");
1885                                format!("({item})")
1886                            })
1887                            .collect();
1888
1889                        let eclipse = if values.len() > 5 { "..." } else { "" };
1890                        write!(f, "Values: {}{}", str_values.join(", "), eclipse)
1891                    }
1892
1893                    LogicalPlan::TableScan(TableScan {
1894                        source,
1895                        table_name,
1896                        projection,
1897                        filters,
1898                        fetch,
1899                        ..
1900                    }) => {
1901                        let projected_fields = match projection {
1902                            Some(indices) => {
1903                                let schema = source.schema();
1904                                let names: Vec<&str> = indices
1905                                    .iter()
1906                                    .map(|i| schema.field(*i).name().as_str())
1907                                    .collect();
1908                                format!(" projection=[{}]", names.join(", "))
1909                            }
1910                            _ => "".to_string(),
1911                        };
1912
1913                        write!(f, "TableScan: {table_name}{projected_fields}")?;
1914
1915                        if !filters.is_empty() {
1916                            let mut full_filter = vec![];
1917                            let mut partial_filter = vec![];
1918                            let mut unsupported_filters = vec![];
1919                            let filters: Vec<&Expr> = filters.iter().collect();
1920
1921                            if let Ok(results) =
1922                                source.supports_filters_pushdown(&filters)
1923                            {
1924                                filters.iter().zip(results.iter()).for_each(
1925                                    |(x, res)| match res {
1926                                        TableProviderFilterPushDown::Exact => {
1927                                            full_filter.push(x)
1928                                        }
1929                                        TableProviderFilterPushDown::Inexact => {
1930                                            partial_filter.push(x)
1931                                        }
1932                                        TableProviderFilterPushDown::Unsupported => {
1933                                            unsupported_filters.push(x)
1934                                        }
1935                                    },
1936                                );
1937                            }
1938
1939                            if !full_filter.is_empty() {
1940                                write!(
1941                                    f,
1942                                    ", full_filters=[{}]",
1943                                    expr_vec_fmt!(full_filter)
1944                                )?;
1945                            };
1946                            if !partial_filter.is_empty() {
1947                                write!(
1948                                    f,
1949                                    ", partial_filters=[{}]",
1950                                    expr_vec_fmt!(partial_filter)
1951                                )?;
1952                            }
1953                            if !unsupported_filters.is_empty() {
1954                                write!(
1955                                    f,
1956                                    ", unsupported_filters=[{}]",
1957                                    expr_vec_fmt!(unsupported_filters)
1958                                )?;
1959                            }
1960                        }
1961
1962                        if let Some(n) = fetch {
1963                            write!(f, ", fetch={n}")?;
1964                        }
1965
1966                        Ok(())
1967                    }
1968                    LogicalPlan::Projection(Projection { expr, .. }) => {
1969                        write!(f, "Projection:")?;
1970                        for (i, expr_item) in expr.iter().enumerate() {
1971                            if i > 0 {
1972                                write!(f, ",")?;
1973                            }
1974                            write!(f, " {expr_item}")?;
1975                        }
1976                        Ok(())
1977                    }
1978                    LogicalPlan::Dml(DmlStatement { table_name, op, .. }) => {
1979                        write!(f, "Dml: op=[{op}] table=[{table_name}]")
1980                    }
1981                    LogicalPlan::Copy(CopyTo {
1982                        input: _,
1983                        output_url,
1984                        file_type,
1985                        options,
1986                        ..
1987                    }) => {
1988                        let op_str = options
1989                            .iter()
1990                            .map(|(k, v)| format!("{k} {v}"))
1991                            .collect::<Vec<String>>()
1992                            .join(", ");
1993
1994                        write!(
1995                            f,
1996                            "CopyTo: format={} output_url={output_url} options: ({op_str})",
1997                            file_type.get_ext()
1998                        )
1999                    }
2000                    LogicalPlan::Ddl(ddl) => {
2001                        write!(f, "{}", ddl.display())
2002                    }
2003                    LogicalPlan::Filter(Filter {
2004                        predicate: expr, ..
2005                    }) => write!(f, "Filter: {expr}"),
2006                    LogicalPlan::Window(Window { window_expr, .. }) => {
2007                        write!(
2008                            f,
2009                            "WindowAggr: windowExpr=[[{}]]",
2010                            expr_vec_fmt!(window_expr)
2011                        )
2012                    }
2013                    LogicalPlan::Aggregate(Aggregate {
2014                        group_expr,
2015                        aggr_expr,
2016                        ..
2017                    }) => write!(
2018                        f,
2019                        "Aggregate: groupBy=[[{}]], aggr=[[{}]]",
2020                        expr_vec_fmt!(group_expr),
2021                        expr_vec_fmt!(aggr_expr)
2022                    ),
2023                    LogicalPlan::Sort(Sort { expr, fetch, .. }) => {
2024                        write!(f, "Sort: ")?;
2025                        for (i, expr_item) in expr.iter().enumerate() {
2026                            if i > 0 {
2027                                write!(f, ", ")?;
2028                            }
2029                            write!(f, "{expr_item}")?;
2030                        }
2031                        if let Some(a) = fetch {
2032                            write!(f, ", fetch={a}")?;
2033                        }
2034
2035                        Ok(())
2036                    }
2037                    LogicalPlan::Join(Join {
2038                        on: keys,
2039                        filter,
2040                        join_constraint,
2041                        join_type,
2042                        ..
2043                    }) => {
2044                        let join_expr: Vec<String> =
2045                            keys.iter().map(|(l, r)| format!("{l} = {r}")).collect();
2046                        let filter_expr = filter
2047                            .as_ref()
2048                            .map(|expr| format!(" Filter: {expr}"))
2049                            .unwrap_or_else(|| "".to_string());
2050                        let join_type = if filter.is_none()
2051                            && keys.is_empty()
2052                            && *join_type == JoinType::Inner
2053                        {
2054                            "Cross".to_string()
2055                        } else {
2056                            join_type.to_string()
2057                        };
2058                        match join_constraint {
2059                            JoinConstraint::On => {
2060                                write!(f, "{join_type} Join:",)?;
2061                                if !join_expr.is_empty() || !filter_expr.is_empty() {
2062                                    write!(
2063                                        f,
2064                                        " {}{}",
2065                                        join_expr.join(", "),
2066                                        filter_expr
2067                                    )?;
2068                                }
2069                                Ok(())
2070                            }
2071                            JoinConstraint::Using => {
2072                                write!(
2073                                    f,
2074                                    "{} Join: Using {}{}",
2075                                    join_type,
2076                                    join_expr.join(", "),
2077                                    filter_expr,
2078                                )
2079                            }
2080                        }
2081                    }
2082                    LogicalPlan::Repartition(Repartition {
2083                        partitioning_scheme,
2084                        ..
2085                    }) => match partitioning_scheme {
2086                        Partitioning::RoundRobinBatch(n) => {
2087                            write!(f, "Repartition: RoundRobinBatch partition_count={n}")
2088                        }
2089                        Partitioning::Hash(expr, n) => {
2090                            let hash_expr: Vec<String> =
2091                                expr.iter().map(|e| format!("{e}")).collect();
2092                            write!(
2093                                f,
2094                                "Repartition: Hash({}) partition_count={}",
2095                                hash_expr.join(", "),
2096                                n
2097                            )
2098                        }
2099                        Partitioning::DistributeBy(expr) => {
2100                            let dist_by_expr: Vec<String> =
2101                                expr.iter().map(|e| format!("{e}")).collect();
2102                            write!(
2103                                f,
2104                                "Repartition: DistributeBy({})",
2105                                dist_by_expr.join(", "),
2106                            )
2107                        }
2108                    },
2109                    LogicalPlan::Limit(limit) => {
2110                        // Attempt to display `skip` and `fetch` as literals if possible, otherwise as expressions.
2111                        let skip_str = match limit.get_skip_type() {
2112                            Ok(SkipType::Literal(n)) => n.to_string(),
2113                            _ => limit
2114                                .skip
2115                                .as_ref()
2116                                .map_or_else(|| "None".to_string(), |x| x.to_string()),
2117                        };
2118                        let fetch_str = match limit.get_fetch_type() {
2119                            Ok(FetchType::Literal(Some(n))) => n.to_string(),
2120                            Ok(FetchType::Literal(None)) => "None".to_string(),
2121                            _ => limit
2122                                .fetch
2123                                .as_ref()
2124                                .map_or_else(|| "None".to_string(), |x| x.to_string()),
2125                        };
2126                        write!(f, "Limit: skip={skip_str}, fetch={fetch_str}",)
2127                    }
2128                    LogicalPlan::Subquery(Subquery { .. }) => {
2129                        write!(f, "Subquery:")
2130                    }
2131                    LogicalPlan::SubqueryAlias(SubqueryAlias { alias, .. }) => {
2132                        write!(f, "SubqueryAlias: {alias}")
2133                    }
2134                    LogicalPlan::Statement(statement) => {
2135                        write!(f, "{}", statement.display())
2136                    }
2137                    LogicalPlan::Distinct(distinct) => match distinct {
2138                        Distinct::All(_) => write!(f, "Distinct:"),
2139                        Distinct::On(DistinctOn {
2140                            on_expr,
2141                            select_expr,
2142                            sort_expr,
2143                            ..
2144                        }) => write!(
2145                            f,
2146                            "DistinctOn: on_expr=[[{}]], select_expr=[[{}]], sort_expr=[[{}]]",
2147                            expr_vec_fmt!(on_expr),
2148                            expr_vec_fmt!(select_expr),
2149                            if let Some(sort_expr) = sort_expr {
2150                                expr_vec_fmt!(sort_expr)
2151                            } else {
2152                                "".to_string()
2153                            },
2154                        ),
2155                    },
2156                    LogicalPlan::Explain { .. } => write!(f, "Explain"),
2157                    LogicalPlan::Analyze { .. } => write!(f, "Analyze"),
2158                    LogicalPlan::Union(_) => write!(f, "Union"),
2159                    LogicalPlan::Extension(e) => e.node.fmt_for_explain(f),
2160                    LogicalPlan::DescribeTable(DescribeTable { .. }) => {
2161                        write!(f, "DescribeTable")
2162                    }
2163                    LogicalPlan::Unnest(Unnest {
2164                        input: plan,
2165                        list_type_columns: list_col_indices,
2166                        struct_type_columns: struct_col_indices,
2167                        ..
2168                    }) => {
2169                        let input_columns = plan.schema().columns();
2170                        let list_type_columns = list_col_indices
2171                            .iter()
2172                            .map(|(i, unnest_info)| {
2173                                format!(
2174                                    "{}|depth={}",
2175                                    &input_columns[*i].to_string(),
2176                                    unnest_info.depth
2177                                )
2178                            })
2179                            .collect::<Vec<String>>();
2180                        let struct_type_columns = struct_col_indices
2181                            .iter()
2182                            .map(|i| &input_columns[*i])
2183                            .collect::<Vec<&Column>>();
2184                        // get items from input_columns indexed by list_col_indices
2185                        write!(
2186                            f,
2187                            "Unnest: lists[{}] structs[{}]",
2188                            expr_vec_fmt!(list_type_columns),
2189                            expr_vec_fmt!(struct_type_columns)
2190                        )
2191                    }
2192                }
2193            }
2194        }
2195        Wrapper(self)
2196    }
2197
2198    /// Return a `LogicalPLan` with all [`LambdaVariable`]'s resolved
2199    ///
2200    /// [`LambdaVariable`]: crate::expr::LambdaVariable
2201    pub fn resolve_lambda_variables(self) -> Result<Transformed<LogicalPlan>> {
2202        self.transform_with_subqueries(|plan| {
2203            let schema = merge_schema(&plan.inputs());
2204
2205            plan.map_expressions(|expr| expr.resolve_lambda_variables(&schema))
2206        })
2207    }
2208}
2209
2210impl Display for LogicalPlan {
2211    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
2212        self.display_indent().fmt(f)
2213    }
2214}
2215
2216impl ToStringifiedPlan for LogicalPlan {
2217    fn to_stringified(&self, plan_type: PlanType) -> StringifiedPlan {
2218        StringifiedPlan::new(plan_type, self.display_indent().to_string())
2219    }
2220}
2221
2222/// Relationship produces 0 or 1 placeholder rows with specified output schema
2223/// In most cases the output schema for `EmptyRelation` would be empty,
2224/// however, it can be non-empty typically for optimizer rules
2225#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2226pub struct EmptyRelation {
2227    /// Whether to produce a placeholder row
2228    pub produce_one_row: bool,
2229    /// The schema description of the output
2230    pub schema: DFSchemaRef,
2231}
2232
2233// Manual implementation needed because of `schema` field. Comparison excludes this field.
2234impl PartialOrd for EmptyRelation {
2235    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2236        self.produce_one_row
2237            .partial_cmp(&other.produce_one_row)
2238            // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
2239            .filter(|cmp| *cmp != Ordering::Equal || self == other)
2240    }
2241}
2242
2243/// A variadic query operation, Recursive CTE.
2244///
2245/// # Recursive Query Evaluation
2246///
2247/// From the [Postgres Docs]:
2248///
2249/// 1. Evaluate the non-recursive term. For `UNION` (but not `UNION ALL`),
2250///    discard duplicate rows. Include all remaining rows in the result of the
2251///    recursive query, and also place them in a temporary working table.
2252///
2253/// 2. So long as the working table is not empty, repeat these steps:
2254///
2255/// * Evaluate the recursive term, substituting the current contents of the
2256///   working table for the recursive self-reference. For `UNION` (but not `UNION
2257///   ALL`), discard duplicate rows and rows that duplicate any previous result
2258///   row. Include all remaining rows in the result of the recursive query, and
2259///   also place them in a temporary intermediate table.
2260///
2261/// * Replace the contents of the working table with the contents of the
2262///   intermediate table, then empty the intermediate table.
2263///
2264/// [Postgres Docs]: https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-RECURSIVE
2265#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2266pub struct RecursiveQuery {
2267    /// Name of the query
2268    pub name: String,
2269    /// The static term (initial contents of the working table)
2270    pub static_term: Arc<LogicalPlan>,
2271    /// The recursive term (evaluated on the contents of the working table until
2272    /// it returns an empty set)
2273    pub recursive_term: Arc<LogicalPlan>,
2274    /// Should the output of the recursive term be deduplicated (`UNION`) or
2275    /// not (`UNION ALL`).
2276    pub is_distinct: bool,
2277    /// Schema exposed to parent plans after reconciling the static and recursive terms.
2278    pub schema: DFSchemaRef,
2279}
2280
2281impl PartialOrd for RecursiveQuery {
2282    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2283        match self.name.partial_cmp(&other.name) {
2284            Some(Ordering::Equal) => {
2285                match self.static_term.partial_cmp(&other.static_term) {
2286                    Some(Ordering::Equal) => {
2287                        match self.recursive_term.partial_cmp(&other.recursive_term) {
2288                            Some(Ordering::Equal) => {
2289                                self.is_distinct.partial_cmp(&other.is_distinct)
2290                            }
2291                            cmp => cmp,
2292                        }
2293                    }
2294                    cmp => cmp,
2295                }
2296            }
2297            cmp => cmp,
2298        }
2299        // If the query definition compares equal but the derived schema differs,
2300        // return `None` instead of contradicting `PartialEq` with `Some(Equal)`.
2301        // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
2302        .filter(|cmp| *cmp != Ordering::Equal || self == other)
2303    }
2304}
2305
2306impl RecursiveQuery {
2307    pub fn try_new(
2308        name: String,
2309        static_term: Arc<LogicalPlan>,
2310        recursive_term: Arc<LogicalPlan>,
2311        is_distinct: bool,
2312    ) -> Result<Self> {
2313        let schema =
2314            recursive_query_output_schema(static_term.schema(), recursive_term.schema())?;
2315        Ok(Self {
2316            name,
2317            static_term,
2318            recursive_term,
2319            is_distinct,
2320            schema,
2321        })
2322    }
2323}
2324
2325/// Compute a recursive query's output schema by considering both its static and
2326/// recursive terms.
2327///
2328/// Field names, types, and metadata come from the static term. A field is
2329/// nullable if either the static or the recursive term produces a nullable
2330/// value in that position, matching how `UNION` reconciles branch nullability.
2331///
2332/// Functional dependencies are intentionally dropped: the recursive term
2333/// appends rows that can duplicate values the static term guarantees unique, so
2334/// any FDs carried by the static term may not hold over the combined output.
2335fn recursive_query_output_schema(
2336    static_schema: &DFSchemaRef,
2337    recursive_schema: &DFSchemaRef,
2338) -> Result<DFSchemaRef> {
2339    if static_schema.fields().len() != recursive_schema.fields().len() {
2340        return Err(DataFusionError::Plan(format!(
2341            "Non-recursive term and recursive term must have the same number of columns ({} != {})",
2342            static_schema.fields().len(),
2343            recursive_schema.fields().len()
2344        )));
2345    }
2346
2347    let fields = static_schema
2348        .iter()
2349        .zip(recursive_schema.fields())
2350        .map(|((qualifier, static_field), recursive_field)| {
2351            let nullable = static_field.is_nullable() || recursive_field.is_nullable();
2352            (
2353                qualifier.cloned(),
2354                static_field.as_ref().clone().with_nullable(nullable).into(),
2355            )
2356        })
2357        .collect::<Vec<_>>();
2358
2359    DFSchema::new_with_metadata(fields, static_schema.metadata().clone())
2360        .map(DFSchemaRef::new)
2361}
2362
2363/// Values expression. See
2364/// [Postgres VALUES](https://www.postgresql.org/docs/current/queries-values.html)
2365/// documentation for more details.
2366#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2367pub struct Values {
2368    /// The table schema
2369    pub schema: DFSchemaRef,
2370    /// Values
2371    pub values: Vec<Vec<Expr>>,
2372}
2373
2374// Manual implementation needed because of `schema` field. Comparison excludes this field.
2375impl PartialOrd for Values {
2376    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2377        self.values
2378            .partial_cmp(&other.values)
2379            // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
2380            .filter(|cmp| *cmp != Ordering::Equal || self == other)
2381    }
2382}
2383
2384/// Evaluates an arbitrary list of expressions (essentially a
2385/// SELECT with an expression list) on its input.
2386#[derive(Clone, PartialEq, Eq, Hash, Debug)]
2387// mark non_exhaustive to encourage use of try_new/new()
2388#[non_exhaustive]
2389pub struct Projection {
2390    /// The list of expressions
2391    pub expr: Vec<Expr>,
2392    /// The incoming logical plan
2393    pub input: Arc<LogicalPlan>,
2394    /// The schema description of the output
2395    pub schema: DFSchemaRef,
2396}
2397
2398// Manual implementation needed because of `schema` field. Comparison excludes this field.
2399impl PartialOrd for Projection {
2400    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2401        match self.expr.partial_cmp(&other.expr) {
2402            Some(Ordering::Equal) => self.input.partial_cmp(&other.input),
2403            cmp => cmp,
2404        }
2405        // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
2406        .filter(|cmp| *cmp != Ordering::Equal || self == other)
2407    }
2408}
2409
2410impl Projection {
2411    /// Create a new Projection
2412    pub fn try_new(expr: Vec<Expr>, input: Arc<LogicalPlan>) -> Result<Self> {
2413        let projection_schema = projection_schema(&input, &expr)?;
2414        Self::try_new_with_schema(expr, input, projection_schema)
2415    }
2416
2417    /// Create a new Projection using the specified output schema
2418    pub fn try_new_with_schema(
2419        expr: Vec<Expr>,
2420        input: Arc<LogicalPlan>,
2421        schema: DFSchemaRef,
2422    ) -> Result<Self> {
2423        #[expect(deprecated)]
2424        if !expr.iter().any(|e| matches!(e, Expr::Wildcard { .. }))
2425            && expr.len() != schema.fields().len()
2426        {
2427            return plan_err!(
2428                "Projection has mismatch between number of expressions ({}) and number of fields in schema ({})",
2429                expr.len(),
2430                schema.fields().len()
2431            );
2432        }
2433        Ok(Self {
2434            expr,
2435            input,
2436            schema,
2437        })
2438    }
2439
2440    /// Create a new Projection using the specified output schema
2441    pub fn new_from_schema(input: Arc<LogicalPlan>, schema: DFSchemaRef) -> Self {
2442        let expr: Vec<Expr> = schema.columns().into_iter().map(Expr::Column).collect();
2443        Self {
2444            expr,
2445            input,
2446            schema,
2447        }
2448    }
2449}
2450
2451/// Computes the schema of the result produced by applying a projection to the input logical plan.
2452///
2453/// # Arguments
2454///
2455/// * `input`: A reference to the input `LogicalPlan` for which the projection schema
2456///   will be computed.
2457/// * `exprs`: A slice of `Expr` expressions representing the projection operation to apply.
2458///
2459/// # Metadata Handling
2460///
2461/// - **Schema-level metadata**: Passed through unchanged from the input schema
2462/// - **Field-level metadata**: Determined by each expression via [`exprlist_to_fields`], which
2463///   calls [`Expr::to_field`] to handle expression-specific metadata (literals, aliases, etc.)
2464///
2465/// # Returns
2466///
2467/// A `Result` containing an `Arc<DFSchema>` representing the schema of the result
2468/// produced by the projection operation. If the schema computation is successful,
2469/// the `Result` will contain the schema; otherwise, it will contain an error.
2470pub fn projection_schema(input: &LogicalPlan, exprs: &[Expr]) -> Result<Arc<DFSchema>> {
2471    // Preserve input schema metadata at the schema level
2472    let metadata = input.schema().metadata().clone();
2473
2474    // Convert expressions to fields with Field properties determined by `Expr::to_field`
2475    let schema =
2476        DFSchema::new_with_metadata(exprlist_to_fields(exprs, input)?, metadata)?
2477            .with_functional_dependencies(calc_func_dependencies_for_project(
2478                exprs, input,
2479            )?)?;
2480
2481    Ok(Arc::new(schema))
2482}
2483
2484/// Aliased subquery
2485#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2486// mark non_exhaustive to encourage use of try_new/new()
2487#[non_exhaustive]
2488pub struct SubqueryAlias {
2489    /// The incoming logical plan
2490    pub input: Arc<LogicalPlan>,
2491    /// The alias for the input relation
2492    pub alias: TableReference,
2493    /// The schema with qualified field names
2494    pub schema: DFSchemaRef,
2495}
2496
2497impl SubqueryAlias {
2498    pub fn try_new(
2499        plan: Arc<LogicalPlan>,
2500        alias: impl Into<TableReference>,
2501    ) -> Result<Self> {
2502        let alias = alias.into();
2503
2504        // Since SubqueryAlias will replace all field qualification for the output schema of `plan`,
2505        // no field must share the same column name as this would lead to ambiguity when referencing
2506        // columns in parent logical nodes.
2507
2508        // Compute unique aliases, if any, for each column of the input's schema.
2509        let aliases = unique_field_aliases(plan.schema().fields());
2510        let is_projection_needed = aliases.iter().any(Option::is_some);
2511
2512        // Insert a projection node, if needed, to make sure aliases are applied.
2513        let plan = if is_projection_needed {
2514            let projection_expressions = aliases
2515                .iter()
2516                .zip(plan.schema().iter())
2517                .map(|(alias, (qualifier, field))| {
2518                    let column =
2519                        Expr::Column(Column::new(qualifier.cloned(), field.name()));
2520                    match alias {
2521                        None => column,
2522                        Some(alias) => {
2523                            Expr::Alias(Alias::new(column, qualifier.cloned(), alias))
2524                        }
2525                    }
2526                })
2527                .collect();
2528            let projection = Projection::try_new(projection_expressions, plan)?;
2529            Arc::new(LogicalPlan::Projection(projection))
2530        } else {
2531            plan
2532        };
2533
2534        // Requalify fields with the new `alias`.
2535        let fields = plan.schema().fields().clone();
2536        let meta_data = plan.schema().metadata().clone();
2537        let func_dependencies = plan.schema().functional_dependencies().clone();
2538
2539        let schema = DFSchema::from_unqualified_fields(fields, meta_data)?;
2540        let schema = schema.as_arrow();
2541
2542        let schema = DFSchemaRef::new(
2543            DFSchema::try_from_qualified_schema(alias.clone(), schema)?
2544                .with_functional_dependencies(func_dependencies)?,
2545        );
2546        Ok(SubqueryAlias {
2547            input: plan,
2548            alias,
2549            schema,
2550        })
2551    }
2552}
2553
2554// Manual implementation needed because of `schema` field. Comparison excludes this field.
2555impl PartialOrd for SubqueryAlias {
2556    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2557        match self.input.partial_cmp(&other.input) {
2558            Some(Ordering::Equal) => self.alias.partial_cmp(&other.alias),
2559            cmp => cmp,
2560        }
2561        // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
2562        .filter(|cmp| *cmp != Ordering::Equal || self == other)
2563    }
2564}
2565
2566/// Filters rows from its input that do not match an
2567/// expression (essentially a WHERE clause with a predicate
2568/// expression).
2569///
2570/// Semantically, `<predicate>` is evaluated for each row of the input;
2571/// If the value of `<predicate>` is true, the input row is passed to
2572/// the output. If the value of `<predicate>` is false, the row is
2573/// discarded.
2574///
2575/// Filter should not be created directly but instead use `try_new()`
2576/// and that these fields are only pub to support pattern matching
2577#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
2578#[non_exhaustive]
2579pub struct Filter {
2580    /// The predicate expression, which must have Boolean type.
2581    pub predicate: Expr,
2582    /// The incoming logical plan
2583    pub input: Arc<LogicalPlan>,
2584}
2585
2586impl Filter {
2587    /// Create a new filter operator.
2588    ///
2589    /// Notes: as Aliases have no effect on the output of a filter operator,
2590    /// they are removed from the predicate expression.
2591    pub fn try_new(predicate: Expr, input: Arc<LogicalPlan>) -> Result<Self> {
2592        Self::try_new_internal(predicate, input)
2593    }
2594
2595    /// Create a new filter operator for a having clause.
2596    /// This is similar to a filter, but its having flag is set to true.
2597    #[deprecated(since = "48.0.0", note = "Use `try_new` instead")]
2598    pub fn try_new_with_having(predicate: Expr, input: Arc<LogicalPlan>) -> Result<Self> {
2599        Self::try_new_internal(predicate, input)
2600    }
2601
2602    fn is_allowed_filter_type(data_type: &DataType) -> bool {
2603        match data_type {
2604            // Interpret NULL as a missing boolean value.
2605            DataType::Boolean | DataType::Null => true,
2606            DataType::Dictionary(_, value_type) => {
2607                Filter::is_allowed_filter_type(value_type.as_ref())
2608            }
2609            _ => false,
2610        }
2611    }
2612
2613    fn try_new_internal(predicate: Expr, input: Arc<LogicalPlan>) -> Result<Self> {
2614        // Filter predicates must return a boolean value so we try and validate that here.
2615        // Note that it is not always possible to resolve the predicate expression during plan
2616        // construction (such as with correlated subqueries) so we make a best effort here and
2617        // ignore errors resolving the expression against the schema.
2618        if let Ok(predicate_type) = predicate.get_type(input.schema())
2619            && !Filter::is_allowed_filter_type(&predicate_type)
2620        {
2621            return plan_err!(
2622                "Cannot create filter with non-boolean predicate '{predicate}' returning {predicate_type}"
2623            );
2624        }
2625
2626        Ok(Self {
2627            predicate: predicate.unalias_nested().data,
2628            input,
2629        })
2630    }
2631
2632    /// Is this filter guaranteed to return 0 or 1 row in a given instantiation?
2633    ///
2634    /// This function will return `true` if its predicate contains a conjunction of
2635    /// `col(a) = <expr>`, where its schema has a unique filter that is covered
2636    /// by this conjunction.
2637    ///
2638    /// For example, for the table:
2639    /// ```sql
2640    /// CREATE TABLE t (a INTEGER PRIMARY KEY, b INTEGER);
2641    /// ```
2642    /// `Filter(a = 2).is_scalar() == true`
2643    /// , whereas
2644    /// `Filter(b = 2).is_scalar() == false`
2645    /// and
2646    /// `Filter(a = 2 OR b = 2).is_scalar() == false`
2647    fn is_scalar(&self) -> bool {
2648        let schema = self.input.schema();
2649
2650        let functional_dependencies = self.input.schema().functional_dependencies();
2651        let unique_keys = functional_dependencies.iter().filter(|dep| {
2652            let nullable = dep.nullable
2653                && dep
2654                    .source_indices
2655                    .iter()
2656                    .any(|&source| schema.field(source).is_nullable());
2657            !nullable
2658                && dep.mode == Dependency::Single
2659                && dep.target_indices.len() == schema.fields().len()
2660        });
2661
2662        let exprs = split_conjunction(&self.predicate);
2663        let eq_pred_cols: HashSet<_> = exprs
2664            .iter()
2665            .filter_map(|expr| {
2666                let Expr::BinaryExpr(BinaryExpr {
2667                    left,
2668                    op: Operator::Eq,
2669                    right,
2670                }) = expr
2671                else {
2672                    return None;
2673                };
2674                // This is a no-op filter expression
2675                if left == right {
2676                    return None;
2677                }
2678
2679                match (left.as_ref(), right.as_ref()) {
2680                    (Expr::Column(_), Expr::Column(_)) => None,
2681                    (Expr::Column(c), _) | (_, Expr::Column(c)) => {
2682                        Some(schema.index_of_column(c).unwrap())
2683                    }
2684                    _ => None,
2685                }
2686            })
2687            .collect();
2688
2689        // If we have a functional dependence that is a subset of our predicate,
2690        // this filter is scalar
2691        for key in unique_keys {
2692            if key.source_indices.iter().all(|c| eq_pred_cols.contains(c)) {
2693                return true;
2694            }
2695        }
2696        false
2697    }
2698}
2699
2700/// Window its input based on a set of window spec and window function (e.g. SUM or RANK)
2701///
2702/// # Output Schema
2703///
2704/// The output schema is the input schema followed by the window function
2705/// expressions, in order.
2706///
2707/// For example, given the input schema `"A", "B", "C"` and the window function
2708/// `SUM(A) OVER (PARTITION BY B+1 ORDER BY C)`, the output schema will be `"A",
2709/// "B", "C", "SUM(A) OVER ..."` where `"SUM(A) OVER ..."` is the name of the
2710/// output column.
2711///
2712/// Note that the `PARTITION BY` expression "B+1" is not produced in the output
2713/// schema.
2714#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2715pub struct Window {
2716    /// The incoming logical plan
2717    pub input: Arc<LogicalPlan>,
2718    /// The window function expression
2719    pub window_expr: Vec<Expr>,
2720    /// The schema description of the window output
2721    pub schema: DFSchemaRef,
2722}
2723
2724impl Window {
2725    /// Create a new window operator.
2726    pub fn try_new(window_expr: Vec<Expr>, input: Arc<LogicalPlan>) -> Result<Self> {
2727        let fields: Vec<(Option<TableReference>, Arc<Field>)> = input
2728            .schema()
2729            .iter()
2730            .map(|(q, f)| (q.cloned(), Arc::clone(f)))
2731            .collect();
2732        let input_len = fields.len();
2733        let mut window_fields = fields;
2734        let expr_fields = exprlist_to_fields(window_expr.as_slice(), &input)?;
2735        window_fields.extend_from_slice(expr_fields.as_slice());
2736        let metadata = input.schema().metadata().clone();
2737
2738        // Update functional dependencies for window:
2739        let mut window_func_dependencies =
2740            input.schema().functional_dependencies().clone();
2741        window_func_dependencies.extend_target_indices(window_fields.len());
2742
2743        // Since we know that ROW_NUMBER outputs will be unique (i.e. it consists
2744        // of consecutive numbers per partition), we can represent this fact with
2745        // functional dependencies.
2746        let mut new_dependencies = window_expr
2747            .iter()
2748            .enumerate()
2749            .filter_map(|(idx, expr)| {
2750                let Expr::WindowFunction(window_fun) = expr else {
2751                    return None;
2752                };
2753                let WindowFunction {
2754                    fun: WindowFunctionDefinition::WindowUDF(udwf),
2755                    params: WindowFunctionParams { partition_by, .. },
2756                } = window_fun.as_ref()
2757                else {
2758                    return None;
2759                };
2760                // When there is no PARTITION BY, row number will be unique
2761                // across the entire table.
2762                if udwf.name() == "row_number" && partition_by.is_empty() {
2763                    Some(idx + input_len)
2764                } else {
2765                    None
2766                }
2767            })
2768            .map(|idx| {
2769                FunctionalDependence::new(vec![idx], vec![], false)
2770                    .with_mode(Dependency::Single)
2771            })
2772            .collect::<Vec<_>>();
2773
2774        if !new_dependencies.is_empty() {
2775            for dependence in new_dependencies.iter_mut() {
2776                dependence.target_indices = (0..window_fields.len()).collect();
2777            }
2778            // Add the dependency introduced because of ROW_NUMBER window function to the functional dependency
2779            let new_deps = FunctionalDependencies::new(new_dependencies);
2780            window_func_dependencies.extend(new_deps);
2781        }
2782
2783        // Validate that FILTER clauses are only used with aggregate window functions
2784        if let Some(e) = window_expr.iter().find(|e| {
2785            matches!(
2786                e,
2787                Expr::WindowFunction(wf)
2788                    if !matches!(wf.fun, WindowFunctionDefinition::AggregateUDF(_))
2789                        && wf.params.filter.is_some()
2790            )
2791        }) {
2792            return plan_err!(
2793                "FILTER clause can only be used with aggregate window functions. Found in '{e}'"
2794            );
2795        }
2796
2797        Self::try_new_with_schema(
2798            window_expr,
2799            input,
2800            Arc::new(
2801                DFSchema::new_with_metadata(window_fields, metadata)?
2802                    .with_functional_dependencies(window_func_dependencies)?,
2803            ),
2804        )
2805    }
2806
2807    /// Create a new window function using the provided schema to avoid the overhead of
2808    /// building the schema again when the schema is already known.
2809    ///
2810    /// This method should only be called when you are absolutely sure that the schema being
2811    /// provided is correct for the window function. If in doubt, call [try_new](Self::try_new) instead.
2812    pub fn try_new_with_schema(
2813        window_expr: Vec<Expr>,
2814        input: Arc<LogicalPlan>,
2815        schema: DFSchemaRef,
2816    ) -> Result<Self> {
2817        let input_fields_count = input.schema().fields().len();
2818        if schema.fields().len() != input_fields_count + window_expr.len() {
2819            return plan_err!(
2820                "Window schema has wrong number of fields. Expected {} got {}",
2821                input_fields_count + window_expr.len(),
2822                schema.fields().len()
2823            );
2824        }
2825
2826        Ok(Window {
2827            input,
2828            window_expr,
2829            schema,
2830        })
2831    }
2832}
2833
2834// Manual implementation needed because of `schema` field. Comparison excludes this field.
2835impl PartialOrd for Window {
2836    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2837        match self.input.partial_cmp(&other.input)? {
2838            Ordering::Equal => {} // continue
2839            not_equal => return Some(not_equal),
2840        }
2841
2842        match self.window_expr.partial_cmp(&other.window_expr)? {
2843            Ordering::Equal => {} // continue
2844            not_equal => return Some(not_equal),
2845        }
2846
2847        // Contract for PartialOrd and PartialEq consistency requires that
2848        // a == b if and only if partial_cmp(a, b) == Some(Equal).
2849        if self == other {
2850            Some(Ordering::Equal)
2851        } else {
2852            None
2853        }
2854    }
2855}
2856
2857/// Produces rows from a table provider by reference or from the context
2858#[derive(Clone)]
2859pub struct TableScan {
2860    /// The name of the table
2861    pub table_name: TableReference,
2862    /// The source of the table
2863    pub source: Arc<dyn TableSource>,
2864    /// Optional column indices to use as a projection
2865    pub projection: Option<Vec<usize>>,
2866    /// The schema description of the output
2867    pub projected_schema: DFSchemaRef,
2868    /// Optional expressions to be used as filters by the table provider
2869    pub filters: Vec<Expr>,
2870    /// Optional number of rows to read
2871    pub fetch: Option<usize>,
2872}
2873
2874impl Debug for TableScan {
2875    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
2876        f.debug_struct("TableScan")
2877            .field("table_name", &self.table_name)
2878            .field("source", &"...")
2879            .field("projection", &self.projection)
2880            .field("projected_schema", &self.projected_schema)
2881            .field("filters", &self.filters)
2882            .field("fetch", &self.fetch)
2883            .finish_non_exhaustive()
2884    }
2885}
2886
2887impl PartialEq for TableScan {
2888    fn eq(&self, other: &Self) -> bool {
2889        self.table_name == other.table_name
2890            && self.projection == other.projection
2891            && self.projected_schema == other.projected_schema
2892            && self.filters == other.filters
2893            && self.fetch == other.fetch
2894    }
2895}
2896
2897impl Eq for TableScan {}
2898
2899// Manual implementation needed because of `source` and `projected_schema` fields.
2900// Comparison excludes these field.
2901impl PartialOrd for TableScan {
2902    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2903        #[derive(PartialEq, PartialOrd)]
2904        struct ComparableTableScan<'a> {
2905            /// The name of the table
2906            pub table_name: &'a TableReference,
2907            /// Optional column indices to use as a projection
2908            pub projection: &'a Option<Vec<usize>>,
2909            /// Optional expressions to be used as filters by the table provider
2910            pub filters: &'a Vec<Expr>,
2911            /// Optional number of rows to read
2912            pub fetch: &'a Option<usize>,
2913        }
2914        let comparable_self = ComparableTableScan {
2915            table_name: &self.table_name,
2916            projection: &self.projection,
2917            filters: &self.filters,
2918            fetch: &self.fetch,
2919        };
2920        let comparable_other = ComparableTableScan {
2921            table_name: &other.table_name,
2922            projection: &other.projection,
2923            filters: &other.filters,
2924            fetch: &other.fetch,
2925        };
2926        comparable_self
2927            .partial_cmp(&comparable_other)
2928            // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
2929            .filter(|cmp| *cmp != Ordering::Equal || self == other)
2930    }
2931}
2932
2933impl Hash for TableScan {
2934    fn hash<H: Hasher>(&self, state: &mut H) {
2935        self.table_name.hash(state);
2936        self.projection.hash(state);
2937        self.projected_schema.hash(state);
2938        self.filters.hash(state);
2939        self.fetch.hash(state);
2940    }
2941}
2942
2943impl TableScan {
2944    /// Initialize TableScan with appropriate schema from the given
2945    /// arguments.
2946    pub fn try_new(
2947        table_name: impl Into<TableReference>,
2948        table_source: Arc<dyn TableSource>,
2949        projection: Option<Vec<usize>>,
2950        filters: Vec<Expr>,
2951        fetch: Option<usize>,
2952    ) -> Result<Self> {
2953        let table_name = table_name.into();
2954
2955        if table_name.table().is_empty() {
2956            return plan_err!("table_name cannot be empty");
2957        }
2958        let schema = table_source.schema();
2959        let func_dependencies = FunctionalDependencies::new_from_constraints(
2960            table_source.constraints(),
2961            schema.fields.len(),
2962        );
2963        let projected_schema = projection
2964            .as_ref()
2965            .map(|p| {
2966                let projected_func_dependencies =
2967                    func_dependencies.project_functional_dependencies(p, p.len());
2968
2969                let df_schema = DFSchema::new_with_metadata(
2970                    p.iter()
2971                        .map(|i| {
2972                            (Some(table_name.clone()), Arc::clone(&schema.fields()[*i]))
2973                        })
2974                        .collect(),
2975                    schema.metadata.clone(),
2976                )?;
2977                df_schema.with_functional_dependencies(projected_func_dependencies)
2978            })
2979            .unwrap_or_else(|| {
2980                let df_schema =
2981                    DFSchema::try_from_qualified_schema(table_name.clone(), &schema)?;
2982                df_schema.with_functional_dependencies(func_dependencies)
2983            })?;
2984        let projected_schema = Arc::new(projected_schema);
2985
2986        Ok(Self {
2987            table_name,
2988            source: table_source,
2989            projection,
2990            projected_schema,
2991            filters,
2992            fetch,
2993        })
2994    }
2995}
2996
2997// Repartition the plan based on a partitioning scheme.
2998#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
2999pub struct Repartition {
3000    /// The incoming logical plan
3001    pub input: Arc<LogicalPlan>,
3002    /// The partitioning scheme
3003    pub partitioning_scheme: Partitioning,
3004}
3005
3006/// Union multiple inputs
3007#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3008pub struct Union {
3009    /// Inputs to merge
3010    pub inputs: Vec<Arc<LogicalPlan>>,
3011    /// Union schema. Should be the same for all inputs.
3012    pub schema: DFSchemaRef,
3013}
3014
3015impl Union {
3016    /// Constructs new Union instance deriving schema from inputs.
3017    /// Schema data types must match exactly.
3018    pub fn try_new(inputs: Vec<Arc<LogicalPlan>>) -> Result<Self> {
3019        let schema = Self::derive_schema_from_inputs(&inputs, false, false)?;
3020        Ok(Union { inputs, schema })
3021    }
3022
3023    /// Constructs new Union instance deriving schema from inputs.
3024    /// Inputs do not have to have matching types and produced schema will
3025    /// take type from the first input.
3026    // TODO (https://github.com/apache/datafusion/issues/14380): Avoid creating uncoerced union at all.
3027    pub fn try_new_with_loose_types(inputs: Vec<Arc<LogicalPlan>>) -> Result<Self> {
3028        let schema = Self::derive_schema_from_inputs(&inputs, true, false)?;
3029        Ok(Union { inputs, schema })
3030    }
3031
3032    /// Constructs a new Union instance that combines rows from different tables by name,
3033    /// instead of by position. This means that the specified inputs need not have schemas
3034    /// that are all the same width.
3035    pub fn try_new_by_name(inputs: Vec<Arc<LogicalPlan>>) -> Result<Self> {
3036        let schema = Self::derive_schema_from_inputs(&inputs, true, true)?;
3037        let inputs = Self::rewrite_inputs_from_schema(&schema, inputs)?;
3038
3039        Ok(Union { inputs, schema })
3040    }
3041
3042    /// When constructing a `UNION BY NAME`, we need to wrap inputs
3043    /// in an additional `Projection` to account for absence of columns
3044    /// in input schemas or differing projection orders.
3045    fn rewrite_inputs_from_schema(
3046        schema: &Arc<DFSchema>,
3047        inputs: Vec<Arc<LogicalPlan>>,
3048    ) -> Result<Vec<Arc<LogicalPlan>>> {
3049        let schema_width = schema.iter().count();
3050        let mut wrapped_inputs = Vec::with_capacity(inputs.len());
3051        for input in inputs {
3052            // Any columns that exist within the derived schema but do not exist
3053            // within an input's schema should be replaced with `NULL` aliased
3054            // to the appropriate column in the derived schema.
3055            let mut expr = Vec::with_capacity(schema_width);
3056            for column in schema.columns() {
3057                if input
3058                    .schema()
3059                    .has_column_with_unqualified_name(column.name())
3060                {
3061                    expr.push(Expr::Column(column));
3062                } else {
3063                    expr.push(
3064                        Expr::Literal(ScalarValue::Null, None).alias(column.name()),
3065                    );
3066                }
3067            }
3068            wrapped_inputs.push(Arc::new(LogicalPlan::Projection(
3069                Projection::try_new_with_schema(expr, input, Arc::clone(schema))?,
3070            )));
3071        }
3072
3073        Ok(wrapped_inputs)
3074    }
3075
3076    /// Constructs new Union instance deriving schema from inputs.
3077    ///
3078    /// If `loose_types` is true, inputs do not need to have matching types and
3079    /// the produced schema will use the type from the first input.
3080    /// TODO (<https://github.com/apache/datafusion/issues/14380>): This is not necessarily reasonable behavior.
3081    ///
3082    /// If `by_name` is `true`, input schemas need not be the same width. That is,
3083    /// the constructed schema follows `UNION BY NAME` semantics.
3084    fn derive_schema_from_inputs(
3085        inputs: &[Arc<LogicalPlan>],
3086        loose_types: bool,
3087        by_name: bool,
3088    ) -> Result<DFSchemaRef> {
3089        if inputs.len() < 2 {
3090            return plan_err!("UNION requires at least two inputs");
3091        }
3092
3093        if by_name {
3094            Self::derive_schema_from_inputs_by_name(inputs, loose_types)
3095        } else {
3096            Self::derive_schema_from_inputs_by_position(inputs, loose_types)
3097        }
3098    }
3099
3100    fn derive_schema_from_inputs_by_name(
3101        inputs: &[Arc<LogicalPlan>],
3102        loose_types: bool,
3103    ) -> Result<DFSchemaRef> {
3104        type FieldData<'a> =
3105            (&'a DataType, bool, Vec<&'a HashMap<String, String>>, usize);
3106        let mut cols: Vec<(&str, FieldData)> = Vec::new();
3107        for input in inputs.iter() {
3108            for field in input.schema().fields() {
3109                if let Some((_, (data_type, is_nullable, metadata, occurrences))) =
3110                    cols.iter_mut().find(|(name, _)| name == field.name())
3111                {
3112                    if !loose_types && *data_type != field.data_type() {
3113                        return plan_err!(
3114                            "Found different types for field {}",
3115                            field.name()
3116                        );
3117                    }
3118
3119                    metadata.push(field.metadata());
3120                    // If the field is nullable in any one of the inputs,
3121                    // then the field in the final schema is also nullable.
3122                    *is_nullable |= field.is_nullable();
3123                    *occurrences += 1;
3124                } else {
3125                    cols.push((
3126                        field.name(),
3127                        (
3128                            field.data_type(),
3129                            field.is_nullable(),
3130                            vec![field.metadata()],
3131                            1,
3132                        ),
3133                    ));
3134                }
3135            }
3136        }
3137
3138        let union_fields = cols
3139            .into_iter()
3140            .map(
3141                |(name, (data_type, is_nullable, unmerged_metadata, occurrences))| {
3142                    // If the final number of occurrences of the field is less
3143                    // than the number of inputs (i.e. the field is missing from
3144                    // one or more inputs), then it must be treated as nullable.
3145                    let final_is_nullable = if occurrences == inputs.len() {
3146                        is_nullable
3147                    } else {
3148                        true
3149                    };
3150
3151                    let mut field =
3152                        Field::new(name, data_type.clone(), final_is_nullable);
3153                    field.set_metadata(intersect_metadata_for_union(unmerged_metadata));
3154
3155                    (None, Arc::new(field))
3156                },
3157            )
3158            .collect::<Vec<(Option<TableReference>, _)>>();
3159
3160        let union_schema_metadata = intersect_metadata_for_union(
3161            inputs.iter().map(|input| input.schema().metadata()),
3162        );
3163
3164        // Functional Dependencies are not preserved after UNION operation
3165        let schema = DFSchema::new_with_metadata(union_fields, union_schema_metadata)?;
3166        let schema = Arc::new(schema);
3167
3168        Ok(schema)
3169    }
3170
3171    fn derive_schema_from_inputs_by_position(
3172        inputs: &[Arc<LogicalPlan>],
3173        loose_types: bool,
3174    ) -> Result<DFSchemaRef> {
3175        let first_schema = inputs[0].schema();
3176        let fields_count = first_schema.fields().len();
3177        for input in inputs.iter().skip(1) {
3178            if fields_count != input.schema().fields().len() {
3179                return plan_err!(
3180                    "UNION queries have different number of columns: \
3181                    left has {} columns whereas right has {} columns",
3182                    fields_count,
3183                    input.schema().fields().len()
3184                );
3185            }
3186        }
3187
3188        let mut name_counts: HashMap<String, usize> = HashMap::new();
3189        let union_fields = (0..fields_count)
3190            .map(|i| {
3191                let fields = inputs
3192                    .iter()
3193                    .map(|input| input.schema().field(i))
3194                    .collect::<Vec<_>>();
3195                let first_field = fields[0];
3196                let base_name = first_field.name().to_string();
3197
3198                let data_type = if loose_types {
3199                    // TODO apply type coercion here, or document why it's better to defer
3200                    // temporarily use the data type from the left input and later rely on the analyzer to
3201                    // coerce the two schemas into a common one.
3202                    first_field.data_type()
3203                } else {
3204                    fields.iter().skip(1).try_fold(
3205                        first_field.data_type(),
3206                        |acc, field| {
3207                            if acc != field.data_type() {
3208                                return plan_err!(
3209                                    "UNION field {i} have different type in inputs: \
3210                                    left has {} whereas right has {}",
3211                                    first_field.data_type(),
3212                                    field.data_type()
3213                                );
3214                            }
3215                            Ok(acc)
3216                        },
3217                    )?
3218                };
3219                let nullable = fields.iter().any(|field| field.is_nullable());
3220
3221                // Generate unique field name
3222                let name = if let Some(count) = name_counts.get_mut(&base_name) {
3223                    *count += 1;
3224                    format!("{base_name}_{count}")
3225                } else {
3226                    name_counts.insert(base_name.clone(), 0);
3227                    base_name
3228                };
3229
3230                let mut field = Field::new(&name, data_type.clone(), nullable);
3231                let field_metadata = intersect_metadata_for_union(
3232                    fields.iter().map(|field| field.metadata()),
3233                );
3234                field.set_metadata(field_metadata);
3235                Ok((None, Arc::new(field)))
3236            })
3237            .collect::<Result<_>>()?;
3238        let union_schema_metadata = intersect_metadata_for_union(
3239            inputs.iter().map(|input| input.schema().metadata()),
3240        );
3241
3242        // Functional Dependencies are not preserved after UNION operation
3243        let schema = DFSchema::new_with_metadata(union_fields, union_schema_metadata)?;
3244        let schema = Arc::new(schema);
3245
3246        Ok(schema)
3247    }
3248}
3249
3250// Manual implementation needed because of `schema` field. Comparison excludes this field.
3251impl PartialOrd for Union {
3252    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3253        self.inputs
3254            .partial_cmp(&other.inputs)
3255            // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
3256            .filter(|cmp| *cmp != Ordering::Equal || self == other)
3257    }
3258}
3259
3260/// Describe the schema of table
3261///
3262/// # Example output:
3263///
3264/// ```sql
3265/// > describe traces;
3266/// +--------------------+-----------------------------+-------------+
3267/// | column_name        | data_type                   | is_nullable |
3268/// +--------------------+-----------------------------+-------------+
3269/// | attributes         | Utf8                        | YES         |
3270/// | duration_nano      | Int64                       | YES         |
3271/// | end_time_unix_nano | Int64                       | YES         |
3272/// | service.name       | Dictionary(Int32, Utf8)     | YES         |
3273/// | span.kind          | Utf8                        | YES         |
3274/// | span.name          | Utf8                        | YES         |
3275/// | span_id            | Dictionary(Int32, Utf8)     | YES         |
3276/// | time               | Timestamp(Nanosecond, None) | NO          |
3277/// | trace_id           | Dictionary(Int32, Utf8)     | YES         |
3278/// | otel.status_code   | Utf8                        | YES         |
3279/// | parent_span_id     | Utf8                        | YES         |
3280/// +--------------------+-----------------------------+-------------+
3281/// ```
3282#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3283pub struct DescribeTable {
3284    /// Table schema
3285    pub schema: Arc<Schema>,
3286    /// schema of describe table output
3287    pub output_schema: DFSchemaRef,
3288}
3289
3290// Manual implementation of `PartialOrd`, returning none since there are no comparable types in
3291// `DescribeTable`. This allows `LogicalPlan` to derive `PartialOrd`.
3292impl PartialOrd for DescribeTable {
3293    fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
3294        // There is no relevant comparison for schemas
3295        None
3296    }
3297}
3298
3299/// Options for EXPLAIN
3300#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3301pub struct ExplainOption {
3302    /// Include detailed debug info
3303    pub verbose: bool,
3304    /// Actually execute the plan and report metrics
3305    pub analyze: bool,
3306    /// Output syntax/format
3307    pub format: ExplainFormat,
3308}
3309
3310impl Default for ExplainOption {
3311    fn default() -> Self {
3312        ExplainOption {
3313            verbose: false,
3314            analyze: false,
3315            format: ExplainFormat::Indent,
3316        }
3317    }
3318}
3319
3320impl ExplainOption {
3321    /// Builder‐style setter for `verbose`
3322    pub fn with_verbose(mut self, verbose: bool) -> Self {
3323        self.verbose = verbose;
3324        self
3325    }
3326
3327    /// Builder‐style setter for `analyze`
3328    pub fn with_analyze(mut self, analyze: bool) -> Self {
3329        self.analyze = analyze;
3330        self
3331    }
3332
3333    /// Builder‐style setter for `format`
3334    pub fn with_format(mut self, format: ExplainFormat) -> Self {
3335        self.format = format;
3336        self
3337    }
3338}
3339
3340/// Produces a relation with string representations of
3341/// various parts of the plan
3342///
3343/// See [the documentation] for more information
3344///
3345/// [the documentation]: https://datafusion.apache.org/user-guide/sql/explain.html
3346#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3347pub struct Explain {
3348    /// Should extra (detailed, intermediate plans) be included?
3349    pub verbose: bool,
3350    /// Output format for explain, if specified.
3351    /// If none, defaults to `text`
3352    pub explain_format: ExplainFormat,
3353    /// The logical plan that is being EXPLAIN'd
3354    pub plan: Arc<LogicalPlan>,
3355    /// Represent the various stages plans have gone through
3356    pub stringified_plans: Vec<StringifiedPlan>,
3357    /// The output schema of the explain (2 columns of text)
3358    pub schema: DFSchemaRef,
3359    /// Used by physical planner to check if should proceed with planning
3360    pub logical_optimization_succeeded: bool,
3361}
3362
3363// Manual implementation needed because of `schema` field. Comparison excludes this field.
3364impl PartialOrd for Explain {
3365    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3366        #[derive(PartialEq, PartialOrd)]
3367        struct ComparableExplain<'a> {
3368            /// Should extra (detailed, intermediate plans) be included?
3369            pub verbose: &'a bool,
3370            /// The logical plan that is being EXPLAIN'd
3371            pub plan: &'a Arc<LogicalPlan>,
3372            /// Represent the various stages plans have gone through
3373            pub stringified_plans: &'a Vec<StringifiedPlan>,
3374            /// Used by physical planner to check if should proceed with planning
3375            pub logical_optimization_succeeded: &'a bool,
3376        }
3377        let comparable_self = ComparableExplain {
3378            verbose: &self.verbose,
3379            plan: &self.plan,
3380            stringified_plans: &self.stringified_plans,
3381            logical_optimization_succeeded: &self.logical_optimization_succeeded,
3382        };
3383        let comparable_other = ComparableExplain {
3384            verbose: &other.verbose,
3385            plan: &other.plan,
3386            stringified_plans: &other.stringified_plans,
3387            logical_optimization_succeeded: &other.logical_optimization_succeeded,
3388        };
3389        comparable_self
3390            .partial_cmp(&comparable_other)
3391            // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
3392            .filter(|cmp| *cmp != Ordering::Equal || self == other)
3393    }
3394}
3395
3396/// Runs the actual plan, and then prints the physical plan with
3397/// with execution metrics.
3398#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3399pub struct Analyze {
3400    /// Should extra detail be included?
3401    pub verbose: bool,
3402    /// The logical plan that is being EXPLAIN ANALYZE'd
3403    pub input: Arc<LogicalPlan>,
3404    /// The output schema of the explain (2 columns of text)
3405    pub schema: DFSchemaRef,
3406}
3407
3408// Manual implementation needed because of `schema` field. Comparison excludes this field.
3409impl PartialOrd for Analyze {
3410    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3411        match self.verbose.partial_cmp(&other.verbose) {
3412            Some(Ordering::Equal) => self.input.partial_cmp(&other.input),
3413            cmp => cmp,
3414        }
3415        // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
3416        .filter(|cmp| *cmp != Ordering::Equal || self == other)
3417    }
3418}
3419
3420/// Extension operator defined outside of DataFusion
3421// TODO(clippy): This clippy `allow` should be removed if
3422// the manual `PartialEq` is removed in favor of a derive.
3423// (see `PartialEq` the impl for details.)
3424#[allow(clippy::allow_attributes)]
3425#[allow(clippy::derived_hash_with_manual_eq)]
3426#[derive(Debug, Clone, Eq, Hash)]
3427pub struct Extension {
3428    /// The runtime extension operator
3429    pub node: Arc<dyn UserDefinedLogicalNode>,
3430}
3431
3432// `PartialEq` cannot be derived for types containing `Arc<dyn Trait>`.
3433// This manual implementation should be removed if
3434// https://github.com/rust-lang/rust/issues/39128 is fixed.
3435impl PartialEq for Extension {
3436    fn eq(&self, other: &Self) -> bool {
3437        self.node.eq(&other.node)
3438    }
3439}
3440
3441impl PartialOrd for Extension {
3442    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3443        self.node.partial_cmp(&other.node)
3444    }
3445}
3446
3447/// Produces the first `n` tuples from its input and discards the rest.
3448#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
3449pub struct Limit {
3450    /// Number of rows to skip before fetch
3451    pub skip: Option<Box<Expr>>,
3452    /// Maximum number of rows to fetch,
3453    /// None means fetching all rows
3454    pub fetch: Option<Box<Expr>>,
3455    /// The logical plan
3456    pub input: Arc<LogicalPlan>,
3457}
3458
3459/// Different types of skip expression in Limit plan.
3460pub enum SkipType {
3461    /// The skip expression is a literal value.
3462    Literal(usize),
3463    /// Currently only supports expressions that can be folded into constants.
3464    UnsupportedExpr,
3465}
3466
3467/// Different types of fetch expression in Limit plan.
3468pub enum FetchType {
3469    /// The fetch expression is a literal value.
3470    /// `Literal(None)` means the fetch expression is not provided.
3471    Literal(Option<usize>),
3472    /// Currently only supports expressions that can be folded into constants.
3473    UnsupportedExpr,
3474}
3475
3476impl Limit {
3477    /// Get the skip type from the limit plan.
3478    pub fn get_skip_type(&self) -> Result<SkipType> {
3479        match self.skip.as_deref() {
3480            Some(expr) => match *expr {
3481                Expr::Literal(ScalarValue::Int64(s), _) => {
3482                    // `skip = NULL` is equivalent to `skip = 0`
3483                    let s = s.unwrap_or(0);
3484                    if s >= 0 {
3485                        Ok(SkipType::Literal(s as usize))
3486                    } else {
3487                        plan_err!("OFFSET must be >=0, '{}' was provided", s)
3488                    }
3489                }
3490                _ => Ok(SkipType::UnsupportedExpr),
3491            },
3492            // `skip = None` is equivalent to `skip = 0`
3493            None => Ok(SkipType::Literal(0)),
3494        }
3495    }
3496
3497    /// Get the fetch type from the limit plan.
3498    pub fn get_fetch_type(&self) -> Result<FetchType> {
3499        match self.fetch.as_deref() {
3500            Some(expr) => match *expr {
3501                Expr::Literal(ScalarValue::Int64(Some(s)), _) => {
3502                    if s >= 0 {
3503                        Ok(FetchType::Literal(Some(s as usize)))
3504                    } else {
3505                        plan_err!("LIMIT must be >= 0, '{}' was provided", s)
3506                    }
3507                }
3508                Expr::Literal(ScalarValue::Int64(None), _) => {
3509                    Ok(FetchType::Literal(None))
3510                }
3511                _ => Ok(FetchType::UnsupportedExpr),
3512            },
3513            None => Ok(FetchType::Literal(None)),
3514        }
3515    }
3516}
3517
3518/// Removes duplicate rows from the input
3519#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
3520pub enum Distinct {
3521    /// Plain `DISTINCT` referencing all selection expressions
3522    All(Arc<LogicalPlan>),
3523    /// The `Postgres` addition, allowing separate control over DISTINCT'd and selected columns
3524    On(DistinctOn),
3525}
3526
3527impl Distinct {
3528    /// return a reference to the nodes input
3529    pub fn input(&self) -> &Arc<LogicalPlan> {
3530        match self {
3531            Distinct::All(input) => input,
3532            Distinct::On(DistinctOn { input, .. }) => input,
3533        }
3534    }
3535}
3536
3537/// Removes duplicate rows from the input
3538#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3539pub struct DistinctOn {
3540    /// The `DISTINCT ON` clause expression list
3541    pub on_expr: Vec<Expr>,
3542    /// The selected projection expression list
3543    pub select_expr: Vec<Expr>,
3544    /// The `ORDER BY` clause, whose initial expressions must match those of the `ON` clause when
3545    /// present. Note that those matching expressions actually wrap the `ON` expressions with
3546    /// additional info pertaining to the sorting procedure (i.e. ASC/DESC, and NULLS FIRST/LAST).
3547    pub sort_expr: Option<Vec<SortExpr>>,
3548    /// The logical plan that is being DISTINCT'd
3549    pub input: Arc<LogicalPlan>,
3550    /// The schema description of the DISTINCT ON output
3551    pub schema: DFSchemaRef,
3552}
3553
3554impl DistinctOn {
3555    /// Create a new `DistinctOn` struct.
3556    pub fn try_new(
3557        on_expr: Vec<Expr>,
3558        select_expr: Vec<Expr>,
3559        sort_expr: Option<Vec<SortExpr>>,
3560        input: Arc<LogicalPlan>,
3561    ) -> Result<Self> {
3562        if on_expr.is_empty() {
3563            return plan_err!("No `ON` expressions provided");
3564        }
3565
3566        let on_expr = normalize_cols(on_expr, input.as_ref())?;
3567        let qualified_fields = exprlist_to_fields(select_expr.as_slice(), &input)?
3568            .into_iter()
3569            .collect();
3570
3571        let dfschema = DFSchema::new_with_metadata(
3572            qualified_fields,
3573            input.schema().metadata().clone(),
3574        )?;
3575
3576        let mut distinct_on = DistinctOn {
3577            on_expr,
3578            select_expr,
3579            sort_expr: None,
3580            input,
3581            schema: Arc::new(dfschema),
3582        };
3583
3584        if let Some(sort_expr) = sort_expr {
3585            distinct_on = distinct_on.with_sort_expr(sort_expr)?;
3586        }
3587
3588        Ok(distinct_on)
3589    }
3590
3591    /// Try to update `self` with a new sort expressions.
3592    ///
3593    /// Validates that the sort expressions are a super-set of the `ON` expressions.
3594    pub fn with_sort_expr(mut self, sort_expr: Vec<SortExpr>) -> Result<Self> {
3595        let sort_expr = normalize_sorts(sort_expr, self.input.as_ref())?;
3596
3597        // Check that the left-most sort expressions are the same as the `ON` expressions.
3598        let mut matched = true;
3599        for (on, sort) in self.on_expr.iter().zip(sort_expr.iter()) {
3600            if on != &sort.expr {
3601                matched = false;
3602                break;
3603            }
3604        }
3605
3606        if self.on_expr.len() > sort_expr.len() || !matched {
3607            return plan_err!(
3608                "SELECT DISTINCT ON expressions must match initial ORDER BY expressions"
3609            );
3610        }
3611
3612        self.sort_expr = Some(sort_expr);
3613        Ok(self)
3614    }
3615}
3616
3617// Manual implementation needed because of `schema` field. Comparison excludes this field.
3618impl PartialOrd for DistinctOn {
3619    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3620        #[derive(PartialEq, PartialOrd)]
3621        struct ComparableDistinctOn<'a> {
3622            /// The `DISTINCT ON` clause expression list
3623            pub on_expr: &'a Vec<Expr>,
3624            /// The selected projection expression list
3625            pub select_expr: &'a Vec<Expr>,
3626            /// The `ORDER BY` clause, whose initial expressions must match those of the `ON` clause when
3627            /// present. Note that those matching expressions actually wrap the `ON` expressions with
3628            /// additional info pertaining to the sorting procedure (i.e. ASC/DESC, and NULLS FIRST/LAST).
3629            pub sort_expr: &'a Option<Vec<SortExpr>>,
3630            /// The logical plan that is being DISTINCT'd
3631            pub input: &'a Arc<LogicalPlan>,
3632        }
3633        let comparable_self = ComparableDistinctOn {
3634            on_expr: &self.on_expr,
3635            select_expr: &self.select_expr,
3636            sort_expr: &self.sort_expr,
3637            input: &self.input,
3638        };
3639        let comparable_other = ComparableDistinctOn {
3640            on_expr: &other.on_expr,
3641            select_expr: &other.select_expr,
3642            sort_expr: &other.sort_expr,
3643            input: &other.input,
3644        };
3645        comparable_self
3646            .partial_cmp(&comparable_other)
3647            // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
3648            .filter(|cmp| *cmp != Ordering::Equal || self == other)
3649    }
3650}
3651
3652/// Aggregates its input based on a set of grouping and aggregate
3653/// expressions (e.g. SUM).
3654///
3655/// # Output Schema
3656///
3657/// The output schema is the group expressions followed by the aggregate
3658/// expressions in order.
3659///
3660/// For example, given the input schema `"A", "B", "C"` and the aggregate
3661/// `SUM(A) GROUP BY C+B`, the output schema will be `"C+B", "SUM(A)"` where
3662/// "C+B" and "SUM(A)" are the names of the output columns. Note that "C+B" is a
3663/// single new column
3664#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3665// mark non_exhaustive to encourage use of try_new/new()
3666#[non_exhaustive]
3667pub struct Aggregate {
3668    /// The incoming logical plan
3669    pub input: Arc<LogicalPlan>,
3670    /// Grouping expressions
3671    pub group_expr: Vec<Expr>,
3672    /// Aggregate expressions.
3673    ///
3674    /// Note these *must* be either [`Expr::AggregateFunction`] or [`Expr::Alias`]
3675    pub aggr_expr: Vec<Expr>,
3676    /// The schema description of the aggregate output
3677    pub schema: DFSchemaRef,
3678}
3679
3680impl Aggregate {
3681    /// Create a new aggregate operator.
3682    pub fn try_new(
3683        input: Arc<LogicalPlan>,
3684        group_expr: Vec<Expr>,
3685        aggr_expr: Vec<Expr>,
3686    ) -> Result<Self> {
3687        let group_expr = enumerate_grouping_sets(group_expr)?;
3688
3689        let is_grouping_set = matches!(group_expr.as_slice(), [Expr::GroupingSet(_)]);
3690
3691        let grouping_expr: Vec<&Expr> = grouping_set_to_exprlist(group_expr.as_slice())?;
3692
3693        let mut qualified_fields = exprlist_to_fields(grouping_expr, &input)?;
3694
3695        // Even columns that cannot be null will become nullable when used in a grouping set.
3696        if is_grouping_set {
3697            qualified_fields = qualified_fields
3698                .into_iter()
3699                .map(|(q, f)| (q, f.as_ref().clone().with_nullable(true).into()))
3700                .collect::<Vec<_>>();
3701            let max_ordinal = max_grouping_set_duplicate_ordinal(&group_expr);
3702            qualified_fields.push((
3703                None,
3704                Field::new(
3705                    Self::INTERNAL_GROUPING_ID,
3706                    Self::grouping_id_type(qualified_fields.len(), max_ordinal),
3707                    false,
3708                )
3709                .into(),
3710            ));
3711        }
3712
3713        qualified_fields.extend(exprlist_to_fields(aggr_expr.as_slice(), &input)?);
3714
3715        let schema = DFSchema::new_with_metadata(
3716            qualified_fields,
3717            input.schema().metadata().clone(),
3718        )?;
3719
3720        Self::try_new_with_schema(input, group_expr, aggr_expr, Arc::new(schema))
3721    }
3722
3723    /// Create a new aggregate operator using the provided schema to avoid the overhead of
3724    /// building the schema again when the schema is already known.
3725    ///
3726    /// This method should only be called when you are absolutely sure that the schema being
3727    /// provided is correct for the aggregate. If in doubt, call [try_new](Self::try_new) instead.
3728    pub fn try_new_with_schema(
3729        input: Arc<LogicalPlan>,
3730        group_expr: Vec<Expr>,
3731        aggr_expr: Vec<Expr>,
3732        schema: DFSchemaRef,
3733    ) -> Result<Self> {
3734        if group_expr.is_empty() && aggr_expr.is_empty() {
3735            return plan_err!(
3736                "Aggregate requires at least one grouping or aggregate expression. \
3737                Aggregate without grouping expressions nor aggregate expressions is \
3738                logically equivalent to, but less efficient than, VALUES producing \
3739                single row. Please use VALUES instead."
3740            );
3741        }
3742        let group_expr_count = grouping_set_expr_count(&group_expr)?;
3743        if schema.fields().len() != group_expr_count + aggr_expr.len() {
3744            return plan_err!(
3745                "Aggregate schema has wrong number of fields. Expected {} got {}",
3746                group_expr_count + aggr_expr.len(),
3747                schema.fields().len()
3748            );
3749        }
3750
3751        let aggregate_func_dependencies =
3752            calc_func_dependencies_for_aggregate(&group_expr, &input, &schema)?;
3753        let new_schema = Arc::unwrap_or_clone(schema);
3754        let schema = Arc::new(
3755            new_schema.with_functional_dependencies(aggregate_func_dependencies)?,
3756        );
3757        Ok(Self {
3758            input,
3759            group_expr,
3760            aggr_expr,
3761            schema,
3762        })
3763    }
3764
3765    fn is_grouping_set(&self) -> bool {
3766        matches!(self.group_expr.as_slice(), [Expr::GroupingSet(_)])
3767    }
3768
3769    /// Get the output expressions.
3770    fn output_expressions(&self) -> Result<Vec<&Expr>> {
3771        static INTERNAL_ID_EXPR: LazyLock<Expr> = LazyLock::new(|| {
3772            Expr::Column(Column::from_name(Aggregate::INTERNAL_GROUPING_ID))
3773        });
3774        let mut exprs = grouping_set_to_exprlist(self.group_expr.as_slice())?;
3775        if self.is_grouping_set() {
3776            exprs.push(&INTERNAL_ID_EXPR);
3777        }
3778        exprs.extend(self.aggr_expr.iter());
3779        debug_assert!(exprs.len() == self.schema.fields().len());
3780        Ok(exprs)
3781    }
3782
3783    /// Get the length of the group by expression in the output schema
3784    /// This is not simply group by expression length. Expression may be
3785    /// GroupingSet, etc. In these case we need to get inner expression lengths.
3786    pub fn group_expr_len(&self) -> Result<usize> {
3787        grouping_set_expr_count(&self.group_expr)
3788    }
3789
3790    /// Returns the data type of the grouping id.
3791    ///
3792    /// The grouping ID packs two pieces of information into a single integer:
3793    /// - The low `group_exprs` bits are the semantic bitmask (a set bit means the
3794    ///   corresponding grouping expression is NULL for this grouping set).
3795    /// - The bits above position `group_exprs` encode a duplicate ordinal that
3796    ///   distinguishes multiple occurrences of the same grouping set pattern.
3797    ///
3798    /// `max_ordinal` is the highest ordinal value that will appear (0 when there
3799    /// are no duplicate grouping sets).  The type is chosen to be the smallest
3800    /// unsigned integer that can represent both parts.
3801    pub fn grouping_id_type(group_exprs: usize, max_ordinal: usize) -> DataType {
3802        let ordinal_bits = usize::BITS as usize - max_ordinal.leading_zeros() as usize;
3803        let total_bits = group_exprs + ordinal_bits;
3804        if total_bits <= 8 {
3805            DataType::UInt8
3806        } else if total_bits <= 16 {
3807            DataType::UInt16
3808        } else if total_bits <= 32 {
3809            DataType::UInt32
3810        } else {
3811            DataType::UInt64
3812        }
3813    }
3814
3815    /// Internal column used when the aggregation is a grouping set.
3816    ///
3817    /// This column packs two values into a single unsigned integer:
3818    ///
3819    /// - **Low bits (positions 0 .. n-1)**: a semantic bitmask where each bit
3820    ///   represents one of the `n` grouping expressions.  The least significant
3821    ///   bit corresponds to the rightmost grouping expression.  A `1` bit means
3822    ///   the corresponding column is replaced with `NULL` for this grouping set;
3823    ///   a `0` bit means it is included.
3824    /// - **High bits (positions n and above)**: a *duplicate ordinal* that
3825    ///   distinguishes multiple occurrences of the same semantic grouping set
3826    ///   pattern within a single query.  The ordinal is `0` for the first
3827    ///   occurrence, `1` for the second, and so on.
3828    ///
3829    /// The integer type is chosen by [`Self::grouping_id_type`] to be the
3830    /// smallest `UInt8 / UInt16 / UInt32 / UInt64` that can represent both
3831    /// parts.
3832    ///
3833    /// For example, for the grouping expressions CUBE(a, b) (no duplicates),
3834    /// the grouping ID column will have the following values:
3835    ///     0b00: Both `a` and `b` are included
3836    ///     0b01: `b` is excluded
3837    ///     0b10: `a` is excluded
3838    ///     0b11: Both `a` and `b` are excluded
3839    ///
3840    /// When the same set appears twice and `n = 2`, the duplicate ordinal is
3841    /// packed into bit 2:
3842    ///     first occurrence:  `0b0_01` (ordinal = 0, mask = 0b01)
3843    ///     second occurrence: `0b1_01` (ordinal = 1, mask = 0b01)
3844    ///
3845    /// The GROUPING function always masks the value with `(1 << n) - 1` before
3846    /// interpreting it so the ordinal bits are invisible to user-facing SQL.
3847    pub const INTERNAL_GROUPING_ID: &'static str = "__grouping_id";
3848}
3849
3850// Manual implementation needed because of `schema` field. Comparison excludes this field.
3851impl PartialOrd for Aggregate {
3852    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3853        match self.input.partial_cmp(&other.input) {
3854            Some(Ordering::Equal) => {
3855                match self.group_expr.partial_cmp(&other.group_expr) {
3856                    Some(Ordering::Equal) => self.aggr_expr.partial_cmp(&other.aggr_expr),
3857                    cmp => cmp,
3858                }
3859            }
3860            cmp => cmp,
3861        }
3862        // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
3863        .filter(|cmp| *cmp != Ordering::Equal || self == other)
3864    }
3865}
3866
3867/// Returns the highest duplicate ordinal across all grouping sets in `group_expr`.
3868///
3869/// The ordinal for each occurrence of a grouping set pattern is its 0-based
3870/// index among identical entries. For example, if the same set appears three
3871/// times, the ordinals are 0, 1, 2 and this function returns 2.
3872/// Returns 0 when no grouping set is duplicated.
3873#[allow(clippy::allow_attributes, clippy::mutable_key_type)] // Expr contains Arc with interior mutability but is intentionally used as hash key
3874fn max_grouping_set_duplicate_ordinal(group_expr: &[Expr]) -> usize {
3875    if let Some(Expr::GroupingSet(GroupingSet::GroupingSets(sets))) = group_expr.first() {
3876        let mut counts: HashMap<&[Expr], usize> = HashMap::new();
3877        for set in sets {
3878            *counts.entry(set).or_insert(0) += 1;
3879        }
3880        counts.into_values().max().unwrap_or(0).saturating_sub(1)
3881    } else {
3882        0
3883    }
3884}
3885
3886/// Checks whether any expression in `group_expr` contains `Expr::GroupingSet`.
3887fn contains_grouping_set(group_expr: &[Expr]) -> bool {
3888    group_expr
3889        .iter()
3890        .any(|expr| matches!(expr, Expr::GroupingSet(_)))
3891}
3892
3893/// Calculates functional dependencies for aggregate expressions.
3894fn calc_func_dependencies_for_aggregate(
3895    // Expressions in the GROUP BY clause:
3896    group_expr: &[Expr],
3897    // Input plan of the aggregate:
3898    input: &LogicalPlan,
3899    // Aggregate schema
3900    aggr_schema: &DFSchema,
3901) -> Result<FunctionalDependencies> {
3902    // We can do a case analysis on how to propagate functional dependencies based on
3903    // whether the GROUP BY in question contains a grouping set expression:
3904    // - If so, the functional dependencies will be empty because we cannot guarantee
3905    //   that GROUP BY expression results will be unique.
3906    // - Otherwise, it may be possible to propagate functional dependencies.
3907    if !contains_grouping_set(group_expr) {
3908        let group_by_expr_names = group_expr
3909            .iter()
3910            .map(|item| item.schema_name().to_string())
3911            .collect::<IndexSet<_>>()
3912            .into_iter()
3913            .collect::<Vec<_>>();
3914        let aggregate_func_dependencies = aggregate_functional_dependencies(
3915            input.schema(),
3916            &group_by_expr_names,
3917            aggr_schema,
3918        );
3919        Ok(aggregate_func_dependencies)
3920    } else {
3921        Ok(FunctionalDependencies::empty())
3922    }
3923}
3924
3925/// This function projects functional dependencies of the `input` plan according
3926/// to projection expressions `exprs`.
3927fn calc_func_dependencies_for_project(
3928    exprs: &[Expr],
3929    input: &LogicalPlan,
3930) -> Result<FunctionalDependencies> {
3931    let input_fields = input.schema().field_names();
3932    // Calculate expression indices (if present) in the input schema.
3933    let proj_indices = exprs
3934        .iter()
3935        .map(|expr| match expr {
3936            #[expect(deprecated)]
3937            Expr::Wildcard { qualifier, options } => {
3938                let wildcard_fields = exprlist_to_fields(
3939                    vec![&Expr::Wildcard {
3940                        qualifier: qualifier.clone(),
3941                        options: options.clone(),
3942                    }],
3943                    input,
3944                )?;
3945                Ok::<_, DataFusionError>(
3946                    wildcard_fields
3947                        .into_iter()
3948                        .filter_map(|(qualifier, f)| {
3949                            let flat_name = qualifier
3950                                .map(|t| format!("{}.{}", t, f.name()))
3951                                .unwrap_or_else(|| f.name().clone());
3952                            input_fields.iter().position(|item| *item == flat_name)
3953                        })
3954                        .collect::<Vec<_>>(),
3955                )
3956            }
3957            Expr::Alias(alias) => {
3958                let name = format!("{}", alias.expr);
3959                Ok(input_fields
3960                    .iter()
3961                    .position(|item| *item == name)
3962                    .map(|i| vec![i])
3963                    .unwrap_or(vec![]))
3964            }
3965            _ => {
3966                let name = format!("{expr}");
3967                Ok(input_fields
3968                    .iter()
3969                    .position(|item| *item == name)
3970                    .map(|i| vec![i])
3971                    .unwrap_or(vec![]))
3972            }
3973        })
3974        .collect::<Result<Vec<_>>>()?
3975        .into_iter()
3976        .flatten()
3977        .collect::<Vec<_>>();
3978
3979    Ok(input
3980        .schema()
3981        .functional_dependencies()
3982        .project_functional_dependencies(&proj_indices, exprs.len()))
3983}
3984
3985/// Sorts its input according to a list of sort expressions.
3986#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
3987pub struct Sort {
3988    /// The sort expressions
3989    pub expr: Vec<SortExpr>,
3990    /// The incoming logical plan
3991    pub input: Arc<LogicalPlan>,
3992    /// Optional fetch limit
3993    pub fetch: Option<usize>,
3994}
3995
3996/// Join two logical plans on one or more join columns
3997#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3998pub struct Join {
3999    /// Left input
4000    pub left: Arc<LogicalPlan>,
4001    /// Right input
4002    pub right: Arc<LogicalPlan>,
4003    /// Equijoin clause expressed as pairs of (left, right) join expressions
4004    pub on: Vec<(Expr, Expr)>,
4005    /// Filters applied during join (non-equi conditions)
4006    pub filter: Option<Expr>,
4007    /// Join type
4008    pub join_type: JoinType,
4009    /// Join constraint
4010    pub join_constraint: JoinConstraint,
4011    /// The output schema, containing fields from the left and right inputs
4012    pub schema: DFSchemaRef,
4013    /// Defines the null equality for the join.
4014    pub null_equality: NullEquality,
4015    /// Whether this is a null-aware anti join (for NOT IN semantics).
4016    ///
4017    /// Only applies to LeftAnti joins. When true, implements SQL NOT IN semantics where:
4018    /// - If the right side (subquery) contains any NULL in join keys, no rows are output
4019    /// - Left side rows with NULL in join keys are not output
4020    ///
4021    /// This is required for correct NOT IN subquery behavior with three-valued logic.
4022    pub null_aware: bool,
4023}
4024
4025impl Join {
4026    /// Creates a new Join operator with automatically computed schema.
4027    ///
4028    /// This constructor computes the schema based on the join type and inputs,
4029    /// removing the need to manually specify the schema or call `recompute_schema`.
4030    ///
4031    /// # Arguments
4032    ///
4033    /// * `left` - Left input plan
4034    /// * `right` - Right input plan
4035    /// * `on` - Join condition as a vector of (left_expr, right_expr) pairs
4036    /// * `filter` - Optional filter expression (for non-equijoin conditions)
4037    /// * `join_type` - Type of join (Inner, Left, Right, etc.)
4038    /// * `join_constraint` - Join constraint (On, Using)
4039    /// * `null_equality` - How to handle nulls in join comparisons
4040    /// * `null_aware` - Whether this is a null-aware anti join (for NOT IN semantics)
4041    ///
4042    /// # Returns
4043    ///
4044    /// A new Join operator with the computed schema
4045    #[expect(clippy::too_many_arguments)]
4046    pub fn try_new(
4047        left: Arc<LogicalPlan>,
4048        right: Arc<LogicalPlan>,
4049        on: Vec<(Expr, Expr)>,
4050        filter: Option<Expr>,
4051        join_type: JoinType,
4052        join_constraint: JoinConstraint,
4053        null_equality: NullEquality,
4054        null_aware: bool,
4055    ) -> Result<Self> {
4056        let join_schema = build_join_schema(left.schema(), right.schema(), &join_type)?;
4057
4058        Ok(Join {
4059            left,
4060            right,
4061            on,
4062            filter,
4063            join_type,
4064            join_constraint,
4065            schema: Arc::new(join_schema),
4066            null_equality,
4067            null_aware,
4068        })
4069    }
4070
4071    /// Create Join with input which wrapped with projection, this method is used in physical planning only to help
4072    /// create the physical join.
4073    pub fn try_new_with_project_input(
4074        original: &LogicalPlan,
4075        left: Arc<LogicalPlan>,
4076        right: Arc<LogicalPlan>,
4077        column_on: (Vec<Column>, Vec<Column>),
4078    ) -> Result<(Self, bool)> {
4079        let original_join = match original {
4080            LogicalPlan::Join(join) => join,
4081            _ => return plan_err!("Could not create join with project input"),
4082        };
4083
4084        let mut left_sch = LogicalPlanBuilder::from(Arc::clone(&left));
4085        let mut right_sch = LogicalPlanBuilder::from(Arc::clone(&right));
4086
4087        let mut requalified = false;
4088
4089        // By definition, the resulting schema of an inner/left/right & full join will have first the left side fields and then the right,
4090        // potentially having duplicate field names. Note this will only qualify fields if they have not been qualified before.
4091        if original_join.join_type == JoinType::Inner
4092            || original_join.join_type == JoinType::Left
4093            || original_join.join_type == JoinType::Right
4094            || original_join.join_type == JoinType::Full
4095        {
4096            (left_sch, right_sch, requalified) =
4097                requalify_sides_if_needed(left_sch.clone(), right_sch.clone())?;
4098        }
4099
4100        let on: Vec<(Expr, Expr)> = column_on
4101            .0
4102            .into_iter()
4103            .zip(column_on.1)
4104            .map(|(l, r)| (Expr::Column(l), Expr::Column(r)))
4105            .collect();
4106
4107        let join_schema = build_join_schema(
4108            left_sch.schema(),
4109            right_sch.schema(),
4110            &original_join.join_type,
4111        )?;
4112
4113        Ok((
4114            Join {
4115                left,
4116                right,
4117                on,
4118                filter: original_join.filter.clone(),
4119                join_type: original_join.join_type,
4120                join_constraint: original_join.join_constraint,
4121                schema: Arc::new(join_schema),
4122                null_equality: original_join.null_equality,
4123                null_aware: original_join.null_aware,
4124            },
4125            requalified,
4126        ))
4127    }
4128}
4129
4130// Manual implementation needed because of `schema` field. Comparison excludes this field.
4131impl PartialOrd for Join {
4132    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
4133        #[derive(PartialEq, PartialOrd)]
4134        struct ComparableJoin<'a> {
4135            /// Left input
4136            pub left: &'a Arc<LogicalPlan>,
4137            /// Right input
4138            pub right: &'a Arc<LogicalPlan>,
4139            /// Equijoin clause expressed as pairs of (left, right) join expressions
4140            pub on: &'a Vec<(Expr, Expr)>,
4141            /// Filters applied during join (non-equi conditions)
4142            pub filter: &'a Option<Expr>,
4143            /// Join type
4144            pub join_type: &'a JoinType,
4145            /// Join constraint
4146            pub join_constraint: &'a JoinConstraint,
4147            /// The null handling behavior for equalities
4148            pub null_equality: &'a NullEquality,
4149        }
4150        let comparable_self = ComparableJoin {
4151            left: &self.left,
4152            right: &self.right,
4153            on: &self.on,
4154            filter: &self.filter,
4155            join_type: &self.join_type,
4156            join_constraint: &self.join_constraint,
4157            null_equality: &self.null_equality,
4158        };
4159        let comparable_other = ComparableJoin {
4160            left: &other.left,
4161            right: &other.right,
4162            on: &other.on,
4163            filter: &other.filter,
4164            join_type: &other.join_type,
4165            join_constraint: &other.join_constraint,
4166            null_equality: &other.null_equality,
4167        };
4168        comparable_self
4169            .partial_cmp(&comparable_other)
4170            // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
4171            .filter(|cmp| *cmp != Ordering::Equal || self == other)
4172    }
4173}
4174
4175/// Subquery
4176#[derive(Clone, PartialEq, Eq, PartialOrd, Hash)]
4177pub struct Subquery {
4178    /// The subquery
4179    pub subquery: Arc<LogicalPlan>,
4180    /// The outer references used in the subquery
4181    pub outer_ref_columns: Vec<Expr>,
4182    /// Span information for subquery projection columns
4183    pub spans: Spans,
4184}
4185
4186impl Normalizeable for Subquery {
4187    fn can_normalize(&self) -> bool {
4188        false
4189    }
4190}
4191
4192impl NormalizeEq for Subquery {
4193    fn normalize_eq(&self, other: &Self) -> bool {
4194        // TODO: may be implement NormalizeEq for LogicalPlan?
4195        *self.subquery == *other.subquery
4196            && self.outer_ref_columns.len() == other.outer_ref_columns.len()
4197            && self
4198                .outer_ref_columns
4199                .iter()
4200                .zip(other.outer_ref_columns.iter())
4201                .all(|(a, b)| a.normalize_eq(b))
4202    }
4203}
4204
4205impl Subquery {
4206    pub fn try_from_expr(plan: &Expr) -> Result<&Subquery> {
4207        match plan {
4208            Expr::ScalarSubquery(it) => Ok(it),
4209            Expr::Cast(cast) => Subquery::try_from_expr(cast.expr.as_ref()),
4210            _ => plan_err!("Could not coerce into ScalarSubquery!"),
4211        }
4212    }
4213
4214    pub fn with_plan(&self, plan: Arc<LogicalPlan>) -> Subquery {
4215        Subquery {
4216            subquery: plan,
4217            outer_ref_columns: self.outer_ref_columns.clone(),
4218            spans: Spans::new(),
4219        }
4220    }
4221}
4222
4223impl Debug for Subquery {
4224    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
4225        write!(f, "<subquery>")
4226    }
4227}
4228
4229/// Logical partitioning schemes supported by [`LogicalPlan::Repartition`]
4230///
4231/// See [`Partitioning`] for more details on partitioning
4232///
4233/// [`Partitioning`]: https://docs.rs/datafusion/latest/datafusion/physical_expr/enum.Partitioning.html#
4234#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
4235pub enum Partitioning {
4236    /// Allocate batches using a round-robin algorithm and the specified number of partitions
4237    RoundRobinBatch(usize),
4238    /// Allocate rows based on a hash of one of more expressions and the specified number
4239    /// of partitions.
4240    Hash(Vec<Expr>, usize),
4241    /// The DISTRIBUTE BY clause is used to repartition the data based on the input expressions
4242    DistributeBy(Vec<Expr>),
4243}
4244
4245/// Represent the unnesting operation on a list column, such as the recursion depth and
4246/// the output column name after unnesting
4247///
4248/// Example: given `ColumnUnnestList { output_column: "output_name", depth: 2 }`
4249///
4250/// ```text
4251///   input             output_name
4252///  ┌─────────┐      ┌─────────┐
4253///  │{{1,2}}  │      │ 1       │
4254///  ├─────────┼─────►├─────────┤
4255///  │{{3}}    │      │ 2       │
4256///  ├─────────┤      ├─────────┤
4257///  │{{4},{5}}│      │ 3       │
4258///  └─────────┘      ├─────────┤
4259///                   │ 4       │
4260///                   ├─────────┤
4261///                   │ 5       │
4262///                   └─────────┘
4263/// ```
4264#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd)]
4265pub struct ColumnUnnestList {
4266    pub output_column: Column,
4267    pub depth: usize,
4268}
4269
4270impl Display for ColumnUnnestList {
4271    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
4272        write!(f, "{}|depth={}", self.output_column, self.depth)
4273    }
4274}
4275
4276/// Unnest a column that contains a nested list type. See
4277/// [`UnnestOptions`] for more details.
4278#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4279pub struct Unnest {
4280    /// The incoming logical plan
4281    pub input: Arc<LogicalPlan>,
4282    /// Columns to run unnest on, can be a list of (List/Struct) columns
4283    pub exec_columns: Vec<Column>,
4284    /// refer to the indices(in the input schema) of columns
4285    /// that have type list to run unnest on
4286    pub list_type_columns: Vec<(usize, ColumnUnnestList)>,
4287    /// refer to the indices (in the input schema) of columns
4288    /// that have type struct to run unnest on
4289    pub struct_type_columns: Vec<usize>,
4290    /// Having items aligned with the output columns
4291    /// representing which column in the input schema each output column depends on
4292    pub dependency_indices: Vec<usize>,
4293    /// The output schema, containing the unnested field column.
4294    pub schema: DFSchemaRef,
4295    /// Options
4296    pub options: UnnestOptions,
4297}
4298
4299// Manual implementation needed because of `schema` field. Comparison excludes this field.
4300impl PartialOrd for Unnest {
4301    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
4302        #[derive(PartialEq, PartialOrd)]
4303        struct ComparableUnnest<'a> {
4304            /// The incoming logical plan
4305            pub input: &'a Arc<LogicalPlan>,
4306            /// Columns to run unnest on, can be a list of (List/Struct) columns
4307            pub exec_columns: &'a Vec<Column>,
4308            /// refer to the indices(in the input schema) of columns
4309            /// that have type list to run unnest on
4310            pub list_type_columns: &'a Vec<(usize, ColumnUnnestList)>,
4311            /// refer to the indices (in the input schema) of columns
4312            /// that have type struct to run unnest on
4313            pub struct_type_columns: &'a Vec<usize>,
4314            /// Having items aligned with the output columns
4315            /// representing which column in the input schema each output column depends on
4316            pub dependency_indices: &'a Vec<usize>,
4317            /// Options
4318            pub options: &'a UnnestOptions,
4319        }
4320        let comparable_self = ComparableUnnest {
4321            input: &self.input,
4322            exec_columns: &self.exec_columns,
4323            list_type_columns: &self.list_type_columns,
4324            struct_type_columns: &self.struct_type_columns,
4325            dependency_indices: &self.dependency_indices,
4326            options: &self.options,
4327        };
4328        let comparable_other = ComparableUnnest {
4329            input: &other.input,
4330            exec_columns: &other.exec_columns,
4331            list_type_columns: &other.list_type_columns,
4332            struct_type_columns: &other.struct_type_columns,
4333            dependency_indices: &other.dependency_indices,
4334            options: &other.options,
4335        };
4336        comparable_self
4337            .partial_cmp(&comparable_other)
4338            // TODO (https://github.com/apache/datafusion/issues/17477) avoid recomparing all fields
4339            .filter(|cmp| *cmp != Ordering::Equal || self == other)
4340    }
4341}
4342
4343impl Unnest {
4344    pub fn try_new(
4345        input: Arc<LogicalPlan>,
4346        exec_columns: Vec<Column>,
4347        options: UnnestOptions,
4348    ) -> Result<Self> {
4349        if exec_columns.is_empty() {
4350            return plan_err!("unnest plan requires at least 1 column to unnest");
4351        }
4352
4353        let mut list_columns: Vec<(usize, ColumnUnnestList)> = vec![];
4354        let mut struct_columns = vec![];
4355        let indices_to_unnest = exec_columns
4356            .iter()
4357            .map(|c| Ok((input.schema().index_of_column(c)?, c)))
4358            .collect::<Result<HashMap<usize, &Column>>>()?;
4359
4360        let input_schema = input.schema();
4361
4362        let mut dependency_indices = vec![];
4363        // Transform input schema into new schema
4364        // Given this comprehensive example
4365        //
4366        // input schema:
4367        // 1.col1_unnest_placeholder: list[list[int]],
4368        // 2.col1: list[list[int]]
4369        // 3.col2: list[int]
4370        // with unnest on unnest(col1,depth=2), unnest(col1,depth=1) and unnest(col2,depth=1)
4371        // output schema:
4372        // 1.unnest_col1_depth_2: int
4373        // 2.unnest_col1_depth_1: list[int]
4374        // 3.col1: list[list[int]]
4375        // 4.unnest_col2_depth_1: int
4376        // Meaning the placeholder column will be replaced by its unnested variation(s), note
4377        // the plural.
4378        let fields = input_schema
4379            .iter()
4380            .enumerate()
4381            .map(|(index, (original_qualifier, original_field))| {
4382                match indices_to_unnest.get(&index) {
4383                    Some(column_to_unnest) => {
4384                        let recursions_on_column = options
4385                            .recursions
4386                            .iter()
4387                            .filter(|p| -> bool { &p.input_column == *column_to_unnest })
4388                            .collect::<Vec<_>>();
4389                        let mut transformed_columns = recursions_on_column
4390                            .iter()
4391                            .map(|r| {
4392                                list_columns.push((
4393                                    index,
4394                                    ColumnUnnestList {
4395                                        output_column: r.output_column.clone(),
4396                                        depth: r.depth,
4397                                    },
4398                                ));
4399                                Ok(get_unnested_columns(
4400                                    &r.output_column.name,
4401                                    original_field.data_type(),
4402                                    r.depth,
4403                                )?
4404                                .into_iter()
4405                                .next()
4406                                .unwrap()) // because unnesting a list column always result into one result
4407                            })
4408                            .collect::<Result<Vec<(Column, Arc<Field>)>>>()?;
4409                        if transformed_columns.is_empty() {
4410                            transformed_columns = get_unnested_columns(
4411                                &column_to_unnest.name,
4412                                original_field.data_type(),
4413                                1,
4414                            )?;
4415                            match original_field.data_type() {
4416                                DataType::Struct(_) => {
4417                                    struct_columns.push(index);
4418                                }
4419                                DataType::List(_)
4420                                | DataType::FixedSizeList(_, _)
4421                                | DataType::LargeList(_)
4422                                | DataType::ListView(_)
4423                                | DataType::LargeListView(_) => {
4424                                    list_columns.push((
4425                                        index,
4426                                        ColumnUnnestList {
4427                                            output_column: Column::from_name(
4428                                                &column_to_unnest.name,
4429                                            ),
4430                                            depth: 1,
4431                                        },
4432                                    ));
4433                                }
4434                                _ => {}
4435                            };
4436                        }
4437
4438                        // new columns dependent on the same original index
4439                        dependency_indices.extend(std::iter::repeat_n(
4440                            index,
4441                            transformed_columns.len(),
4442                        ));
4443                        Ok(transformed_columns
4444                            .iter()
4445                            .map(|(col, field)| {
4446                                (col.relation.to_owned(), field.to_owned())
4447                            })
4448                            .collect())
4449                    }
4450                    None => {
4451                        dependency_indices.push(index);
4452                        Ok(vec![(
4453                            original_qualifier.cloned(),
4454                            Arc::clone(original_field),
4455                        )])
4456                    }
4457                }
4458            })
4459            .collect::<Result<Vec<_>>>()?
4460            .into_iter()
4461            .flatten()
4462            .collect::<Vec<_>>();
4463
4464        let metadata = input_schema.metadata().clone();
4465        let df_schema = DFSchema::new_with_metadata(fields, metadata)?;
4466        // We can use the existing functional dependencies:
4467        let deps = input_schema.functional_dependencies().clone();
4468        let schema = Arc::new(df_schema.with_functional_dependencies(deps)?);
4469
4470        Ok(Unnest {
4471            input,
4472            exec_columns,
4473            list_type_columns: list_columns,
4474            struct_type_columns: struct_columns,
4475            dependency_indices,
4476            schema,
4477            options,
4478        })
4479    }
4480}
4481
4482// Based on data type, either struct or a variant of list
4483// return a set of columns as the result of unnesting
4484// the input columns.
4485// For example, given a column with name "a",
4486// - List(Element) returns ["a"] with data type Element
4487// - Struct(field1, field2) returns ["a.field1","a.field2"]
4488// For list data type, an argument depth is used to specify
4489// the recursion level
4490fn get_unnested_columns(
4491    col_name: &String,
4492    data_type: &DataType,
4493    depth: usize,
4494) -> Result<Vec<(Column, Arc<Field>)>> {
4495    let mut qualified_columns = Vec::with_capacity(1);
4496
4497    match data_type {
4498        DataType::List(_)
4499        | DataType::FixedSizeList(_, _)
4500        | DataType::LargeList(_)
4501        | DataType::ListView(_)
4502        | DataType::LargeListView(_) => {
4503            let data_type = get_unnested_list_datatype_recursive(data_type, depth)?;
4504            let new_field = Arc::new(Field::new(
4505                col_name, data_type,
4506                // Unnesting may produce NULLs even if the list is not null.
4507                // For example: unnest([1], []) -> 1, null
4508                true,
4509            ));
4510            let column = Column::from_name(col_name);
4511            // let column = Column::from((None, &new_field));
4512            qualified_columns.push((column, new_field));
4513        }
4514        DataType::Struct(fields) => {
4515            qualified_columns.extend(fields.iter().map(|f| {
4516                let new_name = format!("{}.{}", col_name, f.name());
4517                let column = Column::from_name(&new_name);
4518                let new_field = f.as_ref().clone().with_name(new_name);
4519                // let column = Column::from((None, &f));
4520                (column, Arc::new(new_field))
4521            }))
4522        }
4523        _ => {
4524            return internal_err!("trying to unnest on invalid data type {data_type}");
4525        }
4526    };
4527    Ok(qualified_columns)
4528}
4529
4530// Get the data type of a multi-dimensional type after unnesting it
4531// with a given depth
4532fn get_unnested_list_datatype_recursive(
4533    data_type: &DataType,
4534    depth: usize,
4535) -> Result<DataType> {
4536    match data_type {
4537        DataType::List(field)
4538        | DataType::FixedSizeList(field, _)
4539        | DataType::LargeList(field)
4540        | DataType::ListView(field)
4541        | DataType::LargeListView(field) => {
4542            if depth == 1 {
4543                return Ok(field.data_type().clone());
4544            }
4545            return get_unnested_list_datatype_recursive(field.data_type(), depth - 1);
4546        }
4547        _ => {}
4548    };
4549
4550    internal_err!("trying to unnest on invalid data type {data_type}")
4551}
4552
4553#[cfg(test)]
4554mod tests {
4555    use super::*;
4556    use crate::builder::LogicalTableSource;
4557    use crate::logical_plan::table_scan;
4558    use crate::select_expr::SelectExpr;
4559    use crate::test::function_stub::{count, count_udaf};
4560    use crate::{
4561        GroupingSet, binary_expr, col, exists, in_subquery, lit, placeholder,
4562        scalar_subquery,
4563    };
4564    use datafusion_common::metadata::ScalarAndMetadata;
4565    use datafusion_common::tree_node::{
4566        TransformedResult, TreeNodeRewriter, TreeNodeVisitor,
4567    };
4568    use datafusion_common::{Constraint, not_impl_err};
4569    use insta::{assert_debug_snapshot, assert_snapshot};
4570    use std::hash::DefaultHasher;
4571
4572    fn employee_schema() -> Schema {
4573        Schema::new(vec![
4574            Field::new("id", DataType::Int32, false),
4575            Field::new("first_name", DataType::Utf8, false),
4576            Field::new("last_name", DataType::Utf8, false),
4577            Field::new("state", DataType::Utf8, false),
4578            Field::new("salary", DataType::Int32, false),
4579        ])
4580    }
4581
4582    fn display_plan() -> Result<LogicalPlan> {
4583        let plan1 = table_scan(Some("employee_csv"), &employee_schema(), Some(vec![3]))?
4584            .build()?;
4585
4586        table_scan(Some("employee_csv"), &employee_schema(), Some(vec![0, 3]))?
4587            .filter(in_subquery(col("state"), Arc::new(plan1)))?
4588            .project(vec![col("id")])?
4589            .build()
4590    }
4591
4592    fn recursive_term_scan(name: &str, fields: Vec<Field>) -> Result<Arc<LogicalPlan>> {
4593        Ok(Arc::new(
4594            table_scan(Some(name), &Schema::new(fields), None)?.build()?,
4595        ))
4596    }
4597
4598    #[test]
4599    fn recursive_query_widens_nullability_per_column() -> Result<()> {
4600        // Column `a` is non-nullable in both terms and must stay non-nullable;
4601        // column `b` is non-nullable in the static term but nullable in the
4602        // recursive term, so the output must widen it to nullable.
4603        let static_term = recursive_term_scan(
4604            "static",
4605            vec![
4606                Field::new("a", DataType::Int32, false),
4607                Field::new("b", DataType::Int32, false),
4608            ],
4609        )?;
4610        let recursive_term = recursive_term_scan(
4611            "rec",
4612            vec![
4613                Field::new("a", DataType::Int32, false),
4614                Field::new("b", DataType::Int32, true),
4615            ],
4616        )?;
4617
4618        let query =
4619            RecursiveQuery::try_new("t".to_string(), static_term, recursive_term, false)?;
4620
4621        // Names and types are taken from the static term.
4622        assert_eq!(query.schema.field(0).name(), "a");
4623        assert_eq!(query.schema.field(1).name(), "b");
4624        assert_eq!(query.schema.field(0).data_type(), &DataType::Int32);
4625        assert_eq!(query.schema.field(1).data_type(), &DataType::Int32);
4626        // Nullability is widened independently per column.
4627        assert!(!query.schema.field(0).is_nullable());
4628        assert!(query.schema.field(1).is_nullable());
4629        // `schema()` returns the widened recursive-query schema.
4630        assert_eq!(
4631            LogicalPlan::RecursiveQuery(query.clone()).schema(),
4632            &query.schema
4633        );
4634        Ok(())
4635    }
4636
4637    #[test]
4638    fn recursive_query_rejects_column_count_mismatch() -> Result<()> {
4639        let static_term =
4640            recursive_term_scan("static", vec![Field::new("a", DataType::Int32, false)])?;
4641        let recursive_term = recursive_term_scan(
4642            "rec",
4643            vec![
4644                Field::new("a", DataType::Int32, false),
4645                Field::new("b", DataType::Int32, false),
4646            ],
4647        )?;
4648
4649        let err =
4650            RecursiveQuery::try_new("t".to_string(), static_term, recursive_term, false)
4651                .unwrap_err();
4652        assert!(
4653            err.strip_backtrace()
4654                .contains("must have the same number of columns"),
4655            "unexpected error: {err}"
4656        );
4657        Ok(())
4658    }
4659
4660    #[test]
4661    fn test_display_indent() -> Result<()> {
4662        let plan = display_plan()?;
4663
4664        assert_snapshot!(plan.display_indent(), @r"
4665        Projection: employee_csv.id
4666          Filter: employee_csv.state IN (<subquery>)
4667            Subquery:
4668              TableScan: employee_csv projection=[state]
4669            TableScan: employee_csv projection=[id, state]
4670        ");
4671        Ok(())
4672    }
4673
4674    #[test]
4675    fn test_display_indent_schema() -> Result<()> {
4676        let plan = display_plan()?;
4677
4678        assert_snapshot!(plan.display_indent_schema(), @r"
4679        Projection: employee_csv.id [id:Int32]
4680          Filter: employee_csv.state IN (<subquery>) [id:Int32, state:Utf8]
4681            Subquery: [state:Utf8]
4682              TableScan: employee_csv projection=[state] [state:Utf8]
4683            TableScan: employee_csv projection=[id, state] [id:Int32, state:Utf8]
4684        ");
4685        Ok(())
4686    }
4687
4688    #[test]
4689    fn test_display_subquery_alias() -> Result<()> {
4690        let plan1 = table_scan(Some("employee_csv"), &employee_schema(), Some(vec![3]))?
4691            .build()?;
4692        let plan1 = Arc::new(plan1);
4693
4694        let plan =
4695            table_scan(Some("employee_csv"), &employee_schema(), Some(vec![0, 3]))?
4696                .project(vec![col("id"), exists(plan1).alias("exists")])?
4697                .build();
4698
4699        assert_snapshot!(plan?.display_indent(), @r"
4700        Projection: employee_csv.id, EXISTS (<subquery>) AS exists
4701          Subquery:
4702            TableScan: employee_csv projection=[state]
4703          TableScan: employee_csv projection=[id, state]
4704        ");
4705        Ok(())
4706    }
4707
4708    #[test]
4709    fn test_display_graphviz() -> Result<()> {
4710        let plan = display_plan()?;
4711
4712        // just test for a few key lines in the output rather than the
4713        // whole thing to make test maintenance easier.
4714        assert_snapshot!(plan.display_graphviz(), @r#"
4715        // Begin DataFusion GraphViz Plan,
4716        // display it online here: https://dreampuf.github.io/GraphvizOnline
4717
4718        digraph {
4719          subgraph cluster_1
4720          {
4721            graph[label="LogicalPlan"]
4722            2[shape=box label="Projection: employee_csv.id"]
4723            3[shape=box label="Filter: employee_csv.state IN (<subquery>)"]
4724            2 -> 3 [arrowhead=none, arrowtail=normal, dir=back]
4725            4[shape=box label="Subquery:"]
4726            3 -> 4 [arrowhead=none, arrowtail=normal, dir=back]
4727            5[shape=box label="TableScan: employee_csv projection=[state]"]
4728            4 -> 5 [arrowhead=none, arrowtail=normal, dir=back]
4729            6[shape=box label="TableScan: employee_csv projection=[id, state]"]
4730            3 -> 6 [arrowhead=none, arrowtail=normal, dir=back]
4731          }
4732          subgraph cluster_7
4733          {
4734            graph[label="Detailed LogicalPlan"]
4735            8[shape=box label="Projection: employee_csv.id\nSchema: [id:Int32]"]
4736            9[shape=box label="Filter: employee_csv.state IN (<subquery>)\nSchema: [id:Int32, state:Utf8]"]
4737            8 -> 9 [arrowhead=none, arrowtail=normal, dir=back]
4738            10[shape=box label="Subquery:\nSchema: [state:Utf8]"]
4739            9 -> 10 [arrowhead=none, arrowtail=normal, dir=back]
4740            11[shape=box label="TableScan: employee_csv projection=[state]\nSchema: [state:Utf8]"]
4741            10 -> 11 [arrowhead=none, arrowtail=normal, dir=back]
4742            12[shape=box label="TableScan: employee_csv projection=[id, state]\nSchema: [id:Int32, state:Utf8]"]
4743            9 -> 12 [arrowhead=none, arrowtail=normal, dir=back]
4744          }
4745        }
4746        // End DataFusion GraphViz Plan
4747        "#);
4748        Ok(())
4749    }
4750
4751    #[test]
4752    fn test_display_pg_json() -> Result<()> {
4753        let plan = display_plan()?;
4754
4755        assert_snapshot!(plan.display_pg_json(), @r#"
4756        [
4757          {
4758            "Plan": {
4759              "Node Type": "Projection",
4760              "Expressions": [
4761                "employee_csv.id"
4762              ],
4763              "Plans": [
4764                {
4765                  "Node Type": "Filter",
4766                  "Condition": "employee_csv.state IN (<subquery>)",
4767                  "Plans": [
4768                    {
4769                      "Node Type": "Subquery",
4770                      "Plans": [
4771                        {
4772                          "Node Type": "TableScan",
4773                          "Relation Name": "employee_csv",
4774                          "Plans": [],
4775                          "Output": [
4776                            "state"
4777                          ]
4778                        }
4779                      ],
4780                      "Output": [
4781                        "state"
4782                      ]
4783                    },
4784                    {
4785                      "Node Type": "TableScan",
4786                      "Relation Name": "employee_csv",
4787                      "Plans": [],
4788                      "Output": [
4789                        "id",
4790                        "state"
4791                      ]
4792                    }
4793                  ],
4794                  "Output": [
4795                    "id",
4796                    "state"
4797                  ]
4798                }
4799              ],
4800              "Output": [
4801                "id"
4802              ]
4803            }
4804          }
4805        ]
4806        "#);
4807        Ok(())
4808    }
4809
4810    /// Tests for the Visitor trait and walking logical plan nodes
4811    #[derive(Debug, Default)]
4812    struct OkVisitor {
4813        strings: Vec<String>,
4814    }
4815
4816    impl<'n> TreeNodeVisitor<'n> for OkVisitor {
4817        type Node = LogicalPlan;
4818
4819        fn f_down(&mut self, plan: &'n LogicalPlan) -> Result<TreeNodeRecursion> {
4820            let s = match plan {
4821                LogicalPlan::Projection { .. } => "pre_visit Projection",
4822                LogicalPlan::Filter { .. } => "pre_visit Filter",
4823                LogicalPlan::TableScan { .. } => "pre_visit TableScan",
4824                _ => {
4825                    return not_impl_err!("unknown plan type");
4826                }
4827            };
4828
4829            self.strings.push(s.into());
4830            Ok(TreeNodeRecursion::Continue)
4831        }
4832
4833        fn f_up(&mut self, plan: &'n LogicalPlan) -> Result<TreeNodeRecursion> {
4834            let s = match plan {
4835                LogicalPlan::Projection { .. } => "post_visit Projection",
4836                LogicalPlan::Filter { .. } => "post_visit Filter",
4837                LogicalPlan::TableScan { .. } => "post_visit TableScan",
4838                _ => {
4839                    return not_impl_err!("unknown plan type");
4840                }
4841            };
4842
4843            self.strings.push(s.into());
4844            Ok(TreeNodeRecursion::Continue)
4845        }
4846    }
4847
4848    #[test]
4849    fn visit_order() {
4850        let mut visitor = OkVisitor::default();
4851        let plan = test_plan();
4852        let res = plan.visit_with_subqueries(&mut visitor);
4853        assert!(res.is_ok());
4854
4855        assert_debug_snapshot!(visitor.strings, @r#"
4856        [
4857            "pre_visit Projection",
4858            "pre_visit Filter",
4859            "pre_visit TableScan",
4860            "post_visit TableScan",
4861            "post_visit Filter",
4862            "post_visit Projection",
4863        ]
4864        "#);
4865    }
4866
4867    #[derive(Debug, Default)]
4868    /// Counter than counts to zero and returns true when it gets there
4869    struct OptionalCounter {
4870        val: Option<usize>,
4871    }
4872
4873    impl OptionalCounter {
4874        fn new(val: usize) -> Self {
4875            Self { val: Some(val) }
4876        }
4877        // Decrements the counter by 1, if any, returning true if it hits zero
4878        fn dec(&mut self) -> bool {
4879            if Some(0) == self.val {
4880                true
4881            } else {
4882                self.val = self.val.take().map(|i| i - 1);
4883                false
4884            }
4885        }
4886    }
4887
4888    #[derive(Debug, Default)]
4889    /// Visitor that returns false after some number of visits
4890    struct StoppingVisitor {
4891        inner: OkVisitor,
4892        /// When Some(0) returns false from pre_visit
4893        return_false_from_pre_in: OptionalCounter,
4894        /// When Some(0) returns false from post_visit
4895        return_false_from_post_in: OptionalCounter,
4896    }
4897
4898    impl<'n> TreeNodeVisitor<'n> for StoppingVisitor {
4899        type Node = LogicalPlan;
4900
4901        fn f_down(&mut self, plan: &'n LogicalPlan) -> Result<TreeNodeRecursion> {
4902            if self.return_false_from_pre_in.dec() {
4903                return Ok(TreeNodeRecursion::Stop);
4904            }
4905            self.inner.f_down(plan)?;
4906
4907            Ok(TreeNodeRecursion::Continue)
4908        }
4909
4910        fn f_up(&mut self, plan: &'n LogicalPlan) -> Result<TreeNodeRecursion> {
4911            if self.return_false_from_post_in.dec() {
4912                return Ok(TreeNodeRecursion::Stop);
4913            }
4914
4915            self.inner.f_up(plan)
4916        }
4917    }
4918
4919    /// test early stopping in pre-visit
4920    #[test]
4921    fn early_stopping_pre_visit() {
4922        let mut visitor = StoppingVisitor {
4923            return_false_from_pre_in: OptionalCounter::new(2),
4924            ..Default::default()
4925        };
4926        let plan = test_plan();
4927        let res = plan.visit_with_subqueries(&mut visitor);
4928        assert!(res.is_ok());
4929
4930        assert_debug_snapshot!(
4931            visitor.inner.strings,
4932            @r#"
4933        [
4934            "pre_visit Projection",
4935            "pre_visit Filter",
4936        ]
4937        "#
4938        );
4939    }
4940
4941    #[test]
4942    fn early_stopping_post_visit() {
4943        let mut visitor = StoppingVisitor {
4944            return_false_from_post_in: OptionalCounter::new(1),
4945            ..Default::default()
4946        };
4947        let plan = test_plan();
4948        let res = plan.visit_with_subqueries(&mut visitor);
4949        assert!(res.is_ok());
4950
4951        assert_debug_snapshot!(
4952            visitor.inner.strings,
4953            @r#"
4954        [
4955            "pre_visit Projection",
4956            "pre_visit Filter",
4957            "pre_visit TableScan",
4958            "post_visit TableScan",
4959        ]
4960        "#
4961        );
4962    }
4963
4964    #[derive(Debug, Default)]
4965    /// Visitor that returns an error after some number of visits
4966    struct ErrorVisitor {
4967        inner: OkVisitor,
4968        /// When Some(0) returns false from pre_visit
4969        return_error_from_pre_in: OptionalCounter,
4970        /// When Some(0) returns false from post_visit
4971        return_error_from_post_in: OptionalCounter,
4972    }
4973
4974    impl<'n> TreeNodeVisitor<'n> for ErrorVisitor {
4975        type Node = LogicalPlan;
4976
4977        fn f_down(&mut self, plan: &'n LogicalPlan) -> Result<TreeNodeRecursion> {
4978            if self.return_error_from_pre_in.dec() {
4979                return not_impl_err!("Error in pre_visit");
4980            }
4981
4982            self.inner.f_down(plan)
4983        }
4984
4985        fn f_up(&mut self, plan: &'n LogicalPlan) -> Result<TreeNodeRecursion> {
4986            if self.return_error_from_post_in.dec() {
4987                return not_impl_err!("Error in post_visit");
4988            }
4989
4990            self.inner.f_up(plan)
4991        }
4992    }
4993
4994    #[test]
4995    fn error_pre_visit() {
4996        let mut visitor = ErrorVisitor {
4997            return_error_from_pre_in: OptionalCounter::new(2),
4998            ..Default::default()
4999        };
5000        let plan = test_plan();
5001        let res = plan.visit_with_subqueries(&mut visitor).unwrap_err();
5002        assert_snapshot!(
5003            res.strip_backtrace(),
5004            @"This feature is not implemented: Error in pre_visit"
5005        );
5006        assert_debug_snapshot!(
5007            visitor.inner.strings,
5008            @r#"
5009        [
5010            "pre_visit Projection",
5011            "pre_visit Filter",
5012        ]
5013        "#
5014        );
5015    }
5016
5017    #[test]
5018    fn error_post_visit() {
5019        let mut visitor = ErrorVisitor {
5020            return_error_from_post_in: OptionalCounter::new(1),
5021            ..Default::default()
5022        };
5023        let plan = test_plan();
5024        let res = plan.visit_with_subqueries(&mut visitor).unwrap_err();
5025        assert_snapshot!(
5026            res.strip_backtrace(),
5027            @"This feature is not implemented: Error in post_visit"
5028        );
5029        assert_debug_snapshot!(
5030            visitor.inner.strings,
5031            @r#"
5032        [
5033            "pre_visit Projection",
5034            "pre_visit Filter",
5035            "pre_visit TableScan",
5036            "post_visit TableScan",
5037        ]
5038        "#
5039        );
5040    }
5041
5042    #[test]
5043    fn test_partial_eq_hash_and_partial_ord() {
5044        let empty_values = Arc::new(LogicalPlan::EmptyRelation(EmptyRelation {
5045            produce_one_row: true,
5046            schema: Arc::new(DFSchema::empty()),
5047        }));
5048
5049        let count_window_function = |schema| {
5050            Window::try_new_with_schema(
5051                vec![Expr::WindowFunction(Box::new(WindowFunction::new(
5052                    WindowFunctionDefinition::AggregateUDF(count_udaf()),
5053                    vec![],
5054                )))],
5055                Arc::clone(&empty_values),
5056                Arc::new(schema),
5057            )
5058            .unwrap()
5059        };
5060
5061        let schema_without_metadata = || {
5062            DFSchema::from_unqualified_fields(
5063                vec![Field::new("count", DataType::Int64, false)].into(),
5064                HashMap::new(),
5065            )
5066            .unwrap()
5067        };
5068
5069        let schema_with_metadata = || {
5070            DFSchema::from_unqualified_fields(
5071                vec![Field::new("count", DataType::Int64, false)].into(),
5072                [("key".to_string(), "value".to_string())].into(),
5073            )
5074            .unwrap()
5075        };
5076
5077        // A Window
5078        let f = count_window_function(schema_without_metadata());
5079
5080        // Same like `f`, different instance
5081        let f2 = count_window_function(schema_without_metadata());
5082        assert_eq!(f, f2);
5083        assert_eq!(hash(&f), hash(&f2));
5084        assert_eq!(f.partial_cmp(&f2), Some(Ordering::Equal));
5085
5086        // Same like `f`, except for schema metadata
5087        let o = count_window_function(schema_with_metadata());
5088        assert_ne!(f, o);
5089        assert_ne!(hash(&f), hash(&o)); // hash can collide for different values but does not collide in this test
5090        assert_eq!(f.partial_cmp(&o), None);
5091    }
5092
5093    fn hash<T: Hash>(value: &T) -> u64 {
5094        let hasher = &mut DefaultHasher::new();
5095        value.hash(hasher);
5096        hasher.finish()
5097    }
5098
5099    #[test]
5100    fn projection_expr_schema_mismatch() -> Result<()> {
5101        let empty_schema = Arc::new(DFSchema::empty());
5102        let p = Projection::try_new_with_schema(
5103            vec![col("a")],
5104            Arc::new(LogicalPlan::EmptyRelation(EmptyRelation {
5105                produce_one_row: false,
5106                schema: Arc::clone(&empty_schema),
5107            })),
5108            empty_schema,
5109        );
5110        assert_snapshot!(p.unwrap_err().strip_backtrace(), @"Error during planning: Projection has mismatch between number of expressions (1) and number of fields in schema (0)");
5111        Ok(())
5112    }
5113
5114    fn test_plan() -> LogicalPlan {
5115        let schema = Schema::new(vec![
5116            Field::new("id", DataType::Int32, false),
5117            Field::new("state", DataType::Utf8, false),
5118        ]);
5119
5120        table_scan(TableReference::none(), &schema, Some(vec![0, 1]))
5121            .unwrap()
5122            .filter(col("state").eq(lit("CO")))
5123            .unwrap()
5124            .project(vec![col("id")])
5125            .unwrap()
5126            .build()
5127            .unwrap()
5128    }
5129
5130    #[test]
5131    fn test_replace_invalid_placeholder() {
5132        // test empty placeholder
5133        let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
5134
5135        let plan = table_scan(TableReference::none(), &schema, None)
5136            .unwrap()
5137            .filter(col("id").eq(placeholder("")))
5138            .unwrap()
5139            .build()
5140            .unwrap();
5141
5142        let param_values = vec![ScalarValue::Int32(Some(42))];
5143        plan.replace_params_with_values(&param_values.clone().into())
5144            .expect_err("unexpectedly succeeded to replace an invalid placeholder");
5145
5146        // test $0 placeholder
5147        let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
5148
5149        let plan = table_scan(TableReference::none(), &schema, None)
5150            .unwrap()
5151            .filter(col("id").eq(placeholder("$0")))
5152            .unwrap()
5153            .build()
5154            .unwrap();
5155
5156        plan.replace_params_with_values(&param_values.clone().into())
5157            .expect_err("unexpectedly succeeded to replace an invalid placeholder");
5158
5159        // test $00 placeholder
5160        let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
5161
5162        let plan = table_scan(TableReference::none(), &schema, None)
5163            .unwrap()
5164            .filter(col("id").eq(placeholder("$00")))
5165            .unwrap()
5166            .build()
5167            .unwrap();
5168
5169        plan.replace_params_with_values(&param_values.into())
5170            .expect_err("unexpectedly succeeded to replace an invalid placeholder");
5171    }
5172
5173    #[test]
5174    fn test_replace_placeholder_mismatched_metadata() {
5175        let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
5176
5177        // Create a prepared statement with explicit fields that do not have metadata
5178        let plan = table_scan(TableReference::none(), &schema, None)
5179            .unwrap()
5180            .filter(col("id").eq(placeholder("$1")))
5181            .unwrap()
5182            .build()
5183            .unwrap();
5184        let prepared_builder = LogicalPlanBuilder::new(plan)
5185            .prepare(
5186                "".to_string(),
5187                vec![Field::new("", DataType::Int32, true).into()],
5188            )
5189            .unwrap();
5190
5191        // Attempt to bind a parameter with metadata
5192        let mut scalar_meta = HashMap::new();
5193        scalar_meta.insert("some_key".to_string(), "some_value".to_string());
5194        let param_values = ParamValues::List(vec![ScalarAndMetadata::new(
5195            ScalarValue::Int32(Some(42)),
5196            Some(scalar_meta.into()),
5197        )]);
5198        prepared_builder
5199            .plan()
5200            .clone()
5201            .with_param_values(param_values)
5202            .expect_err("prepared field metadata mismatch unexpectedly succeeded");
5203    }
5204
5205    #[test]
5206    fn test_replace_placeholder_empty_relation_valid_schema() {
5207        // SELECT $1, $2;
5208        let plan = LogicalPlanBuilder::empty(false)
5209            .project(vec![
5210                SelectExpr::from(placeholder("$1")),
5211                SelectExpr::from(placeholder("$2")),
5212            ])
5213            .unwrap()
5214            .build()
5215            .unwrap();
5216
5217        // original
5218        assert_snapshot!(plan.display_indent_schema(), @r"
5219        Projection: $1, $2 [$1:Null;N, $2:Null;N]
5220          EmptyRelation: rows=0 []
5221        ");
5222
5223        let plan = plan
5224            .with_param_values(vec![ScalarValue::from(1i32), ScalarValue::from("s")])
5225            .unwrap();
5226
5227        // replaced
5228        assert_snapshot!(plan.display_indent_schema(), @r#"
5229        Projection: Int32(1) AS $1, Utf8("s") AS $2 [$1:Int32, $2:Utf8]
5230          EmptyRelation: rows=0 []
5231        "#);
5232    }
5233
5234    #[test]
5235    fn test_nullable_schema_after_grouping_set() {
5236        let schema = Schema::new(vec![
5237            Field::new("foo", DataType::Int32, false),
5238            Field::new("bar", DataType::Int32, false),
5239        ]);
5240
5241        let plan = table_scan(TableReference::none(), &schema, None)
5242            .unwrap()
5243            .aggregate(
5244                vec![Expr::GroupingSet(GroupingSet::GroupingSets(vec![
5245                    vec![col("foo")],
5246                    vec![col("bar")],
5247                ]))],
5248                vec![count(lit(true))],
5249            )
5250            .unwrap()
5251            .build()
5252            .unwrap();
5253
5254        let output_schema = plan.schema();
5255
5256        assert!(
5257            output_schema
5258                .field_with_name(None, "foo")
5259                .unwrap()
5260                .is_nullable(),
5261        );
5262        assert!(
5263            output_schema
5264                .field_with_name(None, "bar")
5265                .unwrap()
5266                .is_nullable()
5267        );
5268    }
5269
5270    #[test]
5271    fn grouping_id_type_accounts_for_duplicate_ordinal_bits() {
5272        // 8 grouping columns fit in UInt8 when there are no duplicate ordinals,
5273        // but adding one duplicate ordinal bit widens the type to UInt16.
5274        assert_eq!(Aggregate::grouping_id_type(8, 0), DataType::UInt8);
5275        assert_eq!(Aggregate::grouping_id_type(8, 1), DataType::UInt16);
5276    }
5277
5278    #[test]
5279    fn test_filter_is_scalar() {
5280        // test empty placeholder
5281        let schema =
5282            Arc::new(Schema::new(vec![Field::new("id", DataType::Int32, false)]));
5283
5284        let source = Arc::new(LogicalTableSource::new(schema));
5285        let schema = Arc::new(
5286            DFSchema::try_from_qualified_schema(
5287                TableReference::bare("tab"),
5288                &source.schema(),
5289            )
5290            .unwrap(),
5291        );
5292        let scan = Arc::new(LogicalPlan::TableScan(TableScan {
5293            table_name: TableReference::bare("tab"),
5294            source: Arc::clone(&source) as Arc<dyn TableSource>,
5295            projection: None,
5296            projected_schema: Arc::clone(&schema),
5297            filters: vec![],
5298            fetch: None,
5299        }));
5300        let col = schema.field_names()[0].clone();
5301
5302        let filter = Filter::try_new(
5303            Expr::Column(col.into()).eq(Expr::Literal(ScalarValue::Int32(Some(1)), None)),
5304            scan,
5305        )
5306        .unwrap();
5307        assert!(!filter.is_scalar());
5308        let unique_schema = Arc::new(
5309            schema
5310                .as_ref()
5311                .clone()
5312                .with_functional_dependencies(
5313                    FunctionalDependencies::new_from_constraints(
5314                        Some(&Constraints::new_unverified(vec![Constraint::Unique(
5315                            vec![0],
5316                        )])),
5317                        1,
5318                    ),
5319                )
5320                .unwrap(),
5321        );
5322        let scan = Arc::new(LogicalPlan::TableScan(TableScan {
5323            table_name: TableReference::bare("tab"),
5324            source,
5325            projection: None,
5326            projected_schema: Arc::clone(&unique_schema),
5327            filters: vec![],
5328            fetch: None,
5329        }));
5330        let col = schema.field_names()[0].clone();
5331
5332        let filter =
5333            Filter::try_new(Expr::Column(col.into()).eq(lit(1i32)), scan).unwrap();
5334        assert!(filter.is_scalar());
5335    }
5336
5337    #[test]
5338    fn test_transform_explain() {
5339        let schema = Schema::new(vec![
5340            Field::new("foo", DataType::Int32, false),
5341            Field::new("bar", DataType::Int32, false),
5342        ]);
5343
5344        let plan = table_scan(TableReference::none(), &schema, None)
5345            .unwrap()
5346            .explain(false, false)
5347            .unwrap()
5348            .build()
5349            .unwrap();
5350
5351        let external_filter = col("foo").eq(lit(true));
5352
5353        // after transformation, because plan is not the same anymore,
5354        // the parent plan is built again with call to LogicalPlan::with_new_inputs -> with_new_exprs
5355        let plan = plan
5356            .transform(|plan| match plan {
5357                LogicalPlan::TableScan(table) => {
5358                    let filter = Filter::try_new(
5359                        external_filter.clone(),
5360                        Arc::new(LogicalPlan::TableScan(table)),
5361                    )
5362                    .unwrap();
5363                    Ok(Transformed::yes(LogicalPlan::Filter(filter)))
5364                }
5365                x => Ok(Transformed::no(x)),
5366            })
5367            .data()
5368            .unwrap();
5369
5370        let actual = format!("{}", plan.display_indent());
5371        assert_snapshot!(actual, @r"
5372        Explain
5373          Filter: foo = Boolean(true)
5374            TableScan: ?table?
5375        ")
5376    }
5377
5378    #[test]
5379    fn test_plan_partial_ord() {
5380        let empty_relation = LogicalPlan::EmptyRelation(EmptyRelation {
5381            produce_one_row: false,
5382            schema: Arc::new(DFSchema::empty()),
5383        });
5384
5385        let describe_table = LogicalPlan::DescribeTable(DescribeTable {
5386            schema: Arc::new(Schema::new(vec![Field::new(
5387                "foo",
5388                DataType::Int32,
5389                false,
5390            )])),
5391            output_schema: DFSchemaRef::new(DFSchema::empty()),
5392        });
5393
5394        let describe_table_clone = LogicalPlan::DescribeTable(DescribeTable {
5395            schema: Arc::new(Schema::new(vec![Field::new(
5396                "foo",
5397                DataType::Int32,
5398                false,
5399            )])),
5400            output_schema: DFSchemaRef::new(DFSchema::empty()),
5401        });
5402
5403        assert_eq!(
5404            empty_relation.partial_cmp(&describe_table),
5405            Some(Ordering::Less)
5406        );
5407        assert_eq!(
5408            describe_table.partial_cmp(&empty_relation),
5409            Some(Ordering::Greater)
5410        );
5411        assert_eq!(describe_table.partial_cmp(&describe_table_clone), None);
5412    }
5413
5414    #[test]
5415    fn test_limit_with_new_children() {
5416        let input = Arc::new(LogicalPlan::Values(Values {
5417            schema: Arc::new(DFSchema::empty()),
5418            values: vec![vec![]],
5419        }));
5420        let cases = [
5421            LogicalPlan::Limit(Limit {
5422                skip: None,
5423                fetch: None,
5424                input: Arc::clone(&input),
5425            }),
5426            LogicalPlan::Limit(Limit {
5427                skip: None,
5428                fetch: Some(Box::new(Expr::Literal(
5429                    ScalarValue::new_ten(&DataType::UInt32).unwrap(),
5430                    None,
5431                ))),
5432                input: Arc::clone(&input),
5433            }),
5434            LogicalPlan::Limit(Limit {
5435                skip: Some(Box::new(Expr::Literal(
5436                    ScalarValue::new_ten(&DataType::UInt32).unwrap(),
5437                    None,
5438                ))),
5439                fetch: None,
5440                input: Arc::clone(&input),
5441            }),
5442            LogicalPlan::Limit(Limit {
5443                skip: Some(Box::new(Expr::Literal(
5444                    ScalarValue::new_one(&DataType::UInt32).unwrap(),
5445                    None,
5446                ))),
5447                fetch: Some(Box::new(Expr::Literal(
5448                    ScalarValue::new_ten(&DataType::UInt32).unwrap(),
5449                    None,
5450                ))),
5451                input,
5452            }),
5453        ];
5454
5455        for limit in cases {
5456            let new_limit = limit
5457                .with_new_exprs(
5458                    limit.expressions(),
5459                    limit.inputs().into_iter().cloned().collect(),
5460                )
5461                .unwrap();
5462            assert_eq!(limit, new_limit);
5463        }
5464    }
5465
5466    #[test]
5467    fn test_with_subqueries_jump() {
5468        // The test plan contains a `Project` node above a `Filter` node, and the
5469        // `Project` node contains a subquery plan with a `Filter` root node, so returning
5470        // `TreeNodeRecursion::Jump` on `Project` should cause not visiting any of the
5471        // `Filter`s.
5472        let subquery_schema =
5473            Schema::new(vec![Field::new("sub_id", DataType::Int32, false)]);
5474
5475        let subquery_plan =
5476            table_scan(TableReference::none(), &subquery_schema, Some(vec![0]))
5477                .unwrap()
5478                .filter(col("sub_id").eq(lit(0)))
5479                .unwrap()
5480                .build()
5481                .unwrap();
5482
5483        let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
5484
5485        let plan = table_scan(TableReference::none(), &schema, Some(vec![0]))
5486            .unwrap()
5487            .filter(col("id").eq(lit(0)))
5488            .unwrap()
5489            .project(vec![col("id"), scalar_subquery(Arc::new(subquery_plan))])
5490            .unwrap()
5491            .build()
5492            .unwrap();
5493
5494        let mut filter_found = false;
5495        plan.apply_with_subqueries(|plan| {
5496            match plan {
5497                LogicalPlan::Projection(..) => return Ok(TreeNodeRecursion::Jump),
5498                LogicalPlan::Filter(..) => filter_found = true,
5499                _ => {}
5500            }
5501            Ok(TreeNodeRecursion::Continue)
5502        })
5503        .unwrap();
5504        assert!(!filter_found);
5505
5506        struct ProjectJumpVisitor {
5507            filter_found: bool,
5508        }
5509
5510        impl ProjectJumpVisitor {
5511            fn new() -> Self {
5512                Self {
5513                    filter_found: false,
5514                }
5515            }
5516        }
5517
5518        impl<'n> TreeNodeVisitor<'n> for ProjectJumpVisitor {
5519            type Node = LogicalPlan;
5520
5521            fn f_down(&mut self, node: &'n Self::Node) -> Result<TreeNodeRecursion> {
5522                match node {
5523                    LogicalPlan::Projection(..) => return Ok(TreeNodeRecursion::Jump),
5524                    LogicalPlan::Filter(..) => self.filter_found = true,
5525                    _ => {}
5526                }
5527                Ok(TreeNodeRecursion::Continue)
5528            }
5529        }
5530
5531        let mut visitor = ProjectJumpVisitor::new();
5532        plan.visit_with_subqueries(&mut visitor).unwrap();
5533        assert!(!visitor.filter_found);
5534
5535        let mut filter_found = false;
5536        plan.clone()
5537            .transform_down_with_subqueries(|plan| {
5538                match plan {
5539                    LogicalPlan::Projection(..) => {
5540                        return Ok(Transformed::new(
5541                            plan,
5542                            false,
5543                            TreeNodeRecursion::Jump,
5544                        ));
5545                    }
5546                    LogicalPlan::Filter(..) => filter_found = true,
5547                    _ => {}
5548                }
5549                Ok(Transformed::no(plan))
5550            })
5551            .unwrap();
5552        assert!(!filter_found);
5553
5554        let mut filter_found = false;
5555        plan.clone()
5556            .transform_down_up_with_subqueries(
5557                |plan| {
5558                    match plan {
5559                        LogicalPlan::Projection(..) => {
5560                            return Ok(Transformed::new(
5561                                plan,
5562                                false,
5563                                TreeNodeRecursion::Jump,
5564                            ));
5565                        }
5566                        LogicalPlan::Filter(..) => filter_found = true,
5567                        _ => {}
5568                    }
5569                    Ok(Transformed::no(plan))
5570                },
5571                |plan| Ok(Transformed::no(plan)),
5572            )
5573            .unwrap();
5574        assert!(!filter_found);
5575
5576        struct ProjectJumpRewriter {
5577            filter_found: bool,
5578        }
5579
5580        impl ProjectJumpRewriter {
5581            fn new() -> Self {
5582                Self {
5583                    filter_found: false,
5584                }
5585            }
5586        }
5587
5588        impl TreeNodeRewriter for ProjectJumpRewriter {
5589            type Node = LogicalPlan;
5590
5591            fn f_down(&mut self, node: Self::Node) -> Result<Transformed<Self::Node>> {
5592                match node {
5593                    LogicalPlan::Projection(..) => {
5594                        return Ok(Transformed::new(
5595                            node,
5596                            false,
5597                            TreeNodeRecursion::Jump,
5598                        ));
5599                    }
5600                    LogicalPlan::Filter(..) => self.filter_found = true,
5601                    _ => {}
5602                }
5603                Ok(Transformed::no(node))
5604            }
5605        }
5606
5607        let mut rewriter = ProjectJumpRewriter::new();
5608        plan.rewrite_with_subqueries(&mut rewriter).unwrap();
5609        assert!(!rewriter.filter_found);
5610    }
5611
5612    #[test]
5613    fn test_with_unresolved_placeholders() {
5614        let field_name = "id";
5615        let placeholder_value = "$1";
5616        let schema = Schema::new(vec![Field::new(field_name, DataType::Int32, false)]);
5617
5618        let plan = table_scan(TableReference::none(), &schema, None)
5619            .unwrap()
5620            .filter(col(field_name).eq(placeholder(placeholder_value)))
5621            .unwrap()
5622            .build()
5623            .unwrap();
5624
5625        // Check that the placeholder parameters have not received a DataType.
5626        let params = plan.get_parameter_fields().unwrap();
5627        assert_eq!(params.len(), 1);
5628
5629        let parameter_type = params.clone().get(placeholder_value).unwrap().clone();
5630        assert_eq!(parameter_type, None);
5631    }
5632
5633    #[test]
5634    fn test_join_with_new_exprs() -> Result<()> {
5635        fn create_test_join(
5636            on: Vec<(Expr, Expr)>,
5637            filter: Option<Expr>,
5638        ) -> Result<LogicalPlan> {
5639            let schema = Schema::new(vec![
5640                Field::new("a", DataType::Int32, false),
5641                Field::new("b", DataType::Int32, false),
5642            ]);
5643
5644            let left_schema = DFSchema::try_from_qualified_schema("t1", &schema)?;
5645            let right_schema = DFSchema::try_from_qualified_schema("t2", &schema)?;
5646
5647            Ok(LogicalPlan::Join(Join {
5648                left: Arc::new(
5649                    table_scan(Some("t1"), left_schema.as_arrow(), None)?.build()?,
5650                ),
5651                right: Arc::new(
5652                    table_scan(Some("t2"), right_schema.as_arrow(), None)?.build()?,
5653                ),
5654                on,
5655                filter,
5656                join_type: JoinType::Inner,
5657                join_constraint: JoinConstraint::On,
5658                schema: Arc::new(left_schema.join(&right_schema)?),
5659                null_equality: NullEquality::NullEqualsNothing,
5660                null_aware: false,
5661            }))
5662        }
5663
5664        {
5665            let join = create_test_join(vec![(col("t1.a"), (col("t2.a")))], None)?;
5666            let LogicalPlan::Join(join) = join.with_new_exprs(
5667                join.expressions(),
5668                join.inputs().into_iter().cloned().collect(),
5669            )?
5670            else {
5671                unreachable!()
5672            };
5673            assert_eq!(join.on, vec![(col("t1.a"), (col("t2.a")))]);
5674            assert_eq!(join.filter, None);
5675        }
5676
5677        {
5678            let join = create_test_join(vec![], Some(col("t1.a").gt(col("t2.a"))))?;
5679            let LogicalPlan::Join(join) = join.with_new_exprs(
5680                join.expressions(),
5681                join.inputs().into_iter().cloned().collect(),
5682            )?
5683            else {
5684                unreachable!()
5685            };
5686            assert_eq!(join.on, vec![]);
5687            assert_eq!(join.filter, Some(col("t1.a").gt(col("t2.a"))));
5688        }
5689
5690        {
5691            let join = create_test_join(
5692                vec![(col("t1.a"), (col("t2.a")))],
5693                Some(col("t1.b").gt(col("t2.b"))),
5694            )?;
5695            let LogicalPlan::Join(join) = join.with_new_exprs(
5696                join.expressions(),
5697                join.inputs().into_iter().cloned().collect(),
5698            )?
5699            else {
5700                unreachable!()
5701            };
5702            assert_eq!(join.on, vec![(col("t1.a"), (col("t2.a")))]);
5703            assert_eq!(join.filter, Some(col("t1.b").gt(col("t2.b"))));
5704        }
5705
5706        {
5707            let join = create_test_join(
5708                vec![(col("t1.a"), (col("t2.a"))), (col("t1.b"), (col("t2.b")))],
5709                None,
5710            )?;
5711            let LogicalPlan::Join(join) = join.with_new_exprs(
5712                vec![
5713                    binary_expr(col("t1.a"), Operator::Plus, lit(1)),
5714                    binary_expr(col("t2.a"), Operator::Plus, lit(2)),
5715                    col("t1.b"),
5716                    col("t2.b"),
5717                    lit(true),
5718                ],
5719                join.inputs().into_iter().cloned().collect(),
5720            )?
5721            else {
5722                unreachable!()
5723            };
5724            assert_eq!(
5725                join.on,
5726                vec![
5727                    (
5728                        binary_expr(col("t1.a"), Operator::Plus, lit(1)),
5729                        binary_expr(col("t2.a"), Operator::Plus, lit(2))
5730                    ),
5731                    (col("t1.b"), (col("t2.b")))
5732                ]
5733            );
5734            assert_eq!(join.filter, Some(lit(true)));
5735        }
5736
5737        Ok(())
5738    }
5739
5740    #[test]
5741    fn test_join_try_new() -> Result<()> {
5742        let schema = Schema::new(vec![
5743            Field::new("a", DataType::Int32, false),
5744            Field::new("b", DataType::Int32, false),
5745        ]);
5746
5747        let left_scan = table_scan(Some("t1"), &schema, None)?.build()?;
5748
5749        let right_scan = table_scan(Some("t2"), &schema, None)?.build()?;
5750
5751        let join_types = vec![
5752            JoinType::Inner,
5753            JoinType::Left,
5754            JoinType::Right,
5755            JoinType::Full,
5756            JoinType::LeftSemi,
5757            JoinType::LeftAnti,
5758            JoinType::RightSemi,
5759            JoinType::RightAnti,
5760            JoinType::LeftMark,
5761        ];
5762
5763        for join_type in join_types {
5764            let join = Join::try_new(
5765                Arc::new(left_scan.clone()),
5766                Arc::new(right_scan.clone()),
5767                vec![(col("t1.a"), col("t2.a"))],
5768                Some(col("t1.b").gt(col("t2.b"))),
5769                join_type,
5770                JoinConstraint::On,
5771                NullEquality::NullEqualsNothing,
5772                false,
5773            )?;
5774
5775            match join_type {
5776                JoinType::LeftSemi | JoinType::LeftAnti => {
5777                    assert_eq!(join.schema.fields().len(), 2);
5778
5779                    let fields = join.schema.fields();
5780                    assert_eq!(
5781                        fields[0].name(),
5782                        "a",
5783                        "First field should be 'a' from left table"
5784                    );
5785                    assert_eq!(
5786                        fields[1].name(),
5787                        "b",
5788                        "Second field should be 'b' from left table"
5789                    );
5790                }
5791                JoinType::RightSemi | JoinType::RightAnti => {
5792                    assert_eq!(join.schema.fields().len(), 2);
5793
5794                    let fields = join.schema.fields();
5795                    assert_eq!(
5796                        fields[0].name(),
5797                        "a",
5798                        "First field should be 'a' from right table"
5799                    );
5800                    assert_eq!(
5801                        fields[1].name(),
5802                        "b",
5803                        "Second field should be 'b' from right table"
5804                    );
5805                }
5806                JoinType::LeftMark => {
5807                    assert_eq!(join.schema.fields().len(), 3);
5808
5809                    let fields = join.schema.fields();
5810                    assert_eq!(
5811                        fields[0].name(),
5812                        "a",
5813                        "First field should be 'a' from left table"
5814                    );
5815                    assert_eq!(
5816                        fields[1].name(),
5817                        "b",
5818                        "Second field should be 'b' from left table"
5819                    );
5820                    assert_eq!(
5821                        fields[2].name(),
5822                        "mark",
5823                        "Third field should be the mark column"
5824                    );
5825
5826                    assert!(!fields[0].is_nullable());
5827                    assert!(!fields[1].is_nullable());
5828                    assert!(!fields[2].is_nullable());
5829                }
5830                _ => {
5831                    assert_eq!(join.schema.fields().len(), 4);
5832
5833                    let fields = join.schema.fields();
5834                    assert_eq!(
5835                        fields[0].name(),
5836                        "a",
5837                        "First field should be 'a' from left table"
5838                    );
5839                    assert_eq!(
5840                        fields[1].name(),
5841                        "b",
5842                        "Second field should be 'b' from left table"
5843                    );
5844                    assert_eq!(
5845                        fields[2].name(),
5846                        "a",
5847                        "Third field should be 'a' from right table"
5848                    );
5849                    assert_eq!(
5850                        fields[3].name(),
5851                        "b",
5852                        "Fourth field should be 'b' from right table"
5853                    );
5854
5855                    if join_type == JoinType::Left {
5856                        // Left side fields (first two) shouldn't be nullable
5857                        assert!(!fields[0].is_nullable());
5858                        assert!(!fields[1].is_nullable());
5859                        // Right side fields (third and fourth) should be nullable
5860                        assert!(fields[2].is_nullable());
5861                        assert!(fields[3].is_nullable());
5862                    } else if join_type == JoinType::Right {
5863                        // Left side fields (first two) should be nullable
5864                        assert!(fields[0].is_nullable());
5865                        assert!(fields[1].is_nullable());
5866                        // Right side fields (third and fourth) shouldn't be nullable
5867                        assert!(!fields[2].is_nullable());
5868                        assert!(!fields[3].is_nullable());
5869                    } else if join_type == JoinType::Full {
5870                        assert!(fields[0].is_nullable());
5871                        assert!(fields[1].is_nullable());
5872                        assert!(fields[2].is_nullable());
5873                        assert!(fields[3].is_nullable());
5874                    }
5875                }
5876            }
5877
5878            assert_eq!(join.on, vec![(col("t1.a"), col("t2.a"))]);
5879            assert_eq!(join.filter, Some(col("t1.b").gt(col("t2.b"))));
5880            assert_eq!(join.join_type, join_type);
5881            assert_eq!(join.join_constraint, JoinConstraint::On);
5882            assert_eq!(join.null_equality, NullEquality::NullEqualsNothing);
5883        }
5884
5885        Ok(())
5886    }
5887
5888    #[test]
5889    fn test_join_try_new_with_using_constraint_and_overlapping_columns() -> Result<()> {
5890        let left_schema = Schema::new(vec![
5891            Field::new("id", DataType::Int32, false), // Common column in both tables
5892            Field::new("name", DataType::Utf8, false), // Unique to left
5893            Field::new("value", DataType::Int32, false), // Common column, different meaning
5894        ]);
5895
5896        let right_schema = Schema::new(vec![
5897            Field::new("id", DataType::Int32, false), // Common column in both tables
5898            Field::new("category", DataType::Utf8, false), // Unique to right
5899            Field::new("value", DataType::Float64, true), // Common column, different meaning
5900        ]);
5901
5902        let left_plan = table_scan(Some("t1"), &left_schema, None)?.build()?;
5903
5904        let right_plan = table_scan(Some("t2"), &right_schema, None)?.build()?;
5905
5906        // Test 1: USING constraint with a common column
5907        {
5908            // In the logical plan, both copies of the `id` column are preserved
5909            // The USING constraint is handled later during physical execution, where the common column appears once
5910            let join = Join::try_new(
5911                Arc::new(left_plan.clone()),
5912                Arc::new(right_plan.clone()),
5913                vec![(col("t1.id"), col("t2.id"))],
5914                None,
5915                JoinType::Inner,
5916                JoinConstraint::Using,
5917                NullEquality::NullEqualsNothing,
5918                false,
5919            )?;
5920
5921            let fields = join.schema.fields();
5922
5923            assert_eq!(fields.len(), 6);
5924
5925            assert_eq!(
5926                fields[0].name(),
5927                "id",
5928                "First field should be 'id' from left table"
5929            );
5930            assert_eq!(
5931                fields[1].name(),
5932                "name",
5933                "Second field should be 'name' from left table"
5934            );
5935            assert_eq!(
5936                fields[2].name(),
5937                "value",
5938                "Third field should be 'value' from left table"
5939            );
5940            assert_eq!(
5941                fields[3].name(),
5942                "id",
5943                "Fourth field should be 'id' from right table"
5944            );
5945            assert_eq!(
5946                fields[4].name(),
5947                "category",
5948                "Fifth field should be 'category' from right table"
5949            );
5950            assert_eq!(
5951                fields[5].name(),
5952                "value",
5953                "Sixth field should be 'value' from right table"
5954            );
5955
5956            assert_eq!(join.join_constraint, JoinConstraint::Using);
5957        }
5958
5959        // Test 2: Complex join condition with expressions
5960        {
5961            // Complex condition: join on id equality AND where left.value < right.value
5962            let join = Join::try_new(
5963                Arc::new(left_plan.clone()),
5964                Arc::new(right_plan.clone()),
5965                vec![(col("t1.id"), col("t2.id"))], // Equijoin condition
5966                Some(col("t1.value").lt(col("t2.value"))), // Non-equi filter condition
5967                JoinType::Inner,
5968                JoinConstraint::On,
5969                NullEquality::NullEqualsNothing,
5970                false,
5971            )?;
5972
5973            let fields = join.schema.fields();
5974            assert_eq!(fields.len(), 6);
5975
5976            assert_eq!(
5977                fields[0].name(),
5978                "id",
5979                "First field should be 'id' from left table"
5980            );
5981            assert_eq!(
5982                fields[1].name(),
5983                "name",
5984                "Second field should be 'name' from left table"
5985            );
5986            assert_eq!(
5987                fields[2].name(),
5988                "value",
5989                "Third field should be 'value' from left table"
5990            );
5991            assert_eq!(
5992                fields[3].name(),
5993                "id",
5994                "Fourth field should be 'id' from right table"
5995            );
5996            assert_eq!(
5997                fields[4].name(),
5998                "category",
5999                "Fifth field should be 'category' from right table"
6000            );
6001            assert_eq!(
6002                fields[5].name(),
6003                "value",
6004                "Sixth field should be 'value' from right table"
6005            );
6006
6007            assert_eq!(join.filter, Some(col("t1.value").lt(col("t2.value"))));
6008        }
6009
6010        // Test 3: Join with null equality behavior set to true
6011        {
6012            let join = Join::try_new(
6013                Arc::new(left_plan.clone()),
6014                Arc::new(right_plan.clone()),
6015                vec![(col("t1.id"), col("t2.id"))],
6016                None,
6017                JoinType::Inner,
6018                JoinConstraint::On,
6019                NullEquality::NullEqualsNull,
6020                false,
6021            )?;
6022
6023            assert_eq!(join.null_equality, NullEquality::NullEqualsNull);
6024        }
6025
6026        Ok(())
6027    }
6028
6029    #[test]
6030    fn test_join_try_new_schema_validation() -> Result<()> {
6031        let left_schema = Schema::new(vec![
6032            Field::new("id", DataType::Int32, false),
6033            Field::new("name", DataType::Utf8, false),
6034            Field::new("value", DataType::Float64, true),
6035        ]);
6036
6037        let right_schema = Schema::new(vec![
6038            Field::new("id", DataType::Int32, false),
6039            Field::new("category", DataType::Utf8, true),
6040            Field::new("code", DataType::Int16, false),
6041        ]);
6042
6043        let left_plan = table_scan(Some("t1"), &left_schema, None)?.build()?;
6044
6045        let right_plan = table_scan(Some("t2"), &right_schema, None)?.build()?;
6046
6047        let join_types = vec![
6048            JoinType::Inner,
6049            JoinType::Left,
6050            JoinType::Right,
6051            JoinType::Full,
6052        ];
6053
6054        for join_type in join_types {
6055            let join = Join::try_new(
6056                Arc::new(left_plan.clone()),
6057                Arc::new(right_plan.clone()),
6058                vec![(col("t1.id"), col("t2.id"))],
6059                Some(col("t1.value").gt(lit(5.0))),
6060                join_type,
6061                JoinConstraint::On,
6062                NullEquality::NullEqualsNothing,
6063                false,
6064            )?;
6065
6066            let fields = join.schema.fields();
6067            assert_eq!(fields.len(), 6, "Expected 6 fields for {join_type} join");
6068
6069            for (i, field) in fields.iter().enumerate() {
6070                let expected_nullable = match (i, &join_type) {
6071                    // Left table fields (indices 0, 1, 2)
6072                    (0, JoinType::Right | JoinType::Full) => true, // id becomes nullable in RIGHT/FULL
6073                    (1, JoinType::Right | JoinType::Full) => true, // name becomes nullable in RIGHT/FULL
6074                    (2, _) => true, // value is already nullable
6075
6076                    // Right table fields (indices 3, 4, 5)
6077                    (3, JoinType::Left | JoinType::Full) => true, // id becomes nullable in LEFT/FULL
6078                    (4, _) => true, // category is already nullable
6079                    (5, JoinType::Left | JoinType::Full) => true, // code becomes nullable in LEFT/FULL
6080
6081                    _ => false,
6082                };
6083
6084                assert_eq!(
6085                    field.is_nullable(),
6086                    expected_nullable,
6087                    "Field {} ({}) nullability incorrect for {:?} join",
6088                    i,
6089                    field.name(),
6090                    join_type
6091                );
6092            }
6093        }
6094
6095        let using_join = Join::try_new(
6096            Arc::new(left_plan.clone()),
6097            Arc::new(right_plan.clone()),
6098            vec![(col("t1.id"), col("t2.id"))],
6099            None,
6100            JoinType::Inner,
6101            JoinConstraint::Using,
6102            NullEquality::NullEqualsNothing,
6103            false,
6104        )?;
6105
6106        assert_eq!(
6107            using_join.schema.fields().len(),
6108            6,
6109            "USING join should have all fields"
6110        );
6111        assert_eq!(using_join.join_constraint, JoinConstraint::Using);
6112
6113        Ok(())
6114    }
6115}