This is an automated email from the ASF dual-hosted git repository.

jonah pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new 7a7c8cb6b0 refactor: move PREPARE/EXECUTE into 
`LogicalPlan::Statement` (#13311)
7a7c8cb6b0 is described below

commit 7a7c8cb6b0961a9b6e556744e3119ae065564484
Author: Jonah Gao <[email protected]>
AuthorDate: Sat Nov 9 11:39:39 2024 +0800

    refactor: move PREPARE/EXECUTE into `LogicalPlan::Statement` (#13311)
    
    * refactor: move PREPARE/EXECUTE into `LogicalPlan::Statement`
    
    * Fix cargo doc
    
    * Restore TableScan
    
    * Fix cargo doc
---
 datafusion/core/src/execution/context/mod.rs       |  16 +-
 datafusion/core/src/physical_planner.rs            |   9 --
 datafusion/core/tests/sql/sql_api.rs               |   4 +-
 datafusion/expr/src/expr_rewriter/mod.rs           |   2 +-
 datafusion/expr/src/logical_plan/builder.rs        |  14 +-
 datafusion/expr/src/logical_plan/display.rs        |  26 +---
 datafusion/expr/src/logical_plan/mod.rs            |  10 +-
 datafusion/expr/src/logical_plan/plan.rs           | 105 ++++----------
 datafusion/expr/src/logical_plan/statement.rs      | 161 +++++++++++++++------
 datafusion/expr/src/logical_plan/tree_node.rs      |  54 ++-----
 .../optimizer/src/common_subexpr_eliminate.rs      |   4 +-
 .../optimizer/src/optimize_projections/mod.rs      |   7 +-
 datafusion/proto/src/logical_plan/mod.rs           |   9 +-
 datafusion/sql/src/statement.rs                    |  13 +-
 datafusion/sql/src/unparser/plan.rs                |   2 -
 datafusion/sql/tests/sql_integration.rs            |   6 +-
 datafusion/sqllogictest/test_files/prepare.slt     |  19 ++-
 17 files changed, 210 insertions(+), 251 deletions(-)

diff --git a/datafusion/core/src/execution/context/mod.rs 
b/datafusion/core/src/execution/context/mod.rs
index 7868a7f9e5..ff009a0164 100644
--- a/datafusion/core/src/execution/context/mod.rs
+++ b/datafusion/core/src/execution/context/mod.rs
@@ -688,11 +688,11 @@ impl SessionContext {
             LogicalPlan::Statement(Statement::SetVariable(stmt)) => {
                 self.set_variable(stmt).await
             }
-            LogicalPlan::Prepare(Prepare {
+            LogicalPlan::Statement(Statement::Prepare(Prepare {
                 name,
                 input,
                 data_types,
-            }) => {
+            })) => {
                 // The number of parameters must match the specified data 
types length.
                 if !data_types.is_empty() {
                     let param_names = input.get_parameter_names()?;
@@ -712,7 +712,9 @@ impl SessionContext {
                 self.state.write().store_prepared(name, data_types, input)?;
                 self.return_empty_dataframe()
             }
-            LogicalPlan::Execute(execute) => self.execute_prepared(execute),
+            LogicalPlan::Statement(Statement::Execute(execute)) => {
+                self.execute_prepared(execute)
+            }
             plan => Ok(DataFrame::new(self.state(), plan)),
         }
     }
@@ -1773,14 +1775,6 @@ impl<'n, 'a> TreeNodeVisitor<'n> for BadPlanVisitor<'a> {
             LogicalPlan::Statement(stmt) if !self.options.allow_statements => {
                 plan_err!("Statement not supported: {}", stmt.name())
             }
-            // TODO: Implement PREPARE as a LogicalPlan::Statement
-            LogicalPlan::Prepare(_) if !self.options.allow_statements => {
-                plan_err!("Statement not supported: PREPARE")
-            }
-            // TODO: Implement EXECUTE as a LogicalPlan::Statement
-            LogicalPlan::Execute(_) if !self.options.allow_statements => {
-                plan_err!("Statement not supported: EXECUTE")
-            }
             _ => Ok(TreeNodeRecursion::Continue),
         }
     }
diff --git a/datafusion/core/src/physical_planner.rs 
b/datafusion/core/src/physical_planner.rs
index 7d475ad2e2..2d3899adb0 100644
--- a/datafusion/core/src/physical_planner.rs
+++ b/datafusion/core/src/physical_planner.rs
@@ -1195,15 +1195,6 @@ impl DefaultPhysicalPlanner {
                 let name = statement.name();
                 return not_impl_err!("Unsupported logical plan: 
Statement({name})");
             }
-            LogicalPlan::Prepare(_) => {
-                // There is no default plan for "PREPARE" -- it must be
-                // handled at a higher level (so that the appropriate
-                // statement can be prepared)
-                return not_impl_err!("Unsupported logical plan: Prepare");
-            }
-            LogicalPlan::Execute(_) => {
-                return not_impl_err!("Unsupported logical plan: Execute");
-            }
             LogicalPlan::Dml(dml) => {
                 // DataFusion is a read-only query engine, but also a library, 
so consumers may implement this
                 return not_impl_err!("Unsupported logical plan: Dml({0})", 
dml.op);
diff --git a/datafusion/core/tests/sql/sql_api.rs 
b/datafusion/core/tests/sql/sql_api.rs
index b2ffefa437..034d6fa23d 100644
--- a/datafusion/core/tests/sql/sql_api.rs
+++ b/datafusion/core/tests/sql/sql_api.rs
@@ -124,12 +124,12 @@ async fn disable_prepare_and_execute_statement() {
     let df = ctx.sql_with_options(prepare_sql, options).await;
     assert_eq!(
         df.unwrap_err().strip_backtrace(),
-        "Error during planning: Statement not supported: PREPARE"
+        "Error during planning: Statement not supported: Prepare"
     );
     let df = ctx.sql_with_options(execute_sql, options).await;
     assert_eq!(
         df.unwrap_err().strip_backtrace(),
-        "Error during planning: Statement not supported: EXECUTE"
+        "Error during planning: Statement not supported: Execute"
     );
 
     let options = options.with_allow_statements(true);
diff --git a/datafusion/expr/src/expr_rewriter/mod.rs 
b/datafusion/expr/src/expr_rewriter/mod.rs
index c86696854c..b944428977 100644
--- a/datafusion/expr/src/expr_rewriter/mod.rs
+++ b/datafusion/expr/src/expr_rewriter/mod.rs
@@ -314,7 +314,7 @@ impl NamePreserver {
                     | LogicalPlan::Join(_)
                     | LogicalPlan::TableScan(_)
                     | LogicalPlan::Limit(_)
-                    | LogicalPlan::Execute(_)
+                    | LogicalPlan::Statement(_)
             ),
         }
     }
diff --git a/datafusion/expr/src/logical_plan/builder.rs 
b/datafusion/expr/src/logical_plan/builder.rs
index 86da750ed0..90235e3f84 100644
--- a/datafusion/expr/src/logical_plan/builder.rs
+++ b/datafusion/expr/src/logical_plan/builder.rs
@@ -42,7 +42,7 @@ use crate::utils::{
 };
 use crate::{
     and, binary_expr, lit, DmlStatement, Expr, ExprSchemable, Operator, 
RecursiveQuery,
-    TableProviderFilterPushDown, TableSource, WriteOp,
+    Statement, TableProviderFilterPushDown, TableSource, WriteOp,
 };
 
 use super::dml::InsertOp;
@@ -500,11 +500,13 @@ impl LogicalPlanBuilder {
 
     /// Make a builder for a prepare logical plan from the builder's plan
     pub fn prepare(self, name: String, data_types: Vec<DataType>) -> 
Result<Self> {
-        Ok(Self::new(LogicalPlan::Prepare(Prepare {
-            name,
-            data_types,
-            input: self.plan,
-        })))
+        Ok(Self::new(LogicalPlan::Statement(Statement::Prepare(
+            Prepare {
+                name,
+                data_types,
+                input: self.plan,
+            },
+        ))))
     }
 
     /// Limit the number of rows returned
diff --git a/datafusion/expr/src/logical_plan/display.rs 
b/datafusion/expr/src/logical_plan/display.rs
index 84efd85419..b808defcb9 100644
--- a/datafusion/expr/src/logical_plan/display.rs
+++ b/datafusion/expr/src/logical_plan/display.rs
@@ -21,10 +21,10 @@ use std::collections::HashMap;
 use std::fmt;
 
 use crate::{
-    expr_vec_fmt, Aggregate, DescribeTable, Distinct, DistinctOn, 
DmlStatement, Execute,
-    Expr, Filter, Join, Limit, LogicalPlan, Partitioning, Prepare, Projection,
-    RecursiveQuery, Repartition, Sort, Subquery, SubqueryAlias,
-    TableProviderFilterPushDown, TableScan, Unnest, Values, Window,
+    expr_vec_fmt, Aggregate, DescribeTable, Distinct, DistinctOn, 
DmlStatement, Expr,
+    Filter, Join, Limit, LogicalPlan, Partitioning, Projection, RecursiveQuery,
+    Repartition, Sort, Subquery, SubqueryAlias, TableProviderFilterPushDown, 
TableScan,
+    Unnest, Values, Window,
 };
 
 use crate::dml::CopyTo;
@@ -618,24 +618,6 @@ impl<'a, 'b> PgJsonVisitor<'a, 'b> {
                     "Detail": format!("{:?}", e.node)
                 })
             }
-            LogicalPlan::Prepare(Prepare {
-                name, data_types, ..
-            }) => {
-                json!({
-                    "Node Type": "Prepare",
-                    "Name": name,
-                    "Data Types": format!("{:?}", data_types)
-                })
-            }
-            LogicalPlan::Execute(Execute {
-                name, parameters, ..
-            }) => {
-                json!({
-                    "Node Type": "Execute",
-                    "Name": name,
-                    "Parameters": expr_vec_fmt!(parameters),
-                })
-            }
             LogicalPlan::DescribeTable(DescribeTable { .. }) => {
                 json!({
                     "Node Type": "DescribeTable"
diff --git a/datafusion/expr/src/logical_plan/mod.rs 
b/datafusion/expr/src/logical_plan/mod.rs
index 59654a2278..b5bd2e0128 100644
--- a/datafusion/expr/src/logical_plan/mod.rs
+++ b/datafusion/expr/src/logical_plan/mod.rs
@@ -36,14 +36,14 @@ pub use ddl::{
 pub use dml::{DmlStatement, WriteOp};
 pub use plan::{
     projection_schema, Aggregate, Analyze, ColumnUnnestList, DescribeTable, 
Distinct,
-    DistinctOn, EmptyRelation, Execute, Explain, Extension, FetchType, Filter, 
Join,
-    JoinConstraint, JoinType, Limit, LogicalPlan, Partitioning, PlanType, 
Prepare,
-    Projection, RecursiveQuery, Repartition, SkipType, Sort, StringifiedPlan, 
Subquery,
+    DistinctOn, EmptyRelation, Explain, Extension, FetchType, Filter, Join,
+    JoinConstraint, JoinType, Limit, LogicalPlan, Partitioning, PlanType, 
Projection,
+    RecursiveQuery, Repartition, SkipType, Sort, StringifiedPlan, Subquery,
     SubqueryAlias, TableScan, ToStringifiedPlan, Union, Unnest, Values, Window,
 };
 pub use statement::{
-    SetVariable, Statement, TransactionAccessMode, TransactionConclusion, 
TransactionEnd,
-    TransactionIsolationLevel, TransactionStart,
+    Execute, Prepare, SetVariable, Statement, TransactionAccessMode,
+    TransactionConclusion, TransactionEnd, TransactionIsolationLevel, 
TransactionStart,
 };
 
 pub use display::display_schema;
diff --git a/datafusion/expr/src/logical_plan/plan.rs 
b/datafusion/expr/src/logical_plan/plan.rs
index db309d9b52..56e13b356b 100644
--- a/datafusion/expr/src/logical_plan/plan.rs
+++ b/datafusion/expr/src/logical_plan/plan.rs
@@ -39,9 +39,9 @@ use crate::utils::{
     split_conjunction,
 };
 use crate::{
-    build_join_schema, expr_vec_fmt, BinaryExpr, CreateMemoryTable, 
CreateView, Expr,
-    ExprSchemable, LogicalPlanBuilder, Operator, TableProviderFilterPushDown,
-    TableSource, WindowFunctionDefinition,
+    build_join_schema, expr_vec_fmt, BinaryExpr, CreateMemoryTable, 
CreateView, Execute,
+    Expr, ExprSchemable, LogicalPlanBuilder, Operator, Prepare,
+    TableProviderFilterPushDown, TableSource, WindowFunctionDefinition,
 };
 
 use arrow::datatypes::{DataType, Field, Schema, SchemaRef};
@@ -262,11 +262,6 @@ pub enum LogicalPlan {
     /// Remove duplicate rows from the input. This is used to
     /// implement SQL `SELECT DISTINCT ...`.
     Distinct(Distinct),
-    /// Prepare a statement and find any bind parameters
-    /// (e.g. `?`). This is used to implement SQL-prepared statements.
-    Prepare(Prepare),
-    /// Execute a prepared statement. This is used to implement SQL 'EXECUTE'.
-    Execute(Execute),
     /// Data Manipulation Language (DML): Insert / Update / Delete
     Dml(DmlStatement),
     /// Data Definition Language (DDL): CREATE / DROP TABLES / VIEWS / SCHEMAS
@@ -314,8 +309,6 @@ impl LogicalPlan {
             LogicalPlan::Statement(statement) => statement.schema(),
             LogicalPlan::Subquery(Subquery { subquery, .. }) => 
subquery.schema(),
             LogicalPlan::SubqueryAlias(SubqueryAlias { schema, .. }) => schema,
-            LogicalPlan::Prepare(Prepare { input, .. }) => input.schema(),
-            LogicalPlan::Execute(Execute { schema, .. }) => schema,
             LogicalPlan::Explain(explain) => &explain.schema,
             LogicalPlan::Analyze(analyze) => &analyze.schema,
             LogicalPlan::Extension(extension) => extension.node.schema(),
@@ -448,18 +441,16 @@ impl LogicalPlan {
             LogicalPlan::Copy(copy) => vec![&copy.input],
             LogicalPlan::Ddl(ddl) => ddl.inputs(),
             LogicalPlan::Unnest(Unnest { input, .. }) => vec![input],
-            LogicalPlan::Prepare(Prepare { input, .. }) => vec![input],
             LogicalPlan::RecursiveQuery(RecursiveQuery {
                 static_term,
                 recursive_term,
                 ..
             }) => vec![static_term, recursive_term],
+            LogicalPlan::Statement(stmt) => stmt.inputs(),
             // plans without inputs
             LogicalPlan::TableScan { .. }
-            | LogicalPlan::Statement { .. }
             | LogicalPlan::EmptyRelation { .. }
             | LogicalPlan::Values { .. }
-            | LogicalPlan::Execute { .. }
             | LogicalPlan::DescribeTable(_) => vec![],
         }
     }
@@ -562,8 +553,6 @@ impl LogicalPlan {
             }
             LogicalPlan::Subquery(_) => Ok(None),
             LogicalPlan::EmptyRelation(_)
-            | LogicalPlan::Prepare(_)
-            | LogicalPlan::Execute(_)
             | LogicalPlan::Statement(_)
             | LogicalPlan::Values(_)
             | LogicalPlan::Explain(_)
@@ -715,8 +704,6 @@ impl LogicalPlan {
             LogicalPlan::RecursiveQuery(_) => Ok(self),
             LogicalPlan::Analyze(_) => Ok(self),
             LogicalPlan::Explain(_) => Ok(self),
-            LogicalPlan::Prepare(_) => Ok(self),
-            LogicalPlan::Execute(_) => Ok(self),
             LogicalPlan::TableScan(_) => Ok(self),
             LogicalPlan::EmptyRelation(_) => Ok(self),
             LogicalPlan::Statement(_) => Ok(self),
@@ -1070,24 +1057,25 @@ impl LogicalPlan {
                     logical_optimization_succeeded: 
e.logical_optimization_succeeded,
                 }))
             }
-            LogicalPlan::Prepare(Prepare {
-                name, data_types, ..
-            }) => {
+            LogicalPlan::Statement(Statement::Prepare(Prepare {
+                name,
+                data_types,
+                ..
+            })) => {
                 self.assert_no_expressions(expr)?;
                 let input = self.only_input(inputs)?;
-                Ok(LogicalPlan::Prepare(Prepare {
+                Ok(LogicalPlan::Statement(Statement::Prepare(Prepare {
                     name: name.clone(),
                     data_types: data_types.clone(),
                     input: Arc::new(input),
-                }))
+                })))
             }
-            LogicalPlan::Execute(Execute { name, schema, .. }) => {
+            LogicalPlan::Statement(Statement::Execute(Execute { name, .. })) 
=> {
                 self.assert_no_inputs(inputs)?;
-                Ok(LogicalPlan::Execute(Execute {
+                Ok(LogicalPlan::Statement(Statement::Execute(Execute {
                     name: name.clone(),
-                    schema: Arc::clone(schema),
                     parameters: expr,
-                }))
+                })))
             }
             LogicalPlan::TableScan(ts) => {
                 self.assert_no_inputs(inputs)?;
@@ -1184,8 +1172,8 @@ impl LogicalPlan {
     /// Replaces placeholder param values (like `$1`, `$2`) in [`LogicalPlan`]
     /// with the specified `param_values`.
     ///
-    /// [`LogicalPlan::Prepare`] are
-    /// converted to their inner logical plan for execution.
+    /// [`Prepare`] statements are converted to
+    /// their inner logical plan for execution.
     ///
     /// # Example
     /// ```
@@ -1242,13 +1230,17 @@ impl LogicalPlan {
         let plan_with_values = self.replace_params_with_values(&param_values)?;
 
         // unwrap Prepare
-        Ok(if let LogicalPlan::Prepare(prepare_lp) = plan_with_values {
-            param_values.verify(&prepare_lp.data_types)?;
-            // try and take ownership of the input if is not shared, clone 
otherwise
-            Arc::unwrap_or_clone(prepare_lp.input)
-        } else {
-            plan_with_values
-        })
+        Ok(
+            if let LogicalPlan::Statement(Statement::Prepare(prepare_lp)) =
+                plan_with_values
+            {
+                param_values.verify(&prepare_lp.data_types)?;
+                // try and take ownership of the input if is not shared, clone 
otherwise
+                Arc::unwrap_or_clone(prepare_lp.input)
+            } else {
+                plan_with_values
+            },
+        )
     }
 
     /// Returns the maximum number of rows that this plan can output, if known.
@@ -1346,8 +1338,6 @@ impl LogicalPlan {
             | LogicalPlan::Dml(_)
             | LogicalPlan::Copy(_)
             | LogicalPlan::DescribeTable(_)
-            | LogicalPlan::Prepare(_)
-            | LogicalPlan::Execute(_)
             | LogicalPlan::Statement(_)
             | LogicalPlan::Extension(_) => None,
         }
@@ -1962,14 +1952,6 @@ impl LogicalPlan {
                     LogicalPlan::Analyze { .. } => write!(f, "Analyze"),
                     LogicalPlan::Union(_) => write!(f, "Union"),
                     LogicalPlan::Extension(e) => e.node.fmt_for_explain(f),
-                    LogicalPlan::Prepare(Prepare {
-                        name, data_types, ..
-                    }) => {
-                        write!(f, "Prepare: {name:?} {data_types:?} ")
-                    }
-                    LogicalPlan::Execute(Execute { name, parameters, .. }) => {
-                        write!(f, "Execute: {} params=[{}]", name, 
expr_vec_fmt!(parameters))
-                    }
                     LogicalPlan::DescribeTable(DescribeTable { .. }) => {
                         write!(f, "DescribeTable")
                     }
@@ -2624,39 +2606,6 @@ impl PartialOrd for Union {
     }
 }
 
-/// Prepare a statement but do not execute it. Prepare statements can have 0 
or more
-/// `Expr::Placeholder` expressions that are filled in during execution
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
-pub struct Prepare {
-    /// The name of the statement
-    pub name: String,
-    /// Data types of the parameters ([`Expr::Placeholder`])
-    pub data_types: Vec<DataType>,
-    /// The logical plan of the statements
-    pub input: Arc<LogicalPlan>,
-}
-
-/// Execute a prepared statement.
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Execute {
-    /// The name of the prepared statement to execute
-    pub name: String,
-    /// The execute parameters
-    pub parameters: Vec<Expr>,
-    /// Dummy schema
-    pub schema: DFSchemaRef,
-}
-
-// Comparison excludes the `schema` field.
-impl PartialOrd for Execute {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        match self.name.partial_cmp(&other.name) {
-            Some(Ordering::Equal) => 
self.parameters.partial_cmp(&other.parameters),
-            cmp => cmp,
-        }
-    }
-}
-
 /// Describe the schema of table
 ///
 /// # Example output:
diff --git a/datafusion/expr/src/logical_plan/statement.rs 
b/datafusion/expr/src/logical_plan/statement.rs
index 7ad18ce7bb..9ba8170f8e 100644
--- a/datafusion/expr/src/logical_plan/statement.rs
+++ b/datafusion/expr/src/logical_plan/statement.rs
@@ -15,17 +15,26 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use datafusion_common::DFSchemaRef;
-use std::cmp::Ordering;
+use arrow::datatypes::DataType;
+use datafusion_common::tree_node::{Transformed, TreeNodeIterator};
+use datafusion_common::{DFSchema, DFSchemaRef, Result};
 use std::fmt::{self, Display};
+use std::sync::{Arc, OnceLock};
+
+use super::tree_node::rewrite_arc;
+use crate::{expr_vec_fmt, Expr, LogicalPlan};
+
+/// Statements have a unchanging empty schema.
+/// TODO: Use `LazyLock` when MSRV is 1.80.0
+static STATEMENT_EMPTY_SCHEMA: OnceLock<DFSchemaRef> = OnceLock::new();
 
 /// Various types of Statements.
 ///
 /// # Transactions:
 ///
 /// While DataFusion does not offer support transactions, it provides
-/// [`LogicalPlan`](crate::LogicalPlan) support to assist building
-/// database systems using DataFusion
+/// [`LogicalPlan`] support to assist building database systems
+/// using DataFusion
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
 pub enum Statement {
     // Begin a transaction
@@ -34,16 +43,17 @@ pub enum Statement {
     TransactionEnd(TransactionEnd),
     /// Set a Variable
     SetVariable(SetVariable),
+    /// Prepare a statement and find any bind parameters
+    /// (e.g. `?`). This is used to implement SQL-prepared statements.
+    Prepare(Prepare),
+    /// Execute a prepared statement. This is used to implement SQL 'EXECUTE'.
+    Execute(Execute),
 }
 
 impl Statement {
     /// Get a reference to the logical plan's schema
     pub fn schema(&self) -> &DFSchemaRef {
-        match self {
-            Statement::TransactionStart(TransactionStart { schema, .. }) => 
schema,
-            Statement::TransactionEnd(TransactionEnd { schema, .. }) => schema,
-            Statement::SetVariable(SetVariable { schema, .. }) => schema,
-        }
+        STATEMENT_EMPTY_SCHEMA.get_or_init(|| Arc::new(DFSchema::empty()))
     }
 
     /// Return a descriptive string describing the type of this
@@ -53,6 +63,63 @@ impl Statement {
             Statement::TransactionStart(_) => "TransactionStart",
             Statement::TransactionEnd(_) => "TransactionEnd",
             Statement::SetVariable(_) => "SetVariable",
+            Statement::Prepare(_) => "Prepare",
+            Statement::Execute(_) => "Execute",
+        }
+    }
+
+    /// Returns input LogicalPlans in the current `Statement`.
+    pub(super) fn inputs(&self) -> Vec<&LogicalPlan> {
+        match self {
+            Statement::Prepare(Prepare { input, .. }) => vec![input.as_ref()],
+            _ => vec![],
+        }
+    }
+
+    /// Rewrites input LogicalPlans in the current `Statement` using `f`.
+    pub(super) fn map_inputs<
+        F: FnMut(LogicalPlan) -> Result<Transformed<LogicalPlan>>,
+    >(
+        self,
+        f: F,
+    ) -> Result<Transformed<Self>> {
+        match self {
+            Statement::Prepare(Prepare {
+                input,
+                name,
+                data_types,
+            }) => Ok(rewrite_arc(input, f)?.update_data(|input| {
+                Statement::Prepare(Prepare {
+                    input,
+                    name,
+                    data_types,
+                })
+            })),
+            _ => Ok(Transformed::no(self)),
+        }
+    }
+
+    /// Returns a iterator over all expressions in the current `Statement`.
+    pub(super) fn expression_iter(&self) -> impl Iterator<Item = &Expr> {
+        match self {
+            Statement::Execute(Execute { parameters, .. }) => 
parameters.iter(),
+            _ => [].iter(),
+        }
+    }
+
+    /// Rewrites all expressions in the current `Statement` using `f`.
+    pub(super) fn map_expressions<F: FnMut(Expr) -> Result<Transformed<Expr>>>(
+        self,
+        f: F,
+    ) -> Result<Transformed<Self>> {
+        match self {
+            Statement::Execute(Execute { name, parameters }) => Ok(parameters
+                .into_iter()
+                .map_until_stop_and_collect(f)?
+                .update_data(|parameters| {
+                    Statement::Execute(Execute { parameters, name })
+                })),
+            _ => Ok(Transformed::no(self)),
         }
     }
 
@@ -85,6 +152,21 @@ impl Statement {
                     }) => {
                         write!(f, "SetVariable: set {variable:?} to {value:?}")
                     }
+                    Statement::Prepare(Prepare {
+                        name, data_types, ..
+                    }) => {
+                        write!(f, "Prepare: {name:?} {data_types:?} ")
+                    }
+                    Statement::Execute(Execute {
+                        name, parameters, ..
+                    }) => {
+                        write!(
+                            f,
+                            "Execute: {} params=[{}]",
+                            name,
+                            expr_vec_fmt!(parameters)
+                        )
+                    }
                 }
             }
         }
@@ -116,67 +198,50 @@ pub enum TransactionIsolationLevel {
 }
 
 /// Indicator that the following statements should be committed or rolled back 
atomically
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
 pub struct TransactionStart {
     /// indicates if transaction is allowed to write
     pub access_mode: TransactionAccessMode,
     // indicates ANSI isolation level
     pub isolation_level: TransactionIsolationLevel,
-    /// Empty schema
-    pub schema: DFSchemaRef,
-}
-
-// Manual implementation needed because of `schema` field. Comparison excludes 
this field.
-impl PartialOrd for TransactionStart {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        match self.access_mode.partial_cmp(&other.access_mode) {
-            Some(Ordering::Equal) => {
-                self.isolation_level.partial_cmp(&other.isolation_level)
-            }
-            cmp => cmp,
-        }
-    }
 }
 
 /// Indicator that any current transaction should be terminated
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
 pub struct TransactionEnd {
     /// whether the transaction committed or aborted
     pub conclusion: TransactionConclusion,
     /// if specified a new transaction is immediately started with same 
characteristics
     pub chain: bool,
-    /// Empty schema
-    pub schema: DFSchemaRef,
-}
-
-// Manual implementation needed because of `schema` field. Comparison excludes 
this field.
-impl PartialOrd for TransactionEnd {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        match self.conclusion.partial_cmp(&other.conclusion) {
-            Some(Ordering::Equal) => self.chain.partial_cmp(&other.chain),
-            cmp => cmp,
-        }
-    }
 }
 
 /// Set a Variable's value -- value in
 /// [`ConfigOptions`](datafusion_common::config::ConfigOptions)
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
 pub struct SetVariable {
     /// The variable name
     pub variable: String,
     /// The value to set
     pub value: String,
-    /// Dummy schema
-    pub schema: DFSchemaRef,
 }
 
-// Manual implementation needed because of `schema` field. Comparison excludes 
this field.
-impl PartialOrd for SetVariable {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        match self.variable.partial_cmp(&other.value) {
-            Some(Ordering::Equal) => self.value.partial_cmp(&other.value),
-            cmp => cmp,
-        }
-    }
+/// Prepare a statement but do not execute it. Prepare statements can have 0 
or more
+/// `Expr::Placeholder` expressions that are filled in during execution
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
+pub struct Prepare {
+    /// The name of the statement
+    pub name: String,
+    /// Data types of the parameters ([`Expr::Placeholder`])
+    pub data_types: Vec<DataType>,
+    /// The logical plan of the statements
+    pub input: Arc<LogicalPlan>,
+}
+
+/// Execute a prepared statement.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
+pub struct Execute {
+    /// The name of the prepared statement to execute
+    pub name: String,
+    /// The execute parameters
+    pub parameters: Vec<Expr>,
 }
diff --git a/datafusion/expr/src/logical_plan/tree_node.rs 
b/datafusion/expr/src/logical_plan/tree_node.rs
index ff2c1ec1d5..d16fe42098 100644
--- a/datafusion/expr/src/logical_plan/tree_node.rs
+++ b/datafusion/expr/src/logical_plan/tree_node.rs
@@ -38,10 +38,9 @@
 //! * [`LogicalPlan::expressions`]: Return a copy of the plan's expressions
 use crate::{
     dml::CopyTo, Aggregate, Analyze, CreateMemoryTable, CreateView, 
DdlStatement,
-    Distinct, DistinctOn, DmlStatement, Execute, Explain, Expr, Extension, 
Filter, Join,
-    Limit, LogicalPlan, Partitioning, Prepare, Projection, RecursiveQuery, 
Repartition,
-    Sort, Subquery, SubqueryAlias, TableScan, Union, Unnest, 
UserDefinedLogicalNode,
-    Values, Window,
+    Distinct, DistinctOn, DmlStatement, Explain, Expr, Extension, Filter, 
Join, Limit,
+    LogicalPlan, Partitioning, Projection, RecursiveQuery, Repartition, Sort, 
Subquery,
+    SubqueryAlias, TableScan, Union, Unnest, UserDefinedLogicalNode, Values, 
Window,
 };
 use std::ops::Deref;
 use std::sync::Arc;
@@ -329,17 +328,6 @@ impl TreeNode for LogicalPlan {
                     options,
                 })
             }),
-            LogicalPlan::Prepare(Prepare {
-                name,
-                data_types,
-                input,
-            }) => rewrite_arc(input, f)?.update_data(|input| {
-                LogicalPlan::Prepare(Prepare {
-                    name,
-                    data_types,
-                    input,
-                })
-            }),
             LogicalPlan::RecursiveQuery(RecursiveQuery {
                 name,
                 static_term,
@@ -358,19 +346,20 @@ impl TreeNode for LogicalPlan {
                     is_distinct,
                 })
             }),
