PostgreSQL AST traversal utilities for pgsql-parser. This package provides a visitor pattern for traversing PostgreSQL Abstract Syntax Tree (AST) nodes, similar to Babel's traverse functionality but specifically designed for PostgreSQL AST structures.
npm install @pgsql/traverse
The walk
function provides improved traversal with NodePath context and early return support:
import { walk, NodePath } from '@pgsql/traverse'; import type { Walker, Visitor } from '@pgsql/traverse'; // Using a simple walker function const walker: Walker = (path: NodePath) => { console.log(`Visiting ${path.tag} at path:`, path.path); // Return false to skip traversing children if (path.tag === 'SelectStmt') { return false; // Skip SELECT statement children } }; walk(ast, walker); // Using a visitor object (recommended for multiple node types) const visitor: Visitor = { SelectStmt: (path) => { console.log('SELECT statement:', path.node); }, RangeVar: (path) => { console.log('Table:', path.node.relname); console.log('Path to table:', path.path); console.log('Parent node:', path.parent?.tag); } }; walk(ast, visitor);
The NodePath
class provides rich context information:
class NodePath<TTag extends NodeTag = NodeTag> { tag: TTag; // Node type (e.g., 'SelectStmt', 'RangeVar') node: Node[TTag]; // The actual node data parent: NodePath | null; // Parent NodePath (null for root) keyPath: readonly (string | number)[]; // Full path array get path(): (string | number)[]; // Copy of keyPath get key(): string | number; // Last element of path }Runtime Schema Integration
The new implementation uses PostgreSQL's runtime schema to precisely determine which fields contain Node types that need traversal, eliminating guesswork and improving accuracy.
The original visit
function is still available for backward compatibility:
import { visit } from '@pgsql/traverse'; const visitor = { SelectStmt: (node, ctx) => { console.log('Found SELECT statement:', node); console.log('Path:', ctx.path); }, RangeVar: (node, ctx) => { console.log('Found table reference:', node.relname); } }; // Parse some SQL and traverse the AST const ast = /* your parsed AST */; visit(ast, visitor);
import { walk } from '@pgsql/traverse'; const visitor = { ParseResult: (path) => { console.log('Parse result version:', path.node.version); console.log('Number of statements:', path.node.stmts.length); }, SelectStmt: (path) => { console.log('SELECT statement found'); } }; walk(parseResult, visitor);
The visitor context provides information about the current traversal state:
const visitor = { RangeVar: (path) => { console.log('Table name:', path.node.relname); console.log('Path to this node:', path.path); console.log('Parent node:', path.parent?.tag); console.log('Key in parent:', path.key); } };Collecting Information During Traversal
import { visit } from '@pgsql/traverse'; import type { Visitor } from '@pgsql/traverse'; const tableNames: string[] = []; const columnRefs: string[] = []; const visitor: Visitor = { RangeVar: (node) => { if (node.relname) { tableNames.push(node.relname); } }, ColumnRef: (node) => { if (node.fields) { node.fields.forEach(field => { if (field.String?.sval) { columnRefs.push(field.String.sval); } }); } } }; visit(ast, visitor); console.log('Tables referenced:', tableNames); console.log('Columns referenced:', columnRefs);
walk(root, callback, parent?, keyPath?)
Walks the tree of PostgreSQL AST nodes using runtime schema for precise traversal.
Parameters:
root
: The AST node to traversecallback
: A walker function or visitor objectparent?
: Optional parent NodePath (for internal use)keyPath?
: Optional key path array (for internal use)Example:
walk(ast, { SelectStmt: (path) => { // Handle SELECT statements // Return false to skip children }, RangeVar: (path) => { // Handle table references } });
visit(node, visitor, ctx?)
(Legacy)
Recursively visits a PostgreSQL AST node, calling any matching visitor functions. Maintained for backward compatibility.
Parameters:
node
: The AST node to traversevisitor
: An object with visitor functions for different node typesctx?
: Optional initial visitor contextAn object type where keys are node type names and values are walker functions:
type Visitor = { [TTag in NodeTag]?: Walker<NodePath<TTag>>; };
A function that receives a NodePath and can return false to skip children:
type Walker<TNodePath extends NodePath = NodePath> = ( path: TNodePath, ) => boolean | void;
A class that encapsulates node traversal context:
class NodePath<TTag extends NodeTag = NodeTag> { tag: TTag; // Node type node: Node[TTag]; // Node data parent: NodePath | null; // Parent path keyPath: readonly (string | number)[]; // Full path get path(): (string | number)[]; // Path copy get key(): string | number; // Current key }
Context information provided to legacy visitor functions:
type VisitorContext = { path: (string | number)[]; // Path to current node parent: any; // Parent node key: string | number; // Key in parent node };
Union type of all PostgreSQL AST node type names:
type NodeTag = keyof Node;
This package works with all PostgreSQL AST node types defined in @pgsql/types
, including:
ParseResult
- Root parse result from libpg-querySelectStmt
- SELECT statementsInsertStmt
- INSERT statementsUpdateStmt
- UPDATE statementsDeleteStmt
- DELETE statementsRangeVar
- Table referencesColumnRef
- Column referencesA_Expr
- ExpressionsA_Const
- ConstantsThis package is designed to work seamlessly with the pgsql-parser ecosystem:
import { parse } from 'pgsql-parser'; import { visit } from '@pgsql/traverse'; const sql = 'SELECT name, email FROM users WHERE age > 18'; const ast = await parse(sql); const visitor = { RangeVar: (node) => { console.log('Table:', node.relname); }, ColumnRef: (node) => { console.log('Column:', node.fields?.[0]?.String?.sval); } }; visit(ast, visitor);
pgsql-parser
.pgsql-parser
for parsing and deparsing SQL queries.AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
No developer or entity involved in creating Software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the Software code or Software CLI, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4