blob: 68b42ba05af5f80010f669ec282cc2a5cf2fa04a [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 super::{
ast::{
BuilderError, DerivedRelationBuilder, QueryBuilder, RelationBuilder,
SelectBuilder, TableRelationBuilder, TableWithJoinsBuilder,
},
rewrite::{
inject_column_aliases_into_subquery, normalize_union_schema,
rewrite_plan_for_sort_on_non_projected_fields,
subquery_alias_inner_query_and_columns, TableAliasRewriter,
},
utils::{
find_agg_node_within_select, find_unnest_node_within_select,
find_window_nodes_within_select, try_transform_to_simple_table_scan_with_filters,
unproject_sort_expr, unproject_unnest_expr, unproject_window_exprs,
},
Unparser,
};
use crate::unparser::extension_unparser::{
UnparseToStatementResult, UnparseWithinStatementResult,
};
use crate::unparser::utils::{find_unnest_node_until_relation, unproject_agg_exprs};
use crate::unparser::{ast::UnnestRelationBuilder, rewrite::rewrite_qualify};
use crate::utils::UNNEST_PLACEHOLDER;
use datafusion_common::{
internal_err, not_impl_err,
tree_node::{TransformedResult, TreeNode},
Column, DataFusionError, Result, ScalarValue, TableReference,
};
use datafusion_expr::expr::OUTER_REFERENCE_COLUMN_PREFIX;
use datafusion_expr::{
expr::Alias, BinaryExpr, Distinct, Expr, JoinConstraint, JoinType, LogicalPlan,
LogicalPlanBuilder, Operator, Projection, SortExpr, TableScan, Unnest,
UserDefinedLogicalNode,
};
use sqlparser::ast::{self, Ident, OrderByKind, SetExpr, TableAliasColumnDef};
use std::{sync::Arc, vec};
/// Convert a DataFusion [`LogicalPlan`] to [`ast::Statement`]
///
/// This function is the opposite of [`SqlToRel::sql_statement_to_plan`] and can
/// be used to, among other things, to convert `LogicalPlan`s to SQL strings.
///
/// # Errors
///
/// This function returns an error if the plan cannot be converted to SQL.
///
/// # See Also
///
/// * [`expr_to_sql`] for converting [`Expr`], a single expression to SQL
///
/// # Example
/// ```
/// use arrow::datatypes::{DataType, Field, Schema};
/// use datafusion_expr::{col, logical_plan::table_scan};
/// use datafusion_sql::unparser::plan_to_sql;
/// let schema = Schema::new(vec![
/// Field::new("id", DataType::Utf8, false),
/// Field::new("value", DataType::Utf8, false),
/// ]);
/// // Scan 'table' and select columns 'id' and 'value'
/// let plan = table_scan(Some("table"), &schema, None)
/// .unwrap()
/// .project(vec![col("id"), col("value")])
/// .unwrap()
/// .build()
/// .unwrap();
/// // convert to AST
/// let sql = plan_to_sql(&plan).unwrap();
/// // use the Display impl to convert to SQL text
/// assert_eq!(
/// sql.to_string(),
/// "SELECT \"table\".id, \"table\".\"value\" FROM \"table\""
/// )
/// ```
///
/// [`SqlToRel::sql_statement_to_plan`]: crate::planner::SqlToRel::sql_statement_to_plan
/// [`expr_to_sql`]: crate::unparser::expr_to_sql
pub fn plan_to_sql(plan: &LogicalPlan) -> Result<ast::Statement> {
let unparser = Unparser::default();
unparser.plan_to_sql(plan)
}
impl Unparser<'_> {
pub fn plan_to_sql(&self, plan: &LogicalPlan) -> Result<ast::Statement> {
let mut plan = normalize_union_schema(plan)?;
if !self.dialect.supports_qualify() {
plan = rewrite_qualify(plan)?;
}
match plan {
LogicalPlan::Projection(_)
| LogicalPlan::Filter(_)
| LogicalPlan::Window(_)
| LogicalPlan::Aggregate(_)
| LogicalPlan::Sort(_)
| LogicalPlan::Join(_)
| LogicalPlan::Repartition(_)
| LogicalPlan::Union(_)
| LogicalPlan::TableScan(_)
| LogicalPlan::EmptyRelation(_)
| LogicalPlan::Subquery(_)
| LogicalPlan::SubqueryAlias(_)
| LogicalPlan::Limit(_)
| LogicalPlan::Statement(_)
| LogicalPlan::Values(_)
| LogicalPlan::Distinct(_) => self.select_to_sql_statement(&plan),
LogicalPlan::Dml(_) => self.dml_to_sql(&plan),
LogicalPlan::Extension(extension) => {
self.extension_to_statement(extension.node.as_ref())
}
LogicalPlan::Explain(_)
| LogicalPlan::Analyze(_)
| LogicalPlan::Ddl(_)
| LogicalPlan::Copy(_)
| LogicalPlan::DescribeTable(_)
| LogicalPlan::RecursiveQuery(_)
| LogicalPlan::Unnest(_) => not_impl_err!("Unsupported plan: {plan:?}"),
}
}
/// Try to unparse a [UserDefinedLogicalNode] to a SQL statement.
/// If multiple unparsers are registered for the same [UserDefinedLogicalNode],
/// the first unparsing result will be returned.
fn extension_to_statement(
&self,
node: &dyn UserDefinedLogicalNode,
) -> Result<ast::Statement> {
let mut statement = None;
for unparser in &self.extension_unparsers {
match unparser.unparse_to_statement(node, self)? {
UnparseToStatementResult::Modified(stmt) => {
statement = Some(stmt);
break;
}
UnparseToStatementResult::Unmodified => {}
}
}
if let Some(statement) = statement {
Ok(statement)
} else {
not_impl_err!("Unsupported extension node: {node:?}")
}
}
/// Try to unparse a [UserDefinedLogicalNode] to a SQL statement.
/// If multiple unparsers are registered for the same [UserDefinedLogicalNode],
/// the first unparser supporting the node will be used.
fn extension_to_sql(
&self,
node: &dyn UserDefinedLogicalNode,
query: &mut Option<&mut QueryBuilder>,
select: &mut Option<&mut SelectBuilder>,
relation: &mut Option<&mut RelationBuilder>,
) -> Result<()> {
for unparser in &self.extension_unparsers {
match unparser.unparse(node, self, query, select, relation)? {
UnparseWithinStatementResult::Modified => return Ok(()),
UnparseWithinStatementResult::Unmodified => {}
}
}
not_impl_err!("Unsupported extension node: {node:?}")
}
fn select_to_sql_statement(&self, plan: &LogicalPlan) -> Result<ast::Statement> {
let mut query_builder = Some(QueryBuilder::default());
let body = self.select_to_sql_expr(plan, &mut query_builder)?;
let query = query_builder.unwrap().body(Box::new(body)).build()?;
Ok(ast::Statement::Query(Box::new(query)))
}
fn select_to_sql_expr(
&self,
plan: &LogicalPlan,
query: &mut Option<QueryBuilder>,
) -> Result<SetExpr> {
let mut select_builder = SelectBuilder::default();
select_builder.push_from(TableWithJoinsBuilder::default());
let mut relation_builder = RelationBuilder::default();
self.select_to_sql_recursively(
plan,
query,
&mut select_builder,
&mut relation_builder,
)?;
// If we were able to construct a full body (i.e. UNION ALL), return it
if let Some(body) = query.as_mut().and_then(|q| q.take_body()) {
return Ok(*body);
}
// If no projection is set, add a wildcard projection to the select
// which will be translated to `SELECT *` in the SQL statement
if !select_builder.already_projected() {
select_builder.projection(vec![ast::SelectItem::Wildcard(
ast::WildcardAdditionalOptions::default(),
)]);
}
let mut twj = select_builder.pop_from().unwrap();
twj.relation(relation_builder);
select_builder.push_from(twj);
Ok(SetExpr::Select(Box::new(select_builder.build()?)))
}
/// Reconstructs a SELECT SQL statement from a logical plan by unprojecting column expressions
/// found in a [Projection] node. This requires scanning the plan tree for relevant Aggregate
/// and Window nodes and matching column expressions to the appropriate agg or window expressions.
fn reconstruct_select_statement(
&self,
plan: &LogicalPlan,
p: &Projection,
select: &mut SelectBuilder,
) -> Result<()> {
let mut exprs = p.expr.clone();
// If an Unnest node is found within the select, find and unproject the unnest column
if let Some(unnest) = find_unnest_node_within_select(plan) {
exprs = exprs
.into_iter()
.map(|e| unproject_unnest_expr(e, unnest))
.collect::<Result<Vec<_>>>()?;
};
match (
find_agg_node_within_select(plan, true),
find_window_nodes_within_select(plan, None, true),
) {
(Some(agg), window) => {
let window_option = window.as_deref();
let items = exprs
.into_iter()
.map(|proj_expr| {
let unproj = unproject_agg_exprs(proj_expr, agg, window_option)?;
self.select_item_to_sql(&unproj)
})
.collect::<Result<Vec<_>>>()?;
select.projection(items);
select.group_by(ast::GroupByExpr::Expressions(
agg.group_expr
.iter()
.map(|expr| self.expr_to_sql(expr))
.collect::<Result<Vec<_>>>()?,
vec![],
));
}
(None, Some(window)) => {
let items = exprs
.into_iter()
.map(|proj_expr| {
let unproj = unproject_window_exprs(proj_expr, &window)?;
self.select_item_to_sql(&unproj)
})
.collect::<Result<Vec<_>>>()?;
select.projection(items);
}
_ => {
let items = exprs
.iter()
.map(|e| self.select_item_to_sql(e))
.collect::<Result<Vec<_>>>()?;
select.projection(items);
}
}
Ok(())
}
fn derive(
&self,
plan: &LogicalPlan,
relation: &mut RelationBuilder,
alias: Option<ast::TableAlias>,
lateral: bool,
) -> Result<()> {
let mut derived_builder = DerivedRelationBuilder::default();
derived_builder.lateral(lateral).alias(alias).subquery({
let inner_statement = self.plan_to_sql(plan)?;
if let ast::Statement::Query(inner_query) = inner_statement {
inner_query
} else {
return internal_err!(
"Subquery must be a Query, but found {inner_statement:?}"
);
}
});
relation.derived(derived_builder);
Ok(())
}
fn derive_with_dialect_alias(
&self,
alias: &str,
plan: &LogicalPlan,
relation: &mut RelationBuilder,
lateral: bool,
columns: Vec<Ident>,
) -> Result<()> {
if self.dialect.requires_derived_table_alias() || !columns.is_empty() {
self.derive(
plan,
relation,
Some(self.new_table_alias(alias.to_string(), columns)),
lateral,
)
} else {
self.derive(plan, relation, None, lateral)
}
}
#[cfg_attr(feature = "recursive_protection", recursive::recursive)]
fn select_to_sql_recursively(
&self,
plan: &LogicalPlan,
query: &mut Option<QueryBuilder>,
select: &mut SelectBuilder,
relation: &mut RelationBuilder,
) -> Result<()> {
match plan {
LogicalPlan::TableScan(scan) => {
if let Some(unparsed_table_scan) = Self::unparse_table_scan_pushdown(
plan,
None,
select.already_projected(),
)? {
return self.select_to_sql_recursively(
&unparsed_table_scan,
query,
select,
relation,
);
}
let mut builder = TableRelationBuilder::default();
let mut table_parts = vec![];
if let Some(catalog_name) = scan.table_name.catalog() {
table_parts
.push(self.new_ident_quoted_if_needs(catalog_name.to_string()));
}
if let Some(schema_name) = scan.table_name.schema() {
table_parts
.push(self.new_ident_quoted_if_needs(schema_name.to_string()));
}
table_parts.push(
self.new_ident_quoted_if_needs(scan.table_name.table().to_string()),
);
builder.name(ast::ObjectName::from(table_parts));
relation.table(builder);
Ok(())
}
LogicalPlan::Projection(p) => {
if let Some(new_plan) = rewrite_plan_for_sort_on_non_projected_fields(p) {
return self
.select_to_sql_recursively(&new_plan, query, select, relation);
}
// Projection can be top-level plan for unnest relation
// The projection generated by the `RecursiveUnnestRewriter` from a UNNEST relation will have
// only one expression, which is the placeholder column generated by the rewriter.
let unnest_input_type = if p.expr.len() == 1 {
Self::check_unnest_placeholder_with_outer_ref(&p.expr[0])
} else {
None
};
if self.dialect.unnest_as_table_factor() && unnest_input_type.is_some() {
if let LogicalPlan::Unnest(unnest) = &p.input.as_ref() {
if let Some(unnest_relation) =
self.try_unnest_to_table_factor_sql(unnest)?
{
relation.unnest(unnest_relation);
return self.select_to_sql_recursively(
p.input.as_ref(),
query,
select,
relation,
);
}
}
}
// If it's a unnest projection, we should provide the table column alias
// to provide a column name for the unnest relation.
let columns = if unnest_input_type.is_some() {
p.expr
.iter()
.map(|e| {
self.new_ident_quoted_if_needs(e.schema_name().to_string())
})
.collect()
} else {
vec![]
};
// Projection can be top-level plan for derived table
if select.already_projected() {
return self.derive_with_dialect_alias(
"derived_projection",
plan,
relation,
unnest_input_type
.filter(|t| matches!(t, UnnestInputType::OuterReference))
.is_some(),
columns,
);
}
self.reconstruct_select_statement(plan, p, select)?;
self.select_to_sql_recursively(p.input.as_ref(), query, select, relation)
}
LogicalPlan::Filter(filter) => {
if let Some(agg) =
find_agg_node_within_select(plan, select.already_projected())
{
let unprojected =
unproject_agg_exprs(filter.predicate.clone(), agg, None)?;
let filter_expr = self.expr_to_sql(&unprojected)?;
select.having(Some(filter_expr));
} else if let (Some(window), true) = (
find_window_nodes_within_select(
plan,
None,
select.already_projected(),
),
self.dialect.supports_qualify(),
) {
let unprojected =
unproject_window_exprs(filter.predicate.clone(), &window)?;
let filter_expr = self.expr_to_sql(&unprojected)?;
select.qualify(Some(filter_expr));
} else {
let filter_expr = self.expr_to_sql(&filter.predicate)?;
select.selection(Some(filter_expr));
}
self.select_to_sql_recursively(
filter.input.as_ref(),
query,
select,
relation,
)
}
LogicalPlan::Limit(limit) => {
// Limit can be top-level plan for derived table
if select.already_projected() {
return self.derive_with_dialect_alias(
"derived_limit",
plan,
relation,
false,
vec![],
);
}
if let Some(fetch) = &limit.fetch {
let Some(query) = query.as_mut() else {
return internal_err!(
"Limit operator only valid in a statement context."
);
};
query.limit(Some(self.expr_to_sql(fetch)?));
}
if let Some(skip) = &limit.skip {
let Some(query) = query.as_mut() else {
return internal_err!(
"Offset operator only valid in a statement context."
);
};
query.offset(Some(ast::Offset {
rows: ast::OffsetRows::None,
value: self.expr_to_sql(skip)?,
}));
}
self.select_to_sql_recursively(
limit.input.as_ref(),
query,
select,
relation,
)
}
LogicalPlan::Sort(sort) => {
// Sort can be top-level plan for derived table
if select.already_projected() {
return self.derive_with_dialect_alias(
"derived_sort",
plan,
relation,
false,
vec![],
);
}
let Some(query_ref) = query else {
return internal_err!(
"Sort operator only valid in a statement context."
);
};
if let Some(fetch) = sort.fetch {
query_ref.limit(Some(ast::Expr::value(ast::Value::Number(
fetch.to_string(),
false,
))));
};
let agg = find_agg_node_within_select(plan, select.already_projected());
// unproject sort expressions
let sort_exprs: Vec<SortExpr> = sort
.expr
.iter()
.map(|sort_expr| {
unproject_sort_expr(sort_expr.clone(), agg, sort.input.as_ref())
})
.collect::<Result<Vec<_>>>()?;
query_ref.order_by(self.sorts_to_sql(&sort_exprs)?);
self.select_to_sql_recursively(
sort.input.as_ref(),
query,
select,
relation,
)
}
LogicalPlan::Aggregate(agg) => {
// Aggregation can be already handled in the projection case
if !select.already_projected() {
// The query returns aggregate and group expressions. If that weren't the case,
// the aggregate would have been placed inside a projection, making the check above^ false
let exprs: Vec<_> = agg
.aggr_expr
.iter()
.chain(agg.group_expr.iter())
.map(|expr| self.select_item_to_sql(expr))
.collect::<Result<Vec<_>>>()?;
select.projection(exprs);
select.group_by(ast::GroupByExpr::Expressions(
agg.group_expr
.iter()
.map(|expr| self.expr_to_sql(expr))
.collect::<Result<Vec<_>>>()?,
vec![],
));
}
self.select_to_sql_recursively(
agg.input.as_ref(),
query,
select,
relation,
)
}
LogicalPlan::Distinct(distinct) => {
// Distinct can be top-level plan for derived table
if select.already_projected() {
return self.derive_with_dialect_alias(
"derived_distinct",
plan,
relation,
false,
vec![],
);
}
// If this distinct is the parent of a Union and we're in a query context,
// then we need to unparse as a `UNION` rather than a `UNION ALL`.
if let Distinct::All(input) = distinct {
if matches!(input.as_ref(), LogicalPlan::Union(_)) {
if let Some(query_mut) = query.as_mut() {
query_mut.distinct_union();
return self.select_to_sql_recursively(
input.as_ref(),
query,
select,
relation,
);
}
}
}
let (select_distinct, input) = match distinct {
Distinct::All(input) => (ast::Distinct::Distinct, input.as_ref()),
Distinct::On(on) => {
let exprs = on
.on_expr
.iter()
.map(|e| self.expr_to_sql(e))
.collect::<Result<Vec<_>>>()?;
let items = on
.select_expr
.iter()
.map(|e| self.select_item_to_sql(e))
.collect::<Result<Vec<_>>>()?;
if let Some(sort_expr) = &on.sort_expr {
if let Some(query_ref) = query {
query_ref.order_by(self.sorts_to_sql(sort_expr)?);
} else {
return internal_err!(
"Sort operator only valid in a statement context."
);
}
}
select.projection(items);
(ast::Distinct::On(exprs), on.input.as_ref())
}
};
select.distinct(Some(select_distinct));
self.select_to_sql_recursively(input, query, select, relation)
}
LogicalPlan::Join(join) => {
let mut table_scan_filters = vec![];
let (left_plan, right_plan) = match join.join_type {
JoinType::RightSemi | JoinType::RightAnti => {
(&join.right, &join.left)
}
_ => (&join.left, &join.right),
};
// If there's an outer projection plan, it will already set up the projection.
// In that case, we don't need to worry about setting up the projection here.
// The outer projection plan will handle projecting the correct columns.
let already_projected = select.already_projected();
let left_plan =
match try_transform_to_simple_table_scan_with_filters(left_plan)? {
Some((plan, filters)) => {
table_scan_filters.extend(filters);
Arc::new(plan)
}
None => Arc::clone(left_plan),
};
self.select_to_sql_recursively(
left_plan.as_ref(),
query,
select,
relation,
)?;
let left_projection: Option<Vec<ast::SelectItem>> = if !already_projected
{
Some(select.pop_projections())
} else {
None
};
let right_plan =
match try_transform_to_simple_table_scan_with_filters(right_plan)? {
Some((plan, filters)) => {
table_scan_filters.extend(filters);
Arc::new(plan)
}
None => Arc::clone(right_plan),
};
let mut right_relation = RelationBuilder::default();
self.select_to_sql_recursively(
right_plan.as_ref(),
query,
select,
&mut right_relation,
)?;
let join_filters = if table_scan_filters.is_empty() {
join.filter.clone()
} else {
// Combine `table_scan_filters` into a single filter using `AND`
let Some(combined_filters) =
table_scan_filters.into_iter().reduce(|acc, filter| {
Expr::BinaryExpr(BinaryExpr {
left: Box::new(acc),
op: Operator::And,
right: Box::new(filter),
})
})
else {
return internal_err!("Failed to combine TableScan filters");
};
// Combine `join.filter` with `combined_filters` using `AND`
match &join.filter {
Some(filter) => Some(Expr::BinaryExpr(BinaryExpr {
left: Box::new(filter.clone()),
op: Operator::And,
right: Box::new(combined_filters),
})),
None => Some(combined_filters),
}
};
let join_constraint = self.join_constraint_to_sql(
join.join_constraint,
&join.on,
join_filters.as_ref(),
)?;
let right_projection: Option<Vec<ast::SelectItem>> = if !already_projected
{
Some(select.pop_projections())
} else {
None
};
match join.join_type {
JoinType::LeftSemi
| JoinType::LeftAnti
| JoinType::LeftMark
| JoinType::RightSemi
| JoinType::RightAnti
| JoinType::RightMark => {
let mut query_builder = QueryBuilder::default();
let mut from = TableWithJoinsBuilder::default();
let mut exists_select: SelectBuilder = SelectBuilder::default();
from.relation(right_relation);
exists_select.push_from(from);
if let Some(filter) = &join.filter {
exists_select.selection(Some(self.expr_to_sql(filter)?));
}
for (left, right) in &join.on {
exists_select.selection(Some(
self.expr_to_sql(&left.clone().eq(right.clone()))?,
));
}
exists_select.projection(vec![ast::SelectItem::UnnamedExpr(
ast::Expr::value(ast::Value::Number("1".to_string(), false)),
)]);
query_builder.body(Box::new(SetExpr::Select(Box::new(
exists_select.build()?,
))));
let negated = match join.join_type {
JoinType::LeftSemi
| JoinType::RightSemi
| JoinType::LeftMark
| JoinType::RightMark => false,
JoinType::LeftAnti | JoinType::RightAnti => true,
_ => unreachable!(),
};
let exists_expr = ast::Expr::Exists {
subquery: Box::new(query_builder.build()?),
negated,
};
match join.join_type {
JoinType::LeftMark | JoinType::RightMark => {
let source_schema =
if join.join_type == JoinType::LeftMark {
right_plan.schema()
} else {
left_plan.schema()
};
let (table_ref, _) = source_schema.qualified_field(0);
let column = self.col_to_sql(&Column::new(
table_ref.cloned(),
"mark",
))?;
select.replace_mark(&column, &exists_expr);
}
_ => {
select.selection(Some(exists_expr));
}
}
if let Some(projection) = left_projection {
select.projection(projection);
}
}
JoinType::Inner
| JoinType::Left
| JoinType::Right
| JoinType::Full => {
let Ok(Some(relation)) = right_relation.build() else {
return internal_err!("Failed to build right relation");
};
let ast_join = ast::Join {
relation,
global: false,
join_operator: self
.join_operator_to_sql(join.join_type, join_constraint)?,
};
let mut from = select.pop_from().unwrap();
from.push_join(ast_join);
select.push_from(from);
if !already_projected {
let Some(left_projection) = left_projection else {
return internal_err!("Left projection is missing");
};
let Some(right_projection) = right_projection else {
return internal_err!("Right projection is missing");
};
let projection = left_projection
.into_iter()
.chain(right_projection)
.collect();
select.projection(projection);
}
}
};
Ok(())
}
LogicalPlan::SubqueryAlias(plan_alias) => {
let (plan, mut columns) =
subquery_alias_inner_query_and_columns(plan_alias);
let unparsed_table_scan = Self::unparse_table_scan_pushdown(
plan,
Some(plan_alias.alias.clone()),
select.already_projected(),
)?;
// if the child plan is a TableScan with pushdown operations, we don't need to
// create an additional subquery for it
if !select.already_projected() && unparsed_table_scan.is_none() {
select.projection(vec![ast::SelectItem::Wildcard(
ast::WildcardAdditionalOptions::default(),
)]);
}
let plan = unparsed_table_scan.unwrap_or_else(|| plan.clone());
if !columns.is_empty()
&& !self.dialect.supports_column_alias_in_table_alias()
{
// Instead of specifying column aliases as part of the outer table, inject them directly into the inner projection
let rewritten_plan =
match inject_column_aliases_into_subquery(plan, columns) {
Ok(p) => p,
Err(e) => {
return internal_err!(
"Failed to transform SubqueryAlias plan: {e}"
)
}
};
columns = vec![];
self.select_to_sql_recursively(
&rewritten_plan,
query,
select,
relation,
)?;
} else {
self.select_to_sql_recursively(&plan, query, select, relation)?;
}
relation.alias(Some(
self.new_table_alias(plan_alias.alias.table().to_string(), columns),
));
Ok(())
}
LogicalPlan::Union(union) => {
// Covers cases where the UNION is a subquery and the projection is at the top level
if select.already_projected() {
return self.derive_with_dialect_alias(
"derived_union",
plan,
relation,
false,
vec![],
);
}
let input_exprs: Vec<SetExpr> = union
.inputs
.iter()
.map(|input| self.select_to_sql_expr(input, query))
.collect::<Result<Vec<_>>>()?;
if input_exprs.len() < 2 {
return internal_err!("UNION operator requires at least 2 inputs");
}
let set_quantifier =
if query.as_ref().is_some_and(|q| q.is_distinct_union()) {
// Setting the SetQuantifier to None will unparse as a `UNION`
// rather than a `UNION ALL`.
ast::SetQuantifier::None
} else {
ast::SetQuantifier::All
};
// Build the union expression tree bottom-up by reversing the order
// note that we are also swapping left and right inputs because of the rev
let union_expr = input_exprs
.into_iter()
.rev()
.reduce(|a, b| SetExpr::SetOperation {
op: ast::SetOperator::Union,
set_quantifier,
left: Box::new(b),
right: Box::new(a),
})
.unwrap();
let Some(query) = query.as_mut() else {
return internal_err!(
"UNION ALL operator only valid in a statement context"
);
};
query.body(Box::new(union_expr));
Ok(())
}
LogicalPlan::Window(window) => {
// Window nodes are handled simultaneously with Projection nodes
self.select_to_sql_recursively(
window.input.as_ref(),
query,
select,
relation,
)
}
LogicalPlan::EmptyRelation(_) => {
// An EmptyRelation could be behind an UNNEST node. If the dialect supports UNNEST as a table factor,
// a TableRelationBuilder will be created for the UNNEST node first.
if !relation.has_relation() {
relation.empty();
}
Ok(())
}
LogicalPlan::Extension(extension) => {
if let Some(query) = query.as_mut() {
self.extension_to_sql(
extension.node.as_ref(),
&mut Some(query),
&mut Some(select),
&mut Some(relation),
)
} else {
self.extension_to_sql(
extension.node.as_ref(),
&mut None,
&mut Some(select),
&mut Some(relation),
)
}
}
LogicalPlan::Unnest(unnest) => {
if !unnest.struct_type_columns.is_empty() {
return internal_err!(
"Struct type columns are not currently supported in UNNEST: {:?}",
unnest.struct_type_columns
);
}
// In the case of UNNEST, the Unnest node is followed by a duplicate Projection node that we should skip.
// Otherwise, there will be a duplicate SELECT clause.
// | Projection: table.col1, UNNEST(table.col2)
// | Unnest: UNNEST(table.col2)
// | Projection: table.col1, table.col2 AS UNNEST(table.col2)
// | Filter: table.col3 = Int64(3)
// | TableScan: table projection=None
if let LogicalPlan::Projection(p) = unnest.input.as_ref() {
// continue with projection input
self.select_to_sql_recursively(&p.input, query, select, relation)
} else {
internal_err!("Unnest input is not a Projection: {unnest:?}")
}
}
LogicalPlan::Subquery(subquery)
if find_unnest_node_until_relation(subquery.subquery.as_ref())
.is_some() =>
{
if self.dialect.unnest_as_table_factor() {
self.select_to_sql_recursively(
subquery.subquery.as_ref(),
query,
select,
relation,
)
} else {
self.derive_with_dialect_alias(
"derived_unnest",
subquery.subquery.as_ref(),
relation,
true,
vec![],
)
}
}
_ => {
not_impl_err!("Unsupported operator: {plan:?}")
}
}
}
/// Try to find the placeholder column name generated by `RecursiveUnnestRewriter`.
///
/// - If the column is a placeholder column match the pattern `Expr::Alias(Expr::Column("__unnest_placeholder(...)"))`,
/// it means it is a scalar column, return [UnnestInputType::Scalar].
/// - If the column is a placeholder column match the pattern `Expr::Alias(Expr::Column("__unnest_placeholder(outer_ref(...)))")`,
/// it means it is an outer reference column, return [UnnestInputType::OuterReference].
/// - If the column is not a placeholder column, return [None].
///
/// `outer_ref` is the display result of [Expr::OuterReferenceColumn]
fn check_unnest_placeholder_with_outer_ref(expr: &Expr) -> Option<UnnestInputType> {
if let Expr::Alias(Alias { expr, .. }) = expr {
if let Expr::Column(Column { name, .. }) = expr.as_ref() {
if let Some(prefix) = name.strip_prefix(UNNEST_PLACEHOLDER) {
if prefix.starts_with(&format!("({OUTER_REFERENCE_COLUMN_PREFIX}(")) {
return Some(UnnestInputType::OuterReference);
}
return Some(UnnestInputType::Scalar);
}
}
}
None
}
fn try_unnest_to_table_factor_sql(
&self,
unnest: &Unnest,
) -> Result<Option<UnnestRelationBuilder>> {
let mut unnest_relation = UnnestRelationBuilder::default();
let LogicalPlan::Projection(projection) = unnest.input.as_ref() else {
return Ok(None);
};
if !matches!(projection.input.as_ref(), LogicalPlan::EmptyRelation(_)) {
// It may be possible that UNNEST is used as a source for the query.
// However, at this point, we don't yet know if it is just a single expression
// from another source or if it's from UNNEST.
//
// Unnest(Projection(EmptyRelation)) denotes a case with `UNNEST([...])`,
// which is normally safe to unnest as a table factor.
// However, in the future, more comprehensive checks can be added here.
return Ok(None);
};
let exprs = projection
.expr
.iter()
.map(|e| self.expr_to_sql(e))
.collect::<Result<Vec<_>>>()?;
unnest_relation.array_exprs(exprs);
Ok(Some(unnest_relation))
}
fn is_scan_with_pushdown(scan: &TableScan) -> bool {
scan.projection.is_some() || !scan.filters.is_empty() || scan.fetch.is_some()
}
/// Try to unparse a table scan with pushdown operations into a new subquery plan.
/// If the table scan is without any pushdown operations, return None.
fn unparse_table_scan_pushdown(
plan: &LogicalPlan,
alias: Option<TableReference>,
already_projected: bool,
) -> Result<Option<LogicalPlan>> {
match plan {
LogicalPlan::TableScan(table_scan) => {
if !Self::is_scan_with_pushdown(table_scan) {
return Ok(None);
}
let table_schema = table_scan.source.schema();
let mut filter_alias_rewriter =
alias.as_ref().map(|alias_name| TableAliasRewriter {
table_schema: &table_schema,
alias_name: alias_name.clone(),
});
let mut builder = LogicalPlanBuilder::scan(
table_scan.table_name.clone(),
Arc::clone(&table_scan.source),
None,
)?;
// We will rebase the column references to the new alias if it exists.
// If the projection or filters are empty, we will append alias to the table scan.
//
// Example:
// select t1.c1 from t1 where t1.c1 > 1 -> select a.c1 from t1 as a where a.c1 > 1
if let Some(ref alias) = alias {
if table_scan.projection.is_some() || !table_scan.filters.is_empty() {
builder = builder.alias(alias.clone())?;
}
}
// Avoid creating a duplicate Projection node, which would result in an additional subquery if a projection already exists.
// For example, if the `optimize_projection` rule is applied, there will be a Projection node, and duplicate projection
// information included in the TableScan node.
if !already_projected {
if let Some(project_vec) = &table_scan.projection {
if project_vec.is_empty() {
builder = builder.project(vec![Expr::Literal(
ScalarValue::Int64(Some(1)),
None,
)])?;
} else {
let project_columns = project_vec
.iter()
.cloned()
.map(|i| {
let schema = table_scan.source.schema();
let field = schema.field(i);
if alias.is_some() {
Column::new(alias.clone(), field.name().clone())
} else {
Column::new(
Some(table_scan.table_name.clone()),
field.name().clone(),
)
}
})
.collect::<Vec<_>>();
builder = builder.project(project_columns)?;
};
}
}
let filter_expr: Result<Option<Expr>> = table_scan
.filters
.iter()
.cloned()
.map(|expr| {
if let Some(ref mut rewriter) = filter_alias_rewriter {
expr.rewrite(rewriter).data()
} else {
Ok(expr)
}
})
.reduce(|acc, expr_result| {
acc.and_then(|acc_expr| {
expr_result.map(|expr| acc_expr.and(expr))
})
})
.transpose();
if let Some(filter) = filter_expr? {
builder = builder.filter(filter)?;
}
if let Some(fetch) = table_scan.fetch {
builder = builder.limit(0, Some(fetch))?;
}
// If the table scan has an alias but no projection or filters, it means no column references are rebased.
// So we will append the alias to this subquery.
// Example:
// select * from t1 limit 10 -> (select * from t1 limit 10) as a
if let Some(alias) = alias {
if table_scan.projection.is_none() && table_scan.filters.is_empty() {
builder = builder.alias(alias)?;
}
}
Ok(Some(builder.build()?))
}
LogicalPlan::SubqueryAlias(subquery_alias) => {
let ret = Self::unparse_table_scan_pushdown(
&subquery_alias.input,
Some(subquery_alias.alias.clone()),
already_projected,
)?;
if let Some(alias) = alias {
if let Some(plan) = ret {
let plan = LogicalPlanBuilder::new(plan).alias(alias)?.build()?;
return Ok(Some(plan));
}
}
Ok(ret)
}
// SubqueryAlias could be rewritten to a plan with a projection as the top node by [rewrite::subquery_alias_inner_query_and_columns].
// The inner table scan could be a scan with pushdown operations.
LogicalPlan::Projection(projection) => {
if let Some(plan) = Self::unparse_table_scan_pushdown(
&projection.input,
alias.clone(),
already_projected,
)? {
let exprs = if alias.is_some() {
let mut alias_rewriter =
alias.as_ref().map(|alias_name| TableAliasRewriter {
table_schema: plan.schema().as_arrow(),
alias_name: alias_name.clone(),
});
projection
.expr
.iter()
.cloned()
.map(|expr| {
if let Some(ref mut rewriter) = alias_rewriter {
expr.rewrite(rewriter).data()
} else {
Ok(expr)
}
})
.collect::<Result<Vec<_>>>()?
} else {
projection.expr.clone()
};
Ok(Some(
LogicalPlanBuilder::from(plan).project(exprs)?.build()?,
))
} else {
Ok(None)
}
}
_ => Ok(None),
}
}
fn select_item_to_sql(&self, expr: &Expr) -> Result<ast::SelectItem> {
match expr {
Expr::Alias(Alias { expr, name, .. }) => {
let inner = self.expr_to_sql(expr)?;
// Determine the alias name to use
let col_name = if let Some(rewritten_name) =
self.dialect.col_alias_overrides(name)?
{
rewritten_name.to_string()
} else {
name.to_string()
};
Ok(ast::SelectItem::ExprWithAlias {
expr: inner,
alias: self.new_ident_quoted_if_needs(col_name),
})
}
_ => {
let inner = self.expr_to_sql(expr)?;
Ok(ast::SelectItem::UnnamedExpr(inner))
}
}
}
fn sorts_to_sql(&self, sort_exprs: &[SortExpr]) -> Result<OrderByKind> {
Ok(OrderByKind::Expressions(
sort_exprs
.iter()
.map(|sort_expr| self.sort_to_sql(sort_expr))
.collect::<Result<Vec<_>>>()?,
))
}
fn join_operator_to_sql(
&self,
join_type: JoinType,
constraint: ast::JoinConstraint,
) -> Result<ast::JoinOperator> {
Ok(match join_type {
JoinType::Inner => match &constraint {
ast::JoinConstraint::On(_)
| ast::JoinConstraint::Using(_)
| ast::JoinConstraint::Natural => ast::JoinOperator::Inner(constraint),
ast::JoinConstraint::None => {
// Inner joins with no conditions or filters are not valid SQL in most systems,
// return a CROSS JOIN instead
ast::JoinOperator::CrossJoin(constraint)
}
},
JoinType::Left => ast::JoinOperator::LeftOuter(constraint),
JoinType::Right => ast::JoinOperator::RightOuter(constraint),
JoinType::Full => ast::JoinOperator::FullOuter(constraint),
JoinType::LeftAnti => ast::JoinOperator::LeftAnti(constraint),
JoinType::LeftSemi => ast::JoinOperator::LeftSemi(constraint),
JoinType::RightAnti => ast::JoinOperator::RightAnti(constraint),
JoinType::RightSemi => ast::JoinOperator::RightSemi(constraint),
JoinType::LeftMark | JoinType::RightMark => {
unimplemented!("Unparsing of Mark join type")
}
})
}
/// Convert the components of a USING clause to the USING AST. Returns
/// 'None' if the conditions are not compatible with a USING expression,
/// e.g. non-column expressions or non-matching names.
fn join_using_to_sql(
&self,
join_conditions: &[(Expr, Expr)],
) -> Option<ast::JoinConstraint> {
let mut object_names = Vec::with_capacity(join_conditions.len());
for (left, right) in join_conditions {
match (left, right) {
(
Expr::Column(Column {
relation: _,
name: left_name,
spans: _,
}),
Expr::Column(Column {
relation: _,
name: right_name,
spans: _,
}),
) if left_name == right_name => {
// For example, if the join condition `t1.id = t2.id`
// this is represented as two columns like `[t1.id, t2.id]`
// This code forms `id` (without relation name)
let ident = self.new_ident_quoted_if_needs(left_name.to_string());
object_names.push(ast::ObjectName::from(vec![ident]));
}
// USING is only valid with matching column names; arbitrary expressions
// are not allowed
_ => return None,
}
}
Some(ast::JoinConstraint::Using(object_names))
}
/// Convert a join constraint and associated conditions and filter to a SQL AST node
fn join_constraint_to_sql(
&self,
constraint: JoinConstraint,
conditions: &[(Expr, Expr)],
filter: Option<&Expr>,
) -> Result<ast::JoinConstraint> {
match (constraint, conditions, filter) {
// No constraints
(JoinConstraint::On | JoinConstraint::Using, [], None) => {
Ok(ast::JoinConstraint::None)
}
(JoinConstraint::Using, conditions, None) => {
match self.join_using_to_sql(conditions) {
Some(using) => Ok(using),
// As above, this should not be reachable from parsed SQL,
// but a user could create this; we "downgrade" to ON.
None => self.join_conditions_to_sql_on(conditions, None),
}
}
// Two cases here:
// 1. Straightforward ON case, with possible equi-join conditions
// and additional filters
// 2. USING with additional filters; we "downgrade" to ON, because
// you can't use USING with arbitrary filters. (This should not
// be accessible from parsed SQL, but may have been a
// custom-built JOIN by a user.)
(JoinConstraint::On | JoinConstraint::Using, conditions, filter) => {
self.join_conditions_to_sql_on(conditions, filter)
}
}
}
// Convert a list of equi0join conditions and an optional filter to a SQL ON
// AST node, with the equi-join conditions and the filter merged into a
// single conditional expression
fn join_conditions_to_sql_on(
&self,
join_conditions: &[(Expr, Expr)],
filter: Option<&Expr>,
) -> Result<ast::JoinConstraint> {
let mut condition = None;
// AND the join conditions together to create the overall condition
for (left, right) in join_conditions {
// Parse left and right
let l = self.expr_to_sql(left)?;
let r = self.expr_to_sql(right)?;
let e = self.binary_op_to_sql(l, r, ast::BinaryOperator::Eq);
condition = match condition {
Some(expr) => Some(self.and_op_to_sql(expr, e)),
None => Some(e),
};
}
// Then AND the non-equijoin filter condition as well
condition = match (condition, filter) {
(Some(expr), Some(filter)) => {
Some(self.and_op_to_sql(expr, self.expr_to_sql(filter)?))
}
(Some(expr), None) => Some(expr),
(None, Some(filter)) => Some(self.expr_to_sql(filter)?),
(None, None) => None,
};
let constraint = match condition {
Some(filter) => ast::JoinConstraint::On(filter),
None => ast::JoinConstraint::None,
};
Ok(constraint)
}
fn and_op_to_sql(&self, lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
self.binary_op_to_sql(lhs, rhs, ast::BinaryOperator::And)
}
fn new_table_alias(&self, alias: String, columns: Vec<Ident>) -> ast::TableAlias {
let columns = columns
.into_iter()
.map(|ident| TableAliasColumnDef {
name: ident,
data_type: None,
})
.collect();
ast::TableAlias {
name: self.new_ident_quoted_if_needs(alias),
columns,
}
}
fn dml_to_sql(&self, plan: &LogicalPlan) -> Result<ast::Statement> {
not_impl_err!("Unsupported plan: {plan:?}")
}
}
impl From<BuilderError> for DataFusionError {
fn from(e: BuilderError) -> Self {
DataFusionError::External(Box::new(e))
}
}
/// The type of the input to the UNNEST table factor.
#[derive(Debug)]
enum UnnestInputType {
/// The input is a column reference. It will be presented like `outer_ref(column_name)`.
OuterReference,
/// The input is a scalar value. It will be presented like a scalar array or struct.
Scalar,
}