pglast.visitors — Other ways to inspect and manipulate the AST

class pglast.visitors.Action

Abstract action singleton.

class pglast.visitors.ActionMeta

Metaclass used to implement action singleton.

class pglast.visitors.Add

Marker used to tell the iterator to insert nodes in the current sequence.

class pglast.visitors.Ancestor(parent=None, node=None, member=None)

Simple object to keep track of the node’s ancestors while it’s being visited.

Parameters
  • parent (Ancestor) – the parent of the new instance

  • node – the tracked object

  • member – either the name of the attribute or the index in the sequence that points to the tracked object in the parent container

An instance of this class represent a particular ancestor in the hierarchy chain: it carries a reference that points to the higher item in the chain, the associated ast.Node instance and a member, either the attribute name or sequential index in the parent node: the latter happens when the parent node is actually a tuple, not an Node instance.

Iteration yields the sequence of involved members, that is the path starting from the root of the AST tree that leads to leaf node.

Accessing an instance with a positive index returns the nth node up in the hierarchy.

When applied (using the @ operator) to an ast.Node instance will traverse that node returning the leaf one corresponding to the whole chain.

Example:

>>> tree = parse_sql('select 1')
>>> root = Ancestor()
>>> root
ROOT
>>> root@tree is tree
True
>>> root[0] is None
True
>>> select_stmt_path = root / (tree, 0) / (tree[0], 'stmt')
>>> select_stmt_path
ROOT → 0 → stmt
>>> select_stmt_path@tree is tree[0].stmt
True
>>> select_stmt_path[0] is tree[0]
True
>>> columns_path = (select_stmt_path
...                 / (tree[0].stmt, 'targetList'))
>>> first_col_path = (columns_path
...                   / (tree[0].stmt.targetList[0], 0))
>>> first_col_path
ROOT → 0 → stmt → targetList → 0
>>> first_col_path[0]
<ResTarget val=<A_Const val=<Integer val=1>>>
>>> first_col_path[1] is columns_path[0]
True

As said, the node is not always a ast.Node, but may be a tuple, possibly containing subtuples, for example the functions slot of RangeFunction:

>>> tree = parse_sql('SELECT * FROM ROWS'
...                  ' FROM(generate_series(10,11), get_users())')
>>> root = Ancestor()
>>> from_clause_path = (root / (tree, 0) / (tree[0], 'stmt')
...                     / (tree[0].stmt, 'fromClause'))
>>> from_clause = tree[0].stmt.fromClause
>>> from_clause_path@tree is from_clause
True
>>> range_function_path = (from_clause_path
...                        / (from_clause, 0))
>>> range_function = from_clause[0]
>>> functions_path = (range_function_path
...                   / (range_function, 'functions'))
>>> functions = from_clause[0].functions
>>> functions_path@tree is functions
True
>>> generate_series_path = (functions_path
...                         / (functions, 0))
>>> generate_series_path@tree is functions[0]
True
>>> type(generate_series_path.node)
<class 'tuple'>
>>> type(generate_series_path.member)
<class 'int'>
>>> type(generate_series_path.node[0])
<class 'tuple'>
>>> generate_series_path.node[0][0]
<FuncCall funcname=(<String val='generate_series'>,)...

As an aid to visitors that apply changes to the AST, there are two methods, update() and apply(), that takes care of the different cases, when node is an AST instance or instead it’s a tuple (or subtuple); the former does not directly change the AST tree, but postpones that when the latter is called.

apply()

Apply the pending change, if any, to the actual node.

find_nearest(cls)

Find the nearest ancestor with a node of the given cls.

update(new_value)

Set new_value as a pending change to the tracked node.

class pglast.visitors.Continue

Marker used to tell the iterator to keep going.

class pglast.visitors.Delete

Marker used to tell the iterator to delete current node.

class pglast.visitors.ReferencedRelations(cte_names=None, skip_with_clause=None)

Concrete implementation of the referenced_relations() function.

Parameters
  • cte_names (set) – the set of surrounding CTE names

  • skip_with_clause (WithClause) – skip this clause, when specified

Calling an instance of this class will return a set of the names of the relations referenced by the given node.

visit_RangeVar(ancestors, node)

Collect relation names, taking into account defined CTE names

class pglast.visitors.Skip

Marker used to tell the iterator to not descend into the current node.

class pglast.visitors.Visitor

Base class implementing the visitor pattern.

To use it, you shall write a subclass that implements a set of particular named methods, specifically visit_XYZ where XYZ is the name of a class name defined in the pglast.ast module.

Instances of this class are callables and accept either a ast.Node instance or a sequence of instances, typically the result of parse_sql. The argument will be traversed in a breadth first order and each Node instance will be passed to the corresponding visit_XYZ method if it is implemented, falling back to the default visit method. If none of them are defined, the node will be ignored.

The visit_XYZ methods receive two arguments: the ancestry chain of the node, an instance of Ancestor and the Node instance itself. The methods may return either None, an action or a new node that will replace the original one.

iterate(node)

Iterate thru node’s AST using a breadth-first traversing.

Parameters

node – either a ast.Node instance or a tuple of those

This is a generator, that yields Node instances together with their ancestors chain as it finds them while traversing the tree.

visit = None

The default visit method for any node without a specific one. When None, nothing happens.

pglast.visitors.referenced_relations(stmt)

Return the set of relation names referenced in the given stmt.

Parameters

stmt – either a string containing the SQL statement or a ast.Node instance

Returns

a set of strings, the names of the relations

Example:

>>> referenced_relations('WITH q1(x,y) AS (SELECT 1,2)'
...                      ' SELECT * FROM q1, q2')
{'q2'}