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'(?