Skip to content

Commit 9e37283

Browse files
authored
fix(exasol)!: case-insensitive alias matching for LOCAL. prefix (#7631)
* add tests with mixed casing * fix group by local fix using the normalization strategy * fix mypy checks * remove unneeded copy
1 parent c6615a9 commit 9e37283

2 files changed

Lines changed: 29 additions & 18 deletions

File tree

sqlglot/generators/exasol.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import typing as t
44

5-
from sqlglot import exp, generator, transforms
5+
from sqlglot import exp, generator
66
from sqlglot.dialects.dialect import (
77
DATE_ADD_OR_SUB,
8+
Dialect,
89
groupconcat_sql,
910
no_last_day_sql,
1011
rename_func,
@@ -35,10 +36,13 @@ def _date_diff_sql(self: ExasolGenerator, expression: exp.DateDiff | exp.TsOrDsD
3536

3637

3738
# https://docs.exasol.com/db/latest/sql/select.htm#:~:text=If%20you%20have,local.x%3E10
38-
def _add_local_prefix_for_aliases(expression: exp.Expr) -> exp.Expr:
39+
def _add_local_prefix_for_aliases(expression: exp.Expr, dialect: Dialect) -> exp.Expr:
40+
def _key(ident: exp.Identifier) -> str:
41+
return dialect.normalize_identifier(ident.copy()).this
42+
3943
if isinstance(expression, exp.Select):
40-
aliases: dict[str, bool] = {
41-
alias.name: bool(alias.args.get("quoted"))
44+
aliases: dict[str, exp.Identifier] = {
45+
_key(alias): alias
4246
for sel in expression.selects
4347
if isinstance(sel, exp.Alias) and (alias := sel.args.get("alias"))
4448
}
@@ -53,11 +57,12 @@ def _add_local_prefix_for_aliases(expression: exp.Expr) -> exp.Expr:
5357
):
5458
table_ident.replace(exp.to_identifier(table_ident.name.upper(), quoted=True))
5559

56-
def prefix_local(node: exp.Expr, visible_aliases: dict[str, bool]) -> exp.Expr:
60+
def prefix_local(node: exp.Expr, visible_aliases: dict[str, exp.Identifier]) -> exp.Expr:
5761
if isinstance(node, exp.Column) and not node.table:
58-
if node.name in visible_aliases:
62+
alias = visible_aliases.get(_key(node.this))
63+
if alias is not None:
5964
return exp.Column(
60-
this=exp.to_identifier(node.name, quoted=visible_aliases[node.name]),
65+
this=alias,
6166
table=exp.to_identifier("LOCAL", quoted=False),
6267
)
6368
return node
@@ -66,16 +71,15 @@ def prefix_local(node: exp.Expr, visible_aliases: dict[str, bool]) -> exp.Expr:
6671
if arg := expression.args.get(key):
6772
expression.set(key, arg.transform(lambda node: prefix_local(node, aliases)))
6873

69-
seen_aliases: dict[str, bool] = {}
74+
seen_aliases: dict[str, exp.Identifier] = {}
7075
new_selects: list[exp.Expr] = []
7176
for sel in expression.selects:
7277
if isinstance(sel, exp.Alias):
7378
inner = sel.this.transform(lambda node: prefix_local(node, seen_aliases))
7479
sel.set("this", inner)
7580

76-
alias_node = sel.args.get("alias")
77-
78-
seen_aliases[sel.alias] = bool(alias_node and getattr(alias_node, "quoted", False))
81+
if alias_node := sel.args.get("alias"):
82+
seen_aliases[_key(alias_node)] = alias_node
7983
new_selects.append(sel)
8084
else:
8185
new_selects.append(sel.transform(lambda node: prefix_local(node, seen_aliases)))
@@ -303,6 +307,12 @@ class ExasolGenerator(generator.Generator):
303307
exp.DType.TIMESTAMPNTZ: "TIMESTAMP",
304308
}
305309

310+
def select_sql(self, expression: exp.Select) -> str:
311+
processed = _qualify_unscoped_star(expression)
312+
processed = _add_local_prefix_for_aliases(processed, self.dialect)
313+
processed = _group_by_all(processed)
314+
return super().select_sql(t.cast(exp.Select, processed))
315+
306316
def datatype_sql(self, expression: exp.DataType) -> str:
307317
# Exasol supports a fixed default precision of 3 for TIMESTAMP WITH LOCAL TIME ZONE
308318
# and does not allow specifying a different custom precision
@@ -390,13 +400,6 @@ def datatype_sql(self, expression: exp.DataType) -> str:
390400
exp.MD5Digest: rename_func("HASHTYPE_MD5"),
391401
# https://docs.exasol.com/db/latest/sql/create_view.htm
392402
exp.CommentColumnConstraint: lambda self, e: f"COMMENT IS {self.sql(e, 'this')}",
393-
exp.Select: transforms.preprocess(
394-
[
395-
_qualify_unscoped_star,
396-
_add_local_prefix_for_aliases,
397-
_group_by_all,
398-
]
399-
),
400403
exp.SubstringIndex: _substring_index_sql,
401404
exp.WeekOfYear: rename_func("WEEK"),
402405
# https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/to_date.htm

tests/dialects/test_exasol.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,14 @@ def test_local_prefix_for_alias(self):
820820
self.validate_identity(
821821
'SELECT a_year AS a_year FROM "LOCAL" GROUP BY "LOCAL".a_year',
822822
)
823+
self.validate_identity(
824+
"SELECT YEAR(a_date) AS A_YEAR FROM my_table WHERE a_year > 2020",
825+
"SELECT YEAR(a_date) AS A_YEAR FROM my_table WHERE LOCAL.A_YEAR > 2020",
826+
)
827+
self.validate_identity(
828+
"SELECT SUM(amount) AS Total FROM my_table HAVING TOTAL > 10000",
829+
"SELECT SUM(amount) AS Total FROM my_table HAVING LOCAL.Total > 10000",
830+
)
823831

824832
test_cases = [
825833
(

0 commit comments

Comments
 (0)