@@ -2375,6 +2375,29 @@ pub fn projection_schema(input: &LogicalPlan, exprs: &[Expr]) -> Result<Arc<DFSc
23752375 exprs, input,
23762376 ) ?) ?;
23772377
2378+ // Propagate ambiguous names from the input for any column passed through
2379+ // unchanged. This prevents a `SELECT * FROM (...) AS alias` wrapper from
2380+ // silently dropping the ambiguity marker set by an inner JOIN or alias.
2381+ let input_ambiguous = input. schema ( ) . ambiguous_names ( ) ;
2382+ if !input_ambiguous. is_empty ( ) {
2383+ // A column is a pass-through when it is `Expr::Column(c)` and `c.name`
2384+ // appears in the input's ambiguous set.
2385+ let inherited: HashSet < String > = exprs
2386+ . iter ( )
2387+ . filter_map ( |e| {
2388+ if let Expr :: Column ( col) = e {
2389+ if input_ambiguous. contains ( & col. name ) {
2390+ return Some ( col. name . clone ( ) ) ;
2391+ }
2392+ }
2393+ None
2394+ } )
2395+ . collect ( ) ;
2396+ if !inherited. is_empty ( ) {
2397+ return Ok ( Arc :: new ( schema. with_ambiguous_names ( inherited) ) ) ;
2398+ }
2399+ }
2400+
23782401 Ok ( Arc :: new ( schema) )
23792402}
23802403
@@ -2406,23 +2429,37 @@ impl SubqueryAlias {
24062429 let aliases = unique_field_aliases ( plan. schema ( ) . fields ( ) ) ;
24072430 let is_projection_needed = aliases. iter ( ) . any ( Option :: is_some) ;
24082431
2409- // Collect the set of unqualified field names that are ambiguous in this
2410- // subquery alias's output schema. A name is ambiguous when two or more
2411- // input columns share the same unqualified name (they come, say, from
2412- // different sides of a JOIN). `unique_field_aliases` renames the
2413- // duplicates to keep the Arrow schema free of duplicates, but we still
2414- // need to reject unqualified references to those names from outer
2415- // queries.
2432+ // Collect unqualified field names that are ambiguous in this alias's
2433+ // output schema. `unique_field_aliases` renames duplicates (e.g. to
2434+ // "id:1") to keep Arrow happy, but outer queries must still be
2435+ // prevented from referencing those names without qualification.
2436+ // We also inherit names already marked ambiguous by the input schema
2437+ // so nested `SELECT * FROM (...) AS sN` wrappers don't lose the marker.
24162438 let ambiguous_names: HashSet < String > = {
24172439 let mut name_counts: HashMap < & str , usize > = HashMap :: new ( ) ;
24182440 for field in plan. schema ( ) . fields ( ) {
24192441 * name_counts. entry ( field. name ( ) . as_str ( ) ) . or_insert ( 0 ) += 1 ;
24202442 }
2421- name_counts
2443+ let mut names : HashSet < String > = name_counts
24222444 . into_iter ( )
24232445 . filter ( |& ( _, count) | count >= 2 )
24242446 . map ( |( name, _) | name. to_string ( ) )
2425- . collect ( )
2447+ . collect ( ) ;
2448+
2449+ // Inherit names still visible in the output (the first occurrence
2450+ // of a renamed duplicate like "id:1" still keeps the name "id").
2451+ let output_field_names: HashSet < & str > = plan
2452+ . schema ( )
2453+ . fields ( )
2454+ . iter ( )
2455+ . map ( |f| f. name ( ) . as_str ( ) )
2456+ . collect ( ) ;
2457+ for inherited in plan. schema ( ) . ambiguous_names ( ) {
2458+ if output_field_names. contains ( inherited. as_str ( ) ) {
2459+ names. insert ( inherited. clone ( ) ) ;
2460+ }
2461+ }
2462+ names
24262463 } ;
24272464
24282465 // Insert a projection node, if needed, to make sure aliases are applied.
0 commit comments