summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2021-04-08 05:01:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2021-04-08 05:01:13 +0000
commit88312cf5b1fc6c62b6e7700b11ae5abb9d878209 (patch)
tree93c8d18a681ab41a268b46865de91886d39a60ba
parentReleasing progress-linux version 0.3.1-1~progress5+u1. (diff)
downloadsqlparse-88312cf5b1fc6c62b6e7700b11ae5abb9d878209.tar.xz
sqlparse-88312cf5b1fc6c62b6e7700b11ae5abb9d878209.zip
Merging upstream version 0.4.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--AUTHORS3
-rw-r--r--CHANGELOG36
-rw-r--r--PKG-INFO121
-rw-r--r--README.rst16
-rw-r--r--docs/source/conf.py2
-rw-r--r--docs/source/intro.rst2
-rw-r--r--setup.cfg50
-rw-r--r--setup.py105
-rw-r--r--sqlparse.egg-info/PKG-INFO121
-rw-r--r--sqlparse.egg-info/SOURCES.txt3
-rw-r--r--sqlparse/__init__.py10
-rw-r--r--sqlparse/__main__.py3
-rwxr-xr-xsqlparse/cli.py42
-rw-r--r--sqlparse/compat.py45
-rw-r--r--sqlparse/engine/__init__.py3
-rw-r--r--sqlparse/engine/filter_stack.py5
-rw-r--r--sqlparse/engine/grouping.py7
-rw-r--r--sqlparse/engine/statement_splitter.py9
-rw-r--r--sqlparse/exceptions.py3
-rw-r--r--sqlparse/filters/__init__.py3
-rw-r--r--sqlparse/filters/aligned_indent.py13
-rw-r--r--sqlparse/filters/others.py34
-rw-r--r--sqlparse/filters/output.py10
-rw-r--r--sqlparse/filters/reindent.py17
-rw-r--r--sqlparse/filters/right_margin.py10
-rw-r--r--sqlparse/filters/tokens.py10
-rw-r--r--sqlparse/formatter.py29
-rw-r--r--sqlparse/keywords.py12
-rw-r--r--sqlparse/lexer.py14
-rw-r--r--sqlparse/sql.py36
-rw-r--r--sqlparse/tokens.py3
-rw-r--r--sqlparse/utils.py6
-rw-r--r--tests/conftest.py6
-rw-r--r--tests/files/casewhen_procedure.sql8
-rw-r--r--tests/files/mysql_handler.sql10
-rw-r--r--tests/test_cli.py81
-rw-r--r--tests/test_format.py28
-rw-r--r--tests/test_grouping.py35
-rw-r--r--tests/test_keywords.py1
-rw-r--r--tests/test_parse.py22
-rw-r--r--tests/test_regressions.py43
-rw-r--r--tests/test_split.py37
-rw-r--r--tests/test_tokenize.py12
-rw-r--r--tox.ini7
44 files changed, 534 insertions, 539 deletions
diff --git a/AUTHORS b/AUTHORS
index 24f3fda..852d4c0 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -10,6 +10,7 @@ Alphabetical list of contributors:
* Adam Greenhall <agreenhall@lyft.com>
* Alexander Beedie <ayembee@gmail.com>
* Alexey Malyshev <nostrict@gmail.com>
+* ali-tny <aliteeney@googlemail.com>
* andrew deryabin <github@djsf.com>
* Andrew Tipton <andrew.tipton@compareglobalgroup.com>
* atronah <atronah.ds@gmail.com>
@@ -26,6 +27,7 @@ Alphabetical list of contributors:
* Florian Bauer <florian.bauer@zmdi.com>
* Fredy Wijaya <fredy.wijaya@gmail.com>
* Gavin Wahl <gwahl@fusionbox.com>
+* hurcy <cinyoung.hur@gmail.com>
* Ian Robertson <ian.robertson@capitalone.com>
* JacekPliszka <Jacek.Pliszka@gmail.com>
* Jesús Leganés Combarro "Piranna" <piranna@gmail.com>
@@ -63,5 +65,6 @@ Alphabetical list of contributors:
* Ville Skyttä <ville.skytta@iki.fi>
* vthriller <farreva232@yandex.ru>
* wayne.wuw <wayne.wuw@alibaba-inc.com>
+* Will Jones <willjones127@gmail.com>
* William Ivanski <william.ivanski@gmail.com>
* Yago Riveiro <yago.riveiro@gmail.com>
diff --git a/CHANGELOG b/CHANGELOG
index 6eca53e..328da90 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,39 @@
+Release 0.4.1 (Oct 08, 2020)
+----------------------------
+
+Bug Fixes
+* Just removed a debug print statement, sorry...
+
+
+Release 0.4.0 (Oct 07, 2020)
+----------------------------
+
+Notable Changes
+
+* Remove support for end-of-life Python 2.7 and 3.4. Python 3.5+ is now
+ required.
+* Remaining strings that only consist of whitespaces are not treated as
+ statements anymore. Code that ignored the last element from
+ sqlparse.split() should be updated accordingly since that function
+ now doesn't return an empty string as the last element in some
+ cases (issue496).
+
+Enhancements
+
+* Add WINDOW keyword (pr579 by ali-tny).
+* Add RLIKE keyword (pr582 by wjones1).
+
+Bug Fixes
+
+* Improved parsing of IN(...) statements (issue566, pr567 by hurcy).
+* Preserve line breaks when removing comments (issue484).
+* Fix parsing error when using square bracket notation (issue583).
+* Fix splitting when using DECLARE ... HANDLER (issue581).
+* Fix splitting of statements using CASE ... WHEN (issue580).
+* Improve formatting of type casts in parentheses.
+* Stabilize formatting of invalid SQL statements.
+
+
Release 0.3.1 (Feb 29, 2020)
----------------------------
diff --git a/PKG-INFO b/PKG-INFO
index d4558b8..8da0cc6 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,60 +1,90 @@
Metadata-Version: 1.2
Name: sqlparse
-Version: 0.3.1
-Summary: Non-validating SQL parser
+Version: 0.4.1
+Summary: A non-validating SQL parser.
Home-page: https://github.com/andialbrecht/sqlparse
Author: Andi Albrecht
Author-email: albrecht.andi@gmail.com
-License: BSD
-Description:
- ``sqlparse`` is a non-validating SQL parser module.
+License: BSD-3-Clause
+Project-URL: Documentation, https://sqlparse.readthedocs.io/
+Project-URL: Release Notes, https://sqlparse.readthedocs.io/en/latest/changes/
+Project-URL: Source, https://github.com/andialbrecht/sqlparse
+Project-URL: Tracker, https://github.com/andialbrecht/sqlparse/issues
+Description: python-sqlparse - Parse SQL statements
+ ======================================
+
+ |buildstatus|_
+ |coverage|_
+ |docs|_
+
+ .. docincludebegin
+
+ sqlparse is a non-validating SQL parser for Python.
It provides support for parsing, splitting and formatting SQL statements.
- Visit the `project page <https://github.com/andialbrecht/sqlparse>`_ for
- additional information and documentation.
+ The module is compatible with Python 3.5+ and released under the terms of the
+ `New BSD license <https://opensource.org/licenses/BSD-3-Clause>`_.
- **Example Usage**
+ Visit the project page at https://github.com/andialbrecht/sqlparse for
+ further information about this project.
- Splitting SQL statements::
+ Quick Start
+ -----------
- >>> import sqlparse
- >>> sqlparse.split('select * from foo; select * from bar;')
- [u'select * from foo; ', u'select * from bar;']
+ .. code-block:: sh
+ $ pip install sqlparse
- Formatting statements::
+ .. code-block:: python
- >>> sql = 'select * from foo where id in (select id from bar);'
- >>> print(sqlparse.format(sql, reindent=True, keyword_case='upper'))
+ >>> import sqlparse
+
+ >>> # Split a string containing two SQL statements:
+ >>> raw = 'select * from foo; select * from bar;'
+ >>> statements = sqlparse.split(raw)
+ >>> statements
+ ['select * from foo;', 'select * from bar;']
+
+ >>> # Format the first statement and print it out:
+ >>> first = statements[0]
+ >>> print(sqlparse.format(first, reindent=True, keyword_case='upper'))
SELECT *
- FROM foo
- WHERE id IN
- (SELECT id
- FROM bar);
-
-
- Parsing::
-
- >>> sql = 'select * from someschema.mytable where id = 1'
- >>> res = sqlparse.parse(sql)
- >>> res
- (<Statement 'select...' at 0x9ad08ec>,)
- >>> stmt = res[0]
- >>> str(stmt) # converting it back to unicode
- 'select * from someschema.mytable where id = 1'
- >>> # This is how the internal representation looks like:
- >>> stmt.tokens
- (<DML 'select' at 0x9b63c34>,
- <Whitespace ' ' at 0x9b63e8c>,
- <Operator '*' at 0x9b63e64>,
- <Whitespace ' ' at 0x9b63c5c>,
- <Keyword 'from' at 0x9b63c84>,
- <Whitespace ' ' at 0x9b63cd4>,
- <Identifier 'somes...' at 0x9b5c62c>,
- <Whitespace ' ' at 0x9b63f04>,
- <Where 'where ...' at 0x9b5caac>)
+ FROM foo;
+
+ >>> # Parsing a SQL statement:
+ >>> parsed = sqlparse.parse('select * from foo')[0]
+ >>> parsed.tokens
+ [<DML 'select' at 0x7f22c5e15368>, <Whitespace ' ' at 0x7f22c5e153b0>, <Wildcard '*' … ]
+ >>>
+
+ Links
+ -----
+
+ Project page
+ https://github.com/andialbrecht/sqlparse
+
+ Bug tracker
+ https://github.com/andialbrecht/sqlparse/issues
+
+ Documentation
+ https://sqlparse.readthedocs.io/
+
+ Online Demo
+ https://sqlformat.org/
+
+
+ sqlparse is licensed under the BSD license.
+
+ Parts of the code are based on pygments written by Georg Brandl and others.
+ pygments-Homepage: http://pygments.org/
+ .. |buildstatus| image:: https://secure.travis-ci.org/andialbrecht/sqlparse.png?branch=master
+ .. _buildstatus: https://travis-ci.org/#!/andialbrecht/sqlparse
+ .. |coverage| image:: https://codecov.io/gh/andialbrecht/sqlparse/branch/master/graph/badge.svg
+ .. _coverage: https://codecov.io/gh/andialbrecht/sqlparse
+ .. |docs| image:: https://readthedocs.org/projects/sqlparse/badge/?version=latest
+ .. _docs: https://sqlparse.readthedocs.io/en/latest/?badge=latest
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
@@ -62,14 +92,15 @@ Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Database
Classifier: Topic :: Software Development
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Python: >=3.5
diff --git a/README.rst b/README.rst
index 2e38ebc..92e15c1 100644
--- a/README.rst
+++ b/README.rst
@@ -3,19 +3,15 @@ python-sqlparse - Parse SQL statements
|buildstatus|_
|coverage|_
+|docs|_
.. docincludebegin
sqlparse is a non-validating SQL parser for Python.
It provides support for parsing, splitting and formatting SQL statements.
-The module is compatible with Python 2.7 and Python 3 (>= 3.4)
-and released under the terms of the `New BSD license
-<https://opensource.org/licenses/BSD-3-Clause>`_.
-
-.. note::
-
- Support for Python<3.4 (including 2.x) will be dropped soon.
+The module is compatible with Python 3.5+ and released under the terms of the
+`New BSD license <https://opensource.org/licenses/BSD-3-Clause>`_.
Visit the project page at https://github.com/andialbrecht/sqlparse for
further information about this project.
@@ -73,5 +69,7 @@ pygments-Homepage: http://pygments.org/
.. |buildstatus| image:: https://secure.travis-ci.org/andialbrecht/sqlparse.png?branch=master
.. _buildstatus: https://travis-ci.org/#!/andialbrecht/sqlparse
-.. |coverage| image:: https://coveralls.io/repos/andialbrecht/sqlparse/badge.svg?branch=master&service=github
-.. _coverage: https://coveralls.io/github/andialbrecht/sqlparse?branch=master
+.. |coverage| image:: https://codecov.io/gh/andialbrecht/sqlparse/branch/master/graph/badge.svg
+.. _coverage: https://codecov.io/gh/andialbrecht/sqlparse
+.. |docs| image:: https://readthedocs.org/projects/sqlparse/badge/?version=latest
+.. _docs: https://sqlparse.readthedocs.io/en/latest/?badge=latest
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 70bd69a..a5be640 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-#
# python-sqlparse documentation build configuration file, created by
# sphinx-quickstart on Thu Feb 26 08:19:28 2009.
#
diff --git a/docs/source/intro.rst b/docs/source/intro.rst
index 330184a..1d3c949 100644
--- a/docs/source/intro.rst
+++ b/docs/source/intro.rst
@@ -120,7 +120,7 @@ To check out the latest sources of this module run
to check out the latest sources from the repository.
-:mod:`sqlparse` is currently tested under Python 2.7, >=3.4 and pypy. Tests are
+:mod:`sqlparse` is currently tested under Python 3.5+ and PyPy. Tests are
automatically run on each commit and for each pull request on Travis:
https://travis-ci.org/andialbrecht/sqlparse
diff --git a/setup.cfg b/setup.cfg
index b554121..cfef89c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,17 +1,51 @@
-[bdist_wheel]
-universal = 1
-
[metadata]
-license_file = LICENSE
+name = sqlparse
+version = attr: sqlparse.__version__
+url = https://github.com/andialbrecht/sqlparse
+author = Andi Albrecht
+author_email = albrecht.andi@gmail.com
+description = A non-validating SQL parser.
+long_description = file: README.rst
+license = BSD-3-Clause
+classifiers =
+ Development Status :: 5 - Production/Stable
+ Intended Audience :: Developers
+ License :: OSI Approved :: BSD License
+ Operating System :: OS Independent
+ Programming Language :: Python
+ Programming Language :: Python :: 3
+ Programming Language :: Python :: 3 :: Only
+ Programming Language :: Python :: 3.5
+ Programming Language :: Python :: 3.6
+ Programming Language :: Python :: 3.7
+ Programming Language :: Python :: 3.8
+ Programming Language :: Python :: 3.9
+ Programming Language :: Python :: Implementation :: CPython
+ Programming Language :: Python :: Implementation :: PyPy
+ Topic :: Database
+ Topic :: Software Development
+project_urls =
+ Documentation = https://sqlparse.readthedocs.io/
+ Release Notes = https://sqlparse.readthedocs.io/en/latest/changes/
+ Source = https://github.com/andialbrecht/sqlparse
+ Tracker = https://github.com/andialbrecht/sqlparse/issues
+
+[options]
+python_requires = >=3.5
+packages = find:
+
+[options.packages.find]
+exclude = tests
+
+[options.entry_points]
+console_scripts =
+ sqlformat = sqlparse.__main__:main
[tool:pytest]
xfail_strict = True
[flake8]
-exclude =
- sqlparse/compat.py
-ignore =
- W503,
+extend-ignore =
E731
[coverage:run]
diff --git a/setup.py b/setup.py
index 3de94e7..ede0aff 100644
--- a/setup.py
+++ b/setup.py
@@ -1,111 +1,12 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This setup script is part of python-sqlparse and is released under
# the BSD License: https://opensource.org/licenses/BSD-3-Clause
-import re
+from setuptools import setup
-from setuptools import setup, find_packages
-
-def get_version():
- """Parse __init__.py for version number instead of importing the file."""
- VERSIONFILE = 'sqlparse/__init__.py'
- VSRE = r'^__version__ = [\'"]([^\'"]*)[\'"]'
- with open(VERSIONFILE) as f:
- verstrline = f.read()
- mo = re.search(VSRE, verstrline, re.M)
- if mo:
- return mo.group(1)
- raise RuntimeError('Unable to find version in {fn}'.format(fn=VERSIONFILE))
-
-
-LONG_DESCRIPTION = """
-``sqlparse`` is a non-validating SQL parser module.
-It provides support for parsing, splitting and formatting SQL statements.
-
-Visit the `project page <https://github.com/andialbrecht/sqlparse>`_ for
-additional information and documentation.
-
-**Example Usage**
-
-
-Splitting SQL statements::
-
- >>> import sqlparse
- >>> sqlparse.split('select * from foo; select * from bar;')
- [u'select * from foo; ', u'select * from bar;']
-
-
-Formatting statements::
-
- >>> sql = 'select * from foo where id in (select id from bar);'
- >>> print(sqlparse.format(sql, reindent=True, keyword_case='upper'))
- SELECT *
- FROM foo
- WHERE id IN
- (SELECT id
- FROM bar);
-
-
-Parsing::
-
- >>> sql = 'select * from someschema.mytable where id = 1'
- >>> res = sqlparse.parse(sql)
- >>> res
- (<Statement 'select...' at 0x9ad08ec>,)
- >>> stmt = res[0]
- >>> str(stmt) # converting it back to unicode
- 'select * from someschema.mytable where id = 1'
- >>> # This is how the internal representation looks like:
- >>> stmt.tokens
- (<DML 'select' at 0x9b63c34>,
- <Whitespace ' ' at 0x9b63e8c>,
- <Operator '*' at 0x9b63e64>,
- <Whitespace ' ' at 0x9b63c5c>,
- <Keyword 'from' at 0x9b63c84>,
- <Whitespace ' ' at 0x9b63cd4>,
- <Identifier 'somes...' at 0x9b5c62c>,
- <Whitespace ' ' at 0x9b63f04>,
- <Where 'where ...' at 0x9b5caac>)
-
-"""
-
-setup(
- name='sqlparse',
- version=get_version(),
- author='Andi Albrecht',
- author_email='albrecht.andi@gmail.com',
- url='https://github.com/andialbrecht/sqlparse',
- description='Non-validating SQL parser',
- long_description=LONG_DESCRIPTION,
- license='BSD',
- python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
- classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: BSD License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
- 'Programming Language :: Python :: 3.8',
- 'Topic :: Database',
- 'Topic :: Software Development',
- ],
- packages=find_packages(exclude=('tests',)),
- entry_points={
- 'console_scripts': [
- 'sqlformat = sqlparse.__main__:main',
- ]
- },
-)
+setup()
diff --git a/sqlparse.egg-info/PKG-INFO b/sqlparse.egg-info/PKG-INFO
index d4558b8..8da0cc6 100644
--- a/sqlparse.egg-info/PKG-INFO
+++ b/sqlparse.egg-info/PKG-INFO
@@ -1,60 +1,90 @@
Metadata-Version: 1.2
Name: sqlparse
-Version: 0.3.1
-Summary: Non-validating SQL parser
+Version: 0.4.1
+Summary: A non-validating SQL parser.
Home-page: https://github.com/andialbrecht/sqlparse
Author: Andi Albrecht
Author-email: albrecht.andi@gmail.com
-License: BSD
-Description:
- ``sqlparse`` is a non-validating SQL parser module.
+License: BSD-3-Clause
+Project-URL: Documentation, https://sqlparse.readthedocs.io/
+Project-URL: Release Notes, https://sqlparse.readthedocs.io/en/latest/changes/
+Project-URL: Source, https://github.com/andialbrecht/sqlparse
+Project-URL: Tracker, https://github.com/andialbrecht/sqlparse/issues
+Description: python-sqlparse - Parse SQL statements
+ ======================================
+
+ |buildstatus|_
+ |coverage|_
+ |docs|_
+
+ .. docincludebegin
+
+ sqlparse is a non-validating SQL parser for Python.
It provides support for parsing, splitting and formatting SQL statements.
- Visit the `project page <https://github.com/andialbrecht/sqlparse>`_ for
- additional information and documentation.
+ The module is compatible with Python 3.5+ and released under the terms of the
+ `New BSD license <https://opensource.org/licenses/BSD-3-Clause>`_.
- **Example Usage**
+ Visit the project page at https://github.com/andialbrecht/sqlparse for
+ further information about this project.
- Splitting SQL statements::
+ Quick Start
+ -----------
- >>> import sqlparse
- >>> sqlparse.split('select * from foo; select * from bar;')
- [u'select * from foo; ', u'select * from bar;']
+ .. code-block:: sh
+ $ pip install sqlparse
- Formatting statements::
+ .. code-block:: python
- >>> sql = 'select * from foo where id in (select id from bar);'
- >>> print(sqlparse.format(sql, reindent=True, keyword_case='upper'))
+ >>> import sqlparse
+
+ >>> # Split a string containing two SQL statements:
+ >>> raw = 'select * from foo; select * from bar;'
+ >>> statements = sqlparse.split(raw)
+ >>> statements
+ ['select * from foo;', 'select * from bar;']
+
+ >>> # Format the first statement and print it out:
+ >>> first = statements[0]
+ >>> print(sqlparse.format(first, reindent=True, keyword_case='upper'))
SELECT *
- FROM foo
- WHERE id IN
- (SELECT id
- FROM bar);
-
-
- Parsing::
-
- >>> sql = 'select * from someschema.mytable where id = 1'
- >>> res = sqlparse.parse(sql)
- >>> res
- (<Statement 'select...' at 0x9ad08ec>,)
- >>> stmt = res[0]
- >>> str(stmt) # converting it back to unicode
- 'select * from someschema.mytable where id = 1'
- >>> # This is how the internal representation looks like:
- >>> stmt.tokens
- (<DML 'select' at 0x9b63c34>,
- <Whitespace ' ' at 0x9b63e8c>,
- <Operator '*' at 0x9b63e64>,
- <Whitespace ' ' at 0x9b63c5c>,
- <Keyword 'from' at 0x9b63c84>,
- <Whitespace ' ' at 0x9b63cd4>,
- <Identifier 'somes...' at 0x9b5c62c>,
- <Whitespace ' ' at 0x9b63f04>,
- <Where 'where ...' at 0x9b5caac>)
+ FROM foo;
+
+ >>> # Parsing a SQL statement:
+ >>> parsed = sqlparse.parse('select * from foo')[0]
+ >>> parsed.tokens
+ [<DML 'select' at 0x7f22c5e15368>, <Whitespace ' ' at 0x7f22c5e153b0>, <Wildcard '*' … ]
+ >>>
+
+ Links
+ -----
+
+ Project page
+ https://github.com/andialbrecht/sqlparse
+
+ Bug tracker
+ https://github.com/andialbrecht/sqlparse/issues
+
+ Documentation
+ https://sqlparse.readthedocs.io/
+
+ Online Demo
+ https://sqlformat.org/
+
+
+ sqlparse is licensed under the BSD license.
+
+ Parts of the code are based on pygments written by Georg Brandl and others.
+ pygments-Homepage: http://pygments.org/
+ .. |buildstatus| image:: https://secure.travis-ci.org/andialbrecht/sqlparse.png?branch=master
+ .. _buildstatus: https://travis-ci.org/#!/andialbrecht/sqlparse
+ .. |coverage| image:: https://codecov.io/gh/andialbrecht/sqlparse/branch/master/graph/badge.svg
+ .. _coverage: https://codecov.io/gh/andialbrecht/sqlparse
+ .. |docs| image:: https://readthedocs.org/projects/sqlparse/badge/?version=latest
+ .. _docs: https://sqlparse.readthedocs.io/en/latest/?badge=latest
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
@@ -62,14 +92,15 @@ Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Database
Classifier: Topic :: Software Development
-Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Python: >=3.5
diff --git a/sqlparse.egg-info/SOURCES.txt b/sqlparse.egg-info/SOURCES.txt
index b3717d1..6f51c22 100644
--- a/sqlparse.egg-info/SOURCES.txt
+++ b/sqlparse.egg-info/SOURCES.txt
@@ -22,7 +22,6 @@ docs/source/ui.rst
sqlparse/__init__.py
sqlparse/__main__.py
sqlparse/cli.py
-sqlparse/compat.py
sqlparse/exceptions.py
sqlparse/formatter.py
sqlparse/keywords.py
@@ -59,6 +58,7 @@ tests/test_tokenize.py
tests/files/_Make_DirEntry.sql
tests/files/begintag.sql
tests/files/begintag_2.sql
+tests/files/casewhen_procedure.sql
tests/files/dashcomment.sql
tests/files/encoding_gbk.sql
tests/files/encoding_utf8.sql
@@ -68,5 +68,6 @@ tests/files/function_psql2.sql
tests/files/function_psql3.sql
tests/files/function_psql4.sql
tests/files/huge_select.sql
+tests/files/mysql_handler.sql
tests/files/stream.sql
tests/files/test_cp1251.sql \ No newline at end of file
diff --git a/sqlparse/__init__.py b/sqlparse/__init__.py
index f7e7aa6..edac28d 100644
--- a/sqlparse/__init__.py
+++ b/sqlparse/__init__.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -16,9 +15,8 @@ from sqlparse import tokens
from sqlparse import filters
from sqlparse import formatter
-from sqlparse.compat import text_type
-__version__ = '0.3.1'
+__version__ = '0.4.1'
__all__ = ['engine', 'filters', 'formatter', 'sql', 'tokens', 'cli']
@@ -58,7 +56,7 @@ def format(sql, encoding=None, **options):
options = formatter.validate_options(options)
stack = formatter.build_filter_stack(stack, options)
stack.postprocess.append(filters.SerializerUnicode())
- return u''.join(stack.run(sql, encoding))
+ return ''.join(stack.run(sql, encoding))
def split(sql, encoding=None):
@@ -69,4 +67,4 @@ def split(sql, encoding=None):
:returns: A list of strings.
"""
stack = engine.FilterStack()
- return [text_type(stmt).strip() for stmt in stack.run(sql, encoding)]
+ return [str(stmt).strip() for stmt in stack.run(sql, encoding)]
diff --git a/sqlparse/__main__.py b/sqlparse/__main__.py
index 867d75d..2bf2513 100644
--- a/sqlparse/__main__.py
+++ b/sqlparse/__main__.py
@@ -1,7 +1,6 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
diff --git a/sqlparse/cli.py b/sqlparse/cli.py
index 25555a5..7a8aacb 100755
--- a/sqlparse/cli.py
+++ b/sqlparse/cli.py
@@ -1,7 +1,6 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -23,10 +22,8 @@ Why does this file exist, and why not put this in __main__?
import argparse
import sys
from io import TextIOWrapper
-from codecs import open, getreader
import sqlparse
-from sqlparse.compat import PY2
from sqlparse.exceptions import SQLParseError
@@ -62,16 +59,16 @@ def create_parser():
metavar='CHOICE',
dest='keyword_case',
choices=_CASE_CHOICES,
- help='change case of keywords, CHOICE is one of {0}'.format(
- ', '.join('"{0}"'.format(x) for x in _CASE_CHOICES)))
+ help='change case of keywords, CHOICE is one of {}'.format(
+ ', '.join('"{}"'.format(x) for x in _CASE_CHOICES)))
group.add_argument(
'-i', '--identifiers',
metavar='CHOICE',
dest='identifier_case',
choices=_CASE_CHOICES,
- help='change case of identifiers, CHOICE is one of {0}'.format(
- ', '.join('"{0}"'.format(x) for x in _CASE_CHOICES)))
+ help='change case of identifiers, CHOICE is one of {}'.format(
+ ', '.join('"{}"'.format(x) for x in _CASE_CHOICES)))
group.add_argument(
'-l', '--language',
@@ -153,7 +150,7 @@ def create_parser():
def _error(msg):
"""Print msg and optionally exit with return code exit_."""
- sys.stderr.write(u'[ERROR] {0}\n'.format(msg))
+ sys.stderr.write('[ERROR] {}\n'.format(msg))
return 1
@@ -162,29 +159,26 @@ def main(args=None):
args = parser.parse_args(args)
if args.filename == '-': # read from stdin
- if PY2:
- data = getreader(args.encoding)(sys.stdin).read()
- else:
- wrapper = TextIOWrapper(sys.stdin.buffer, encoding=args.encoding)
- try:
- data = wrapper.read()
- finally:
- wrapper.detach()
+ wrapper = TextIOWrapper(sys.stdin.buffer, encoding=args.encoding)
+ try:
+ data = wrapper.read()
+ finally:
+ wrapper.detach()
else:
try:
- with open(args.filename, 'r', args.encoding) as f:
+ with open(args.filename, encoding=args.encoding) as f:
data = ''.join(f.readlines())
- except IOError as e:
+ except OSError as e:
return _error(
- u'Failed to read {0}: {1}'.format(args.filename, e))
+ 'Failed to read {}: {}'.format(args.filename, e))
close_stream = False
if args.outfile:
try:
- stream = open(args.outfile, 'w', args.encoding)
+ stream = open(args.outfile, 'w', encoding=args.encoding)
close_stream = True
- except IOError as e:
- return _error(u'Failed to open {0}: {1}'.format(args.outfile, e))
+ except OSError as e:
+ return _error('Failed to open {}: {}'.format(args.outfile, e))
else:
stream = sys.stdout
@@ -192,7 +186,7 @@ def main(args=None):
try:
formatter_opts = sqlparse.formatter.validate_options(formatter_opts)
except SQLParseError as e:
- return _error(u'Invalid options: {0}'.format(e))
+ return _error('Invalid options: {}'.format(e))
s = sqlparse.format(data, **formatter_opts)
stream.write(s)
diff --git a/sqlparse/compat.py b/sqlparse/compat.py
deleted file mode 100644
index d2214be..0000000
--- a/sqlparse/compat.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of python-sqlparse and is released under
-# the BSD License: https://opensource.org/licenses/BSD-3-Clause
-
-"""Python 2/3 compatibility.
-
-This module only exists to avoid a dependency on six
-for very trivial stuff. We only need to take care of
-string types, buffers and metaclasses.
-
-Parts of the code is copied directly from six:
-https://bitbucket.org/gutworth/six
-"""
-
-import sys
-from io import TextIOBase
-
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-
-
-if PY3:
- def unicode_compatible(cls):
- return cls
-
- text_type = str
- string_types = (str,)
- from io import StringIO
- file_types = (StringIO, TextIOBase)
-
-
-elif PY2:
- def unicode_compatible(cls):
- cls.__unicode__ = cls.__str__
- cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
- return cls
-
- text_type = unicode
- string_types = (str, unicode,)
- from StringIO import StringIO
- file_types = (file, StringIO, TextIOBase)
diff --git a/sqlparse/engine/__init__.py b/sqlparse/engine/__init__.py
index 0b3f3eb..6d54d51 100644
--- a/sqlparse/engine/__init__.py
+++ b/sqlparse/engine/__init__.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
diff --git a/sqlparse/engine/filter_stack.py b/sqlparse/engine/filter_stack.py
index fc77fd6..9665a22 100644
--- a/sqlparse/engine/filter_stack.py
+++ b/sqlparse/engine/filter_stack.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -13,7 +12,7 @@ from sqlparse.engine import grouping
from sqlparse.engine.statement_splitter import StatementSplitter
-class FilterStack(object):
+class FilterStack:
def __init__(self):
self.preprocess = []
self.stmtprocess = []
diff --git a/sqlparse/engine/grouping.py b/sqlparse/engine/grouping.py
index daaffb0..175ae8e 100644
--- a/sqlparse/engine/grouping.py
+++ b/sqlparse/engine/grouping.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -192,7 +191,7 @@ def group_assignment(tlist):
def group_comparison(tlist):
sqlcls = (sql.Parenthesis, sql.Function, sql.Identifier,
- sql.Operation)
+ sql.Operation, sql.TypedLiteral)
ttypes = T_NUMERICAL + T_STRING + T_NAME
def match(token):
@@ -433,6 +432,8 @@ def _group(tlist, cls, match,
pidx, prev_ = None, None
for idx, token in enumerate(list(tlist)):
tidx = idx - tidx_offset
+ if tidx < 0: # tidx shouldn't get negative
+ continue
if token.is_whitespace:
continue
diff --git a/sqlparse/engine/statement_splitter.py b/sqlparse/engine/statement_splitter.py
index 1e9af3c..a991959 100644
--- a/sqlparse/engine/statement_splitter.py
+++ b/sqlparse/engine/statement_splitter.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -9,7 +8,7 @@
from sqlparse import sql, tokens as T
-class StatementSplitter(object):
+class StatementSplitter:
"""Filter that split stream at individual statements"""
def __init__(self):
@@ -67,7 +66,7 @@ class StatementSplitter(object):
self._begin_depth = max(0, self._begin_depth - 1)
return -1
- if (unified in ('IF', 'FOR', 'WHILE')
+ if (unified in ('IF', 'FOR', 'WHILE', 'CASE')
and self._is_create and self._begin_depth > 0):
return 1
@@ -104,5 +103,5 @@ class StatementSplitter(object):
self.consume_ws = True
# Yield pending statement (if any)
- if self.tokens:
+ if self.tokens and not all(t.is_whitespace for t in self.tokens):
yield sql.Statement(self.tokens)
diff --git a/sqlparse/exceptions.py b/sqlparse/exceptions.py
index 01e60f7..11285da 100644
--- a/sqlparse/exceptions.py
+++ b/sqlparse/exceptions.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
diff --git a/sqlparse/filters/__init__.py b/sqlparse/filters/__init__.py
index c60d84d..5bd6b32 100644
--- a/sqlparse/filters/__init__.py
+++ b/sqlparse/filters/__init__.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
diff --git a/sqlparse/filters/aligned_indent.py b/sqlparse/filters/aligned_indent.py
index 85b11e5..dc60926 100644
--- a/sqlparse/filters/aligned_indent.py
+++ b/sqlparse/filters/aligned_indent.py
@@ -1,17 +1,15 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
# the BSD License: https://opensource.org/licenses/BSD-3-Clause
from sqlparse import sql, tokens as T
-from sqlparse.compat import text_type
from sqlparse.utils import offset, indent
-class AlignedIndentFilter(object):
+class AlignedIndentFilter:
join_words = (r'((LEFT\s+|RIGHT\s+|FULL\s+)?'
r'(INNER\s+|OUTER\s+|STRAIGHT\s+)?|'
r'(CROSS\s+|NATURAL\s+)?)?JOIN\b')
@@ -73,7 +71,7 @@ class AlignedIndentFilter(object):
end_token = tlist.token_next_by(m=(T.Keyword, 'END'))[1]
cases.append((None, [end_token]))
- condition_width = [len(' '.join(map(text_type, cond))) if cond else 0
+ condition_width = [len(' '.join(map(str, cond))) if cond else 0
for cond, _ in cases]
max_cond_width = max(condition_width)
@@ -82,8 +80,7 @@ class AlignedIndentFilter(object):
stmt = cond[0] if cond else value[0]
if i > 0:
- tlist.insert_before(stmt, self.nl(
- offset_ - len(text_type(stmt))))
+ tlist.insert_before(stmt, self.nl(offset_ - len(str(stmt))))
if cond:
ws = sql.Token(T.Whitespace, self.char * (
max_cond_width - condition_width[i]))
@@ -110,7 +107,7 @@ class AlignedIndentFilter(object):
):
token_indent = token.value.split()[0]
else:
- token_indent = text_type(token)
+ token_indent = str(token)
tlist.insert_before(token, self.nl(token_indent))
tidx += 1
tidx, token = self._next_token(tlist, tidx)
diff --git a/sqlparse/filters/others.py b/sqlparse/filters/others.py
index 52b8617..e0e1ca1 100644
--- a/sqlparse/filters/others.py
+++ b/sqlparse/filters/others.py
@@ -1,22 +1,33 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
# the BSD License: https://opensource.org/licenses/BSD-3-Clause
+import re
+
from sqlparse import sql, tokens as T
from sqlparse.utils import split_unquoted_newlines
-class StripCommentsFilter(object):
+class StripCommentsFilter:
+
@staticmethod
def _process(tlist):
def get_next_comment():
# TODO(andi) Comment types should be unified, see related issue38
return tlist.token_next_by(i=sql.Comment, t=T.Comment)
+ def _get_insert_token(token):
+ """Returns either a whitespace or the line breaks from token."""
+ # See issue484 why line breaks should be preserved.
+ m = re.search(r'((\r\n|\r|\n)+) *$', token.value)
+ if m is not None:
+ return sql.Token(T.Whitespace.Newline, m.groups()[0])
+ else:
+ return sql.Token(T.Whitespace, ' ')
+
tidx, token = get_next_comment()
while token:
pidx, prev_ = tlist.token_prev(tidx, skip_ws=False)
@@ -27,15 +38,12 @@ class StripCommentsFilter(object):
or prev_.is_whitespace or prev_.match(T.Punctuation, '(')
or next_.is_whitespace or next_.match(T.Punctuation, ')')):
# Insert a whitespace to ensure the following SQL produces
- # a valid SQL (see #425). For example:
- #
- # Before: select a--comment\nfrom foo
- # After: select a from foo
- if prev_ is not None and next_ is None:
- tlist.tokens.insert(tidx, sql.Token(T.Whitespace, ' '))
+ # a valid SQL (see #425).
+ if prev_ is not None and not prev_.match(T.Punctuation, '('):
+ tlist.tokens.insert(tidx, _get_insert_token(token))
tlist.tokens.remove(token)
else:
- tlist.tokens[tidx] = sql.Token(T.Whitespace, ' ')
+ tlist.tokens[tidx] = _get_insert_token(token)
tidx, token = get_next_comment()
@@ -45,7 +53,7 @@ class StripCommentsFilter(object):
return stmt
-class StripWhitespaceFilter(object):
+class StripWhitespaceFilter:
def _stripws(self, tlist):
func_name = '_stripws_{cls}'.format(cls=type(tlist).__name__)
func = getattr(self, func_name.lower(), self._stripws_default)
@@ -90,7 +98,7 @@ class StripWhitespaceFilter(object):
return stmt
-class SpacesAroundOperatorsFilter(object):
+class SpacesAroundOperatorsFilter:
@staticmethod
def _process(tlist):
@@ -118,7 +126,7 @@ class SpacesAroundOperatorsFilter(object):
# ---------------------------
# postprocess
-class SerializerUnicode(object):
+class SerializerUnicode:
@staticmethod
def process(stmt):
lines = split_unquoted_newlines(stmt)
diff --git a/sqlparse/filters/output.py b/sqlparse/filters/output.py
index 3fbc46d..253537e 100644
--- a/sqlparse/filters/output.py
+++ b/sqlparse/filters/output.py
@@ -1,16 +1,14 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
# the BSD License: https://opensource.org/licenses/BSD-3-Clause
from sqlparse import sql, tokens as T
-from sqlparse.compat import text_type
-class OutputFilter(object):
+class OutputFilter:
varname_prefix = ''
def __init__(self, varname='sql'):
@@ -23,11 +21,11 @@ class OutputFilter(object):
def process(self, stmt):
self.count += 1
if self.count > 1:
- varname = u'{f.varname}{f.count}'.format(f=self)
+ varname = '{f.varname}{f.count}'.format(f=self)
else:
varname = self.varname
- has_nl = len(text_type(stmt).strip().splitlines()) > 1
+ has_nl = len(str(stmt).strip().splitlines()) > 1
stmt.tokens = self._process(stmt.tokens, varname, has_nl)
return stmt
diff --git a/sqlparse/filters/reindent.py b/sqlparse/filters/reindent.py
index acec8ca..9fb232f 100644
--- a/sqlparse/filters/reindent.py
+++ b/sqlparse/filters/reindent.py
@@ -1,17 +1,15 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
# the BSD License: https://opensource.org/licenses/BSD-3-Clause
from sqlparse import sql, tokens as T
-from sqlparse.compat import text_type
from sqlparse.utils import offset, indent
-class ReindentFilter(object):
+class ReindentFilter:
def __init__(self, width=2, char=' ', wrap_after=0, n='\n',
comma_first=False, indent_after_first=False,
indent_columns=False):
@@ -42,7 +40,7 @@ class ReindentFilter(object):
return self.offset + self.indent * self.width
def _get_offset(self, token):
- raw = u''.join(map(text_type, self._flatten_up_to_token(token)))
+ raw = ''.join(map(str, self._flatten_up_to_token(token)))
line = (raw or '\n').splitlines()[-1]
# Now take current offset into account and return relative offset.
return len(line) - len(self.char * self.leading_ws)
@@ -71,7 +69,7 @@ class ReindentFilter(object):
tidx, token = self._next_token(tlist)
while token:
pidx, prev_ = tlist.token_prev(tidx, skip_ws=False)
- uprev = text_type(prev_)
+ uprev = str(prev_)
if prev_ and prev_.is_whitespace:
del tlist.tokens[pidx]
@@ -104,9 +102,10 @@ class ReindentFilter(object):
def _process_where(self, tlist):
tidx, token = tlist.token_next_by(m=(T.Keyword, 'WHERE'))
+ if not token:
+ return
# issue121, errors in statement fixed??
tlist.insert_before(tidx, self.nl())
-
with indent(self):
self._process_default(tlist)
@@ -114,6 +113,8 @@ class ReindentFilter(object):
ttypes = T.Keyword.DML, T.Keyword.DDL
_, is_dml_dll = tlist.token_next_by(t=ttypes)
fidx, first = tlist.token_next_by(m=sql.Parenthesis.M_OPEN)
+ if first is None:
+ return
with indent(self, 1 if is_dml_dll else 0):
tlist.tokens.insert(0, self.nl()) if is_dml_dll else None
@@ -234,7 +235,7 @@ class ReindentFilter(object):
self._process(stmt)
if self._last_stmt is not None:
- nl = '\n' if text_type(self._last_stmt).endswith('\n') else '\n\n'
+ nl = '\n' if str(self._last_stmt).endswith('\n') else '\n\n'
stmt.tokens.insert(0, sql.Token(T.Whitespace, nl))
self._last_stmt = stmt
diff --git a/sqlparse/filters/right_margin.py b/sqlparse/filters/right_margin.py
index 1658138..3e67056 100644
--- a/sqlparse/filters/right_margin.py
+++ b/sqlparse/filters/right_margin.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -9,11 +8,10 @@
import re
from sqlparse import sql, tokens as T
-from sqlparse.compat import text_type
# FIXME: Doesn't work
-class RightMarginFilter(object):
+class RightMarginFilter:
keep_together = (
# sql.TypeCast, sql.Identifier, sql.Alias,
)
@@ -32,14 +30,14 @@ class RightMarginFilter(object):
elif token.is_group and type(token) not in self.keep_together:
token.tokens = self._process(token, token.tokens)
else:
- val = text_type(token)
+ val = str(token)
if len(self.line) + len(val) > self.width:
match = re.search(r'^ +', self.line)
if match is not None:
indent = match.group()
else:
indent = ''
- yield sql.Token(T.Whitespace, '\n{0}'.format(indent))
+ yield sql.Token(T.Whitespace, '\n{}'.format(indent))
self.line = indent
self.line += val
yield token
diff --git a/sqlparse/filters/tokens.py b/sqlparse/filters/tokens.py
index 93182b1..cc00a84 100644
--- a/sqlparse/filters/tokens.py
+++ b/sqlparse/filters/tokens.py
@@ -1,21 +1,19 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
# the BSD License: https://opensource.org/licenses/BSD-3-Clause
from sqlparse import tokens as T
-from sqlparse.compat import text_type
-class _CaseFilter(object):
+class _CaseFilter:
ttype = None
def __init__(self, case=None):
case = case or 'upper'
- self.convert = getattr(text_type, case)
+ self.convert = getattr(str, case)
def process(self, stream):
for ttype, value in stream:
@@ -38,7 +36,7 @@ class IdentifierCaseFilter(_CaseFilter):
yield ttype, value
-class TruncateStringFilter(object):
+class TruncateStringFilter:
def __init__(self, width, char):
self.width = width
self.char = char
diff --git a/sqlparse/formatter.py b/sqlparse/formatter.py
index 8962759..1d1871c 100644
--- a/sqlparse/formatter.py
+++ b/sqlparse/formatter.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -17,32 +16,32 @@ def validate_options(options):
kwcase = options.get('keyword_case')
if kwcase not in [None, 'upper', 'lower', 'capitalize']:
raise SQLParseError('Invalid value for keyword_case: '
- '{0!r}'.format(kwcase))
+ '{!r}'.format(kwcase))
idcase = options.get('identifier_case')
if idcase not in [None, 'upper', 'lower', 'capitalize']:
raise SQLParseError('Invalid value for identifier_case: '
- '{0!r}'.format(idcase))
+ '{!r}'.format(idcase))
ofrmt = options.get('output_format')
if ofrmt not in [None, 'sql', 'python', 'php']:
raise SQLParseError('Unknown output format: '
- '{0!r}'.format(ofrmt))
+ '{!r}'.format(ofrmt))
strip_comments = options.get('strip_comments', False)
if strip_comments not in [True, False]:
raise SQLParseError('Invalid value for strip_comments: '
- '{0!r}'.format(strip_comments))
+ '{!r}'.format(strip_comments))
space_around_operators = options.get('use_space_around_operators', False)
if space_around_operators not in [True, False]:
raise SQLParseError('Invalid value for use_space_around_operators: '
- '{0!r}'.format(space_around_operators))
+ '{!r}'.format(space_around_operators))
strip_ws = options.get('strip_whitespace', False)
if strip_ws not in [True, False]:
raise SQLParseError('Invalid value for strip_whitespace: '
- '{0!r}'.format(strip_ws))
+ '{!r}'.format(strip_ws))
truncate_strings = options.get('truncate_strings')
if truncate_strings is not None:
@@ -50,17 +49,17 @@ def validate_options(options):
truncate_strings = int(truncate_strings)
except (ValueError, TypeError):
raise SQLParseError('Invalid value for truncate_strings: '
- '{0!r}'.format(truncate_strings))
+ '{!r}'.format(truncate_strings))
if truncate_strings <= 1:
raise SQLParseError('Invalid value for truncate_strings: '
- '{0!r}'.format(truncate_strings))
+ '{!r}'.format(truncate_strings))
options['truncate_strings'] = truncate_strings
options['truncate_char'] = options.get('truncate_char', '[...]')
indent_columns = options.get('indent_columns', False)
if indent_columns not in [True, False]:
raise SQLParseError('Invalid value for indent_columns: '
- '{0!r}'.format(indent_columns))
+ '{!r}'.format(indent_columns))
elif indent_columns:
options['reindent'] = True # enforce reindent
options['indent_columns'] = indent_columns
@@ -68,27 +67,27 @@ def validate_options(options):
reindent = options.get('reindent', False)
if reindent not in [True, False]:
raise SQLParseError('Invalid value for reindent: '
- '{0!r}'.format(reindent))
+ '{!r}'.format(reindent))
elif reindent:
options['strip_whitespace'] = True
reindent_aligned = options.get('reindent_aligned', False)
if reindent_aligned not in [True, False]:
raise SQLParseError('Invalid value for reindent_aligned: '
- '{0!r}'.format(reindent))
+ '{!r}'.format(reindent))
elif reindent_aligned:
options['strip_whitespace'] = True
indent_after_first = options.get('indent_after_first', False)
if indent_after_first not in [True, False]:
raise SQLParseError('Invalid value for indent_after_first: '
- '{0!r}'.format(indent_after_first))
+ '{!r}'.format(indent_after_first))
options['indent_after_first'] = indent_after_first
indent_tabs = options.get('indent_tabs', False)
if indent_tabs not in [True, False]:
raise SQLParseError('Invalid value for indent_tabs: '
- '{0!r}'.format(indent_tabs))
+ '{!r}'.format(indent_tabs))
elif indent_tabs:
options['indent_char'] = '\t'
else:
diff --git a/sqlparse/keywords.py b/sqlparse/keywords.py
index 9c37e50..a5deb93 100644
--- a/sqlparse/keywords.py
+++ b/sqlparse/keywords.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -45,7 +44,7 @@ SQL_REGEX = {
(r'(?<!\w)[$:?]\w+', tokens.Name.Placeholder),
(r'\\\w+', tokens.Command),
-
+ (r'(NOT\s+)?(IN)\b', tokens.Operator.Comparison),
# FIXME(andi): VALUES shouldn't be listed here
# see https://github.com/andialbrecht/sqlparse/pull/64
# AS and IN are special, it may be followed by a parenthesis, but
@@ -74,7 +73,7 @@ SQL_REGEX = {
# sqlite names can be escaped with [square brackets]. left bracket
# cannot be preceded by word character or a right bracket --
# otherwise it's probably an array index
- (r'(?<![\w\])])(\[[^\]]+\])', tokens.Name),
+ (r'(?<![\w\])])(\[[^\]\[]+\])', tokens.Name),
(r'((LEFT\s+|RIGHT\s+|FULL\s+)?(INNER\s+|OUTER\s+|STRAIGHT\s+)?'
r'|(CROSS\s+|NATURAL\s+)?)?JOIN\b', tokens.Keyword),
(r'END(\s+IF|\s+LOOP|\s+WHILE)?\b', tokens.Keyword),
@@ -85,11 +84,12 @@ SQL_REGEX = {
(r'DOUBLE\s+PRECISION\b', tokens.Name.Builtin),
(r'GROUP\s+BY\b', tokens.Keyword),
(r'ORDER\s+BY\b', tokens.Keyword),
+ (r'HANDLER\s+FOR\b', tokens.Keyword),
(r'(LATERAL\s+VIEW\s+)'
r'(EXPLODE|INLINE|PARSE_URL_TUPLE|POSEXPLODE|STACK)\b',
tokens.Keyword),
(r"(AT|WITH')\s+TIME\s+ZONE\s+'[^']+'", tokens.Keyword.TZCast),
- (r'(NOT\s+)?(LIKE|ILIKE)\b', tokens.Operator.Comparison),
+ (r'(NOT\s+)?(LIKE|ILIKE|RLIKE)\b', tokens.Operator.Comparison),
(r'[0-9_A-ZÀ-Ü][_$#\w]*', is_keyword),
(r'[;:()\[\],\.]', tokens.Punctuation),
(r'[<>=~!]+', tokens.Operator.Comparison),
@@ -295,7 +295,6 @@ KEYWORDS = {
'GRANTED': tokens.Keyword,
'GROUPING': tokens.Keyword,
- 'HANDLER': tokens.Keyword,
'HAVING': tokens.Keyword,
'HIERARCHY': tokens.Keyword,
'HOLD': tokens.Keyword,
@@ -834,6 +833,7 @@ KEYWORDS_ORACLE = {
# PostgreSQL Syntax
KEYWORDS_PLPGSQL = {
+ 'WINDOW': tokens.Keyword,
'PARTITION': tokens.Keyword,
'OVER': tokens.Keyword,
'PERFORM': tokens.Keyword,
diff --git a/sqlparse/lexer.py b/sqlparse/lexer.py
index fd007a4..4397f18 100644
--- a/sqlparse/lexer.py
+++ b/sqlparse/lexer.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -13,13 +12,14 @@
# It's separated from the rest of pygments to increase performance
# and to allow some customizations.
+from io import TextIOBase
+
from sqlparse import tokens
from sqlparse.keywords import SQL_REGEX
-from sqlparse.compat import text_type, file_types
from sqlparse.utils import consume
-class Lexer(object):
+class Lexer:
"""Lexer
Empty class. Leaving for backwards-compatibility
"""
@@ -38,10 +38,10 @@ class Lexer(object):
``stack`` is the initial stack (default: ``['root']``)
"""
- if isinstance(text, file_types):
+ if isinstance(text, TextIOBase):
text = text.read()
- if isinstance(text, text_type):
+ if isinstance(text, str):
pass
elif isinstance(text, bytes):
if encoding:
@@ -52,7 +52,7 @@ class Lexer(object):
except UnicodeDecodeError:
text = text.decode('unicode-escape')
else:
- raise TypeError(u"Expected text or file-like object, got {!r}".
+ raise TypeError("Expected text or file-like object, got {!r}".
format(type(text)))
iterable = enumerate(text)
diff --git a/sqlparse/sql.py b/sqlparse/sql.py
index a942bcd..6a32c26 100644
--- a/sqlparse/sql.py
+++ b/sqlparse/sql.py
@@ -1,18 +1,15 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
# the BSD License: https://opensource.org/licenses/BSD-3-Clause
"""This module contains classes representing syntactical elements of SQL."""
-from __future__ import print_function
import re
from sqlparse import tokens as T
-from sqlparse.compat import string_types, text_type, unicode_compatible
from sqlparse.utils import imt, remove_quotes
@@ -39,8 +36,7 @@ class NameAliasMixin:
return self._get_first_name(reverse=True)
-@unicode_compatible
-class Token(object):
+class Token:
"""Base class for all other classes in this module.
It represents a single token and has two instance attributes:
@@ -52,7 +48,7 @@ class Token(object):
'is_group', 'is_whitespace')
def __init__(self, ttype, value):
- value = text_type(value)
+ value = str(value)
self.value = value
self.ttype = ttype
self.parent = None
@@ -72,15 +68,15 @@ class Token(object):
cls = self._get_repr_name()
value = self._get_repr_value()
- q = u'"' if value.startswith("'") and value.endswith("'") else u"'"
- return u"<{cls} {q}{value}{q} at 0x{id:2X}>".format(
+ q = '"' if value.startswith("'") and value.endswith("'") else "'"
+ return "<{cls} {q}{value}{q} at 0x{id:2X}>".format(
id=id(self), **locals())
def _get_repr_name(self):
return str(self.ttype).split('.')[-1]
def _get_repr_value(self):
- raw = text_type(self)
+ raw = str(self)
if len(raw) > 7:
raw = raw[:6] + '...'
return re.sub(r'\s+', ' ', raw)
@@ -105,7 +101,7 @@ class Token(object):
if not type_matched or values is None:
return type_matched
- if isinstance(values, string_types):
+ if isinstance(values, str):
values = (values,)
if regex:
@@ -150,7 +146,6 @@ class Token(object):
return False
-@unicode_compatible
class TokenList(Token):
"""A group of tokens.
@@ -163,11 +158,11 @@ class TokenList(Token):
def __init__(self, tokens=None):
self.tokens = tokens or []
[setattr(token, 'parent', self) for token in self.tokens]
- super(TokenList, self).__init__(None, text_type(self))
+ super().__init__(None, str(self))
self.is_group = True
def __str__(self):
- return u''.join(token.value for token in self.flatten())
+ return ''.join(token.value for token in self.flatten())
# weird bug
# def __len__(self):
@@ -190,14 +185,14 @@ class TokenList(Token):
value = token._get_repr_value()
last = idx == (token_count - 1)
- pre = u'`- ' if last else u'|- '
+ pre = '`- ' if last else '|- '
- q = u'"' if value.startswith("'") and value.endswith("'") else u"'"
- print(u"{_pre}{pre}{idx} {cls} {q}{value}{q}"
+ q = '"' if value.startswith("'") and value.endswith("'") else "'"
+ print("{_pre}{pre}{idx} {cls} {q}{value}{q}"
.format(**locals()), file=f)
if token.is_group and (max_depth is None or depth < max_depth):
- parent_pre = u' ' if last else u'| '
+ parent_pre = ' ' if last else '| '
token._pprint_tree(max_depth, depth + 1, f, _pre + parent_pre)
def get_token_at_offset(self, offset):
@@ -216,8 +211,7 @@ class TokenList(Token):
"""
for token in self.tokens:
if token.is_group:
- for item in token.flatten():
- yield item
+ yield from token.flatten()
else:
yield token
@@ -328,7 +322,7 @@ class TokenList(Token):
grp = start
grp.tokens.extend(subtokens)
del self.tokens[start_idx + 1:end_idx]
- grp.value = text_type(start)
+ grp.value = str(start)
else:
subtokens = self.tokens[start_idx:end_idx]
grp = grp_cls(subtokens)
diff --git a/sqlparse/tokens.py b/sqlparse/tokens.py
index eefc0b4..d92bbdc 100644
--- a/sqlparse/tokens.py
+++ b/sqlparse/tokens.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
diff --git a/sqlparse/utils.py b/sqlparse/utils.py
index 3283274..299a84c 100644
--- a/sqlparse/utils.py
+++ b/sqlparse/utils.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
#
-# Copyright (C) 2009-2018 the sqlparse authors and contributors
+# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
@@ -10,7 +9,6 @@ import itertools
import re
from collections import deque
from contextlib import contextmanager
-from sqlparse.compat import text_type
# This regular expression replaces the home-cooked parser that was here before.
# It is much faster, but requires an extra post-processing step to get the
@@ -40,7 +38,7 @@ def split_unquoted_newlines(stmt):
Unlike str.splitlines(), this will ignore CR/LF/CR+LF if the requisite
character is inside of a string."""
- text = text_type(stmt)
+ text = str(stmt)
lines = SPLIT_REGEX.split(text)
outputlines = ['']
for line in lines:
diff --git a/tests/conftest.py b/tests/conftest.py
index f2473a4..939c481 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
"""Helpers for testing."""
import io
@@ -35,7 +33,7 @@ def load_file(filepath):
# https://stackoverflow.com/questions/18011902/py-test-pass-a-parameter-to-a-fixture-function/33879151#33879151
# Syntax is noisy and requires specific variable names
# And seems to be limited to only 1 argument.
- with io.open(filepath(filename), encoding=encoding) as f:
+ with open(filepath(filename), encoding=encoding) as f:
return f.read().strip()
return make_load_file
@@ -44,6 +42,6 @@ def load_file(filepath):
@pytest.fixture()
def get_stream(filepath):
def make_stream(filename, encoding='utf-8'):
- return io.open(filepath(filename), encoding=encoding)
+ return open(filepath(filename), encoding=encoding)
return make_stream
diff --git a/tests/files/casewhen_procedure.sql b/tests/files/casewhen_procedure.sql
new file mode 100644
index 0000000..e590d49
--- /dev/null
+++ b/tests/files/casewhen_procedure.sql
@@ -0,0 +1,8 @@
+create procedure procName()
+begin
+ select case when column = 'value' then column else 0 end;
+end;
+create procedure procName()
+begin
+ select 1;
+end;
diff --git a/tests/files/mysql_handler.sql b/tests/files/mysql_handler.sql
new file mode 100644
index 0000000..702374e
--- /dev/null
+++ b/tests/files/mysql_handler.sql
@@ -0,0 +1,10 @@
+create procedure proc1()
+begin
+ declare handler for foo begin end;
+ select 1;
+end;
+
+create procedure proc2()
+begin
+ select 1;
+end;
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 5f1ea0e..b681a60 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
import subprocess
import sys
@@ -71,75 +69,54 @@ def test_stdout(filepath, load_file, capsys):
def test_script():
# Call with the --help option as a basic sanity check.
- cmd = "{0:s} -m sqlparse.cli --help".format(sys.executable)
+ cmd = "{:s} -m sqlparse.cli --help".format(sys.executable)
assert subprocess.call(cmd.split()) == 0
-def test_encoding_utf8_stdout(filepath, load_file, capfd):
- path = filepath('encoding_utf8.sql')
- expected = load_file('encoding_utf8.sql', 'utf-8')
- sys.stdout.encoding = 'utf-8'
- sqlparse.cli.main([path])
- out, _ = capfd.readouterr()
- assert out == expected
-
-
-def test_encoding_utf8_output_file(filepath, load_file, tmpdir):
- in_path = filepath('encoding_utf8.sql')
- expected = load_file('encoding_utf8.sql', 'utf-8')
- out_path = tmpdir.dirname + '/encoding_utf8.out.sql'
- sqlparse.cli.main([in_path, '-o', out_path])
- out = load_file(out_path, 'utf-8')
- assert out == expected
-
-
-def test_encoding_gbk_stdout(filepath, load_file, capfd):
- path = filepath('encoding_gbk.sql')
- expected = load_file('encoding_gbk.sql', 'gbk')
- sys.stdout.encoding = 'gbk'
- sqlparse.cli.main([path, '--encoding', 'gbk'])
+@pytest.mark.parametrize('fpath, encoding', (
+ ('encoding_utf8.sql', 'utf-8'),
+ ('encoding_gbk.sql', 'gbk'),
+))
+def test_encoding_stdout(fpath, encoding, filepath, load_file, capfd):
+ path = filepath(fpath)
+ expected = load_file(fpath, encoding)
+ sqlparse.cli.main([path, '--encoding', encoding])
out, _ = capfd.readouterr()
assert out == expected
-def test_encoding_gbk_output_file(filepath, load_file, tmpdir):
- in_path = filepath('encoding_gbk.sql')
- expected = load_file('encoding_gbk.sql', 'gbk')
- out_path = tmpdir.dirname + '/encoding_gbk.out.sql'
- sqlparse.cli.main([in_path, '--encoding', 'gbk', '-o', out_path])
- out = load_file(out_path, 'gbk')
+@pytest.mark.parametrize('fpath, encoding', (
+ ('encoding_utf8.sql', 'utf-8'),
+ ('encoding_gbk.sql', 'gbk'),
+))
+def test_encoding_output_file(fpath, encoding, filepath, load_file, tmpdir):
+ in_path = filepath(fpath)
+ expected = load_file(fpath, encoding)
+ out_path = tmpdir.dirname + '/encoding_out.sql'
+ sqlparse.cli.main([in_path, '--encoding', encoding, '-o', out_path])
+ out = load_file(out_path, encoding)
assert out == expected
-def test_encoding_stdin_utf8(filepath, load_file, capfd):
- path = filepath('encoding_utf8.sql')
- expected = load_file('encoding_utf8.sql', 'utf-8')
+@pytest.mark.parametrize('fpath, encoding', (
+ ('encoding_utf8.sql', 'utf-8'),
+ ('encoding_gbk.sql', 'gbk'),
+))
+def test_encoding_stdin(fpath, encoding, filepath, load_file, capfd):
+ path = filepath(fpath)
+ expected = load_file(fpath, encoding)
old_stdin = sys.stdin
- with open(path, 'r') as f:
+ with open(path) as f:
sys.stdin = f
- sys.stdout.encoding = 'utf-8'
- sqlparse.cli.main(['-'])
+ sqlparse.cli.main(['-', '--encoding', encoding])
sys.stdin = old_stdin
out, _ = capfd.readouterr()
assert out == expected
-def test_encoding_stdin_gbk(filepath, load_file, capfd):
- path = filepath('encoding_gbk.sql')
- expected = load_file('encoding_gbk.sql', 'gbk')
- old_stdin = sys.stdin
- with open(path, 'r') as stream:
- sys.stdin = stream
- sys.stdout.encoding = 'gbk'
- sqlparse.cli.main(['-', '--encoding', 'gbk'])
- sys.stdin = old_stdin
- out, _ = capfd.readouterr()
- assert out == expected
-
-
def test_encoding(filepath, capsys):
path = filepath('test_cp1251.sql')
- expected = u'insert into foo values (1); -- Песня про надежду\n'
+ expected = 'insert into foo values (1); -- Песня про надежду\n'
sqlparse.cli.main([path, '--encoding=cp1251'])
out, _ = capsys.readouterr()
assert out == expected
diff --git a/tests/test_format.py b/tests/test_format.py
index 811e083..7117d9d 100644
--- a/tests/test_format.py
+++ b/tests/test_format.py
@@ -1,12 +1,10 @@
-# -*- coding: utf-8 -*-
-
import pytest
import sqlparse
from sqlparse.exceptions import SQLParseError
-class TestFormat(object):
+class TestFormat:
def test_keywordcase(self):
sql = 'select * from bar; -- select foo\n'
res = sqlparse.format(sql, keyword_case='upper')
@@ -43,26 +41,26 @@ class TestFormat(object):
def test_strip_comments_single(self):
sql = 'select *-- statement starts here\nfrom foo'
res = sqlparse.format(sql, strip_comments=True)
- assert res == 'select * from foo'
+ assert res == 'select *\nfrom foo'
sql = 'select * -- statement starts here\nfrom foo'
res = sqlparse.format(sql, strip_comments=True)
- assert res == 'select * from foo'
+ assert res == 'select *\nfrom foo'
sql = 'select-- foo\nfrom -- bar\nwhere'
res = sqlparse.format(sql, strip_comments=True)
- assert res == 'select from where'
+ assert res == 'select\nfrom\nwhere'
sql = 'select *-- statement starts here\n\nfrom foo'
res = sqlparse.format(sql, strip_comments=True)
- assert res == 'select * from foo'
+ assert res == 'select *\n\nfrom foo'
sql = 'select * from foo-- statement starts here\nwhere'
res = sqlparse.format(sql, strip_comments=True)
- assert res == 'select * from foo where'
+ assert res == 'select * from foo\nwhere'
sql = 'select a-- statement starts here\nfrom foo'
res = sqlparse.format(sql, strip_comments=True)
- assert res == 'select a from foo'
+ assert res == 'select a\nfrom foo'
sql = '--comment\nselect a-- statement starts here\n' \
'from foo--comment\nf'
res = sqlparse.format(sql, strip_comments=True)
- assert res == 'select a from foo f'
+ assert res == 'select a\nfrom foo\nf'
def test_strip_comments_invalid_option(self):
sql = 'select-- foo\nfrom -- bar\nwhere'
@@ -123,7 +121,7 @@ class TestFormat(object):
== "SELECT some_column LIKE 'value\\\\\\'\r' WHERE id = 1\n")
-class TestFormatReindentAligned(object):
+class TestFormatReindentAligned:
@staticmethod
def formatter(sql):
return sqlparse.format(sql, reindent_aligned=True)
@@ -294,7 +292,7 @@ class TestFormatReindentAligned(object):
' from table'])
-class TestSpacesAroundOperators(object):
+class TestSpacesAroundOperators:
@staticmethod
def formatter(sql):
return sqlparse.format(sql, use_space_around_operators=True)
@@ -321,7 +319,7 @@ class TestSpacesAroundOperators(object):
assert self.formatter(sql) == 'select a * b - c from table'
-class TestFormatReindent(object):
+class TestFormatReindent:
def test_option(self):
with pytest.raises(SQLParseError):
sqlparse.format('foo', reindent=2)
@@ -598,7 +596,7 @@ class TestFormatReindent(object):
' , (5, 6)'])
-class TestOutputFormat(object):
+class TestOutputFormat:
def test_python(self):
sql = 'select * from foo;'
f = lambda sql: sqlparse.format(sql, output_format='python')
@@ -663,7 +661,7 @@ def test_format_column_ordering():
def test_truncate_strings():
- sql = "update foo set value = '{0}';".format('x' * 1000)
+ sql = "update foo set value = '{}';".format('x' * 1000)
formatted = sqlparse.format(sql, truncate_strings=10)
assert formatted == "update foo set value = 'xxxxxxxxxx[...]';"
formatted = sqlparse.format(sql, truncate_strings=3, truncate_char='YYY')
diff --git a/tests/test_grouping.py b/tests/test_grouping.py
index a147063..cf629e9 100644
--- a/tests/test_grouping.py
+++ b/tests/test_grouping.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
import pytest
import sqlparse
@@ -36,7 +34,7 @@ def test_grouping_assignment(s):
@pytest.mark.parametrize('s', ["x > DATE '2020-01-01'", "x > TIMESTAMP '2020-01-01 00:00:00'"])
def test_grouping_typed_literal(s):
parsed = sqlparse.parse(s)[0]
- assert isinstance(parsed[4], sql.TypedLiteral)
+ assert isinstance(parsed[0][4], sql.TypedLiteral)
@pytest.mark.parametrize('s, a, b', [
@@ -373,10 +371,20 @@ def test_grouping_function_not_in():
# issue183
p = sqlparse.parse('in(1, 2)')[0]
assert len(p.tokens) == 2
- assert p.tokens[0].ttype == T.Keyword
+ assert p.tokens[0].ttype == T.Comparison
assert isinstance(p.tokens[1], sql.Parenthesis)
+def test_in_comparison():
+ # issue566
+ p = sqlparse.parse('a in (1, 2)')[0]
+ assert len(p.tokens) == 1
+ assert isinstance(p.tokens[0], sql.Comparison)
+ assert len(p.tokens[0].tokens) == 5
+ assert p.tokens[0].left.value == 'a'
+ assert p.tokens[0].right.value == '(1, 2)'
+
+
def test_grouping_varchar():
p = sqlparse.parse('"text" Varchar(50) NOT NULL')[0]
assert isinstance(p.tokens[2], sql.Function)
@@ -391,10 +399,6 @@ def test_statement_get_type():
assert f(' update foo').get_type() == 'UPDATE'
assert f('\nupdate foo').get_type() == 'UPDATE'
assert f('foo').get_type() == 'UNKNOWN'
- # Statements that have a whitespace after the closing semicolon
- # are parsed as two statements where later only consists of the
- # trailing whitespace.
- assert f('\n').get_type() == 'UNKNOWN'
def test_identifier_with_operators():
@@ -484,7 +488,7 @@ def test_comparison_with_parenthesis():
))
def test_comparison_with_strings(operator):
# issue148
- p = sqlparse.parse("foo {0} 'bar'".format(operator))[0]
+ p = sqlparse.parse("foo {} 'bar'".format(operator))[0]
assert len(p.tokens) == 1
assert isinstance(p.tokens[0], sql.Comparison)
assert p.tokens[0].right.value == "'bar'"
@@ -550,9 +554,20 @@ def test_comparison_with_functions():
assert p.tokens[0].right.value == 'bar.baz'
+def test_comparison_with_typed_literal():
+ p = sqlparse.parse("foo = DATE 'bar.baz'")[0]
+ assert len(p.tokens) == 1
+ comp = p.tokens[0]
+ assert isinstance(comp, sql.Comparison)
+ assert len(comp.tokens) == 5
+ assert comp.left.value == 'foo'
+ assert isinstance(comp.right, sql.TypedLiteral)
+ assert comp.right.value == "DATE 'bar.baz'"
+
+
@pytest.mark.parametrize('start', ['FOR', 'FOREACH'])
def test_forloops(start):
- p = sqlparse.parse('{0} foo in bar LOOP foobar END LOOP'.format(start))[0]
+ p = sqlparse.parse('{} foo in bar LOOP foobar END LOOP'.format(start))[0]
assert (len(p.tokens)) == 1
assert isinstance(p.tokens[0], sql.For)
diff --git a/tests/test_keywords.py b/tests/test_keywords.py
index c197f36..d4ded4b 100644
--- a/tests/test_keywords.py
+++ b/tests/test_keywords.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import pytest
from sqlparse import tokens
diff --git a/tests/test_parse.py b/tests/test_parse.py
index c28cb06..5fe6ed2 100644
--- a/tests/test_parse.py
+++ b/tests/test_parse.py
@@ -1,12 +1,10 @@
-# -*- coding: utf-8 -*-
-
"""Tests sqlparse.parse()."""
+from io import StringIO
import pytest
import sqlparse
from sqlparse import sql, tokens as T
-from sqlparse.compat import StringIO, text_type
def test_parse_tokenize():
@@ -102,6 +100,12 @@ def test_parse_square_brackets_notation_isnt_too_greedy():
assert t[0].tokens[-1].get_real_name() == '[bar]'
+def test_parse_square_brackets_notation_isnt_too_greedy2():
+ # see issue583
+ t = sqlparse.parse('[(foo[i])]')[0].tokens
+ assert isinstance(t[0], sql.SquareBrackets) # not Identifier!
+
+
def test_parse_keyword_like_identifier():
# see issue47
t = sqlparse.parse('foo.key')[0].tokens
@@ -409,26 +413,26 @@ def test_dbldollar_as_literal(sql, is_literal):
def test_non_ascii():
- _test_non_ascii = u"insert into test (id, name) values (1, 'тест');"
+ _test_non_ascii = "insert into test (id, name) values (1, 'тест');"
s = _test_non_ascii
stmts = sqlparse.parse(s)
assert len(stmts) == 1
statement = stmts[0]
- assert text_type(statement) == s
+ assert str(statement) == s
assert statement._pprint_tree() is None
s = _test_non_ascii.encode('utf-8')
stmts = sqlparse.parse(s, 'utf-8')
assert len(stmts) == 1
statement = stmts[0]
- assert text_type(statement) == _test_non_ascii
+ assert str(statement) == _test_non_ascii
assert statement._pprint_tree() is None
def test_get_real_name():
# issue 369
- s = u"update a t set t.b=1"
+ s = "update a t set t.b=1"
stmts = sqlparse.parse(s)
assert len(stmts) == 1
assert 'a' == stmts[0].tokens[2].get_real_name()
@@ -437,14 +441,14 @@ def test_get_real_name():
def test_from_subquery():
# issue 446
- s = u'from(select 1)'
+ s = 'from(select 1)'
stmts = sqlparse.parse(s)
assert len(stmts) == 1
assert len(stmts[0].tokens) == 2
assert stmts[0].tokens[0].value == 'from'
assert stmts[0].tokens[0].ttype == T.Keyword
- s = u'from (select 1)'
+ s = 'from (select 1)'
stmts = sqlparse.parse(s)
assert len(stmts) == 1
assert len(stmts[0].tokens) == 3
diff --git a/tests/test_regressions.py b/tests/test_regressions.py
index 2ed0ff3..b247ce2 100644
--- a/tests/test_regressions.py
+++ b/tests/test_regressions.py
@@ -1,10 +1,7 @@
-# -*- coding: utf-8 -*-
-
import pytest
import sqlparse
from sqlparse import sql, tokens as T
-from sqlparse.compat import PY2
def test_issue9():
@@ -20,9 +17,9 @@ def test_issue9():
def test_issue13():
- parsed = sqlparse.parse(("select 'one';\n"
- "select 'two\\'';\n"
- "select 'three';"))
+ parsed = sqlparse.parse("select 'one';\n"
+ "select 'two\\'';\n"
+ "select 'three';")
assert len(parsed) == 3
assert str(parsed[1]).strip() == "select 'two\\'';"
@@ -73,8 +70,8 @@ def test_issue39():
def test_issue40():
# make sure identifier lists in subselects are grouped
- p = sqlparse.parse(('SELECT id, name FROM '
- '(SELECT id, name FROM bar) as foo'))[0]
+ p = sqlparse.parse('SELECT id, name FROM '
+ '(SELECT id, name FROM bar) as foo')[0]
assert len(p.tokens) == 7
assert p.tokens[2].__class__ == sql.IdentifierList
assert p.tokens[-1].__class__ == sql.Identifier
@@ -149,7 +146,7 @@ def test_issue83():
def test_comment_encoding_when_reindent():
# There was an UnicodeEncodeError in the reindent filter that
# casted every comment followed by a keyword to str.
- sql = u'select foo -- Comment containing Ümläuts\nfrom bar'
+ sql = 'select foo -- Comment containing Ümläuts\nfrom bar'
formatted = sqlparse.format(sql, reindent=True)
assert formatted == sql
@@ -158,11 +155,9 @@ def test_parse_sql_with_binary():
# See https://github.com/andialbrecht/sqlparse/pull/88
# digest = '‚|ËêŠplL4¡h‘øN{'
digest = '\x82|\xcb\x0e\xea\x8aplL4\xa1h\x91\xf8N{'
- sql = "select * from foo where bar = '{0}'".format(digest)
+ sql = "select * from foo where bar = '{}'".format(digest)
formatted = sqlparse.format(sql, reindent=True)
- tformatted = "select *\nfrom foo\nwhere bar = '{0}'".format(digest)
- if PY2:
- tformatted = tformatted.decode('unicode-escape')
+ tformatted = "select *\nfrom foo\nwhere bar = '{}'".format(digest)
assert formatted == tformatted
@@ -180,7 +175,7 @@ def test_format_accepts_encoding(load_file):
# issue20
sql = load_file('test_cp1251.sql', 'cp1251')
formatted = sqlparse.format(sql, reindent=True, encoding='cp1251')
- tformatted = u'insert into foo\nvalues (1); -- Песня про надежду'
+ tformatted = 'insert into foo\nvalues (1); -- Песня про надежду'
assert formatted == tformatted
@@ -275,7 +270,7 @@ def test_issue186_get_type():
def test_issue212_py2unicode():
- t1 = sql.Token(T.String, u'schöner ')
+ t1 = sql.Token(T.String, 'schöner ')
t2 = sql.Token(T.String, 'bug')
token_list = sql.TokenList([t1, t2])
assert str(token_list) == 'schöner bug'
@@ -337,11 +332,9 @@ def test_issue315_utf8_by_default():
'\x9b\xb2.'
'\xec\x82\xac\xeb\x9e\x91\xed\x95\xb4\xec\x9a\x94'
)
- sql = "select * from foo where bar = '{0}'".format(digest)
+ sql = "select * from foo where bar = '{}'".format(digest)
formatted = sqlparse.format(sql, reindent=True)
- tformatted = "select *\nfrom foo\nwhere bar = '{0}'".format(digest)
- if PY2:
- tformatted = tformatted.decode('utf-8')
+ tformatted = "select *\nfrom foo\nwhere bar = '{}'".format(digest)
assert formatted == tformatted
@@ -406,3 +399,15 @@ def test_issue489_tzcasts():
p = sqlparse.parse('select bar at time zone \'UTC\' as foo')[0]
assert p.tokens[-1].has_alias() is True
assert p.tokens[-1].get_alias() == 'foo'
+
+
+def test_as_in_parentheses_indents():
+ # did raise NoneType has no attribute is_group in _process_parentheses
+ formatted = sqlparse.format('(as foo)', reindent=True)
+ assert formatted == '(as foo)'
+
+
+def test_format_invalid_where_clause():
+ # did raise ValueError
+ formatted = sqlparse.format('where, foo', reindent=True)
+ assert formatted == 'where, foo'
diff --git a/tests/test_split.py b/tests/test_split.py
index a93e3d4..a9d7576 100644
--- a/tests/test_split.py
+++ b/tests/test_split.py
@@ -1,13 +1,11 @@
-# -*- coding: utf-8 -*-
-
# Tests splitting functions.
import types
+from io import StringIO
import pytest
import sqlparse
-from sqlparse.compat import StringIO, text_type
def test_split_semicolon():
@@ -33,7 +31,7 @@ def test_split_create_function(load_file, fn):
sql = load_file(fn)
stmts = sqlparse.parse(sql)
assert len(stmts) == 1
- assert text_type(stmts[0]) == sql
+ assert str(stmts[0]) == sql
def test_split_dashcomments(load_file):
@@ -74,12 +72,12 @@ def test_split_dropif():
def test_split_comment_with_umlaut():
- sql = (u'select * from foo;\n'
- u'-- Testing an umlaut: ä\n'
- u'select * from bar;')
+ sql = ('select * from foo;\n'
+ '-- Testing an umlaut: ä\n'
+ 'select * from bar;')
stmts = sqlparse.parse(sql)
assert len(stmts) == 2
- assert ''.join(text_type(q) for q in stmts) == sql
+ assert ''.join(str(q) for q in stmts) == sql
def test_split_comment_end_of_line():
@@ -99,6 +97,12 @@ def test_split_casewhen():
assert len(stmts) == 2
+def test_split_casewhen_procedure(load_file):
+ # see issue580
+ stmts = sqlparse.split(load_file('casewhen_procedure.sql'))
+ assert len(stmts) == 2
+
+
def test_split_cursor_declare():
sql = ('DECLARE CURSOR "foo" AS SELECT 1;\n'
'SELECT 2;')
@@ -125,11 +129,11 @@ def test_split_stream():
def test_split_encoding_parsestream():
stream = StringIO("SELECT 1; SELECT 2;")
stmts = list(sqlparse.parsestream(stream))
- assert isinstance(stmts[0].tokens[0].value, text_type)
+ assert isinstance(stmts[0].tokens[0].value, str)
def test_split_unicode_parsestream():
- stream = StringIO(u'SELECT ö')
+ stream = StringIO('SELECT ö')
stmts = list(sqlparse.parsestream(stream))
assert str(stmts[0]) == 'SELECT ö'
@@ -141,6 +145,13 @@ def test_split_simple():
assert stmts[1] == 'select * from bar;'
+def test_split_ignores_empty_newlines():
+ stmts = sqlparse.split('select foo;\nselect bar;\n')
+ assert len(stmts) == 2
+ assert stmts[0] == 'select foo;'
+ assert stmts[1] == 'select bar;'
+
+
def test_split_quotes_with_new_line():
stmts = sqlparse.split('select "foo\nbar"')
assert len(stmts) == 1
@@ -149,3 +160,9 @@ def test_split_quotes_with_new_line():
stmts = sqlparse.split("select 'foo\n\bar'")
assert len(stmts) == 1
assert stmts[0] == "select 'foo\n\bar'"
+
+
+def test_split_mysql_handler_for(load_file):
+ # see issue581
+ stmts = sqlparse.split(load_file('mysql_handler.sql'))
+ assert len(stmts) == 2
diff --git a/tests/test_tokenize.py b/tests/test_tokenize.py
index 3e8831b..af0ba16 100644
--- a/tests/test_tokenize.py
+++ b/tests/test_tokenize.py
@@ -1,13 +1,11 @@
-# -*- coding: utf-8 -*-
-
import types
+from io import StringIO
import pytest
import sqlparse
from sqlparse import lexer
from sqlparse import sql, tokens as T
-from sqlparse.compat import StringIO
def test_tokenize_simple():
@@ -152,7 +150,7 @@ def test_stream_error():
'INNER JOIN',
'LEFT INNER JOIN'])
def test_parse_join(expr):
- p = sqlparse.parse('{0} foo'.format(expr))[0]
+ p = sqlparse.parse('{} foo'.format(expr))[0]
assert len(p.tokens) == 3
assert p.tokens[0].ttype is T.Keyword
@@ -204,6 +202,12 @@ def test_parse_order_by():
assert p.tokens[0].ttype is T.Keyword
+def test_parse_window_as():
+ p = sqlparse.parse('WINDOW w AS')[0]
+ assert len(p.tokens) == 5
+ assert p.tokens[0].ttype is T.Keyword
+
+
@pytest.mark.parametrize('s', (
"LIKE", "ILIKE", "NOT LIKE", "NOT ILIKE",
"NOT LIKE", "NOT ILIKE",
diff --git a/tox.ini b/tox.ini
index 2e5010a..0087d50 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,22 +1,17 @@
[tox]
skip_missing_interpreters = True
envlist =
- py27
- py34
py35
py36
py37
py38
- pypy_54
+ pypy3
flake8
[testenv]
deps =
pytest
pytest-cov
- pytest-travis-fold
-passenv =
- TRAVIS
commands =
sqlformat --version
pytest --cov=sqlparse {posargs}