Skip to content

Commit

Permalink
Add a note to the parse_plpgsql() mentioning its shortcomings
Browse files Browse the repository at this point in the history
This fixes issue #88.
  • Loading branch information
lelit committed Sep 29, 2024
1 parent a61d5bd commit 7121094
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 7 deletions.
4 changes: 2 additions & 2 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
.. :Created: gio 10 ago 2017 10:14:17 CEST
.. :Author: Lele Gaifax <lele@metapensiero.it>
.. :License: GNU General Public License version 3 or later
.. :Copyright: © 2017, 2018, 2019, 2021, 2022 Lele Gaifax
.. :Copyright: © 2017, 2018, 2019, 2021, 2022, 2024 Lele Gaifax
..
===================
Expand All @@ -13,7 +13,7 @@
This chapter briefly explains some implementation detail.

.. automodule:: pglast
:members: Error, prettify, split, __author__, __version__
:members: Error, parse_plpgsql, prettify, split, __author__, __version__

.. toctree::
:maxdepth: 2
Expand Down
123 changes: 118 additions & 5 deletions pglast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# :Created: mer 02 ago 2017 15:11:02 CEST
# :Author: Lele Gaifax <lele@metapensiero.it>
# :License: GNU General Public License version 3 or later
# :Copyright: © 2017, 2018, 2019, 2021, 2022 Lele Gaifax
# :Copyright: © 2017, 2018, 2019, 2021, 2022, 2024 Lele Gaifax
#

from collections import namedtuple
Expand All @@ -26,10 +26,123 @@


def parse_plpgsql(statement):
"""Parse the given ``PLPGSQL`` `statement` and return its abstract syntax tree.
Note that this currently returns the raw tree, represented by plain Python structures such
as lists, dictionaries and scalar values.
"""Parse the given ``PLPGSQL`` `statement` and return its tokens stream.
.. note:: This is currently somewhat of limited usefulness, because neither ``libpg_query``
[#]_ nor ``pglast`` expose proper ``AST`` nodes for the PostgreSQL's procedural extension
language, and thus it returns the raw tree, represented by plain Python structures such
as lists, dictionaries and scalar values.
Consider the following examples, two different ways to parse the same ``SQL`` statement.
The first uses :func:`parse_plpgsql`:
.. testcode::
from pprint import pprint
from pglast import parse_plpgsql
STMT = '''\\
CREATE FUNCTION add (a integer, b integer)
RETURNS integer AS $$
BEGIN
RETURN a + b;
END;
$$ LANGUAGE plpgsql
'''
as_plpgsql = parse_plpgsql(STMT)
pprint(as_plpgsql, depth=6)
and emits this structure:
.. testoutput::
:options: -ELLIPSIS
[{'PLpgSQL_function': {'action': {'PLpgSQL_stmt_block': {'body': [{...}],
'lineno': 2}},
'datums': [{'PLpgSQL_var': {'datatype': {...},
'refname': 'a'}},
{'PLpgSQL_var': {'datatype': {...},
'refname': 'b'}},
{'PLpgSQL_var': {'datatype': {...},
'refname': 'found'}}]}}]
As you can see, is just a list of plain Python dictionaries, more or less representing
*syntax tokens*.
If you use :func:`~.parser.parse_sql` instead:
.. testcode::
from pglast import parse_sql
as_sql = parse_sql(STMT)
pprint([stmt(skip_none=True) for stmt in as_sql])
you obtain a richer representation of the statement:
.. testoutput::
:options: -ELLIPSIS
[{'@': 'RawStmt',
'stmt': {'@': 'CreateFunctionStmt',
'funcname': ({'@': 'String', 'sval': 'add'},),
'is_procedure': False,
'options': ({'@': 'DefElem',
'arg': ({'@': 'String',
'sval': '\\nBEGIN\\n RETURN a + b;\\nEND;\\n'},),
'defaction': {'#': 'DefElemAction',
'name': 'DEFELEM_UNSPEC',
'value': 0},
'defname': 'as',
'location': 68},
{'@': 'DefElem',
'arg': {'@': 'String', 'sval': 'plpgsql'},
'defaction': {'#': 'DefElemAction',
'name': 'DEFELEM_UNSPEC',
'value': 0},
'defname': 'language',
'location': 104}),
'parameters': ({'@': 'FunctionParameter',
'argType': {'@': 'TypeName',
'location': 32,
'names': ({'@': 'String',
'sval': 'pg_catalog'},
{'@': 'String',
'sval': 'int4'}),
'pct_type': False,
'setof': False,
'typemod': -1},
'mode': {'#': 'FunctionParameterMode',
'name': 'FUNC_PARAM_DEFAULT',
'value': 'd'},
'name': 'a'},
{'@': 'FunctionParameter',
'argType': {'@': 'TypeName',
'location': 43,
'names': ({'@': 'String',
'sval': 'pg_catalog'},
{'@': 'String',
'sval': 'int4'}),
'pct_type': False,
'setof': False,
'typemod': -1},
'mode': {'#': 'FunctionParameterMode',
'name': 'FUNC_PARAM_DEFAULT',
'value': 'd'},
'name': 'b'}),
'replace': False,
'returnType': {'@': 'TypeName',
'location': 60,
'names': ({'@': 'String', 'sval': 'pg_catalog'},
{'@': 'String', 'sval': 'int4'}),
'pct_type': False,
'setof': False,
'typemod': -1}},
'stmt_len': 0,
'stmt_location': 0}]
.. [#] See also https://github.com/pganalyze/libpg_query/issues/110.
"""

from json import loads
Expand Down

0 comments on commit 7121094

Please sign in to comment.