Skip to content

Commit aac10a4

Browse files
authored
init (#12453)
1 parent 3ac92ad commit aac10a4

File tree

3 files changed

+76
-27
lines changed

3 files changed

+76
-27
lines changed

datafusion/sql/src/unparser/plan.rs

+25-12
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
use crate::unparser::utils::unproject_agg_exprs;
1919
use datafusion_common::{
20-
internal_err, not_impl_err, plan_err, Column, DataFusionError, Result, TableReference,
20+
internal_err, not_impl_err, plan_err,
21+
tree_node::{TransformedResult, TreeNode},
22+
Column, DataFusionError, Result, TableReference,
2123
};
2224
use datafusion_expr::{
2325
expr::Alias, Distinct, Expr, JoinConstraint, JoinType, LogicalPlan,
@@ -34,7 +36,7 @@ use super::{
3436
rewrite::{
3537
inject_column_aliases, normalize_union_schema,
3638
rewrite_plan_for_sort_on_non_projected_fields,
37-
subquery_alias_inner_query_and_columns,
39+
subquery_alias_inner_query_and_columns, TableAliasRewriter,
3840
},
3941
utils::{find_agg_node_within_select, unproject_window_exprs, AggVariant},
4042
Unparser,
@@ -554,13 +556,11 @@ impl Unparser<'_> {
554556
) -> Result<LogicalPlan> {
555557
match plan {
556558
LogicalPlan::TableScan(table_scan) => {
557-
// TODO: support filters for table scan with alias. Remove this check after #12368 issue.
558-
// see the issue: https://github.com/apache/datafusion/issues/12368
559-
if alias.is_some() && !table_scan.filters.is_empty() {
560-
return not_impl_err!(
561-
"Subquery alias is not supported for table scan with pushdown filters"
562-
);
563-
}
559+
let mut filter_alias_rewriter =
560+
alias.as_ref().map(|alias_name| TableAliasRewriter {
561+
table_schema: table_scan.source.schema(),
562+
alias_name: alias_name.clone(),
563+
});
564564

565565
let mut builder = LogicalPlanBuilder::scan(
566566
table_scan.table_name.clone(),
@@ -587,12 +587,25 @@ impl Unparser<'_> {
587587
builder = builder.project(project_columns)?;
588588
}
589589

590-
let filter_expr = table_scan
590+
let filter_expr: Result<Option<Expr>> = table_scan
591591
.filters
592592
.iter()
593593
.cloned()
594-
.reduce(|acc, expr| acc.and(expr));
595-
if let Some(filter) = filter_expr {
594+
.map(|expr| {
595+
if let Some(ref mut rewriter) = filter_alias_rewriter {
596+
expr.rewrite(rewriter).data()
597+
} else {
598+
Ok(expr)
599+
}
600+
})
601+
.reduce(|acc, expr_result| {
602+
acc.and_then(|acc_expr| {
603+
expr_result.map(|expr| acc_expr.and(expr))
604+
})
605+
})
606+
.transpose();
607+
608+
if let Some(filter) = filter_expr? {
596609
builder = builder.filter(filter)?;
597610
}
598611

datafusion/sql/src/unparser/rewrite.rs

+38-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ use std::{
2020
sync::Arc,
2121
};
2222

23+
use arrow_schema::SchemaRef;
2324
use datafusion_common::{
24-
tree_node::{Transformed, TransformedResult, TreeNode},
25-
Result,
25+
tree_node::{Transformed, TransformedResult, TreeNode, TreeNodeRewriter},
26+
Column, Result, TableReference,
2627
};
2728
use datafusion_expr::{expr::Alias, tree_node::transform_sort_vec};
2829
use datafusion_expr::{Expr, LogicalPlan, Projection, Sort, SortExpr};
@@ -300,3 +301,38 @@ fn find_projection(logical_plan: &LogicalPlan) -> Option<&Projection> {
300301
_ => None,
301302
}
302303
}
304+
/// A `TreeNodeRewriter` implementation that rewrites `Expr::Column` expressions by
305+
/// replacing the column's name with an alias if the column exists in the provided schema.
306+
///
307+
/// This is typically used to apply table aliases in query plans, ensuring that
308+
/// the column references in the expressions use the correct table alias.
309+
///
310+
/// # Fields
311+
///
312+
/// * `table_schema`: The schema (`SchemaRef`) representing the table structure
313+
/// from which the columns are referenced. This is used to look up columns by their names.
314+
/// * `alias_name`: The alias (`TableReference`) that will replace the table name
315+
/// in the column references when applicable.
316+
pub struct TableAliasRewriter {
317+
pub table_schema: SchemaRef,
318+
pub alias_name: TableReference,
319+
}
320+
321+
impl TreeNodeRewriter for TableAliasRewriter {
322+
type Node = Expr;
323+
324+
fn f_down(&mut self, expr: Expr) -> Result<Transformed<Expr>> {
325+
match expr {
326+
Expr::Column(column) => {
327+
if let Ok(field) = self.table_schema.field_with_name(&column.name) {
328+
let new_column =
329+
Column::new(Some(self.alias_name.clone()), field.name().clone());
330+
Ok(Transformed::yes(Expr::Column(new_column)))
331+
} else {
332+
Ok(Transformed::no(Expr::Column(column)))
333+
}
334+
}
335+
_ => Ok(Transformed::no(expr)),
336+
}
337+
}
338+
}

datafusion/sql/tests/cases/plan_to_sql.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -705,19 +705,19 @@ fn test_table_scan_pushdown() -> Result<()> {
705705
"SELECT * FROM t1 WHERE ((t1.id > 1) AND (t1.age < 2))"
706706
);
707707

708-
// TODO: support filters for table scan with alias. Enable this test after #12368 issue is fixed
709-
// see the issue: https://github.com/apache/datafusion/issues/12368
710-
// let table_scan_with_filter_alias = table_scan_with_filters(
711-
// Some("t1"),
712-
// &schema,
713-
// None,
714-
// vec![col("id").gt(col("age"))],
715-
// )?.alias("ta")?.build()?;
716-
// let table_scan_with_filter_alias = plan_to_sql(&table_scan_with_filter_alias)?;
717-
// assert_eq!(
718-
// format!("{}", table_scan_with_filter_alias),
719-
// "SELECT * FROM t1 AS ta WHERE (ta.id > ta.age)"
720-
// );
708+
let table_scan_with_filter_alias = table_scan_with_filters(
709+
Some("t1"),
710+
&schema,
711+
None,
712+
vec![col("id").gt(col("age"))],
713+
)?
714+
.alias("ta")?
715+
.build()?;
716+
let table_scan_with_filter_alias = plan_to_sql(&table_scan_with_filter_alias)?;
717+
assert_eq!(
718+
format!("{}", table_scan_with_filter_alias),
719+
"SELECT * FROM t1 AS ta WHERE (ta.id > ta.age)"
720+
);
721721

722722
let table_scan_with_projection_and_filter = table_scan_with_filters(
723723
Some("t1"),

0 commit comments

Comments
 (0)