Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Development Version
-------------------

Nothing yet.
* Fix SELECT-list alignment for CURRENT_DATE, CURRENT_TIME, and
CURRENT_TIMESTAMP with explicit AS aliases (issue550).


Release 0.6.0 (Aug 13, 2026)
Expand Down
4 changes: 3 additions & 1 deletion sqlparse/engine/grouping.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@ def match(token):
return token.is_keyword and token.normalized == 'AS'

def valid_prev(token):
return token.normalized == 'NULL' or not token.is_keyword
return (token.normalized in ('NULL', 'CURRENT_DATE', 'CURRENT_TIME',
'CURRENT_TIMESTAMP')
or not token.is_keyword)

def valid_next(token):
ttypes = T.DML, T.DDL, T.CTE
Expand Down
33 changes: 33 additions & 0 deletions tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,39 @@ def test_identifier_list(self):
'from a,',
' b'])

@pytest.mark.parametrize('date', [
'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP',
])
@pytest.mark.parametrize('case', [str.upper, str.lower, str.title])
def test_date_scalar_as_alias(self, date, case):
# issue550
aliased = f'{case(date)} {case("AS")} CREATED_AT'
s = (f"SELECT 'value 1' AS VALUE_1, {aliased}, "
"'testing' AS VALUE_2, 'testing2' AS VALUE_3 FROM TABLE_1")
expected = '\n'.join([
"SELECT 'value 1' AS VALUE_1,",
f' {aliased},',
" 'testing' AS VALUE_2,",
" 'testing2' AS VALUE_3",
'FROM TABLE_1',
])
assert sqlparse.format(s, reindent=True) == expected
assert sqlparse.format(expected, reindent=True) == expected

def test_date_scalar_as_alias_trailing_comma(self):
# Preserve the trailing comma in issue550's reported output separately
# from the valid SELECT-list regression above.
s = ("SELECT 'value 1' AS VALUE_1, CURRENT_DATE AS CREATED_AT, "
"'testing' AS VALUE_2, 'testing2' AS VALUE_3, FROM TABLE_1")
assert str(sqlparse.parse(s)[0]) == s
assert sqlparse.format(s, reindent=True) == '\n'.join([
"SELECT 'value 1' AS VALUE_1,",
' CURRENT_DATE AS CREATED_AT,',
" 'testing' AS VALUE_2,",
" 'testing2' AS VALUE_3,",
'FROM TABLE_1',
])

def test_identifier_list_with_wrap_after(self):
f = lambda sql: sqlparse.format(sql, reindent=True, wrap_after=14)
s = 'select foo, bar, baz from table1, table2 where 1 = 2'
Expand Down
22 changes: 22 additions & 0 deletions tests/test_grouping.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,28 @@ def test_grouping_alias():
assert p.tokens[4].get_alias() == 'view'


@pytest.mark.parametrize('expression', [
'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'NULL', 'created_on',
])
@pytest.mark.parametrize('case', [str.upper, str.lower, str.title])
def test_grouping_date_scalar_as_alias(expression, case):
# issue550: the scalar and its explicit alias must stay in one projection.
aliased = f'{case(expression)}\t{case("AS")}\n CREATED_AT'
s = (f"SELECT 'value 1' AS VALUE_1, {aliased}, "
"'testing' AS VALUE_2, 'testing2' AS VALUE_3 FROM TABLE_1")
p = sqlparse.parse(s)[0]
assert str(p) == s
assert isinstance(p.tokens[2], sql.IdentifierList)
identifiers = list(p.tokens[2].get_identifiers())
assert [str(item) for item in identifiers] == [
"'value 1' AS VALUE_1", aliased,
"'testing' AS VALUE_2", "'testing2' AS VALUE_3",
]
assert [item.get_alias() for item in identifiers] == [
'VALUE_1', 'CREATED_AT', 'VALUE_2', 'VALUE_3',
]


def test_grouping_alias_case():
# see issue46
p = sqlparse.parse('CASE WHEN 1 THEN 2 ELSE 3 END foo')[0]
Expand Down