+            LogicalPlan::Statement(stmt) => {
+                stmt.map_inputs(f)?.update_data(LogicalPlan::Statement)
+            }
             // plans without inputs
             LogicalPlan::TableScan { .. }
-            | LogicalPlan::Statement { .. }
             | LogicalPlan::EmptyRelation { .. }
             | LogicalPlan::Values { .. }
-            | LogicalPlan::Execute { .. }
             | LogicalPlan::DescribeTable(_) => Transformed::no(self),
         })
     }
 }
 
 /// Applies `f` to rewrite a `Arc<LogicalPlan>` without copying, if possible
-fn rewrite_arc<F: FnMut(LogicalPlan) -> Result<Transformed<LogicalPlan>>>(
+pub(super) fn rewrite_arc<F: FnMut(LogicalPlan) -> 
Result<Transformed<LogicalPlan>>>(
     plan: Arc<LogicalPlan>,
     mut f: F,
 ) -> Result<Transformed<Arc<LogicalPlan>>> {
@@ -506,15 +495,12 @@ impl LogicalPlan {
                 .chain(fetch.iter())
                 .map(|e| e.deref())
                 .apply_until_stop(f),
-            LogicalPlan::Execute(Execute { parameters, .. }) => {
-                parameters.iter().apply_until_stop(f)
-            }
+            LogicalPlan::Statement(stmt) => 
stmt.expression_iter().apply_until_stop(f),
             // plans without expressions
             LogicalPlan::EmptyRelation(_)
             | LogicalPlan::RecursiveQuery(_)
             | LogicalPlan::Subquery(_)
             | LogicalPlan::SubqueryAlias(_)
-            | LogicalPlan::Statement(_)
             | LogicalPlan::Analyze(_)
             | LogicalPlan::Explain(_)
             | LogicalPlan::Union(_)
@@ -522,8 +508,7 @@ impl LogicalPlan {
             | LogicalPlan::Dml(_)
             | LogicalPlan::Ddl(_)
             | LogicalPlan::Copy(_)
-            | LogicalPlan::DescribeTable(_)
-            | LogicalPlan::Prepare(_) => Ok(TreeNodeRecursion::Continue),
+            | LogicalPlan::DescribeTable(_) => Ok(TreeNodeRecursion::Continue),
         }
     }
 
@@ -738,27 +723,15 @@ impl LogicalPlan {
                     })
                 })
             }
