diff --git a/sqlparse/filters/aligned_indent.py b/sqlparse/filters/aligned_indent.py index 6ac99d62..0b94095b 100644 --- a/sqlparse/filters/aligned_indent.py +++ b/sqlparse/filters/aligned_indent.py @@ -70,6 +70,12 @@ def _process_case(self, tlist): cases = tlist.get_cases(skip_ws=True) # align the end as well end_token = tlist.token_next_by(m=(T.Keyword, 'END'))[1] + if end_token is None: + # A malformed CASE expression can leave END nested inside a + # sibling group instead of being a direct child of this token + # list (get_cases and token_next_by only look at direct + # children), so there's nothing valid to align it against. + return cases.append((None, [end_token])) condition_width = [len(' '.join(map(str, cond))) if cond else 0 diff --git a/sqlparse/filters/others.py b/sqlparse/filters/others.py index 95bc436c..f6dc7f8d 100644 --- a/sqlparse/filters/others.py +++ b/sqlparse/filters/others.py @@ -112,11 +112,15 @@ def _stripws_identifierlist(self, tlist): return self._stripws_default(tlist) def _stripws_parenthesis(self, tlist): - while tlist.tokens[1].is_whitespace: + # A malformed parenthesis can end up with only one or two direct + # children once grouping is done (e.g. the whole inside collapses + # into a single nested group), so don't assume tokens[1]/tokens[-2] + # are always there. + while len(tlist.tokens) > 2 and tlist.tokens[1].is_whitespace: tlist.tokens.pop(1) - while tlist.tokens[-2].is_whitespace: + while len(tlist.tokens) > 2 and tlist.tokens[-2].is_whitespace: tlist.tokens.pop(-2) - if tlist.tokens[-2].is_group: + if len(tlist.tokens) > 1 and tlist.tokens[-2].is_group: # save to remove the last whitespace while tlist.tokens[-2].tokens[-1].is_whitespace: tlist.tokens[-2].tokens.pop(-1) diff --git a/tests/test_regressions.py b/tests/test_regressions.py index aca7f7b3..b5fc7913 100644 --- a/tests/test_regressions.py +++ b/tests/test_regressions.py @@ -516,3 +516,19 @@ def limit_recursion(): def test_max_recursion(limit_recursion): with pytest.raises(SQLParseError): sqlparse.parse('[' * 1000 + ']' * 1000) + + +def test_stripws_parenthesis_with_no_direct_children_issue885(): + # Malformed input can collapse the whole parenthesis body into a single + # nested group, leaving fewer than two direct children of the + # Parenthesis token list. This used to raise IndexError. + assert sqlparse.format('( AS )', strip_whitespace=True) == '( AS )' + + +def test_aligned_indent_case_without_direct_end_issue886(): + # Malformed CASE expressions can end up with the END keyword nested + # inside a sibling group rather than being a direct child of the Case + # token list, so token_next_by can't find it and used to raise + # ValueError when that None was later used as an insertion point. + sql = "CASE 'a' := WHERE END SELECT GO # ->>" + assert sqlparse.format(sql, reindent_aligned=True) == sql