|
1 | | -# pgsql-parser |
| 1 | +# PostgreSQL AST Tools |
2 | 2 |
|
3 | 3 | <p align="center" width="100%"> |
4 | 4 | <img height="120" src="https://github.com/launchql/pgsql-parser/assets/545047/6440fa7d-918b-4a3b-8d1b-755d85de8bea" /> |
|
11 | 11 | <a href="https://www.npmjs.com/package/pgsql-parser"><img height="20" src="https://img.shields.io/npm/dt/pgsql-parser"></a> |
12 | 12 | <a href="https://www.npmjs.com/package/pgsql-parser"><img height="20" src="https://img.shields.io/npm/dw/pgsql-parser"/></a> |
13 | 13 | <a href="https://github.com/launchql/pgsql-parser/blob/main/LICENSE-MIT"><img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/></a> |
14 | | - <a href="https://www.npmjs.com/package/pgsql-parser"><img height="20" src="https://img.shields.io/github/package-json/v/launchql/pgsql-parser?filename=packages%2Fparser%2Fpackage.json"/></a> |
15 | 14 | </p> |
16 | 15 |
|
17 | | -The real PostgreSQL parser for Node.js, `pgsql-parser` provides symmetric parsing and deparsing of SQL statements using the actual [PostgreSQL parser](https://github.com/pganalyze/libpg_query). It allows you to parse SQL queries into AST and modify or reconstruct SQL queries from the AST. |
| 16 | +A comprehensive monorepo for PostgreSQL Abstract Syntax Tree (AST) parsing, manipulation, and code generation. This collection of packages provides everything you need to work with PostgreSQL at the AST level, from parsing SQL queries to generating type-safe TypeScript definitions. |
18 | 17 |
|
19 | | -## Installation |
| 18 | +## 📦 Packages Overview |
20 | 19 |
|
21 | | -```sh |
22 | | -npm install pgsql-parser |
23 | | -``` |
| 20 | +| Package | Description | Key Features | |
| 21 | +|---------|-------------|--------------| |
| 22 | +| [**pgsql-parser**](./packages/parser) | The real PostgreSQL parser for Node.js | • Uses actual PostgreSQL C parser via WebAssembly<br>• Symmetric parsing and deparsing<br>• Battle-tested with 23,000+ SQL statements | |
| 23 | +| [**pgsql-deparser**](./packages/deparser) | Lightning-fast SQL generation from AST | • Pure TypeScript, zero dependencies<br>• No WebAssembly overhead<br>• Perfect for AST-to-SQL conversion only | |
| 24 | +| [**@pgsql/cli**](./packages/pgsql-cli) | Unified CLI for all PostgreSQL AST operations | • Parse SQL to AST<br>• Deparse AST to SQL<br>• Generate TypeScript from protobuf<br>• Single tool for all operations | |
| 25 | +| [**@pgsql/utils**](./packages/utils) | Type-safe AST node creation utilities | • Programmatic AST construction<br>• Enum value conversions<br>• Seamless integration with types | |
| 26 | +| [**pg-proto-parser**](./packages/proto-parser) | PostgreSQL protobuf parser and code generator | • Generate TypeScript interfaces from protobuf<br>• Create enum mappings and utilities<br>• AST helper generation | |
24 | 27 |
|
25 | | -## Key Features |
| 28 | +## 🚀 Quick Start |
26 | 29 |
|
27 | | -- **True PostgreSQL Parsing:** Utilizes the real PostgreSQL source code for accurate parsing. |
28 | | -- **Symmetric Parsing and Deparsing:** Convert SQL to AST and back, enabling query manipulation. |
29 | | -- **AST Manipulation:** Easily modify parts of a SQL statement through the AST. |
30 | | -- **WebAssembly Powered:** Cross-platform compatibility without native dependencies. |
| 30 | +### Installation |
31 | 31 |
|
32 | | -## API |
| 32 | +Choose the packages you need: |
| 33 | + |
| 34 | +```bash |
| 35 | +# For parsing SQL to AST and back |
| 36 | +npm install pgsql-parser |
33 | 37 |
|
34 | | -The package exports both async and sync methods. Async methods handle initialization automatically, while sync methods require explicit initialization. |
| 38 | +# For only converting AST to SQL (lighter weight) |
| 39 | +npm install pgsql-deparser |
35 | 40 |
|
36 | | -⚠️ We recommend using `@pgsql/deparser` instead of `deparse` from `pgsql-parser`. The deparser package is more complete, supports sub-expressions, and doesn't require the WebAssembly module, making it lighter and more flexible for most use cases. It will soon be deprecated, in a minor version bump. |
| 41 | +# For the unified CLI tool |
| 42 | +npm install -g @pgsql/cli |
| 43 | + |
| 44 | +# For programmatic AST construction |
| 45 | +npm install @pgsql/utils |
| 46 | + |
| 47 | +# For protobuf parsing and code generation |
| 48 | +npm install pg-proto-parser |
| 49 | +``` |
37 | 50 |
|
38 | | -### Async Methods (Recommended) |
| 51 | +### Basic Usage |
39 | 52 |
|
| 53 | +#### Parse SQL to AST |
40 | 54 | ```typescript |
41 | | -import { parse, deparse, parseFunction } from 'pgsql-parser'; |
42 | | - |
43 | | -// Parse SQL to AST |
44 | | -const stmts = await parse('SELECT * FROM test_table'); |
45 | | - |
46 | | -// Deparse AST back to SQL |
47 | | -const sql = await deparse(stmts); |
48 | | - |
49 | | -// Parse PL/pgSQL functions |
50 | | -const funcAst = await parseFunction(` |
51 | | - CREATE FUNCTION get_count() RETURNS integer AS $$ |
52 | | - BEGIN |
53 | | - RETURN (SELECT COUNT(*) FROM users); |
54 | | - END; |
55 | | - $$ LANGUAGE plpgsql; |
56 | | -`); |
| 55 | +import { parse } from 'pgsql-parser'; |
| 56 | + |
| 57 | +const ast = await parse('SELECT * FROM users WHERE id = 1'); |
| 58 | +console.log(JSON.stringify(ast, null, 2)); |
57 | 59 | ``` |
58 | 60 |
|
59 | | -### Sync Methods |
| 61 | +#### Convert AST back to SQL |
| 62 | +```typescript |
| 63 | +import { deparse } from 'pgsql-deparser'; |
60 | 64 |
|
61 | | -Sync methods require explicit initialization using `loadModule()`: |
| 65 | +const sql = deparse(ast); |
| 66 | +console.log(sql); // SELECT * FROM users WHERE id = 1 |
| 67 | +``` |
62 | 68 |
|
| 69 | +#### Build AST Programmatically |
63 | 70 | ```typescript |
64 | | -import { loadModule, parseSync, deparseSync } from 'pgsql-parser'; |
| 71 | +import ast from '@pgsql/utils'; |
| 72 | + |
| 73 | +const selectStmt = ast.selectStmt({ |
| 74 | + targetList: [ |
| 75 | + ast.resTarget({ |
| 76 | + val: ast.columnRef({ |
| 77 | + fields: [ast.aStar()] |
| 78 | + }) |
| 79 | + }) |
| 80 | + ], |
| 81 | + fromClause: [ |
| 82 | + ast.rangeVar({ |
| 83 | + relname: 'users', |
| 84 | + inh: true, |
| 85 | + relpersistence: 'p' |
| 86 | + }) |
| 87 | + ], |
| 88 | + limitOption: 'LIMIT_OPTION_DEFAULT', |
| 89 | + op: 'SETOP_NONE' |
| 90 | +}); |
| 91 | +``` |
| 92 | + |
| 93 | +#### Use the CLI |
| 94 | +```bash |
| 95 | +# Parse SQL file |
| 96 | +pgsql parse query.sql |
65 | 97 |
|
66 | | -// Initialize first (required for sync methods) |
67 | | -await loadModule(); |
| 98 | +# Convert AST to SQL |
| 99 | +pgsql deparse ast.json |
68 | 100 |
|
69 | | -// Now safe to use sync methods |
70 | | -const stmts = parseSync('SELECT * FROM test_table'); |
71 | | -const sql = deparseSync(stmts); |
| 101 | +# Generate TypeScript from protobuf |
| 102 | +pgsql proto-gen --inFile pg_query.proto --outDir out --types --enums |
72 | 103 | ``` |
73 | 104 |
|
74 | | -**Note:** We recommend using async methods as they handle initialization automatically. Use sync methods only when necessary, and always call `loadModule()` first. |
| 105 | +## 🏗️ Architecture |
75 | 106 |
|
76 | | -## Parser Example |
| 107 | +This monorepo is organized to provide modular, focused tools that work together seamlessly: |
77 | 108 |
|
78 | | -Rewrite part of a SQL query: |
| 109 | +``` |
| 110 | +pgsql-parser/ |
| 111 | +├── packages/ |
| 112 | +│ ├── parser/ # Core parser with WebAssembly |
| 113 | +│ ├── deparser/ # Pure TypeScript deparser |
| 114 | +│ ├── pgsql-cli/ # Unified CLI tool |
| 115 | +│ ├── utils/ # AST construction utilities |
| 116 | +│ ├── proto-parser/ # Protobuf code generation |
| 117 | +│ └── transform/ # (Not production-ready yet) |
| 118 | +└── ... |
| 119 | +``` |
| 120 | + |
| 121 | +### Package Relationships |
| 122 | + |
| 123 | +- **pgsql-parser** provides full parsing and deparsing capabilities using the actual PostgreSQL parser |
| 124 | +- **pgsql-deparser** offers a lightweight alternative for just converting AST to SQL |
| 125 | +- **@pgsql/utils** helps construct ASTs programmatically with type safety |
| 126 | +- **pg-proto-parser** generates TypeScript definitions from PostgreSQL protobuf files |
| 127 | +- **@pgsql/cli** unifies all functionality into a single command-line tool |
79 | 128 |
|
80 | | -```js |
81 | | -import { parse, deparse } from 'pgsql-parser'; |
| 129 | +## 🛠️ Development |
82 | 130 |
|
83 | | -const stmts = await parse('SELECT * FROM test_table'); |
| 131 | +This project uses Yarn workspaces and Lerna for monorepo management. |
84 | 132 |
|
85 | | -// Assuming the structure of stmts is known and matches the expected type |
86 | | -stmts[0].RawStmt.stmt.SelectStmt.fromClause[0].RangeVar.relname = 'another_table'; |
| 133 | +### Setup |
| 134 | +```bash |
| 135 | +# Install dependencies |
| 136 | +yarn install |
87 | 137 |
|
88 | | -console.log(await deparse(stmts)); |
| 138 | +# Build all packages |
| 139 | +yarn build |
89 | 140 |
|
90 | | -// SELECT * FROM "another_table" |
| 141 | +# Run tests |
| 142 | +yarn test |
91 | 143 | ``` |
92 | 144 |
|
93 | | -## Deparser Example |
| 145 | +### Building Individual Packages |
| 146 | +```bash |
| 147 | +cd packages/parser |
| 148 | +npm run build |
| 149 | +``` |
| 150 | + |
| 151 | +## 📚 Documentation |
| 152 | + |
| 153 | +Each package has its own detailed README: |
94 | 154 |
|
95 | | -The `pgsql-deparser` module serializes ASTs to SQL in pure TypeScript, avoiding the full parser's native dependencies. It's useful when only SQL string conversion from ASTs is needed, and is written in pure TypeScript for easy cross-environment deployment. |
| 155 | +- [pgsql-parser Documentation](./packages/parser/README.md) |
| 156 | +- [pgsql-deparser Documentation](./packages/deparser/README.md) |
| 157 | +- [@pgsql/cli Documentation](./packages/pgsql-cli/README.md) |
| 158 | +- [@pgsql/utils Documentation](./packages/utils/README.md) |
| 159 | +- [pg-proto-parser Documentation](./packages/proto-parser/README.md) |
96 | 160 |
|
97 | | -Here's how you can use the deparser in your TypeScript code, using [`@pgsql/utils`](https://github.com/launchql/pgsql-parser/tree/main/packages/utils) to create an AST for `deparse`: |
| 161 | +## 🎯 Use Cases |
98 | 162 |
|
99 | | -```ts |
100 | | -import * as t from '@pgsql/utils'; |
101 | | -import { RangeVar, SelectStmt } from '@pgsql/types'; |
102 | | -import { deparseSync as deparse } from 'pgsql-deparser'; |
| 163 | +- **SQL Query Analysis**: Parse queries to understand their structure and components |
| 164 | +- **Query Transformation**: Modify queries programmatically at the AST level |
| 165 | +- **SQL Generation**: Build complex queries programmatically with type safety |
| 166 | +- **Code Generation**: Generate TypeScript types from PostgreSQL schemas |
| 167 | +- **Query Validation**: Validate SQL syntax using the real PostgreSQL parser |
| 168 | +- **Database Tooling**: Build developer tools that understand PostgreSQL deeply |
103 | 169 |
|
104 | | -// This could have been obtained from any JSON or AST, not necessarily @pgsql/utils |
105 | | -const stmt: { SelectStmt: SelectStmt } = t.nodes.selectStmt({ |
| 170 | +## 💡 Examples |
| 171 | + |
| 172 | +### Transform a Query |
| 173 | +```typescript |
| 174 | +import { parse } from 'pgsql-parser'; |
| 175 | +import { deparse } from 'pgsql-deparser'; |
| 176 | + |
| 177 | +// Parse the original query |
| 178 | +const ast = await parse('SELECT * FROM users WHERE active = true'); |
| 179 | + |
| 180 | +// Modify the table name |
| 181 | +ast[0].RawStmt.stmt.SelectStmt.fromClause[0].RangeVar.relname = 'customers'; |
| 182 | + |
| 183 | +// Generate the modified SQL |
| 184 | +const newSql = deparse(ast); |
| 185 | +console.log(newSql); // SELECT * FROM customers WHERE active = TRUE |
| 186 | +``` |
| 187 | + |
| 188 | +### Build a Query Programmatically |
| 189 | +```typescript |
| 190 | +import ast from '@pgsql/utils'; |
| 191 | +import { deparse } from 'pgsql-deparser'; |
| 192 | + |
| 193 | +const query = ast.selectStmt({ |
106 | 194 | targetList: [ |
107 | | - t.nodes.resTarget({ |
108 | | - val: t.nodes.columnRef({ |
109 | | - fields: [t.nodes.aStar()] |
| 195 | + ast.resTarget({ |
| 196 | + val: ast.columnRef({ |
| 197 | + fields: [ast.string({ str: 'name' })] |
| 198 | + }) |
| 199 | + }), |
| 200 | + ast.resTarget({ |
| 201 | + val: ast.columnRef({ |
| 202 | + fields: [ast.string({ str: 'email' })] |
110 | 203 | }) |
111 | 204 | }) |
112 | 205 | ], |
113 | 206 | fromClause: [ |
114 | | - t.nodes.rangeVar({ |
115 | | - relname: 'some_table', |
| 207 | + ast.rangeVar({ |
| 208 | + relname: 'users', |
116 | 209 | inh: true, |
117 | 210 | relpersistence: 'p' |
118 | 211 | }) |
119 | 212 | ], |
| 213 | + whereClause: ast.aExpr({ |
| 214 | + kind: 'AEXPR_OP', |
| 215 | + name: [ast.string({ str: '>' })], |
| 216 | + lexpr: ast.columnRef({ |
| 217 | + fields: [ast.string({ str: 'age' })] |
| 218 | + }), |
| 219 | + rexpr: ast.aConst({ |
| 220 | + val: ast.integer({ ival: 18 }) |
| 221 | + }) |
| 222 | + }), |
120 | 223 | limitOption: 'LIMIT_OPTION_DEFAULT', |
121 | 224 | op: 'SETOP_NONE' |
122 | 225 | }); |
123 | 226 |
|
124 | | -// Modify the AST if needed |
125 | | -(stmt.SelectStmt.fromClause[0] as {RangeVar: RangeVar}).RangeVar.relname = 'another_table'; |
126 | | - |
127 | | -// Deparse the modified AST back to a SQL string |
128 | | -console.log(deparse(stmt)); |
129 | | - |
130 | | -// Output: SELECT * FROM another_table |
131 | | -``` |
132 | | - |
133 | | -## CLI |
134 | | - |
135 | | -``` |
136 | | -npm install -g pgsql-parser |
| 227 | +console.log(deparse(query)); |
| 228 | +// SELECT name, email FROM users WHERE age > 18 |
137 | 229 | ``` |
138 | 230 |
|
139 | | -### usage |
| 231 | +## 🤝 Contributing |
140 | 232 |
|
141 | | -```sh |
142 | | -pgsql-parser <sqlfile> |
143 | | -``` |
| 233 | +We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. |
144 | 234 |
|
145 | | -## Versions |
| 235 | +### Development Workflow |
| 236 | +1. Fork the repository |
| 237 | +2. Create your feature branch (`git checkout -b feature/amazing-feature`) |
| 238 | +3. Commit your changes (`git commit -m 'Add some amazing feature'`) |
| 239 | +4. Push to the branch (`git push origin feature/amazing-feature`) |
| 240 | +5. Open a Pull Request |
146 | 241 |
|
147 | | -As of PG 13, PG majors versions maintained will have a matching dedicated major npm version. Only the latest Postgres stable release receives active updates. |
| 242 | +## 📄 License |
148 | 243 |
|
149 | | -Our latest is built with `17-latest` branch from libpg_query |
| 244 | +This project is licensed under the MIT License - see the [LICENSE](LICENSE-MIT) file for details. |
150 | 245 |
|
151 | | -| PostgreSQL Major Version | libpg_query | Status | npm tag | |
152 | | -|--------------------------|-------------|---------------------|---------| |
153 | | -| 17 | 17-latest | Active Development | `latest` | |
154 | | -| 16 | (n/a) | Not supported | |
155 | | -| 15 | (n/a) | Not supported | |
156 | | -| 14 | (n/a) | Not supported | |
157 | | -| 13 | 13-latest | Only Critical Fixes | `13.16.0` | |
158 | | -| 12 | (n/a) | Not supported | |
159 | | -| 11 | (n/a) | Not supported | |
160 | | -| 10 | 10-latest | Not supported | `@1.3.1` ([tree](https://github.com/launchql/pgsql-parser/tree/39b7b1adc8914253226e286a48105785219a81ca)) | |
| 246 | +## 🙏 Credits |
161 | 247 |
|
| 248 | +Built on the excellent work of several contributors: |
162 | 249 |
|
163 | | -## Related |
| 250 | +* **[Dan Lynch](https://github.com/pyramation)** — official maintainer since 2018 and architect of the current implementation |
| 251 | +* **[Lukas Fittl](https://github.com/lfittl)** for [libpg_query](https://github.com/pganalyze/libpg_query) — the core PostgreSQL parser that powers this project |
| 252 | +* **[Greg Richardson](https://github.com/gregnr)** for AST guidance and pushing the transition to WASM for better interoperability |
| 253 | +* **[Ethan Resnick](https://github.com/ethanresnick)** for the original Node.js N-API bindings |
| 254 | +* **[Zac McCormick](https://github.com/zhm)** for the foundational [node-pg-query-native](https://github.com/zhm/node-pg-query-native) parser |
164 | 255 |
|
165 | | -* [pgsql-parser](https://github.com/launchql/pgsql-parser): The real PostgreSQL parser for Node.js, providing symmetric parsing and deparsing of SQL statements with actual PostgreSQL parser integration. |
166 | | -* [pgsql-deparser](https://github.com/launchql/pgsql-parser/tree/main/packages/deparser): A streamlined tool designed for converting PostgreSQL ASTs back into SQL queries, focusing solely on deparser functionality to complement `pgsql-parser`. |
167 | | -* [pgsql-enums](https://github.com/launchql/pgsql-parser/tree/main/packages/pgsql-enums): A utility package offering easy access to PostgreSQL enumeration types in JSON format, aiding in string and integer conversions of enums used within ASTs to compliment `pgsql-parser` |
168 | | -* [@pgsql/enums](https://github.com/launchql/pgsql-parser/tree/main/packages/enums): Provides PostgreSQL AST enums in TypeScript, enhancing type safety and usability in projects interacting with PostgreSQL AST nodes. |
169 | | -* [@pgsql/types](https://github.com/launchql/pgsql-parser/tree/main/packages/types): Offers TypeScript type definitions for PostgreSQL AST nodes, facilitating type-safe construction, analysis, and manipulation of ASTs. |
170 | | -* [@pgsql/utils](https://github.com/launchql/pgsql-parser/tree/main/packages/utils): A comprehensive utility library for PostgreSQL, offering type-safe AST node creation and enum value conversions, simplifying the construction and manipulation of PostgreSQL ASTs. |
171 | | -* [pg-proto-parser](https://github.com/launchql/pg-proto-parser): A TypeScript tool that parses PostgreSQL Protocol Buffers definitions to generate TypeScript interfaces, utility functions, and JSON mappings for enums. |
172 | | -* [libpg-query](https://github.com/launchql/libpg-query-node): The real PostgreSQL parser exposed for Node.js, used primarily in `pgsql-parser` for parsing and deparsing SQL queries. |
| 256 | +## 🔗 Related Projects |
173 | 257 |
|
174 | | -## Credits |
| 258 | +### Core Packages (in this monorepo) |
| 259 | +* [pgsql-parser](https://www.npmjs.com/package/pgsql-parser): The real PostgreSQL parser for Node.js |
| 260 | +* [pgsql-deparser](https://www.npmjs.com/package/pgsql-deparser): Lightning-fast SQL generation from AST |
| 261 | +* [@pgsql/cli](https://www.npmjs.com/package/@pgsql/cli): Unified CLI for PostgreSQL AST operations |
| 262 | +* [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): Type-safe AST construction utilities |
| 263 | +* [pg-proto-parser](https://www.npmjs.com/package/pg-proto-parser): PostgreSQL protobuf parser and code generator |
175 | 264 |
|
176 | | -Thanks [@lfittl](https://github.com/lfittl) for building the core `libpg_query` suite: |
177 | 265 |
|
178 | | -* [libpg_query](https://github.com/pganalyze/libpg_query) |
179 | | -* [pg_query](https://github.com/lfittl/pg_query) |
180 | | -* [pg_query.go](https://github.com/lfittl/pg_query.go) |
181 | 266 |
|
182 | | -Thanks to [@zhm](https://github.com/zhm) for the [OG Parser](https://github.com/zhm/pg-query-parser/blob/master/LICENSE.md) that started it all. |
| 267 | +### External Dependencies |
| 268 | +* [libpg-query](https://github.com/launchql/libpg-query-node): The PostgreSQL parser exposed for Node.js |
183 | 269 |
|
184 | | -## Disclaimer |
| 270 | +## ⚖️ Disclaimer |
185 | 271 |
|
186 | 272 | AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND. |
187 | 273 |
|
|
0 commit comments