From 19b08a8b50c46c0cb10105759b95bc359154ed98 Mon Sep 17 00:00:00 2001 From: adenzhou1350 <209601943+adenzhou1350@users.noreply.github.com> Date: Tue, 6 Oct 2026 02:04:40 +0800 Subject: [PATCH] Recognize JOIN before parenthesized subqueries --- CHANGELOG | 3 ++- sqlparse/keywords.py | 5 +++-- tests/test_join_parenthesis.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/test_join_parenthesis.py diff --git a/CHANGELOG b/CHANGELOG index 44d5938e..06d5689e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,8 @@ Development Version ------------------- -Nothing yet. +* Recognize JOIN immediately before a parenthesized subquery as a keyword + instead of a function name (issue615). Release 0.6.0 (Aug 13, 2026) diff --git a/sqlparse/keywords.py b/sqlparse/keywords.py index dd6e5d15..20361cd7 100644 --- a/sqlparse/keywords.py +++ b/sqlparse/keywords.py @@ -157,6 +157,9 @@ def find_delimited_spans(text): # FIXME(atronah): never match, # because `re.match` doesn't work with look-behind regexp feature (r'(?<=\.)[A-ZÀ-Ü]\w*', tokens.Name), # .'Name' + # JOIN before a parenthesis is a clause, not a function (issue615). + (r'((LEFT\s+|RIGHT\s+|FULL\s+)?(INNER\s+|OUTER\s+|STRAIGHT\s+)?' + r'|(CROSS\s+|NATURAL\s+)?)?JOIN\b', tokens.Keyword), (r'[A-ZÀ-Ü]\w*(?=\()', tokens.Name), # side effect: change kw to func (r'-?0x[\dA-F]+', tokens.Number.Hexadecimal), (r'-?\d+(\.\d+)?E-?\d+', tokens.Number.Float), @@ -171,8 +174,6 @@ def find_delimited_spans(text): # cannot be preceded by word character or a right bracket -- # otherwise it's probably an array index (r'(?