@@ -24,7 +24,7 @@ use std::iter::once;
2424use std:: sync:: Arc ;
2525
2626use crate :: dml:: CopyTo ;
27- use crate :: expr:: { Alias , Sort as SortExpr } ;
27+ use crate :: expr:: { Alias , PlannedReplaceSelectItem , Sort as SortExpr } ;
2828use crate :: expr_rewriter:: {
2929 coerce_plan_expr_for_schema, normalize_col,
3030 normalize_col_with_schemas_and_ambiguity_check, normalize_cols, normalize_sorts,
@@ -36,9 +36,11 @@ use crate::logical_plan::{
3636 Projection , Repartition , Sort , SubqueryAlias , TableScan , Union , Unnest , Values ,
3737 Window ,
3838} ;
39+ use crate :: select_expr:: SelectExpr ;
3940use crate :: utils:: {
40- can_hash, columnize_expr, compare_sort_expr, expr_to_columns,
41- find_valid_equijoin_key_pair, group_window_expr_by_sort_keys,
41+ can_hash, columnize_expr, compare_sort_expr, expand_qualified_wildcard,
42+ expand_wildcard, expr_to_columns, find_valid_equijoin_key_pair,
43+ group_window_expr_by_sort_keys,
4244} ;
4345use crate :: {
4446 and, binary_expr, lit, DmlStatement , Expr , ExprSchemable , Operator , RecursiveQuery ,
@@ -520,10 +522,11 @@ impl LogicalPlanBuilder {
520522 }
521523 Ok ( plan)
522524 }
525+
523526 /// Apply a projection without alias.
524527 pub fn project (
525528 self ,
526- expr : impl IntoIterator < Item = impl Into < Expr > > ,
529+ expr : impl IntoIterator < Item = impl Into < SelectExpr > > ,
527530 ) -> Result < Self > {
528531 project ( Arc :: unwrap_or_clone ( self . plan ) , expr) . map ( Self :: new)
529532 }
@@ -532,7 +535,7 @@ impl LogicalPlanBuilder {
532535 /// (true to validate, false to not validate)
533536 pub fn project_with_validation (
534537 self ,
535- expr : Vec < ( impl Into < Expr > , bool ) > ,
538+ expr : Vec < ( impl Into < SelectExpr > , bool ) > ,
536539 ) -> Result < Self > {
537540 project_with_validation ( Arc :: unwrap_or_clone ( self . plan ) , expr) . map ( Self :: new)
538541 }
@@ -1657,7 +1660,7 @@ pub fn union_by_name(
16571660/// * An invalid expression is used (e.g. a `sort` expression)
16581661pub fn project (
16591662 plan : LogicalPlan ,
1660- expr : impl IntoIterator < Item = impl Into < Expr > > ,
1663+ expr : impl IntoIterator < Item = impl Into < SelectExpr > > ,
16611664) -> Result < LogicalPlan > {
16621665 project_with_validation ( plan, expr. into_iter ( ) . map ( |e| ( e, true ) ) )
16631666}
@@ -1671,15 +1674,54 @@ pub fn project(
16711674/// * An invalid expression is used (e.g. a `sort` expression)
16721675fn project_with_validation (
16731676 plan : LogicalPlan ,
1674- expr : impl IntoIterator < Item = ( impl Into < Expr > , bool ) > ,
1677+ expr : impl IntoIterator < Item = ( impl Into < SelectExpr > , bool ) > ,
16751678) -> Result < LogicalPlan > {
16761679 let mut projected_expr = vec ! [ ] ;
16771680 for ( e, validate) in expr {
16781681 let e = e. into ( ) ;
16791682 match e {
1680- #[ expect( deprecated) ]
1681- Expr :: Wildcard { .. } => projected_expr. push ( e) ,
1682- _ => {
1683+ SelectExpr :: Wildcard ( opt) => {
1684+ let expanded = expand_wildcard ( plan. schema ( ) , & plan, Some ( & opt) ) ?;
1685+
1686+ // If there is a REPLACE statement, replace that column with the given
1687+ // replace expression. Column name remains the same.
1688+ let expanded = if let Some ( replace) = opt. replace {
1689+ replace_columns ( expanded, & replace) ?
1690+ } else {
1691+ expanded
1692+ } ;
1693+
1694+ for e in expanded {
1695+ if validate {
1696+ projected_expr
1697+ . push ( columnize_expr ( normalize_col ( e, & plan) ?, & plan) ?)
1698+ } else {
1699+ projected_expr. push ( e)
1700+ }
1701+ }
1702+ }
1703+ SelectExpr :: QualifiedWildcard ( table_ref, opt) => {
1704+ let expanded =
1705+ expand_qualified_wildcard ( & table_ref, plan. schema ( ) , Some ( & opt) ) ?;
1706+
1707+ // If there is a REPLACE statement, replace that column with the given
1708+ // replace expression. Column name remains the same.
1709+ let expanded = if let Some ( replace) = opt. replace {
1710+ replace_columns ( expanded, & replace) ?
1711+ } else {
1712+ expanded
1713+ } ;
1714+
1715+ for e in expanded {
1716+ if validate {
1717+ projected_expr
1718+ . push ( columnize_expr ( normalize_col ( e, & plan) ?, & plan) ?)
1719+ } else {
1720+ projected_expr. push ( e)
1721+ }
1722+ }
1723+ }
1724+ SelectExpr :: Expression ( e) => {
16831725 if validate {
16841726 projected_expr. push ( columnize_expr ( normalize_col ( e, & plan) ?, & plan) ?)
16851727 } else {
@@ -1693,6 +1735,29 @@ fn project_with_validation(
16931735 Projection :: try_new ( projected_expr, Arc :: new ( plan) ) . map ( LogicalPlan :: Projection )
16941736}
16951737
1738+ /// If there is a REPLACE statement in the projected expression in the form of
1739+ /// "REPLACE (some_column_within_an_expr AS some_column)", this function replaces
1740+ /// that column with the given replace expression. Column name remains the same.
1741+ /// Multiple REPLACEs are also possible with comma separations.
1742+ fn replace_columns (
1743+ mut exprs : Vec < Expr > ,
1744+ replace : & PlannedReplaceSelectItem ,
1745+ ) -> Result < Vec < Expr > > {
1746+ for expr in exprs. iter_mut ( ) {
1747+ if let Expr :: Column ( Column { name, .. } ) = expr {
1748+ if let Some ( ( _, new_expr) ) = replace
1749+ . items ( )
1750+ . iter ( )
1751+ . zip ( replace. expressions ( ) . iter ( ) )
1752+ . find ( |( item, _) | item. column_name . value == * name)
1753+ {
1754+ * expr = new_expr. clone ( ) . alias ( name. clone ( ) )
1755+ }
1756+ }
1757+ }
1758+ Ok ( exprs)
1759+ }
1760+
16961761/// Create a SubqueryAlias to wrap a LogicalPlan.
16971762pub fn subquery_alias (
16981763 plan : LogicalPlan ,
@@ -1811,7 +1876,7 @@ pub fn wrap_projection_for_join_if_necessary(
18111876 projection. extend ( join_key_items) ;
18121877
18131878 LogicalPlanBuilder :: from ( input)
1814- . project ( projection) ?
1879+ . project ( projection. into_iter ( ) . map ( SelectExpr :: from ) ) ?
18151880 . build ( ) ?
18161881 } else {
18171882 input
0 commit comments