Skip to content

Group VALUES inside parentheses (issue506) - #913

Draft
adarshsm wants to merge 1 commit into
andialbrecht:masterfrom
adarshsm:fix/506-values-in-parenthesis
Draft

adarshsm wants to merge 1 commit into
andialbrecht:masterfrom
adarshsm:fix/506-values-in-parenthesis

Conversation

@adarshsm

Copy link
Copy Markdown

Fixes #506.

The bug

VALUES rows are laid out one per line at the top level, but as soon as the
VALUES list sits inside parentheses (a derived table, a CTE body, a JOIN
source) reindent breaks lines inside each row instead:

>>> print(sqlparse.format("SELECT foo FROM (VALUES ('abc', 'foo'), ('def', 'bar'), ('ghi', 'baz')) as tmp (foo, bar)", reindent=True))
SELECT foo
FROM (
      VALUES ('abc',
              'foo'), ('def',
                       'bar'), ('ghi',
                                'baz')) as tmp (foo, bar)

Still reproduces on current master.

Root cause

group_values() is the only grouping step in group() that doesn't recurse
into sublists — it only scans the statement's own tokens. A nested VALUES
therefore never becomes a sql.Values group:

Parenthesis '(VALUE...'
 |- Punctuation '('
 |- Keyword 'VALUES'
 |- Parenthesis '(1, 2)'
 |- Punctuation ','
 ...

ReindentFilter._process_values() never runs for it, and the inner
IdentifierLists are laid out as ordinary column lists (the
tlist.within(sql.Values) check in _process_identifierlist() is false).

The fix

Decorate group_values with @recurse(), like the other grouping functions.
Nested lists now group exactly like top-level ones, so they get the same
layout (with and without comma_first):

SELECT foo
FROM (
      VALUES ('abc', 'foo'),
             ('def', 'bar'),
             ('ghi', 'baz')) as tmp (foo, bar)

I also compared before/after output for WITH v AS (VALUES ...),
JOIN (VALUES ...) AS v (...), IN (SELECT ... FROM (VALUES ...)) and
MySQL's ON DUPLICATE KEY UPDATE a = VALUES(a) — the first three now match
the top-level layout, the last is unchanged, and str(parse(s)[0]) == s
holds for all of them.

Testing

  • test_grouping_identifiers: nested VALUES is a sql.Values group.

  • New TestFormatReindent::test_values_in_parenthesis (plain and
    comma_first).

  • Both fail on master and pass with the fix.

  • Full suite: 507 passed, 2 xfailed, 1 xpassed. ruff check sqlparse/
    clean. benchmarks/bench_grouping.py still reports linear on all vectors.

  • ran the tests (pytest)

  • all style issues addressed (ruff)

  • your changes are covered by tests

  • your changes are documented, if needed (CHANGELOG)

🤖 Generated with Claude Code

group_values() only looked at the statement's own tokens, so a VALUES
list nested in parentheses (subquery, CTE, JOIN source) was never
grouped as sql.Values. reindent then treated each row's items as a
plain identifier list and broke lines inside every row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Strange behaviour with VALUES statements

1 participant