From 753367cf66d0b9a9028610b6647738d68285ddc5 Mon Sep 17 00:00:00 2001 From: Illes Solt Date: Wed, 30 Sep 2026 10:05:27 +0200 Subject: [PATCH 1/2] Add test for quoted anonymous function names Reproduces #688: a quoted function name such as "timezone"(...) crashes column extraction with AttributeError. Co-Authored-By: Claude Opus 5.5 --- test/test_getting_columns.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_getting_columns.py b/test/test_getting_columns.py index fb0d59ae..00594b98 100644 --- a/test/test_getting_columns.py +++ b/test/test_getting_columns.py @@ -708,6 +708,22 @@ def test_dateadd_unit_not_column(): assert parser.columns_dict == {"select": ["DateReleased"]} +def test_quoted_anonymous_function_name(): + # solved: https://github.com/macbre/sql-metadata/issues/688 + # a quoted function name is an Identifier, not a str, in sqlglot's AST + query = """ + SELECT date_trunc('DAY', "timezone"('UTC', tbl.data_timestamp)) + FROM tbl + """ + parser = Parser(query) + assert parser.tables == ["tbl"] + assert parser.columns == ["tbl.data_timestamp"] + + # date-part unit is still skipped when the function name is quoted + parser = Parser('SELECT "dateadd"(dd, 30, DateReleased) FROM test') + assert parser.tables == ["test"] + assert parser.columns == ["DateReleased"] + def test_backtick_column_with_operation(): # solved: https://github.com/macbre/sql-metadata/issues/448 query = "SELECT `col1 with space` / `col2_anything` FROM table1" From 2b3180704b8e5af870bb156c8b9ce1fec9ef4692 Mon Sep 17 00:00:00 2001 From: Illes Solt Date: Wed, 30 Sep 2026 10:05:57 +0200 Subject: [PATCH 2/2] Fix crash on quoted anonymous function names exp.Anonymous.this is an Identifier when the function name is quoted, so calling .lower() on it raised AttributeError. Use .name, which sqlglot normalises to a str for both forms. Resolves #688. Co-Authored-By: Claude Opus 5.5 --- sql_metadata/column_extractor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql_metadata/column_extractor.py b/sql_metadata/column_extractor.py index 1efdb8d8..13aa43cb 100644 --- a/sql_metadata/column_extractor.py +++ b/sql_metadata/column_extractor.py @@ -135,7 +135,7 @@ def _is_date_part_unit(node: exp.Column) -> bool: parent = node.parent if ( isinstance(parent, exp.Anonymous) - and parent.this.lower() in _DATE_PART_FUNCTIONS + and parent.name.lower() in _DATE_PART_FUNCTIONS ): exprs = parent.expressions return len(exprs) > 0 and exprs[0] is node