diff --git a/CHANGELOG b/CHANGELOG index 44d5938e..3b68fdb9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,11 @@ Development Version ------------------- -Nothing yet. +Bug Fixes + +* `VALUES` lists inside parentheses, e.g. `FROM (VALUES (1, 2), (3, 4))`, are + now grouped as `Values` like top-level ones, so `reindent` puts one row per + line instead of breaking inside each row (issue506). Release 0.6.0 (Aug 13, 2026) diff --git a/sqlparse/engine/grouping.py b/sqlparse/engine/grouping.py index d8cfa9e1..7e5fec0c 100644 --- a/sqlparse/engine/grouping.py +++ b/sqlparse/engine/grouping.py @@ -427,6 +427,7 @@ def align_comments(tlist): tidx, token = tlist.token_next_by(i=sql.Comment, idx=tidx) +@recurse() def group_values(tlist): tidx, token = tlist.token_next_by(m=(T.Keyword, 'VALUES')) start_idx = tidx diff --git a/tests/test_format.py b/tests/test_format.py index 93495067..61d32211 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -649,6 +649,25 @@ def test_insert_values(self): ' , (3, 4)', ' , (5, 6)']) + def test_values_in_parenthesis(self): + # issue506 + f = lambda sql: sqlparse.format(sql, reindent=True) + s = ("select foo from (values ('a', 'b'), ('c', 'd'))" + " as tmp (foo, bar)") + assert f(s) == '\n'.join([ + 'select foo', + 'from (', + " values ('a', 'b'),", + " ('c', 'd')) as tmp (foo, bar)"]) + + f = lambda sql: sqlparse.format(sql, reindent=True, + comma_first=True) + assert f(s) == '\n'.join([ + 'select foo', + 'from (', + " values ('a', 'b')", + " , ('c', 'd')) as tmp (foo, bar)"]) + class TestOutputFormat: def test_python(self): diff --git a/tests/test_grouping.py b/tests/test_grouping.py index 20fab9b7..256edc32 100644 --- a/tests/test_grouping.py +++ b/tests/test_grouping.py @@ -82,6 +82,14 @@ def test_grouping_identifiers(): assert types == [T.DML, T.Keyword, None, None, T.Punctuation] assert isinstance(parsed.tokens[6], sql.Values) + # issue506: VALUES inside parenthesis + s = "select * from (values (1, 2), (3, 4)) as t (a, b)" + parsed = sqlparse.parse(s)[0] + assert str(parsed) == s + values = parsed.tokens[-1].tokens[0].tokens[1] + assert isinstance(values, sql.Values) + assert str(values) == "values (1, 2), (3, 4)" + s = "select 1.0*(a+b) as col, sum(c)/sum(d) from myschema.mytable" parsed = sqlparse.parse(s)[0] assert len(parsed.tokens) == 7