|
| 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