Skip to content

Commit 15ca924

Browse files
authored
fix(optimizer): Fix expansion of SELECT * REPLACE, RENAME (#3742)
* fix(optimizer): Fix expansion of SELECT * REPLACE, RENAME * Refactor variable names * PR Feedback 1 * Removing added empty line
1 parent 89fc63c commit 15ca924

File tree

3 files changed

+56
-9
lines changed

3 files changed

+56
-9
lines changed

sqlglot/optimizer/qualify_columns.py

+28-6
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,9 @@ def _expand_stars(
513513

514514
new_selections = []
515515
except_columns: t.Dict[int, t.Set[str]] = {}
516-
replace_columns: t.Dict[int, t.Dict[str, str]] = {}
516+
replace_columns: t.Dict[int, t.Dict[str, exp.Alias]] = {}
517+
rename_columns: t.Dict[int, t.Dict[str, str]] = {}
518+
517519
coalesced_columns = set()
518520
dialect = resolver.schema.dialect
519521

@@ -548,11 +550,13 @@ def _expand_stars(
548550
tables.extend(scope.selected_sources)
549551
_add_except_columns(expression, tables, except_columns)
550552
_add_replace_columns(expression, tables, replace_columns)
553+
_add_rename_columns(expression, tables, rename_columns)
551554
elif expression.is_star:
552555
if not isinstance(expression, exp.Dot):
553556
tables.append(expression.table)
554557
_add_except_columns(expression.this, tables, except_columns)
555558
_add_replace_columns(expression.this, tables, replace_columns)
559+
_add_rename_columns(expression.this, tables, rename_columns)
556560
elif is_bigquery:
557561
struct_fields = _expand_struct_stars(expression)
558562
if struct_fields:
@@ -578,6 +582,8 @@ def _expand_stars(
578582

579583
table_id = id(table)
580584
columns_to_exclude = except_columns.get(table_id) or set()
585+
renamed_columns = rename_columns.get(table_id, {})
586+
replaced_columns = replace_columns.get(table_id, {})
581587

582588
if pivot:
583589
if pivot_output_columns and pivot_exclude_columns:
@@ -606,10 +612,12 @@ def _expand_stars(
606612
alias(exp.func("coalesce", *coalesce_args), alias=name, copy=False)
607613
)
608614
else:
609-
alias_ = replace_columns.get(table_id, {}).get(name, name)
610-
column = exp.column(name, table=table)
615+
alias_ = renamed_columns.get(name, name)
616+
selection_expr = replaced_columns.get(name) or exp.column(name, table=table)
611617
new_selections.append(
612-
alias(column, alias_, copy=False) if alias_ != name else column
618+
alias(selection_expr, alias_, copy=False)
619+
if alias_ != name
620+
else selection_expr
613621
)
614622

615623
# Ensures we don't overwrite the initial selections with an empty list
@@ -631,15 +639,29 @@ def _add_except_columns(
631639
except_columns[id(table)] = columns
632640

633641

642+
def _add_rename_columns(
643+
expression: exp.Expression, tables, rename_columns: t.Dict[int, t.Dict[str, str]]
644+
) -> None:
645+
rename = expression.args.get("rename")
646+
647+
if not rename:
648+
return
649+
650+
columns = {e.this.name: e.alias for e in rename}
651+
652+
for table in tables:
653+
rename_columns[id(table)] = columns
654+
655+
634656
def _add_replace_columns(
635-
expression: exp.Expression, tables, replace_columns: t.Dict[int, t.Dict[str, str]]
657+
expression: exp.Expression, tables, replace_columns: t.Dict[int, t.Dict[str, exp.Alias]]
636658
) -> None:
637659
replace = expression.args.get("replace")
638660

639661
if not replace:
640662
return
641663

642-
columns = {e.this.name: e.alias for e in replace}
664+
columns = {e.alias: e for e in replace}
643665

644666
for table in tables:
645667
replace_columns[id(table)] = columns

sqlglot/optimizer/scope.py

+1
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ def columns(self):
284284
or column.name not in named_selects
285285
)
286286
)
287+
or (isinstance(ancestor, exp.Star) and not column.arg_key == "except")
287288
):
288289
self._columns.append(column)
289290

tests/fixtures/optimizer/qualify_columns.sql

+27-3
Original file line numberDiff line numberDiff line change
@@ -385,14 +385,14 @@ WITH player AS (SELECT player.name, player.asset.info FROM players) SELECT * FRO
385385
WITH player AS (SELECT players.player.name AS name, players.player.asset.info AS info FROM players AS players) SELECT player.name AS name, player.info AS info FROM player AS player;
386386

387387
--------------------------------------
388-
-- Except and Replace
388+
-- Except, Replace, Rename
389389
--------------------------------------
390390
# execute: false
391-
SELECT * REPLACE(a AS d) FROM x;
391+
SELECT * RENAME(a AS d) FROM x;
392392
SELECT x.a AS d, x.b AS b FROM x AS x;
393393

394394
# execute: false
395-
SELECT * EXCEPT(b) REPLACE(a AS d) FROM x;
395+
SELECT * EXCEPT(b) RENAME(a AS d) FROM x;
396396
SELECT x.a AS d FROM x AS x;
397397

398398
SELECT x.* EXCEPT(a), y.* FROM x, y;
@@ -416,6 +416,30 @@ SELECT x.a AS a, x.b AS b, y.b AS b FROM x AS x LEFT JOIN x AS y ON x.a = y.a;
416416
SELECT COALESCE(CAST(t1.a AS VARCHAR), '') AS a, t2.* EXCEPT (a) FROM x AS t1, x AS t2;
417417
SELECT COALESCE(CAST(t1.a AS VARCHAR), '') AS a, t2.b AS b FROM x AS t1, x AS t2;
418418

419+
# execute: false
420+
SELECT * REPLACE(2 AS a) FROM x;
421+
SELECT 2 AS a, x.b AS b FROM x AS x;
422+
423+
# execute: false
424+
SELECT * EXCEPT (a, b) REPLACE (a AS a) FROM x;
425+
SELECT * EXCEPT (a, b) REPLACE (x.a AS a) FROM x AS x;
426+
427+
# execute: false
428+
SELECT * REPLACE(COALESCE(b, a) AS a, a as b) FROM x;
429+
SELECT COALESCE(x.b, x.a) AS a, x.a AS b FROM x AS x;
430+
431+
# execute: false
432+
SELECT * REPLACE(1 AS a) RENAME(b as alias_b) FROM x;
433+
SELECT 1 AS a, x.b AS alias_b FROM x AS x;
434+
435+
# execute: false
436+
SELECT * EXCEPT(a) REPLACE(COALESCE(a, b) AS b) RENAME(b AS new_b) FROM x;
437+
SELECT COALESCE(x.a, x.b) AS new_b FROM x AS x;
438+
439+
# execute: false
440+
SELECT * REPLACE(1 AS a, a AS b) RENAME(b AS new_b) FROM x;
441+
SELECT 1 AS a, x.a AS new_b FROM x AS x;
442+
419443
--------------------------------------
420444
-- Using
421445
--------------------------------------

0 commit comments

Comments
 (0)