PostgreSQL SQL & PL/pgSQL AST Parser (@pgsql/parser)
The `@pgsql/parser` package is a core component within a comprehensive monorepo for PostgreSQL Abstract Syntax Tree (AST) parsing, manipulation, and code generation. It provides a robust, multi-version PostgreSQL parser capable of converting SQL and PL/pgSQL queries into hydrated ASTs, supporting PostgreSQL versions 13 through 17. The current stable version for `@pgsql/parser` is approximately 17.0.4, with active development and frequent updates across the monorepo's packages, such as `@pgsql/deparser` (around 0.7.3). Key differentiators include its direct integration with `libpg-query` (the actual PostgreSQL parser exposed for Node.js), offering high fidelity to PostgreSQL's native parsing logic. It also ships with extensive TypeScript type definitions (`@pgsql/types`), utilities for programmatic AST construction (`@pgsql/utils`), and traversal tools (`@pgsql/traverse`), making it suitable for advanced static analysis, query transformation, and code generation tasks. This library aims to provide a complete toolkit for working with PostgreSQL at the AST level.
Common errors
-
Cannot find package '@pgsql/parser' or its corresponding type declarations.
cause The `@pgsql/parser` package has not been installed, or TypeScript cannot resolve it.fixInstall the package: `npm install @pgsql/parser` or `yarn add @pgsql/parser`. -
Syntax error at or near "..."
cause The input SQL or PL/pgSQL string contains invalid syntax according to the PostgreSQL grammar, or the parser version used does not support certain syntax.fixCarefully review the SQL/PL/pgSQL string for typos, missing punctuation, incorrect keywords, or dialect-specific syntax not compatible with standard PostgreSQL. Ensure the `@pgsql/parser` version supports the target PostgreSQL version's syntax. -
TypeError: parse is not a function (or similar for require)
cause Attempting to use CommonJS `require` syntax in an ESM module, or vice-versa, or incorrectly accessing the named export `parse`.fixFor ESM (`type: 'module'` in package.json or `.mjs` files): `import { parse } from '@pgsql/parser';`. For CommonJS (`type: 'commonjs'` or `.cjs` files): `const { parse } = require('@pgsql/parser');`.
Warnings
- breaking The package `plpgsql-parser` with version `0.5.8` as specified is likely an outdated reference. The current, actively maintained parser from the `constructive-io/pgsql-parser` monorepo is `@pgsql/parser`, which is at major version `17.x.x`. Users should install `@pgsql/parser` and other scoped packages like `@pgsql/deparser` and `@pgsql/types`.
- gotcha The package structure is a monorepo. While a top-level `pgsql-parser` package might exist in the monorepo, the recommended and actively developed components for parsing, deparsing, and types are the scoped packages (`@pgsql/parser`, `@pgsql/deparser`, `@pgsql/types`, etc.). Relying on a non-scoped `pgsql-parser` may lead to outdated versions or missing functionalities.
- breaking Breaking changes occur between major versions of `@pgsql/parser` due to updates in the underlying PostgreSQL parser (libpg-query) and API refinements. For example, changes in AST node structures or function signatures may be introduced to align with new PostgreSQL features or improve consistency.
- gotcha Direct manipulation of the PostgreSQL AST can be complex due to its depth and the specific structure reflecting PostgreSQL's internal representation. Incorrect modifications can lead to invalid SQL or runtime errors during deparsing.
Install
-
npm install plpgsql-parser -
yarn add plpgsql-parser -
pnpm add plpgsql-parser
Imports
- parse
const { parse } = require('pgsql-parser');import { parse } from '@pgsql/parser'; - deparse
import { deparse } from '@pgsql/parser';import { deparse } from '@pgsql/deparser'; - SelectStmt
import { SelectStmt } from '@pgsql/parser';import type { SelectStmt } from '@pgsql/types';
Quickstart
import { parse } from '@pgsql/parser';
import { deparse } from '@pgsql/deparser';
import type { SelectStmt } from '@pgsql/types';
async function processSqlQuery(sql: string) {
try {
console.log(`Parsing SQL: ${sql}`);
// Parse the SQL query into an Abstract Syntax Tree (AST)
const ast = await parse(sql);
console.log('Parsed AST (partial):', JSON.stringify(ast.stmts[0]?.stmt, null, 2));
// Example: Accessing a specific type of statement and its properties
if (ast.stmts[0]?.stmt.SelectStmt) {
const selectStmt = ast.stmts[0].stmt.SelectStmt as SelectStmt;
console.log('Target list items:', selectStmt.targetList?.map(t => JSON.stringify(t.ResTarget?.val)));
}
// Convert the AST back to SQL (deparse)
const deparsedSql = await deparse(ast);
console.log(`Deparsed SQL: ${deparsedSql}`);
} catch (error: any) {
console.error('Error processing SQL:', error.message);
if (error.sqlDetails) {
console.error('SQL Details:', error.sqlDetails);
}
}
}
// Example SQL queries
processSqlQuery('SELECT id, name FROM users WHERE status = $1 ORDER BY created_at DESC;');
processSqlQuery('CREATE FUNCTION add(a INT, b INT) RETURNS INT LANGUAGE plpgsql AS $$ BEGIN RETURN a + b; END; $$;');
// Intentionally malformed SQL to demonstrate error handling
// processSqlQuery('SELECT * FROM users WHERE;');