-            LogicalPlan::Execute(Execute {
-                parameters,
-                name,
-                schema,
-            }) => parameters
-                .into_iter()
-                .map_until_stop_and_collect(f)?
-                .update_data(|parameters| {
-                    LogicalPlan::Execute(Execute {
-                        parameters,
-                        name,
-                        schema,
-                    })
-                }),
+            LogicalPlan::Statement(stmt) => {
+                stmt.map_expressions(f)?.update_data(LogicalPlan::Statement)
+            }
             // plans without expressions
             LogicalPlan::EmptyRelation(_)
             | LogicalPlan::Unnest(_)
             | LogicalPlan::RecursiveQuery(_)
             | LogicalPlan::Subquery(_)
             | LogicalPlan::SubqueryAlias(_)
-            | LogicalPlan::Statement(_)
             | LogicalPlan::Analyze(_)
             | LogicalPlan::Explain(_)
             | LogicalPlan::Union(_)
@@ -766,8 +739,7 @@ impl LogicalPlan {
             | LogicalPlan::Dml(_)
             | LogicalPlan::Ddl(_)
             | LogicalPlan::Copy(_)
-            | LogicalPlan::DescribeTable(_)
-            | LogicalPlan::Prepare(_) => Transformed::no(self),
+            | LogicalPlan::DescribeTable(_) => Transformed::no(self),
         })
     }
 
