Skip to content

Commit 115e208

Browse files
committed
Add option to remove trailing semicolon when splitting (fixes andialbrecht#742).
1 parent 6eca7ae commit 115e208

File tree

6 files changed

+52
-3
lines changed

6 files changed

+52
-3
lines changed

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ Notable Changes
66
* Drop support for Python 3.5.
77
* Python 3.12 is now supported (pr725, by hugovk).
88

9+
Enhancements:
10+
11+
* Splitting statements now allows to remove the semicolon at the end.
12+
Some database backends love statements without semicolon (issue742).
13+
914
Bug Fixes
1015

1116
* Ignore dunder attributes when creating Tokens (issue672).

sqlparse/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ def format(sql, encoding=None, **options):
5959
return ''.join(stack.run(sql, encoding))
6060

6161

62-
def split(sql, encoding=None):
62+
def split(sql, encoding=None, strip_semicolon=False):
6363
"""Split *sql* into single statements.
6464
6565
:param sql: A string containing one or more SQL statements.
6666
:param encoding: The encoding of the statement (optional).
67+
:param strip_semicolon: If True, remove trainling semicolons
68+
(default: False).
6769
:returns: A list of strings.
6870
"""
69-
stack = engine.FilterStack()
71+
stack = engine.FilterStack(strip_semicolon=strip_semicolon)
7072
return [str(stmt).strip() for stmt in stack.run(sql, encoding)]

sqlparse/engine/filter_stack.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@
1010
from sqlparse import lexer
1111
from sqlparse.engine import grouping
1212
from sqlparse.engine.statement_splitter import StatementSplitter
13+
from sqlparse.filters import StripTrailingSemicolonFilter
1314

1415

1516
class FilterStack:
16-
def __init__(self):
17+
def __init__(self, strip_semicolon=False):
1718
self.preprocess = []
1819
self.stmtprocess = []
1920
self.postprocess = []
2021
self._grouping = False
22+
if strip_semicolon:
23+
self.stmtprocess.append(StripTrailingSemicolonFilter())
2124

2225
def enable_grouping(self):
2326
self._grouping = True

sqlparse/filters/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from sqlparse.filters.others import SerializerUnicode
99
from sqlparse.filters.others import StripCommentsFilter
1010
from sqlparse.filters.others import StripWhitespaceFilter
11+
from sqlparse.filters.others import StripTrailingSemicolonFilter
1112
from sqlparse.filters.others import SpacesAroundOperatorsFilter
1213

1314
from sqlparse.filters.output import OutputPHPFilter
@@ -25,6 +26,7 @@
2526
'SerializerUnicode',
2627
'StripCommentsFilter',
2728
'StripWhitespaceFilter',
29+
'StripTrailingSemicolonFilter',
2830
'SpacesAroundOperatorsFilter',
2931

3032
'OutputPHPFilter',

sqlparse/filters/others.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ def process(self, stmt):
126126
return stmt
127127

128128

129+
class StripTrailingSemicolonFilter:
130+
131+
def process(self, stmt):
132+
while stmt.tokens and (stmt.tokens[-1].is_whitespace
133+
or stmt.tokens[-1].value == ';'):
134+
stmt.tokens.pop()
135+
return stmt
136+
137+
129138
# ---------------------------
130139
# postprocess
131140

tests/test_split.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,31 @@ def test_split_mysql_handler_for(load_file):
166166
# see issue581
167167
stmts = sqlparse.split(load_file('mysql_handler.sql'))
168168
assert len(stmts) == 2
169+
170+
171+
@pytest.mark.parametrize('sql, expected', [
172+
('select * from foo;', ['select * from foo']),
173+
('select * from foo', ['select * from foo']),
174+
('select * from foo; select * from bar;', [
175+
'select * from foo',
176+
'select * from bar',
177+
]),
178+
(' select * from foo;\n\nselect * from bar;\n\n\n\n', [
179+
'select * from foo',
180+
'select * from bar',
181+
]),
182+
('select * from foo\n\n; bar', ['select * from foo', 'bar']),
183+
])
184+
def test_split_strip_semicolon(sql, expected):
185+
stmts = sqlparse.split(sql, strip_semicolon=True)
186+
assert len(stmts) == len(expected)
187+
for idx, expectation in enumerate(expected):
188+
assert stmts[idx] == expectation
189+
190+
191+
def test_split_strip_semicolon_procedure(load_file):
192+
stmts = sqlparse.split(load_file('mysql_handler.sql'),
193+
strip_semicolon=True)
194+
assert len(stmts) == 2
195+
assert stmts[0].endswith('end')
196+
assert stmts[1].endswith('end')

0 commit comments

Comments
 (0)