fix: keep RENAME TABLE clauses as separate identifiers (issue779)
RENAME TABLE a TO b, c TO d grouped the target of the first clause and the source of the second into a single IdentifierList, because the comma between the two clauses was treated as an identifier-list separator. Detect the rename layout by the TO keywords flanking both sides of the comma and leave such a comma ungrouped, so each renamed table stays a separate Identifier. Genuine target lists such as GRANT ... TO a, b are left untouched.
This commit is contained in:
1 parent
95c0c262cf
commit
241869a841
2 files changed
+46
-1
No files matched your search
@@ -309,13 +309,34 @@ def group_operator(tlist):
|
|||||||
|
|
||||||
def group_identifier_list(tlist):
|
def group_identifier_list(tlist):
|
||||||
m_role = T.Keyword, ('null', 'role')
|
m_role = T.Keyword, ('null', 'role')
|
||||||
|
m_to = T.Keyword, 'TO'
|
||||||
sqlcls = (sql.Function, sql.Case, sql.Identifier, sql.Comparison,
|
sqlcls = (sql.Function, sql.Case, sql.Identifier, sql.Comparison,
|
||||||
sql.IdentifierList, sql.Operation)
|
sql.IdentifierList, sql.Operation)
|
||||||
ttypes = (T_NUMERICAL + T_STRING + T_NAME
|
ttypes = (T_NUMERICAL + T_STRING + T_NAME
|
||||||
+ (T.Keyword, T.Comment, T.Wildcard))
|
+ (T.Keyword, T.Comment, T.Wildcard))
|
||||||
|
|
||||||
|
def is_rename_separator(token):
|
||||||
|
# In ``RENAME TABLE a TO b, c TO d`` the comma separates independent
|
||||||
|
# rename clauses, so the target of one clause (``b``) and the source
|
||||||
|
# of the next (``c``) must not be merged into a single IdentifierList.
|
||||||
|
# Detect that layout by the ``TO`` keywords flanking both sides of the
|
||||||
|
# comma and leave such a comma ungrouped (issue779).
|
||||||
|
parent = token.parent
|
||||||
|
if parent is None:
|
||||||
|
return False
|
||||||
|
cidx = parent.token_index(token)
|
||||||
|
pidx, prev_ = parent.token_prev(cidx)
|
||||||
|
nidx, next_ = parent.token_next(cidx)
|
||||||
|
if prev_ is None or next_ is None:
|
||||||
|
return False
|
||||||
|
_, before_prev = parent.token_prev(pidx)
|
||||||
|
_, after_next = parent.token_next(nidx)
|
||||||
|
return (before_prev is not None and before_prev.match(*m_to)
|
||||||
|
and after_next is not None and after_next.match(*m_to))
|
||||||
|
|
||||||
def match(token):
|
def match(token):
|
||||||
return token.match(T.Punctuation, ',')
|
return token.match(T.Punctuation, ',') \
|
||||||
|
and not is_rename_separator(token)
|
||||||
|
|
||||||
def valid(token):
|
def valid(token):
|
||||||
return imt(token, i=sqlcls, m=m_role, t=ttypes)
|
return imt(token, i=sqlcls, m=m_role, t=ttypes)
|
||||||
|
|||||||
@@ -414,6 +414,30 @@ def test_issue562_tzcasts():
|
|||||||
'SELECT f(HOUR\n from bar AT TIME ZONE \'UTC\')\nfrom foo'
|
'SELECT f(HOUR\n from bar AT TIME ZONE \'UTC\')\nfrom foo'
|
||||||
|
|
||||||
|
|
||||||
|
def test_issue779_rename_table_pairs():
|
||||||
|
# The comma in ``RENAME TABLE a TO b, c TO d`` separates independent
|
||||||
|
# rename clauses. The target of one clause and the source of the next
|
||||||
|
# must stay separate Identifiers and not be merged into an IdentifierList.
|
||||||
|
p = sqlparse.parse('RENAME TABLE a TO b, c TO d')[0]
|
||||||
|
idents = [t for t in p.tokens if isinstance(t, sql.Identifier)]
|
||||||
|
assert [t.value for t in idents] == ['a', 'b', 'c', 'd']
|
||||||
|
assert not [t for t in p.tokens if isinstance(t, sql.IdentifierList)]
|
||||||
|
comma = p.token_next_by(m=(T.Punctuation, ','))[1]
|
||||||
|
assert comma is not None and comma.parent is p
|
||||||
|
|
||||||
|
# Backtick-quoted identifiers from the original report behave the same.
|
||||||
|
p = sqlparse.parse(
|
||||||
|
'rename table `db`.`test1` to test2, `db`.`test3` to test4')[0]
|
||||||
|
idents = [t for t in p.tokens if isinstance(t, sql.Identifier)]
|
||||||
|
assert [t.value for t in idents] == [
|
||||||
|
'`db`.`test1`', 'test2', '`db`.`test3`', 'test4']
|
||||||
|
assert not [t for t in p.tokens if isinstance(t, sql.IdentifierList)]
|
||||||
|
|
||||||
|
# A genuine ``TO`` target list (e.g. GRANT ... TO a, b) must still group.
|
||||||
|
p = sqlparse.parse('GRANT SELECT ON t TO a, b')[0]
|
||||||
|
assert p.token_next_by(i=sql.IdentifierList)[1] is not None
|
||||||
|
|
||||||
|
|
||||||
def test_as_in_parentheses_indents():
|
def test_as_in_parentheses_indents():
|
||||||
# did raise NoneType has no attribute is_group in _process_parentheses
|
# did raise NoneType has no attribute is_group in _process_parentheses
|
||||||
formatted = sqlparse.format('(as foo)', reindent=True)
|
formatted = sqlparse.format('(as foo)', reindent=True)
|
||||||
|
|||||||
Reference in new issue
Block a user