diff --git a/datafusion/optimizer/src/common_subexpr_eliminate.rs 
b/datafusion/optimizer/src/common_subexpr_eliminate.rs
index 53a0453d80..71327ad3e2 100644
--- a/datafusion/optimizer/src/common_subexpr_eliminate.rs
+++ b/datafusion/optimizer/src/common_subexpr_eliminate.rs
@@ -563,9 +563,7 @@ impl OptimizerRule for CommonSubexprEliminate {
             | LogicalPlan::Dml(_)
             | LogicalPlan::Copy(_)
             | LogicalPlan::Unnest(_)
-            | LogicalPlan::RecursiveQuery(_)
-            | LogicalPlan::Prepare(_)
-            | LogicalPlan::Execute(_) => {
+            | LogicalPlan::RecursiveQuery(_) => {
                 // This rule handles recursion itself in a 
`ApplyOrder::TopDown` like
                 // manner.
                 plan.map_children(|c| self.rewrite(c, config))?
diff --git a/datafusion/optimizer/src/optimize_projections/mod.rs 
b/datafusion/optimizer/src/optimize_projections/mod.rs
index 67d888abda..04a523f9b1 100644
--- a/datafusion/optimizer/src/optimize_projections/mod.rs
+++ b/datafusion/optimizer/src/optimize_projections/mod.rs
@@ -295,7 +295,7 @@ fn optimize_projections(
                 })
                 .collect::<Result<_>>()?
         }
-        LogicalPlan::Limit(_) | LogicalPlan::Prepare(_) => {
+        LogicalPlan::Limit(_) => {
             // Pass index requirements from the parent as well as column 
indices
             // that appear in this plan's expressions to its child. These 
operators
             // do not benefit from "small" inputs, so the projection_beneficial
@@ -311,6 +311,7 @@ fn optimize_projections(
         | LogicalPlan::Explain(_)
         | LogicalPlan::Analyze(_)
         | LogicalPlan::Subquery(_)
+        | LogicalPlan::Statement(_)
         | LogicalPlan::Distinct(Distinct::All(_)) => {
             // These plans require all their fields, and their children should
             // be treated as final plans -- otherwise, we may have schema a
@@ -346,10 +347,8 @@ fn optimize_projections(
         }
         LogicalPlan::EmptyRelation(_)
         | LogicalPlan::RecursiveQuery(_)
-        | LogicalPlan::Statement(_)
         | LogicalPlan::Values(_)
-        | LogicalPlan::DescribeTable(_)
-        | LogicalPlan::Execute(_) => {
+        | LogicalPlan::DescribeTable(_) => {
             // These operators have no inputs, so stop the optimization 
process.
             return Ok(Transformed::no(plan));
         }
diff --git a/datafusion/proto/src/logical_plan/mod.rs 
b/datafusion/proto/src/logical_plan/mod.rs
index 1993598f5c..a55fecec98 100644
--- a/datafusion/proto/src/logical_plan/mod.rs
+++ b/datafusion/proto/src/logical_plan/mod.rs
@@ -66,7 +66,7 @@ use datafusion_expr::{
         SubqueryAlias, TableScan, Values, Window,
     },
     DistinctOn, DropView, Expr, LogicalPlan, LogicalPlanBuilder, ScalarUDF, 
SortExpr,
-    WindowUDF,
+    Statement, WindowUDF,
 };
 use datafusion_expr::{AggregateUDF, ColumnUnnestList, FetchType, SkipType, 
Unnest};
 
@@ -1502,11 +1502,11 @@ impl AsLogicalPlan for LogicalPlanNode {
                     )),
                 })
             }
-            LogicalPlan::Prepare(Prepare {
+            LogicalPlan::Statement(Statement::Prepare(Prepare {
                 name,
                 data_types,
                 input,
-            }) => {
+            })) => {
                 let input =
                     LogicalPlanNode::try_from_logical_plan(input, 
extension_codec)?;
                 Ok(LogicalPlanNode {
@@ -1633,9 +1633,6 @@ impl AsLogicalPlan for LogicalPlanNode {
             LogicalPlan::RecursiveQuery(_) => Err(proto_error(
                 "LogicalPlan serde is not yet implemented for RecursiveQuery",
             )),
-            LogicalPlan::Execute(_) => Err(proto_error(
-                "LogicalPlan serde is not yet implemented for Execute",
-            )),
         }
     }
 }
diff --git a/datafusion/sql/src/statement.rs b/datafusion/sql/src/statement.rs
index 00949aa13a..3b32a43757 100644
--- a/datafusion/sql/src/statement.rs
+++ b/datafusion/sql/src/statement.rs
@@ -636,11 +636,11 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
                     *statement,
                     &mut planner_context,
                 )?;
-                Ok(LogicalPlan::Prepare(Prepare {
+                Ok(LogicalPlan::Statement(PlanStatement::Prepare(Prepare {
                     name: ident_to_string(&name),
                     data_types,
                     input: Arc::new(plan),
-                }))
+                })))
             }
             Statement::Execute {
                 name,
@@ -660,11 +660,10 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
                     .map(|expr| self.sql_to_expr(expr, &empty_schema, 
planner_context))
                     .collect::<Result<Vec<Expr>>>()?;
 
-                Ok(LogicalPlan::Execute(Execute {
+                Ok(LogicalPlan::Statement(PlanStatement::Execute(Execute {
                     name: ident_to_string(&name),
                     parameters,
-                    schema: DFSchemaRef::new(empty_schema),
-                }))
+                })))
             }
 
             Statement::ShowTables {
@@ -841,7 +840,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
                 let statement = 
PlanStatement::TransactionStart(TransactionStart {
                     access_mode,
                     isolation_level,
-                    schema: DFSchemaRef::new(DFSchema::empty()),
                 });
                 Ok(LogicalPlan::Statement(statement))
             }
@@ -849,7 +847,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
                 let statement = PlanStatement::TransactionEnd(TransactionEnd {
                     conclusion: TransactionConclusion::Commit,
                     chain,
-                    schema: DFSchemaRef::new(DFSchema::empty()),
                 });
                 Ok(LogicalPlan::Statement(statement))
             }
