From 3448f62e4185370329bb2279341a1ca36eb94ed0 Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Sat, 12 Sep 2026 11:05:36 -0500 Subject: [PATCH 1/2] Fix SELECT tokenization before parenthesis --- sqlparse/lexer.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sqlparse/lexer.py b/sqlparse/lexer.py index 5d4a5f97..738214da 100644 --- a/sqlparse/lexer.py +++ b/sqlparse/lexer.py @@ -154,6 +154,11 @@ def get_tokens(self, text, encoding=None): if not m: continue + elif action is tokens.Name and m.group().upper() == 'SELECT': + # The function-name lookahead runs before keyword lookup. + # SELECT followed immediately by "(" is still a DML + # keyword, not a function name (see issue #775). + yield tokens.Keyword.DML, m.group() elif isinstance(action, tokens._TokenType): yield action, m.group() elif action is keywords.PROCESS_AS_KEYWORD: From 8ca735e53a81260b68eafab829510687650141d7 Mon Sep 17 00:00:00 2001 From: Rami Abdelrazzaq Date: Sat, 12 Sep 2026 11:05:45 -0500 Subject: [PATCH 2/2] Add regression test for SELECT before parenthesis --- tests/test_issue775.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/test_issue775.py diff --git a/tests/test_issue775.py b/tests/test_issue775.py new file mode 100644 index 00000000..384fe599 --- /dev/null +++ b/tests/test_issue775.py @@ -0,0 +1,13 @@ +import sqlparse + +from sqlparse import sql +from sqlparse import tokens as T + + +def test_select_followed_by_parenthesis_without_space(): + statement = sqlparse.parse('select(select 1)')[0] + + assert statement.get_type() == 'SELECT' + assert statement.tokens[0].ttype is T.Keyword.DML + assert statement.tokens[0].value == 'select' + assert isinstance(statement.tokens[1], sql.Parenthesis)