This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] 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 04fbade7f2 Consolidate and document SQL AST shims (#22094)
04fbade7f2 is described below
commit 04fbade7f2861914ecfc49b9658e590e14c1f78c
Author: Andrew Lamb <[email protected]>
AuthorDate: Mon May 11 23:17:43 2026 -0400
Consolidate and document SQL AST shims (#22094)
## Which issue does this PR close?
- Related to https://github.com/apache/datafusion/pull/22069
## Rationale for this change
While upgrading sqlparser, it was not clear why there were feature gates
for the `sql` feature:
https://github.com/apache/datafusion/pull/22069/changes#r3204705488
There is a mode to avoid the `sqlparser` dependency, added by @timsaucer
in
- #17332
I think this feature makes sense but it is a little hard to understand
because it is implemented with a bunch of `#[cfg(not(feature = "sql"))]`
and there is no central place that explains the design.
## What changes are included in this PR?
1. Consolidate the SQL AST shim structures in a separate module
2. Document the design so it is easier to understand
## Are these changes tested?
Yes by CI
## Are there any user-facing changes?
<!--
If there are user-facing changes then we may require documentation to be
updated before approving the PR.
-->
<!--
If there are any breaking changes to public APIs, please add the `api
change` label.
-->
---
datafusion/expr/src/expr.rs | 123 ++-------------------------
datafusion/expr/src/lib.rs | 2 +
datafusion/expr/src/logical_plan/ddl.rs | 4 +-
datafusion/expr/src/sql.rs | 143 ++++++++++++++++++++++++++++++++
datafusion/expr/src/utils.rs | 2 +-
5 files changed, 155 insertions(+), 119 deletions(-)
diff --git a/datafusion/expr/src/expr.rs b/datafusion/expr/src/expr.rs
index 582e8e41dd..8c58e8f709 100644
--- a/datafusion/expr/src/expr.rs
+++ b/datafusion/expr/src/expr.rs
@@ -46,7 +46,13 @@ use datafusion_common::{
use datafusion_expr_common::placement::ExpressionPlacement;
use datafusion_functions_window_common::field::WindowUDFFieldArgs;
#[cfg(feature = "sql")]
-use sqlparser::ast::{
+pub use sqlparser::ast::{
+ ExceptSelectItem, ExcludeSelectItem, IlikeSelectItem, RenameSelectItem,
+ ReplaceSelectElement,
+};
+// Use shims for sqlparser types when the sql feature is disabled.
+#[cfg(not(feature = "sql"))]
+pub use crate::sql::{
ExceptSelectItem, ExcludeSelectItem, IlikeSelectItem, RenameSelectItem,
ReplaceSelectElement,
};
@@ -1416,63 +1422,6 @@ impl Lambda {
}
}
-#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
-#[cfg(not(feature = "sql"))]
-pub struct IlikeSelectItem {
- pub pattern: String,
-}
-#[cfg(not(feature = "sql"))]
-impl Display for IlikeSelectItem {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "ILIKE '{}'", &self.pattern)?;
- Ok(())
- }
-}
-#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
-#[cfg(not(feature = "sql"))]
-pub enum ExcludeSelectItem {
- Single(Ident),
- Multiple(Vec<Ident>),
-}
-#[cfg(not(feature = "sql"))]
-impl Display for ExcludeSelectItem {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "EXCLUDE")?;
- match self {
- Self::Single(column) => {
- write!(f, " {column}")?;
- }
- Self::Multiple(columns) => {
- write!(f, " ({})", display_comma_separated(columns))?;
- }
- }
- Ok(())
- }
-}
-#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
-#[cfg(not(feature = "sql"))]
-pub struct ExceptSelectItem {
- pub first_element: Ident,
- pub additional_elements: Vec<Ident>,
-}
-#[cfg(not(feature = "sql"))]
-impl Display for ExceptSelectItem {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "EXCEPT ")?;
- if self.additional_elements.is_empty() {
- write!(f, "({})", self.first_element)?;
- } else {
- write!(
- f,
- "({}, {})",
- self.first_element,
- display_comma_separated(&self.additional_elements)
- )?;
- }
- Ok(())
- }
-}
-
pub fn display_comma_separated<T>(slice: &[T]) -> String
where
T: Display,
@@ -1481,64 +1430,6 @@ where
slice.iter().map(|v| format!("{v}")).join(", ")
}
-#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
-#[cfg(not(feature = "sql"))]
-pub enum RenameSelectItem {
- Single(String),
- Multiple(Vec<String>),
-}
-#[cfg(not(feature = "sql"))]
-impl Display for RenameSelectItem {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "RENAME")?;
- match self {
- Self::Single(column) => {
- write!(f, " {column}")?;
- }
- Self::Multiple(columns) => {
- write!(f, " ({})", display_comma_separated(columns))?;
- }
- }
- Ok(())
- }
-}
-
-#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
-#[cfg(not(feature = "sql"))]
-pub struct Ident {
- /// The value of the identifier without quotes.
- pub value: String,
- /// The starting quote if any. Valid quote characters are the single quote,
- /// double quote, backtick, and opening square bracket.
- pub quote_style: Option<char>,
- /// The span of the identifier in the original SQL string.
- pub span: String,
-}
-#[cfg(not(feature = "sql"))]
-impl Display for Ident {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "[{}]", self.value)
- }
-}
-
-#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
-#[cfg(not(feature = "sql"))]
-pub struct ReplaceSelectElement {
- pub expr: String,
- pub column_name: Ident,
- pub as_keyword: bool,
-}
-#[cfg(not(feature = "sql"))]
-impl Display for ReplaceSelectElement {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- if self.as_keyword {
- write!(f, "{} AS {}", self.expr, self.column_name)
- } else {
- write!(f, "{} {}", self.expr, self.column_name)
- }
- }
-}
-
/// Additional options for wildcards, e.g. Snowflake `EXCLUDE`/`RENAME` and
Bigquery `EXCEPT`.
#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug, Default)]
pub struct WildcardOptions {
diff --git a/datafusion/expr/src/lib.rs b/datafusion/expr/src/lib.rs
index c0d2187d94..da7f20783b 100644
--- a/datafusion/expr/src/lib.rs
+++ b/datafusion/expr/src/lib.rs
@@ -80,6 +80,8 @@ pub mod statistics {
mod predicate_bounds;
pub mod preimage;
pub mod ptr_eq;
+#[cfg(not(feature = "sql"))]
+pub mod sql;
pub mod test;
pub mod tree_node;
pub mod type_coercion;
diff --git a/datafusion/expr/src/logical_plan/ddl.rs
b/datafusion/expr/src/logical_plan/ddl.rs
index 8a46e842a8..5779fb0c4e 100644
--- a/datafusion/expr/src/logical_plan/ddl.rs
+++ b/datafusion/expr/src/logical_plan/ddl.rs
@@ -24,9 +24,9 @@ use std::{
hash::{Hash, Hasher},
};
-#[cfg(not(feature = "sql"))]
-use crate::expr::Ident;
use crate::expr::Sort;
+#[cfg(not(feature = "sql"))]
+use crate::sql::Ident;
use arrow::datatypes::DataType;
use datafusion_common::tree_node::{Transformed, TreeNodeContainer,
TreeNodeRecursion};
use datafusion_common::{
diff --git a/datafusion/expr/src/sql.rs b/datafusion/expr/src/sql.rs
new file mode 100644
index 0000000000..c9c3fa5336
--- /dev/null
+++ b/datafusion/expr/src/sql.rs
@@ -0,0 +1,143 @@
+// 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.
+
+//! Local copies of [`sqlparser::ast`] structures
+//!
+//! These types are used when the `sql` feature is disabled. When `sql` is
+//! enabled, the upstream types from [`sqlparser`] are used instead.
+//!
+//! These definitions should be structurally compatible with the upstream
+//! `sqlparser` types, so that code which switches between them via `cfg` keeps
+//! compiling.
+//!
+//! See [#17332](https://github.com/apache/datafusion/pull/17332) for
+//! more detail.
+
+use crate::expr::display_comma_separated;
+use std::fmt;
+use std::fmt::{Display, Formatter};
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
+pub struct IlikeSelectItem {
+ pub pattern: String,
+}
+
+impl Display for IlikeSelectItem {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "ILIKE '{}'", &self.pattern)?;
+ Ok(())
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
+pub enum ExcludeSelectItem {
+ Single(Ident),
+ Multiple(Vec<Ident>),
+}
+
+impl Display for ExcludeSelectItem {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "EXCLUDE")?;
+ match self {
+ Self::Single(column) => {
+ write!(f, " {column}")?;
+ }
+ Self::Multiple(columns) => {
+ write!(f, " ({})", display_comma_separated(columns))?;
+ }
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
+pub struct ExceptSelectItem {
+ pub first_element: Ident,
+ pub additional_elements: Vec<Ident>,
+}
+
+impl Display for ExceptSelectItem {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "EXCEPT ")?;
+ if self.additional_elements.is_empty() {
+ write!(f, "({})", self.first_element)?;
+ } else {
+ write!(
+ f,
+ "({}, {})",
+ self.first_element,
+ display_comma_separated(&self.additional_elements)
+ )?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
+pub enum RenameSelectItem {
+ Single(String),
+ Multiple(Vec<String>),
+}
+
+impl Display for RenameSelectItem {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "RENAME")?;
+ match self {
+ Self::Single(column) => {
+ write!(f, " {column}")?;
+ }
+ Self::Multiple(columns) => {
+ write!(f, " ({})", display_comma_separated(columns))?;
+ }
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
+pub struct Ident {
+ /// The value of the identifier without quotes.
+ pub value: String,
+ /// The starting quote if any. Valid quote characters are the single quote,
+ /// double quote, backtick, and opening square bracket.
+ pub quote_style: Option<char>,
+ /// The span of the identifier in the original SQL string.
+ pub span: String,
+}
+
+impl Display for Ident {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "[{}]", self.value)
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
+pub struct ReplaceSelectElement {
+ pub expr: String,
+ pub column_name: Ident,
+ pub as_keyword: bool,
+}
+
+impl Display for ReplaceSelectElement {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ if self.as_keyword {
+ write!(f, "{} AS {}", self.expr, self.column_name)
+ } else {
+ write!(f, "{} {}", self.expr, self.column_name)
+ }
+ }
+}
diff --git a/datafusion/expr/src/utils.rs b/datafusion/expr/src/utils.rs
index fdb4b6de78..659306d3bf 100644
--- a/datafusion/expr/src/utils.rs
+++ b/datafusion/expr/src/utils.rs
@@ -39,7 +39,7 @@ use datafusion_common::{
};
#[cfg(not(feature = "sql"))]
-use crate::expr::{ExceptSelectItem, ExcludeSelectItem};
+use crate::sql::{ExceptSelectItem, ExcludeSelectItem};
use indexmap::IndexSet;
#[cfg(feature = "sql")]
use sqlparser::ast::{ExceptSelectItem, ExcludeSelectItem};
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]