Skip to content

Commit beeb09d

Browse files
feat: add @pgsql/traverse package for AST traversal
- Implement visitor pattern for PostgreSQL AST nodes - Add comprehensive test suite with various node types - Follow existing package structure and build patterns - Support both individual nodes and ParseResult objects - Include Jest configuration for TypeScript testing - Add README with usage examples and API documentation Co-Authored-By: Dan Lynch <[email protected]>
1 parent d8838c5 commit beeb09d

File tree

8 files changed

+603
-0
lines changed

8 files changed

+603
-0
lines changed

packages/traverse/README.md

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
# @pgsql/traverse
2+
3+
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.
4+
5+
## Installation
6+
7+
```bash
8+
npm install @pgsql/traverse
9+
```
10+
11+
## Usage
12+
13+
### Basic Traversal
14+
15+
```typescript
16+
import { visit } from '@pgsql/traverse';
17+
import type { Visitor } from '@pgsql/traverse';
18+
19+
const visitor: Visitor = {
20+
SelectStmt: (node, ctx) => {
21+
console.log('Found SELECT statement:', node);
22+
},
23+
RangeVar: (node, ctx) => {
24+
console.log('Found table reference:', node.relname);
25+
}
26+
};
27+
28+
const ast = {
29+
SelectStmt: {
30+
targetList: [
31+
{
32+
ResTarget: {
33+
val: {
34+
ColumnRef: {
35+
fields: [{ A_Star: {} }]
36+
}
37+
}
38+
}
39+
}
40+
],
41+
fromClause: [
42+
{
43+
RangeVar: {
44+
relname: 'users',
45+
inh: true,
46+
relpersistence: 'p'
47+
}
48+
}
49+
],
50+
limitOption: 'LIMIT_OPTION_DEFAULT',
51+
op: 'SETOP_NONE'
52+
}
53+
};
54+
55+
visit(ast, visitor);
56+
```
57+
58+
### Working with ParseResult
59+
60+
```typescript
61+
import { visit } from '@pgsql/traverse';
62+
import type { Visitor } from '@pgsql/traverse';
63+
64+
const visitor: Visitor = {
65+
ParseResult: (node, ctx) => {
66+
console.log('PostgreSQL version:', node.version);
67+
},
68+
RawStmt: (node, ctx) => {
69+
console.log('Found statement at path:', ctx.path);
70+
}
71+
};
72+
73+
const parseResult = {
74+
ParseResult: {
75+
version: 170004,
76+
stmts: [
77+
{
78+
RawStmt: {
79+
stmt: {
80+
SelectStmt: {
81+
targetList: [],
82+
limitOption: 'LIMIT_OPTION_DEFAULT',
83+
op: 'SETOP_NONE'
84+
}
85+
}
86+
}
87+
}
88+
]
89+
}
90+
};
91+
92+
visit(parseResult, visitor);
93+
```
94+
95+
### Using Visitor Context
96+
97+
The visitor context provides useful information about the current traversal state:
98+
99+
```typescript
100+
import { visit } from '@pgsql/traverse';
101+
import type { Visitor, VisitorContext } from '@pgsql/traverse';
102+
103+
const visitor: Visitor = {
104+
RangeVar: (node, ctx: VisitorContext) => {
105+
console.log('Table name:', node.relname);
106+
console.log('Path to this node:', ctx.path);
107+
console.log('Parent object:', ctx.parent);
108+
console.log('Key in parent:', ctx.key);
109+
}
110+
};
111+
```
112+
113+
### Collecting Information During Traversal
114+
115+
```typescript
116+
import { visit } from '@pgsql/traverse';
117+
import type { Visitor } from '@pgsql/traverse';
118+
119+
const tableNames: string[] = [];
120+
const columnRefs: string[] = [];
121+
122+
const visitor: Visitor = {
123+
RangeVar: (node) => {
124+
if (node.relname) {
125+
tableNames.push(node.relname);
126+
}
127+
},
128+
ColumnRef: (node) => {
129+
if (node.fields) {
130+
node.fields.forEach(field => {
131+
if (field.String?.sval) {
132+
columnRefs.push(field.String.sval);
133+
}
134+
});
135+
}
136+
}
137+
};
138+
139+
visit(ast, visitor);
140+
141+
console.log('Tables referenced:', tableNames);
142+
console.log('Columns referenced:', columnRefs);
143+
```
144+
145+
## API
146+
147+
### `visit(node, visitor, ctx?)`
148+
149+
Recursively visits a PostgreSQL AST node, calling any matching visitor functions.
150+
151+
**Parameters:**
152+
- `node: KnownNode` - The AST node to traverse (can be a ParseResult, wrapped node, or any PostgreSQL AST node)
153+
- `visitor: Visitor` - Object containing visitor functions for different node types
154+
- `ctx?: VisitorContext` - Optional initial context (usually not needed)
155+
156+
### Types
157+
158+
#### `Visitor`
159+
160+
An object where keys are PostgreSQL AST node type names and values are visitor functions:
161+
162+
```typescript
163+
type Visitor = {
164+
[K in keyof KnownNode]?: (node: KnownNode[K], ctx: VisitorContext) => void;
165+
};
166+
```
167+
168+
#### `VisitorContext`
169+
170+
Context information provided to each visitor function:
171+
172+
```typescript
173+
type VisitorContext = {
174+
path: (string | number)[]; // Path to current node (e.g., ['SelectStmt', 'fromClause', 0])
175+
parent: any; // Parent object containing this node
176+
key: string | number; // Key or index of this node in its parent
177+
};
178+
```
179+
180+
## Supported Node Types
181+
182+
This package works with all PostgreSQL AST node types defined in `@pgsql/types`, including:
183+
184+
- `ParseResult` - Root parse result from libpg-query
185+
- `SelectStmt` - SELECT statements
186+
- `InsertStmt` - INSERT statements
187+
- `UpdateStmt` - UPDATE statements
188+
- `DeleteStmt` - DELETE statements
189+
- `RangeVar` - Table references
190+
- `ColumnRef` - Column references
191+
- `A_Expr` - Expressions
192+
- `A_Const` - Constants
193+
- And many more...
194+
195+
## Integration with pgsql-parser
196+
197+
This package is designed to work seamlessly with the pgsql-parser ecosystem:
198+
199+
```typescript
200+
import { parse } from 'pgsql-parser';
201+
import { visit } from '@pgsql/traverse';
202+
203+
const sql = 'SELECT name, email FROM users WHERE age > 18';
204+
const ast = await parse(sql);
205+
206+
const visitor = {
207+
RangeVar: (node) => {
208+
console.log('Table:', node.relname);
209+
},
210+
ColumnRef: (node) => {
211+
console.log('Column:', node.fields?.[0]?.String?.sval);
212+
}
213+
};
214+
215+
visit(ast, visitor);
216+
```
217+
218+
## Related
219+
220+
* [pgsql-parser](https://www.npmjs.com/package/pgsql-parser): The real PostgreSQL parser for Node.js
221+
* [pgsql-deparser](https://www.npmjs.com/package/pgsql-deparser): Convert PostgreSQL ASTs back into SQL queries
222+
* [@pgsql/types](https://www.npmjs.com/package/@pgsql/types): TypeScript type definitions for PostgreSQL AST nodes
223+
* [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): Utility library for PostgreSQL AST node creation
224+
225+
## License
226+
227+
SEE LICENSE IN LICENSE

0 commit comments

Comments
 (0)