Skip to content

Don't anchor the aligned CASE indent on a missing END token - #942

Open
adarshsm wants to merge 1 commit into
andialbrecht:masterfrom
adarshsm:fix/886-aligned-indent-case-without-end
Open

adarshsm wants to merge 1 commit into
andialbrecht:masterfrom
adarshsm:fix/886-aligned-indent-case-without-end

Conversation

@adarshsm

Copy link
Copy Markdown

Fixes #886.

AlignedIndentFilter._process_case appends a pseudo-case holding the END token so it can align it too, then uses that case's first token as the insert anchor:

end_token = tlist.token_next_by(m=(T.Keyword, 'END'))[1]
cases.append((None, [end_token]))
...
stmt = cond[0] if cond else value[0]
if i > 0:
    tlist.insert_before(stmt, self.nl(offset_ - len(str(stmt))))

token_next_by() returns (None, None) when the Case group has no END token of its own. That happens when the END is swallowed by a nested group:

>>> sqlparse.format("CASE 'a' := WHERE END SELECT GO # ->>", reindent_aligned=True)
ValueError: list.index(x): x not in list
>>> case = next(t for t in sqlparse.parse("CASE 'a' := WHERE END SELECT GO # ->>")[0].get_sublists())
>>> [(type(t).__name__, t.value) for t in case.tokens]
[('Token', 'CASE'), ('Token', ' '), ('Assignment', "'a' := WHERE END")]
>>> case.token_next_by(m=(T.Keyword, 'END'))
(None, None)

value[0] is then None, and insert_before(None) fails inside token_index().

Change

sqlparse/filters/aligned_indent.py: only append the END pseudo-case when an END token was actually found. There is nothing to align otherwise, so skipping it is correct rather than merely non-crashing.

Behaviour

Reported input now formats to itself instead of raising. Well-formed CASE alignment is untouched:

>>> print(sqlparse.format('SELECT CASE WHEN a=1 THEN 2 ELSE 3 END FROM t', reindent_aligned=True))
SELECT CASE WHEN a=1 THEN 2
            ELSE 3
             END
  FROM t

Tests

test_issue886_reindent_aligned_malformed_case in tests/test_regressions.py.

uv run --group dev pytest tests/
507 passed, 2 xfailed, 1 xpassed

uv run --group dev ruff check sqlparse/
All checks passed!

A CHANGELOG entry is added under Development Version.

AlignedIndentFilter._process_case appends a pseudo-case for the END token
so it can align it, then uses that case's first token as the insert
anchor. token_next_by() returns (None, None) when the CASE has no END
token of its own - which happens when the END is swallowed by a nested
group - and the filter then called insert_before(None):

    >>> sqlparse.format("CASE 'a' := WHERE END SELECT GO # ->>",
    ...                 reindent_aligned=True)
    ValueError: list.index(x): x not in list

Only add the END pseudo-case when an END token was actually found.
Alignment of well-formed CASE constructs is unchanged.

Closes andialbrecht#886
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.

reindent_aligned=True raises ValueError (token_index) on a malformed CASE input

1 participant