@@ -860,7 +857,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
                 let statement = PlanStatement::TransactionEnd(TransactionEnd {
                     conclusion: TransactionConclusion::Rollback,
                     chain,
-                    schema: DFSchemaRef::new(DFSchema::empty()),
                 });
                 Ok(LogicalPlan::Statement(statement))
             }
@@ -1535,7 +1531,6 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
         let statement = PlanStatement::SetVariable(SetVariable {
             variable: variable_lower,
             value: value_string,
-            schema: DFSchemaRef::new(DFSchema::empty()),
         });
 
         Ok(LogicalPlan::Statement(statement))
diff --git a/datafusion/sql/src/unparser/plan.rs 
b/datafusion/sql/src/unparser/plan.rs
index 8167ddacff..c46bc62063 100644
--- a/datafusion/sql/src/unparser/plan.rs
+++ b/datafusion/sql/src/unparser/plan.rs
@@ -111,8 +111,6 @@ impl Unparser<'_> {
             LogicalPlan::Explain(_)
             | LogicalPlan::Analyze(_)
             | LogicalPlan::Extension(_)
-            | LogicalPlan::Prepare(_)
-            | LogicalPlan::Execute(_)
             | LogicalPlan::Ddl(_)
             | LogicalPlan::Copy(_)
             | LogicalPlan::DescribeTable(_)
