Skip to content

Commit ea86f64

Browse files
authored
Merge branch 'master' into dependabot/github_actions/actions/checkout-7
2 parents c6b13cf + a6094e9 commit ea86f64

5 files changed

Lines changed: 43 additions & 19 deletions

File tree

.github/workflows/python-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
python-version: ${{ matrix.python-version }}
3333

3434
- name: Load ~/.cache directory and Poetry .venv
35-
uses: actions/cache@v5
35+
uses: actions/cache@v6
3636
with:
3737
path: |
3838
~/.cache

sql_metadata/nested_resolver.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def __init__(
207207
def extract_cte_names(
208208
self,
209209
cte_name_map: dict[str, str],
210-
) -> list[str]:
210+
) -> UniqueList:
211211
"""Extract CTE names from the AST.
212212
213213
Called by :attr:`Parser.with_names`.
@@ -247,7 +247,7 @@ def extract_cte_bodies(
247247
@staticmethod
248248
def extract_subqueries(
249249
ast: exp.Expression,
250-
) -> tuple[list[str], dict[str, str]]:
250+
) -> tuple[UniqueList, dict[str, str]]:
251251
"""Extract subquery names and bodies in a single post-order walk.
252252
253253
Aliased subqueries keep their alias as the name. Unaliased
@@ -262,7 +262,7 @@ def extract_subqueries(
262262
:returns: ``(names, bodies)`` where *names* is ordered innermost-first,
263263
e.g. ``(["subquery_1", "sub"], {...})``.
264264
"""
265-
names: list[str] = UniqueList()
265+
names = UniqueList()
266266
bodies: dict[str, str] = {}
267267
NestedResolver._walk_subqueries(ast, names, bodies, 0)
268268
return names, bodies

sql_metadata/parser.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ def __init__(self, sql: str = "", disable_logging: bool = False) -> None:
6363
self._columns_aliases_dict: dict[str, UniqueList] = {}
6464
self._output_columns: list[str] = []
6565

66-
self._tables: list[str] | None = None
66+
self._tables: UniqueList | None = None
6767
self._table_aliases: dict[str, str] | None = None
6868

69-
self._with_names: list[str] | None = None
69+
self._with_names: UniqueList | None = None
7070
self._with_queries: dict[str, str] | None = None
7171
self._subqueries: dict[str, str] | None = None
72-
self._subqueries_names: list[str] | None = None
72+
self._subqueries_names: UniqueList | None = None
7373

7474
self._limit_and_offset: tuple[int, int] | None = None
7575

@@ -168,7 +168,7 @@ def tokens(self) -> list[str]:
168168
return self._tokens
169169

170170
@property
171-
def columns(self) -> list[str]:
171+
def columns(self) -> UniqueList:
172172
"""Return the list of column names referenced in the query.
173173
174174
Walks the sqlglot AST via :class:`ColumnExtractor` in a single DFS
@@ -177,7 +177,7 @@ def columns(self) -> list[str]:
177177
SQL), falls back to a regex extraction of ``INTO … (col1, col2)``
178178
column lists.
179179
180-
:rtype: list[str]
180+
:rtype: UniqueList
181181
"""
182182
if self._columns_extracted:
183183
return self._columns
@@ -276,10 +276,10 @@ def columns_aliases_dict(self) -> dict[str, UniqueList]:
276276
return self._columns_aliases_dict
277277

278278
@property
279-
def columns_aliases_names(self) -> list[str]:
279+
def columns_aliases_names(self) -> UniqueList:
280280
"""Return the names of all column aliases used in the query.
281281
282-
:rtype: list[str]
282+
:rtype: UniqueList
283283
"""
284284
if not self._columns_extracted:
285285
_ = self.columns
@@ -299,14 +299,14 @@ def output_columns(self) -> list[str]:
299299
return self._output_columns
300300

301301
@property
302-
def tables(self) -> list[str]:
302+
def tables(self) -> UniqueList:
303303
"""Return the list of table names referenced in the query.
304304
305305
Tables are extracted from the AST by :class:`TableExtractor`,
306306
sorted by their position in the SQL text, and filtered to exclude
307307
CTE names (which appear in :attr:`with_names` instead).
308308
309-
:rtype: list[str]
309+
:rtype: UniqueList
310310
"""
311311
if self._tables is not None:
312312
return self._tables
@@ -339,10 +339,10 @@ def tables_aliases(self) -> dict[str, str]:
339339
return self._table_aliases
340340

341341
@property
342-
def with_names(self) -> list[str]:
342+
def with_names(self) -> UniqueList:
343343
"""Return the CTE (Common Table Expression) names from the query.
344344
345-
:rtype: list[str]
345+
:rtype: UniqueList
346346
"""
347347
if self._with_names is not None:
348348
return self._with_names
@@ -387,13 +387,13 @@ def subqueries(self) -> dict[str, str]:
387387
return self._subqueries
388388

389389
@property
390-
def subqueries_names(self) -> list[str]:
390+
def subqueries_names(self) -> UniqueList:
391391
"""Return the names of all subqueries (innermost first).
392392
393393
Aliased subqueries use their alias; unaliased ones get
394394
auto-generated names (``subquery_1``, ``subquery_2``, …).
395395
396-
:rtype: list[str]
396+
:rtype: UniqueList
397397
"""
398398
if self._subqueries_names is not None:
399399
return self._subqueries_names
@@ -482,7 +482,9 @@ def values_dict(self) -> dict[str, Any] | None:
482482
is_multi = values and isinstance(values[0], list)
483483
first_row = values[0] if is_multi else values
484484
if not columns:
485-
columns = [f"column_{ind + 1}" for ind in range(len(first_row))]
485+
columns = UniqueList(
486+
f"column_{ind + 1}" for ind in range(len(first_row))
487+
)
486488

487489
if is_multi:
488490
self._values_dict = {

sql_metadata/table_extractor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def __init__(
141141
# Public API
142142
# -------------------------------------------------------------------
143143

144-
def extract(self) -> list[str]:
144+
def extract(self) -> UniqueList:
145145
"""Extract table names, excluding CTE definitions.
146146
147147
For ``CREATE TABLE`` statements, the target table is always placed

test/test_getting_tables.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,28 @@ def test_unmatched_parentheses_graceful():
948948
assert isinstance(tables, list)
949949

950950

951+
def test_mysql_view_definition_with_bracketed_join():
952+
# solved: https://github.com/macbre/sql-metadata/issues/253
953+
# MySQL view definitions wrap the FROM source in parentheses and
954+
# double-bracket the ON clause; used to crash with AttributeError.
955+
query = (
956+
"select `t`.`symbol` AS `symbol` "
957+
"from (`stock`.`top_momentum_sector` `s` "
958+
"join `stock`.`daily_companies` `t` "
959+
"on((`s`.`symbol` = `t`.`symbol`)))"
960+
)
961+
parser = Parser(query)
962+
assert parser.tables == ["stock.top_momentum_sector", "stock.daily_companies"]
963+
assert parser.columns == [
964+
"stock.daily_companies.symbol",
965+
"stock.top_momentum_sector.symbol",
966+
]
967+
assert parser.tables_aliases == {
968+
"s": "stock.top_momentum_sector",
969+
"t": "stock.daily_companies",
970+
}
971+
972+
951973
def test_degraded_parse_falls_through_to_last_dialect():
952974
"""SELECT UNIQUE triggers multi-dialect retry."""
953975
p = Parser("SELECT UNIQUE col FROM t")

0 commit comments

Comments
 (0)