blob: bfc6b53d1136e125adf80e7c7669d5965fb07f3f [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use arrow::datatypes::FieldRef;
use datafusion_common::metadata::format_type_and_metadata;
use datafusion_common::{DFSchema, DFSchemaRef};
use itertools::Itertools as _;
use std::fmt::{self, Display};
use std::sync::{Arc, LazyLock};
use crate::{expr_vec_fmt, Expr, LogicalPlan};
/// Various types of Statements.
///
/// # Transactions:
///
/// While DataFusion does not offer support transactions, it provides
/// [`LogicalPlan`] support to assist building database systems
/// using DataFusion
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
pub enum Statement {
// Begin a transaction
TransactionStart(TransactionStart),
// Commit or rollback a transaction
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),
/// Deallocate a prepared statement.
/// This is used to implement SQL 'DEALLOCATE'.
Deallocate(Deallocate),
}
impl Statement {
/// Get a reference to the logical plan's schema
pub fn schema(&self) -> &DFSchemaRef {
// Statements have an unchanging empty schema.
static STATEMENT_EMPTY_SCHEMA: LazyLock<DFSchemaRef> =
LazyLock::new(|| Arc::new(DFSchema::empty()));
&STATEMENT_EMPTY_SCHEMA
}
/// Return a descriptive string describing the type of this
/// [`Statement`]
pub fn name(&self) -> &str {
match self {
Statement::TransactionStart(_) => "TransactionStart",
Statement::TransactionEnd(_) => "TransactionEnd",
Statement::SetVariable(_) => "SetVariable",
Statement::Prepare(_) => "Prepare",
Statement::Execute(_) => "Execute",
Statement::Deallocate(_) => "Deallocate",
}
}
/// 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![],
}
}
/// Return a `format`able structure with the a human readable
/// description of this LogicalPlan node per node, not including
/// children.
///
/// See [crate::LogicalPlan::display] for an example
pub fn display(&self) -> impl Display + '_ {
struct Wrapper<'a>(&'a Statement);
impl Display for Wrapper<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
Statement::TransactionStart(TransactionStart {
access_mode,
isolation_level,
..
}) => {
write!(f, "TransactionStart: {access_mode:?} {isolation_level:?}")
}
Statement::TransactionEnd(TransactionEnd {
conclusion,
chain,
..
}) => {
write!(f, "TransactionEnd: {conclusion:?} chain:={chain}")
}
Statement::SetVariable(SetVariable {
variable, value, ..
}) => {
write!(f, "SetVariable: set {variable:?} to {value:?}")
}
Statement::Prepare(Prepare { name, fields, .. }) => {
write!(
f,
"Prepare: {name:?} [{}]",
fields
.iter()
.map(|f| format_type_and_metadata(
f.data_type(),
Some(f.metadata())
))
.join(", ")
)
}
Statement::Execute(Execute {
name, parameters, ..
}) => {
write!(
f,
"Execute: {} params=[{}]",
name,
expr_vec_fmt!(parameters)
)
}
Statement::Deallocate(Deallocate { name }) => {
write!(f, "Deallocate: {name}")
}
}
}
}
Wrapper(self)
}
}
/// Indicates if a transaction was committed or aborted
#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
pub enum TransactionConclusion {
Commit,
Rollback,
}
/// Indicates if this transaction is allowed to write
#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
pub enum TransactionAccessMode {
ReadOnly,
ReadWrite,
}
/// Indicates ANSI transaction isolation level
#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
pub enum TransactionIsolationLevel {
ReadUncommitted,
ReadCommitted,
RepeatableRead,
Serializable,
Snapshot,
}
/// Indicator that the following statements should be committed or rolled back atomically
#[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,
}
/// Indicator that any current transaction should be terminated
#[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,
}
/// Set a Variable's value -- value in
/// [`ConfigOptions`](datafusion_common::config::ConfigOptions)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
pub struct SetVariable {
/// The variable name
pub variable: String,
/// The value to set
pub value: String,
}
/// 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 fields: Vec<FieldRef>,
/// 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>,
}
/// Deallocate a prepared statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
pub struct Deallocate {
/// The name of the prepared statement to deallocate
pub name: String,
}