diff --git a/datafusion/sql/tests/sql_integration.rs 
b/datafusion/sql/tests/sql_integration.rs
index 698c408e53..b2f128778a 100644
--- a/datafusion/sql/tests/sql_integration.rs
+++ b/datafusion/sql/tests/sql_integration.rs
@@ -33,7 +33,7 @@ use datafusion_expr::{
     logical_plan::{LogicalPlan, Prepare},
     test::function_stub::sum_udaf,
     ColumnarValue, CreateExternalTable, CreateIndex, DdlStatement, ScalarUDF,
-    ScalarUDFImpl, Signature, Volatility,
+    ScalarUDFImpl, Signature, Statement, Volatility,
 };
 use datafusion_functions::{string, unicode};
 use datafusion_sql::{
@@ -2710,7 +2710,9 @@ fn prepare_stmt_quick_test(
     assert_eq!(format!("{assert_plan}"), expected_plan);
 
     // verify data types
-    if let LogicalPlan::Prepare(Prepare { data_types, .. }) = assert_plan {
+    if let LogicalPlan::Statement(Statement::Prepare(Prepare { data_types, .. 
})) =
+        assert_plan
+    {
         let dt = format!("{data_types:?}");
         assert_eq!(dt, expected_data_types);
     }
diff --git a/datafusion/sqllogictest/test_files/prepare.slt 
b/datafusion/sqllogictest/test_files/prepare.slt
index b0c67af9e1..f149cec96f 100644
--- a/datafusion/sqllogictest/test_files/prepare.slt
+++ b/datafusion/sqllogictest/test_files/prepare.slt
@@ -199,19 +199,34 @@ EXECUTE get_N_rand_ints_from_last_run(2);
 statement ok
 DROP TABLE test;
 
+statement ok
+SET datafusion.explain.logical_plan_only=true;
+
+# OptimizeProjections rule works with PREPARE and pushes down the `id` 
projection to TableScan
+query TT
+EXPLAIN PREPARE my_plan(INT) AS SELECT id + $1 FROM person;
+----
+logical_plan
+01)Prepare: "my_plan" [Int32]
+02)--Projection: person.id + $1
+03)----TableScan: person projection=[id]
 
 # test creating logical plan for EXECUTE statements
 query TT
 EXPLAIN EXECUTE my_plan;
 ----
 logical_plan Execute: my_plan params=[]
-physical_plan_error This feature is not implemented: Unsupported logical plan: 
Execute
 
 query TT
 EXPLAIN EXECUTE my_plan(10*2 + 1, 'Foo');
 ----
 logical_plan Execute: my_plan params=[Int64(21), Utf8("Foo")]
-physical_plan_error This feature is not implemented: Unsupported logical plan: 
Execute
 
 query error DataFusion error: Schema error: No field named a\.
 EXPLAIN EXECUTE my_plan(a);
+
+statement ok
+SET datafusion.explain.logical_plan_only=false;
+
+statement ok
+DROP TABLE person;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to