diff options
| author | Andi Albrecht <albrecht.andi@gmail.com> | 2009-04-07 19:29:38 +0200 |
|---|---|---|
| committer | Andi Albrecht <albrecht.andi@gmail.com> | 2009-04-07 19:29:38 +0200 |
| commit | e6ac0d076e3de6a3e24bba00e49f733d7f69fda6 (patch) | |
| tree | 25c80a5cc9ca7a41a5a890e7c4171c42403ea65c | |
| parent | 882205e8345ddacbbc0e59bc092f43e7e1f5d2cf (diff) | |
| parent | 55410fc4c2e8163f0e89d14652b267ea5765e30c (diff) | |
| download | sqlparse-e6ac0d076e3de6a3e24bba00e49f733d7f69fda6.tar.gz | |
Merged docs branch.
| -rw-r--r-- | .hgignore | 2 | ||||
| -rw-r--r-- | docs/agogo/layout.html | 237 | ||||
| -rw-r--r-- | docs/agogo/static/agogo.css_t | 328 | ||||
| -rw-r--r-- | docs/agogo/static/bgfooter.png (renamed from docs/tango/static/bgfooter.png) | bin | 434 -> 434 bytes | |||
| -rw-r--r-- | docs/agogo/static/bgtop.png (renamed from docs/tango/static/bgtop.png) | bin | 430 -> 430 bytes | |||
| -rw-r--r-- | docs/agogo/theme.conf | 18 | ||||
| -rw-r--r-- | docs/source/analyzing.rst | 64 | ||||
| -rw-r--r-- | docs/source/api.rst | 33 | ||||
| -rw-r--r-- | docs/source/cmdline.rst | 8 | ||||
| -rw-r--r-- | docs/source/conf.py | 4 | ||||
| -rw-r--r-- | docs/source/index.rst | 9 | ||||
| -rw-r--r-- | docs/source/intro.rst | 108 | ||||
| -rw-r--r-- | docs/source/ui.rst | 15 | ||||
| -rw-r--r-- | docs/tango/static/default.css_t | 90 | ||||
| -rw-r--r-- | docs/tango/theme.conf | 28 | ||||
| -rw-r--r-- | sqlparse/__init__.py | 11 | ||||
| -rw-r--r-- | sqlparse/sql.py | 96 |
17 files changed, 880 insertions, 171 deletions
diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..2f3bba6 --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +syntax: glob +docs/build
\ No newline at end of file diff --git a/docs/agogo/layout.html b/docs/agogo/layout.html new file mode 100644 index 0000000..2a6989c --- /dev/null +++ b/docs/agogo/layout.html @@ -0,0 +1,237 @@ +{%- block doctype -%} +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +{%- endblock %} +{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} +{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} + +{%- macro relbar() %} + <div class="related"> + <h3>{{ _('Navigation') }}</h3> + <ul> + {%- for rellink in rellinks %} + <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}> + <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}" + {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a> + {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li> + {%- endfor %} + {%- block rootrellink %} + <li><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>{{ reldelim1 }}</li> + {%- endblock %} + {%- for parent in parents %} + <li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li> + {%- endfor %} + {%- block relbaritems %} {% endblock %} + </ul> + </div> + {%- endmacro %} + + {%- macro sidebar() %} + {%- if not embedded %}{% if not theme_nosidebar|tobool %} + <div class="sphinxsidebar"> + <div class="sphinxsidebarwrapper"> + {%- block sidebarlogo %} + {%- if logo %} + <p class="logo"><a href="{{ pathto(master_doc) }}"> + <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/> + </a></p> + {%- endif %} + {%- endblock %} + {%- block sidebartoc %} + {%- if display_toc %} + <h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3> + {{ toc }} + {%- endif %} + {%- endblock %} + {%- block sidebarrel %} + {%- if prev %} + <h4>{{ _('Previous topic') }}</h4> + <p class="topless"><a href="{{ prev.link|e }}" + title="{{ _('previous chapter') }}">{{ prev.title }}</a></p> + {%- endif %} + {%- if next %} + <h4>{{ _('Next topic') }}</h4> + <p class="topless"><a href="{{ next.link|e }}" + title="{{ _('next chapter') }}">{{ next.title }}</a></p> + {%- endif %} + {%- endblock %} + {%- block sidebarsourcelink %} + {%- if show_source and has_source and sourcename %} + <h3>{{ _('This Page') }}</h3> + <ul class="this-page-menu"> + <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}" + rel="nofollow">{{ _('Show Source') }}</a></li> + </ul> + {%- endif %} + {%- endblock %} + {%- if customsidebar %} + {% include customsidebar %} + {%- endif %} + {%- block sidebarsearch %} + {%- if pagename != "search" %} + <div id="searchbox" style="display: none"> + <h3>{{ _('Quick search') }}</h3> + <form class="search" action="{{ pathto('search') }}" method="get"> + <input type="text" name="q" size="18" /> + <input type="submit" value="{{ _('Go') }}" /> + <input type="hidden" name="check_keywords" value="yes" /> + <input type="hidden" name="area" value="default" /> + </form> + <p class="searchtip" style="font-size: 90%"> + {{ _('Enter search terms or a module, class or function name.') }} + </p> + </div> + <script type="text/javascript">$('#searchbox').show(0);</script> + {%- endif %} + {%- endblock %} + </div> + </div> + {%- endif %}{% endif %} + {%- endmacro %} + +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + {{ metatags }} + {%- if not embedded %} + {%- set titlesuffix = " — "|safe + docstitle|e %} + {%- else %} + {%- set titlesuffix = "" %} + {%- endif %} + <title>{{ title|striptags }}{{ titlesuffix }}</title> + <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" /> + <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" /> + {%- if not embedded %} + <script type="text/javascript"> + var DOCUMENTATION_OPTIONS = { + URL_ROOT: '{{ pathto("", 1) }}', + VERSION: '{{ release|e }}', + COLLAPSE_MODINDEX: false, + FILE_SUFFIX: '{{ file_suffix }}', + HAS_SOURCE: {{ has_source|lower }} + }; + </script> + {%- for scriptfile in script_files %} + <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script> + {%- endfor %} + {%- if use_opensearch %} + <link rel="search" type="application/opensearchdescription+xml" + title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" + href="{{ pathto('_static/opensearch.xml', 1) }}"/> + {%- endif %} + {%- if favicon %} + <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/> + {%- endif %} + {%- endif %} + {%- block linktags %} + {%- if hasdoc('about') %} + <link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" /> + {%- endif %} + {%- if hasdoc('genindex') %} + <link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" /> + {%- endif %} + {%- if hasdoc('search') %} + <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" /> + {%- endif %} + {%- if hasdoc('copyright') %} + <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" /> + {%- endif %} + <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" /> + {%- if parents %} + <link rel="up" title="{{ parents[-1].title|striptags }}" href="{{ parents[-1].link|e }}" /> + {%- endif %} + {%- if next %} + <link rel="next" title="{{ next.title|striptags }}" href="{{ next.link|e }}" /> + {%- endif %} + {%- if prev %} + <link rel="prev" title="{{ prev.title|striptags }}" href="{{ prev.link|e }}" /> + {%- endif %} + {%- endblock %} + {%- block extrahead %} {% endblock %} + </head> + <body> + + <div class="header-wrapper"> + <div class="header"> + <h1><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a></h1> + <div class="rel"> + {%- for rellink in rellinks %} + <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}" + {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a> + {%- if not loop.last %}{{ reldelim2 }}{% endif %} + {%- endfor %} + </div> + </div> + </div> + + <div class="content-wrapper"> + <div class="content"> + <div class="document"> + {%- block document %} + <div class="documentwrapper"> + {%- if not embedded %}{% if not theme_nosidebar|tobool %} + <div class="bodywrapper"> + {%- endif %}{% endif %} + <div class="body"> + {% block body %} {% endblock %} + </div> + {%- if not embedded %}{% if not theme_nosidebar|tobool %} + </div> + {%- endif %}{% endif %} + </div> + {%- endblock %} + </div> + <div class="sidebar"> + <h3>{{ _('Contents') }}</h3> + {{ toctree() }} + <h3 style="margin-top: 1.5em;">{{ _('Search') }}</h3> + <form class="search" action="{{ pathto('search') }}" method="get"> + <input type="text" name="q" size="18" /> + <input type="submit" value="{{ _('Go') }}" /> + <input type="hidden" name="check_keywords" value="yes" /> + <input type="hidden" name="area" value="default" /> + </form> + <p class="searchtip" style="font-size: 90%"> + {{ _('Enter search terms or a module, class or function name.') }} + </p> + </div> + <div class="clearer"></div> + </div> + </div> + + <div class="footer-wrapper"> + {%- block footer %} + <div class="footer"> + <div class="left"> + {%- for rellink in rellinks %} + <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}" + {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a> + {%- if not loop.last %}{{ reldelim2 }}{% endif %} + {%- endfor %} + {%- if show_source and has_source and sourcename %} + <br/> + <a href="{{ pathto('_sources/' + sourcename, true)|e }}" + rel="nofollow">{{ _('Show Source') }}</a> + {%- endif %} + </div> + + <div class="right"> + {%- if hasdoc('copyright') %} + {% trans path=pathto('copyright'), copyright=copyright|e %}© <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %} + {%- else %} + {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- endif %} + {%- if last_updated %} + {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} + {%- endif %} + {%- if show_sphinx %}<br/> + {% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %} + {%- endif %} + </div> + <div class="clearer"></div> + </div> + {%- endblock %} + </div> + + </body> +</html> diff --git a/docs/agogo/static/agogo.css_t b/docs/agogo/static/agogo.css_t new file mode 100644 index 0000000..97dc91b --- /dev/null +++ b/docs/agogo/static/agogo.css_t @@ -0,0 +1,328 @@ +* { + margin: 0px; + padding: 0px; +} + +body { + font-family: {{ theme_bodyfont }}; + line-height: 1.4em; + font-size: 14px; + color: black; + background-color: {{ theme_bgcolor }}; +} + + +/* Page layout */ + +div.header, div.content, div.footer { + width: {{ theme_pagewidth }}; + margin-left: auto; + margin-right: auto; +} + +div.header-wrapper { + background: {{ theme_headerbg }}; + border-bottom: 3px solid #2e3436; +} + + +/* Default body styles */ +a { + text-decoration: none; + color: {{ theme_linkcolor }}; +} + +.clearer { + clear: both; +} + +.left { + float: left; +} + +.right { + float: right; +} + +h1, h2, h3, h4 { + font-family: {{ theme_headerfont }}; + font-weight: normal; + color: {{ theme_headercolor2 }}; + margin-bottom: .8em; +} + +h1 { + color: {{ theme_headercolor1 }}; +} + +h2 { + padding-bottom: .5em; + border-bottom: 1px solid {{ theme_headercolor2 }}; +} + +a.headerlink { + visibility: hidden; + color: #dddddd; + padding-left: .3em; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + + + +/* Header */ + +div.header { + padding-top: 10px; + padding-bottom: 10px; +} + +div.header h1 { + font-family: {{ theme_headerfont }}; + font-weight: normal; + font-size: 160%; + letter-spacing: .08em; +} + +div.header h1 a { + color: white; +} + +div.header div.rel { + margin-top: 1em; +} + +div.header div.rel a { + color: {{ theme_headerlinkcolor }}; + letter-spacing: .1em; + text-transform: uppercase; +} + + +/* Content */ +div.content-wrapper { + background-color: white; + padding-top: 20px; + padding-bottom: 20px; +} + +div.document { + width: {{ theme_documentwidth }}; + float: left; +} + +div.body { + padding-right: 2em; + text-align: justify; +} + +div.document ul { + margin-left: 1.2em; + list-style-type: square; +} + +div.document dd { + margin-left: 1.2em; + margin-top: .4em; + margin-bottom: 1em; +} + +div.document .section { + margin-top: 1.7em; +} +div.document .section:first-child { + margin-top: 0px; +} + +div.document div.highlight { + padding: 3px; + background-color: #eeeeec; + border-top: 2px solid #dddddd; + border-bottom: 2px solid #dddddd; + margin-top: .8em; + margin-bottom: .8em; +} + +div.document h2 { + margin-top: .7em; +} + +div.document p { + margin-bottom: .5em; +} + +div.document li.toctree-l1 { + margin-bottom: 1em; +} + +div.document .descname { + font-weight: bold; +} + +div.document .docutils.literal { + background-color: #eeeeec; + padding: 1px; +} + +div.document .docutils.xref.literal { + background-color: transparent; + padding: 0px; +} + + +/* Sidebar */ + +div.sidebar { + width: {{ theme_sidebarwidth }}; + float: right; + font-size: .9em; +} + +div.sidebar h3 { + color: #2e3436; + text-transform: uppercase; + font-size: 130%; + letter-spacing: .1em; +} + +div.sidebar ul { + list-style-type: none; +} + +div.sidebar li.toctree-l1 a { + display: block; + padding: 1px; + border: 1px solid #dddddd; + background-color: #eeeeec; + margin-bottom: .4em; + padding-left: 3px; + color: #2e3436; +} + +div.sidebar li.toctree-l2 a { + background-color: transparent; + border: none; + border-bottom: 1px solid #dddddd; +} + +div.sidebar li.toctree-l2:last-child a { + border-bottom: none; +} + +div.sidebar li.toctree-l1.current a { + border-right: 5px solid {{ theme_headerlinkcolor }}; +} + +div.sidebar li.toctree-l1.current li.toctree-l2 a { + border-right: none; +} + + +/* Footer */ + +div.footer-wrapper { + background: {{ theme_footerbg }}; + border-top: 4px solid #babdb6; + padding-top: 10px; + padding-bottom: 10px; + min-height: 80px; +} + +div.footer, div.footer a { + color: #888a85; +} + +div.footer .right { + text-align: right; +} + +div.footer .left { + text-transform: uppercase; +} + + +/* Styles copied form basic theme */ + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + diff --git a/docs/tango/static/bgfooter.png b/docs/agogo/static/bgfooter.png Binary files differindex 9ce5bdd..9ce5bdd 100644 --- a/docs/tango/static/bgfooter.png +++ b/docs/agogo/static/bgfooter.png diff --git a/docs/tango/static/bgtop.png b/docs/agogo/static/bgtop.png Binary files differindex a0d4709..a0d4709 100644 --- a/docs/tango/static/bgtop.png +++ b/docs/agogo/static/bgtop.png diff --git a/docs/agogo/theme.conf b/docs/agogo/theme.conf new file mode 100644 index 0000000..9cdac5d --- /dev/null +++ b/docs/agogo/theme.conf @@ -0,0 +1,18 @@ +[theme] +inherit = basic +stylesheet = agogo.css +pygments_style = tango + +[options] +bodyfont = "Verdana", Arial, sans-serif +headerfont = "Georgia", "Times New Roman", serif +pagewidth = 70em +documentwidth = 50em +sidebarwidth = 20em +bgcolor = #eeeeec +headerbg = url(bgtop.png) top left repeat-x +footerbg = url(bgfooter.png) top left repeat-x +linkcolor = #ce5c00 +headercolor1 = #204a87 +headercolor2 = #3465a4 +headerlinkcolor = #fcaf3e
\ No newline at end of file diff --git a/docs/source/analyzing.rst b/docs/source/analyzing.rst new file mode 100644 index 0000000..07b1fe9 --- /dev/null +++ b/docs/source/analyzing.rst @@ -0,0 +1,64 @@ +.. _analyze: + +Analyzing the Parsed Statement +============================== + +When the :meth:`~sqlparse.parse` function is called the returned value +is a tree-ish representation of the analyzed statements. The returned +objects can be used by applications to retrieve further information about +the parsed SQL. + + +Base Classes +------------ + +All returned objects inherit from these base classes. +The :class:`~sqlparse.sql.Token` class represents a single token and +:class:`~sqlparse.sql.TokenList` class is a group of tokens. +The latter provides methods for inspecting it's child tokens. + +.. autoclass:: sqlparse.sql.Token + :members: + +.. autoclass:: sqlparse.sql.TokenList + :members: + + +SQL Representing Classes +------------------------ + +The following classes represent distinct parts of a SQL statement. + +.. autoclass:: sqlparse.sql.Statement + :members: + +.. autoclass:: sqlparse.sql.Comment + :members: + +.. autoclass:: sqlparse.sql.Identifier + :members: + +.. autoclass:: sqlparse.sql.IdentifierList + :members: + +.. autoclass:: sqlparse.sql.Where + :members: + +.. autoclass:: sqlparse.sql.Case + :members: + +.. autoclass:: sqlparse.sql.Parenthesis + :members: + +.. autoclass:: sqlparse.sql.If + :members: + +.. autoclass:: sqlparse.sql.For + :members: + +.. autoclass:: sqlparse.sql.Assignment + :members: + +.. autoclass:: sqlparse.sql.Comparsion + :members: + diff --git a/docs/source/api.rst b/docs/source/api.rst index 3bce389..2531c9b 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -1,6 +1,9 @@ :mod:`sqlparse` -- Parse SQL statements ======================================= +.. module:: sqlparse + :synopsis: Parse SQL statements. + The :mod:`sqlparse` module provides the following functions on module-level. .. autofunction:: sqlparse.split @@ -10,3 +13,33 @@ The :mod:`sqlparse` module provides the following functions on module-level. .. autofunction:: sqlparse.parse +.. _formatting: + +Formatting of SQL Statements +---------------------------- + +The :meth:`~sqlparse.format` function accepts the following keyword arguments. + +``keyword_case`` + Changes how keywords are formatted. Allowed values are "upper", "lower" + and "capitalize". + +``identifier_case`` + Changes how identifiers are formatted. Allowed values are "upper", "lower", + and "capitalize". + +``strip_comments`` + If ``True`` comments are removed from the statements. + +``reindent`` + If ``True`` the indentations of the statements are changed. + +``indent_tabs`` + If ``True`` tabs instead of spaces are used for indentation. + +``indent_width`` + The width of the indentation, defaults to 2. + +``output_format`` + If given the output is additionally formatted to be used as a variable + in a programming language. Allowed values are "python" and "php". diff --git a/docs/source/cmdline.rst b/docs/source/cmdline.rst deleted file mode 100644 index 93a35a7..0000000 --- a/docs/source/cmdline.rst +++ /dev/null @@ -1,8 +0,0 @@ -``sqlformat`` -- Command Line Script -=========================================== - -The :mod:`sqlparse` module is shipped with the script -:program:`sqlformat` which provides a command line interface to the formatting -functions. - -.. todo:: Describe options and example usage. diff --git a/docs/source/conf.py b/docs/source/conf.py index b84245a..63e1b90 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -83,7 +83,7 @@ exclude_trees = [] #show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = 'tango' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] @@ -93,7 +93,7 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'sphinxdoc' +html_theme = 'agogo' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/source/index.rst b/docs/source/index.rst index 40e99e0..44d03a2 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,17 +3,16 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to python-sqlparse's documentation! -=========================================== - -Contents: +python-sqlparse's documentation contents +======================================== .. toctree:: :maxdepth: 2 intro api - cmdline + analyzing + ui changes diff --git a/docs/source/intro.rst b/docs/source/intro.rst index 3d7a888..931659f 100644 --- a/docs/source/intro.rst +++ b/docs/source/intro.rst @@ -2,44 +2,58 @@ Introduction ============ :mod:`sqlparse` is a non-validating SQL parser for Python. - It provides support for parsing, splitting and formatting SQL statements. - -:mod:`sqlparse` is released under the terms of the +The module is released under the terms of the `New BSD license <http://www.opensource.org/licenses/bsd-license.php>`_. -Visit http://sqlformat.appspot.com to try it's formatting features. +Visit the project page at http://python-sqlparse.googlecode.com for +further information about this project. Download & Installation ----------------------- -To download and install :mod:`sqlparse` on your system run the following -commands: +The latest released version can be obtained from the +`downloads page <http://code.google.com/p/python-sqlparse/downloads/list>`_ +on the project's website. To extract the source archive and to install +the module on your system run .. code-block:: bash - $ git clone git://github.com/andialbrecht/python-sqlparse.git - $ cd python-sqlparse.git/ - $ sudo python setup.py install + $ tar cvfz python-sqlparse-VERSION.tar.gz + $ cd python-sqlparse/ + $ sudo python setup.py install + +Alternatively you can install :mod:`sqlparse` from the +`Python Packge Index <http://pypi.python.org/pypi/sqlparse>`_ with your +favorite tool for installing Python modules. For example when using +`pip <http://pypi.python.org/pypi/pip>`_ run :command:`pip install sqlparse`. -A tarball of the current sources is available under the following URL: -http://github.com/andialbrecht/python-sqlparse/tarball/master +Getting Started +--------------- -Example Usage -------------- +The :mod:`sqlparse` module provides three simple functions on module level +to achieve some common tasks when working with SQL statements. +This section shows some simple usage examples of these functions. -Here are some usage examples of this module. +Let's get started with splitting a string containing one or more SQL +statements into a list of single statements using :meth:`~sqlparse.split`: -Splitting statements:: +.. code-block:: python >>> import sqlparse >>> sql = 'select * from foo; select * from bar;' >>> sqlparse.split(sql) - <<< [u'select * from foo; ', u'select * from bar;'] + [u'select * from foo; ', u'select * from bar;'] + +The end of a statement is identified by the occurrence of a semicolon. +Semicolons within certain SQL constructs like ``BEGIN ... END`` blocks +are handled correctly by the splitting mechanism. + +SQL statements can be beautified by using the :meth:`~sqlarse.format` function. -Formatting statemtents:: +.. code-block:: python >>> sql = 'select * from foo where id in (select id from bar);' >>> print sqlparse.format(sql, reindent=True, keyword_case='upper') @@ -49,18 +63,37 @@ Formatting statemtents:: (SELECT id FROM bar); -Now, let's have a deeper look at the internals:: +In this case all keywords in the given SQL are uppercased and the +indentation is changed to make it more readable. Read :ref:`formatting` for +a full reference of supported options given as keyword arguments +to that function. + +Before proceeding with a closer look at the internal representation of +SQL statements, you should be aware that this SQL parser is intentionally +non-validating. It assumes that the given input is at least some kind +of SQL and then it tries to analyze as much as possible without making +too much assumptions about the concrete dialect or the actual statement. +At least it's up to the user of this API to interpret the results right. + +When using the :meth:`~sqlparse.parse` function a tuple of +:class:`~sqlparse.sql.Statement` instances is returned: + +.. code-block:: python >>> sql = 'select * from "someschema"."mytable" where id = 1' - >>> pared = sqlparse.parse(sql) - >>> pared - <<< (<Statement 'select...' at 0x9ad08ec>,) - >>> stmt = parsed[0] - >>> stmt.to_unicode() # converting it back to unicode - <<< u'select * from "someschema"."mytable" where id = 1' - >>> # This is how the internal representation looks like: + >>> parsed = sqlparse.parse(sql) + >>> parsed + (<Statement 'select...' at 0x9ad08ec>,) + +Each item of the tuple is a single statement as identified by the above +mentioned :meth:`~sqlparse.split` function. So let's grab the only element +from that list and have a look at the ``tokens`` attribute. +Sub-tokens are stored in this attribute. + +.. code-block:: python + + >>> stmt = parsed[0] # grab the Statement object >>> stmt.tokens - <<< (<DML 'select' at 0x9b63c34>, <Whitespace ' ' at 0x9b63e8c>, <Operator '*' at 0x9b63e64>, @@ -70,16 +103,33 @@ Now, let's have a deeper look at the internals:: <Identifier '"somes...' at 0x9b5c62c>, <Whitespace ' ' at 0x9b63f04>, <Where 'where ...' at 0x9b5caac>) - >>> +Each object can be converted back to a string at any time: + +.. code-block:: python + + >>> stmt.to_unicode() + u'select * from "someschema"."mytable" where id = 1' + >>> stmt.tokens[-1].to_unicode() # or just the WHERE part + u'where id = 1' -.. todo:: Describe general concepts - Why non-validating? Processing stages (tokens, groups,...), filter, +Details of the returned objects are described in :ref:`analyze`. Development & Contributing -------------------------- +The source code of this module is hosted on +`bitbucket.org <http://bitbucket.org/andialbrecht/python-sqlparse/>`_. + +Run + +.. code-block:: bash + + $ hg clone http://bitbucket.org/andialbrecht/python-sqlparse/ + +to check out the latest sources from the Mercurial repository. + Please file bug reports and feature requests on the project site at http://code.google.com/p/python-sqlparse/issues/entry or if you have code to contribute upload it to http://codereview.appspot.com and diff --git a/docs/source/ui.rst b/docs/source/ui.rst new file mode 100644 index 0000000..264916e --- /dev/null +++ b/docs/source/ui.rst @@ -0,0 +1,15 @@ +User Interfaces +=============== + +``sqlformat`` + The ``sqlformat`` command line script ist distributed with the module. + Run :command:`sqlformat --help` to list available options and for usage + hints. + +``sqlformat.appspot.com`` + An example `Google App Engine <http://code.google.com/appengine/>`_ + application that exposes the formatting features using a web front-end. + See http://sqlformat.appspot.com for details. + The source for this application is available from a source code check out + of the :mod:`sqlparse` module (see :file:`extras/appengine`). + diff --git a/docs/tango/static/default.css_t b/docs/tango/static/default.css_t deleted file mode 100644 index c86759a..0000000 --- a/docs/tango/static/default.css_t +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Sphinx stylesheet -- default theme - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -@import url("basic.css"); - -body { - color: #000000; - font-family: "Free Sans", Arial, Verdana, sans; - font-size: 11pt; - width: 70em; - margin: 0; - margin-left: auto; - margin-right: auto; -} - - -div.related { - background: #eeeeec; /* #d3d7cf; */ - color: white; - padding-bottom: .6em; - padding-top: .6em; -} - -div.related a { - color: #2e3436; - text-decoration: none; -} - -div.document { - background: #eeeeec; -} - -div.body { - background: white; - padding: .5em; - -moz-border-radius: 0px 10px 10px 10px; - border-right: 1px solid #eeeeec; -} - -div.document h1, h2, h3, h4 { - color: #204a87; - font-weight: normal; - letter-spacing: .05em; -} - -div.document p, dl, div.highlight { - margin-left: 25px; -} - -div.document dd p, dd { - margin-left: .5em; -} - -div.document a { - color: #f57900; -} - -div.sphinxsidebarwrapper { - background: url(bgfooter.png) top left repeat-x #eeeeec; - padding-right: .5em; -} - -div.sphinxsidebar ul { - margin: 0; - padding: 0; -} - -div.sphinxsidebar a { - color: #888a85; - text-decoration: none; -} - -div.footer { - font-size: .8em; - color: #888a85; - text-align: right; - padding: 10px; -} - -div.footer a { - color: #888a85; -} - -div.highlight pre { - background-color: #eeeeec; - border: 1px solid #babdb6; - padding: 7px; -}
\ No newline at end of file diff --git a/docs/tango/theme.conf b/docs/tango/theme.conf deleted file mode 100644 index 812330f..0000000 --- a/docs/tango/theme.conf +++ /dev/null @@ -1,28 +0,0 @@ -[theme] -inherit = basic -stylesheet = default.css -pygments_style = sphinx - -[options] -rightsidebar = false -stickysidebar = false - -footerbgcolor = #11303d -footertextcolor = #ffffff -sidebarbgcolor = #1c4e63 -sidebartextcolor = #ffffff -sidebarlinkcolor = #98dbcc -relbarbgcolor = #133f52 -relbartextcolor = #ffffff -relbarlinkcolor = #ffffff -bgcolor = #ffffff -textcolor = #000000 -headbgcolor = #f2f2f2 -headtextcolor = #20435c -headlinkcolor = #c60f0f -linkcolor = #355f7c -codebgcolor = #eeffcc -codetextcolor = #333333 - -bodyfont = sans-serif -headfont = 'Trebuchet MS', sans-serif diff --git a/sqlparse/__init__.py b/sqlparse/__init__.py index 01b3bd8..9a97dc0 100644 --- a/sqlparse/__init__.py +++ b/sqlparse/__init__.py @@ -30,9 +30,7 @@ def parse(sql): *sql* is a single string containting one or more SQL statements. - The returned :class:`~sqlparse.parser.Statement` are fully analyzed. - - Returns a list of :class:`~sqlparse.parser.Statement` instances. + Returns a tuple of :class:`~sqlparse.sql.Statement` instances. """ stack = engine.FilterStack() stack.full_analyze() @@ -42,10 +40,9 @@ def parse(sql): def format(sql, **options): """Format *sql* according to *options*. - Returns a list of :class:`~sqlparse.parse.Statement` instances like - :meth:`parse`, but the statements are formatted according to *options*. + Available options are documented in :ref:`formatting`. - Available options are documented in the :mod:`~sqlparse.format` module. + Returns the formatted SQL statement as string. """ stack = engine.FilterStack() options = formatter.validate_options(options) @@ -55,7 +52,7 @@ def format(sql, **options): def split(sql): - """Split *sql* into separate statements. + """Split *sql* into single statements. Returns a list of strings. """ diff --git a/sqlparse/sql.py b/sqlparse/sql.py index 372a3c7..db9f1c1 100644 --- a/sqlparse/sql.py +++ b/sqlparse/sql.py @@ -9,6 +9,12 @@ from sqlparse import tokens as T class Token(object): + """Base class for all other classes in this module. + + It represents a single token and has two instance attributes: + ``value`` is the unchange value of the token and ``ttype`` is + the type of the token. + """ __slots__ = ('value', 'ttype') @@ -28,6 +34,7 @@ class Token(object): return self.value def to_unicode(self): + """Returns a unicode representation of this object.""" return unicode(self) def _get_repr_name(self): @@ -42,6 +49,17 @@ class Token(object): return re.sub('\s+', ' ', short) def match(self, ttype, values, regex=False): + """Checks whether the token matches the given arguments. + + *ttype* is a token type. If this token doesn't match the given token + type. + *values* is a list of possible values for this token. The values + are OR'ed together so if only one of the values matches ``True`` + is returned. Except for keyword tokens the comparsion is + case-sensitive. For convenience it's ok to pass in a single string. + If *regex* is ``True`` (default is ``False``) the given values are + treated as regular expressions. + """ if self.ttype is not ttype: return False if values is None: @@ -64,13 +82,20 @@ class Token(object): return self.value in values def is_group(self): + """Returns ``True`` if this object has children.""" return False def is_whitespace(self): + """Return ``True`` if this token is a whitespace token.""" return self.ttype and self.ttype in T.Whitespace class TokenList(Token): + """A group of tokens. + + It has an additional instance attribute ``tokens`` which holds a + list of child-tokens. + """ __slots__ = ('value', 'ttype', 'tokens') @@ -104,6 +129,10 @@ class TokenList(Token): token._pprint_tree(max_depth, depth+1) def flatten(self): + """Generator yielding ungrouped tokens. + + This method is recursively called for all child tokens. + """ for token in self.tokens: if isinstance(token, TokenList): for item in token.flatten(): @@ -118,6 +147,11 @@ class TokenList(Token): return [x for x in self.tokens if isinstance(x, TokenList)] def token_first(self, ignore_whitespace=True): + """Returns the first child token. + + If *ignore_whitespace* is ``True`` (the default), whitespace + tokens are ignored. + """ for token in self.tokens: if ignore_whitespace and token.is_whitespace(): continue @@ -125,6 +159,13 @@ class TokenList(Token): return None def token_next_by_instance(self, idx, clss): + """Returns the next token matching a class. + + *idx* is where to start searching in the list of child tokens. + *clss* is a list of classes the token should be an instance of. + + If no matching token can be found ``None`` is returned. + """ if type(clss) not in (types.ListType, types.TupleType): clss = (clss,) if type(clss) is not types.TupleType: @@ -135,6 +176,7 @@ class TokenList(Token): return None def token_next_by_type(self, idx, ttypes): + """Returns next matching token by it's token type.""" if not isinstance(ttypes, (types.TupleType, types.ListType)): ttypes = [ttypes] for token in self.tokens[idx:]: @@ -143,6 +185,7 @@ class TokenList(Token): return None def token_next_match(self, idx, ttype, value, regex=False): + """Returns next token where it's ``match`` method returns ``True``.""" if type(idx) != types.IntType: idx = self.token_index(idx) for token in self.tokens[idx:]: @@ -162,6 +205,11 @@ class TokenList(Token): return None def token_prev(self, idx, skip_ws=True): + """Returns the previous token relative to *idx*. + + If *skip_ws* is ``True`` (the default) whitespace tokens are ignored. + ``None`` is returned if there's no previous token. + """ while idx != 0: idx -= 1 if self.tokens[idx].is_whitespace() and skip_ws: @@ -169,6 +217,11 @@ class TokenList(Token): return self.tokens[idx] def token_next(self, idx, skip_ws=True): + """Returns the next token relative to *idx*. + + If *skip_ws* is ``True`` (the default) whitespace tokens are ignored. + ``None`` is returned if there's no next token. + """ while idx < len(self.tokens)-1: idx += 1 if self.tokens[idx].is_whitespace() and skip_ws: @@ -180,7 +233,11 @@ class TokenList(Token): return self.tokens.index(token) def tokens_between(self, start, end, exclude_end=False): - """Return all tokens between (and including) start and end.""" + """Return all tokens between (and including) start and end. + + If *exclude_end* is ``True`` (default is ``False``) the end token + is included too. + """ if exclude_end: offset = 0 else: @@ -188,7 +245,7 @@ class TokenList(Token): return self.tokens[self.token_index(start):self.token_index(end)+offset] def group_tokens(self, grp_cls, tokens): - """Replace tokens by instance of grp_cls.""" + """Replace tokens by an instance of *grp_cls*.""" idx = self.token_index(tokens[0]) for t in tokens: self.tokens.remove(t) @@ -197,14 +254,22 @@ class TokenList(Token): return grp def insert_before(self, where, token): + """Inserts *token* before *where*.""" self.tokens.insert(self.token_index(where), token) class Statement(TokenList): + """Represents a SQL statement.""" __slots__ = ('value', 'ttype', 'tokens') def get_type(self): + """Returns the type of a statement. + + The returned value is a string holding an upper-cased reprint of + the first DML or DDL keyword. If the first token in this group + isn't a DML or DDL keyword "UNKNOWN" is returned. + """ first_token = self.token_first() if first_token.ttype in (T.Keyword.DML, T.Keyword.DDL): return first_token.value.upper() @@ -213,13 +278,19 @@ class Statement(TokenList): class Identifier(TokenList): + """Represents an identifier. + + Identifiers may have aliases or typecasts. + """ __slots__ = ('value', 'ttype', 'tokens') def has_alias(self): + """Returns ``True`` if an alias is present.""" return self.get_alias() is not None def get_alias(self): + """Returns the alias for this identifier or ``None``.""" kw = self.token_next_match(0, T.Keyword, 'AS') if kw is not None: alias = self.token_next(self.token_index(kw)) @@ -236,15 +307,23 @@ class Identifier(TokenList): return alias.to_unicode() def get_name(self): + """Returns the name of this identifier. + + This is either it's alias or it's real name. The returned valued can + be considered as the name under which the object corresponding to + this identifier is known within the current statement. + """ alias = self.get_alias() if alias is not None: return alias return self.get_real_name() def get_real_name(self): + """Returns the real name (object name) of this identifier.""" return self.token_next_by_type(0, T.Name).value def get_typecast(self): + """Returns the typecast or ``None`` of this object as a string.""" marker = self.token_next_match(0, T.Punctuation, '::') if marker is None: return None @@ -255,37 +334,50 @@ class Identifier(TokenList): class IdentifierList(TokenList): + """A list of :class:`~sqlparse.sql.Identifier`\'s.""" __slots__ = ('value', 'ttype', 'tokens') def get_identifiers(self): + """Returns the identifiers. + + Whitespaces and punctuations are not included in this list. + """ return [x for x in self.tokens if isinstance(x, Identifier)] class Parenthesis(TokenList): + """Tokens between parenthesis.""" __slots__ = ('value', 'ttype', 'tokens') class Assignment(TokenList): + """An assignment like 'var := val;'""" __slots__ = ('value', 'ttype', 'tokens') class If(TokenList): + """An 'if' clause with possible 'else if' or 'else' parts.""" __slots__ = ('value', 'ttype', 'tokens') class For(TokenList): + """A 'FOR' loop.""" __slots__ = ('value', 'ttype', 'tokens') class Comparsion(TokenList): + """A comparsion used for example in WHERE clauses.""" __slots__ = ('value', 'ttype', 'tokens') class Comment(TokenList): + """A comment.""" __slots__ = ('value', 'ttype', 'tokens') class Where(TokenList): + """A WHERE clause.""" __slots__ = ('value', 'ttype', 'tokens') class Case(TokenList): + """A CASE statement with one or more WHEN and possibly an ELSE part.""" __slots__ = ('value', 'ttype', 'tokens') |
