{"id":13526946,"url":"https://github.com/oguimbal/pgsql-ast-parser","last_synced_at":"2025-05-15T20:05:25.783Z","repository":{"id":36958673,"uuid":"311733813","full_name":"oguimbal/pgsql-ast-parser","owner":"oguimbal","description":"Yet another simple Postgres SQL parser","archived":false,"fork":false,"pushed_at":"2024-08-20T10:53:39.000Z","size":804,"stargazers_count":320,"open_issues_count":45,"forks_count":44,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-05-11T19:43:25.792Z","etag":null,"topics":["ast","deno","hacktoberfest","node","nodejs","parser","pgsql","postgres","postgres-sql-parser","postgresql","sql","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oguimbal.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-10T17:23:01.000Z","updated_at":"2025-05-05T10:22:56.000Z","dependencies_parsed_at":"2024-01-08T14:35:07.728Z","dependency_job_id":"48a09f49-bf08-47b0-9812-231e5e03ec1f","html_url":"https://github.com/oguimbal/pgsql-ast-parser","commit_stats":{"total_commits":328,"total_committers":21,"mean_commits":"15.619047619047619","dds":0.1585365853658537,"last_synced_commit":"2c84d4ca6d566162c32ff8d0667c8e3bcd94a828"},"previous_names":[],"tags_count":107,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oguimbal%2Fpgsql-ast-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oguimbal%2Fpgsql-ast-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oguimbal%2Fpgsql-ast-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oguimbal%2Fpgsql-ast-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oguimbal","download_url":"https://codeload.github.com/oguimbal/pgsql-ast-parser/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254414499,"owners_count":22067272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","deno","hacktoberfest","node","nodejs","parser","pgsql","postgres","postgres-sql-parser","postgresql","sql","typescript"],"created_at":"2024-08-01T06:01:38.215Z","updated_at":"2025-05-15T20:05:17.542Z","avatar_url":"https://github.com/oguimbal.png","language":"TypeScript","funding_links":[],"categories":["Repository","TypeScript"],"sub_categories":["Parsing"],"readme":"\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://npmjs.org/package/pgsql-ast-parser\"\u003e\u003cimg src=\"http://img.shields.io/npm/v/pgsql-ast-parser.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://npmjs.org/package/pgsql-ast-parser\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/pgsql-ast-parser.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://david-dm.org/oguimbal/pgsql-ast-parser\"\u003e\u003cimg src=\"https://david-dm.org/oguimbal/pgsql-ast-parser.svg\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://github.com/oguimbal/pgsql-ast-parser/workflows/CI/badge.svg\"\u003e\n\u003c/p\u003e\n\n\n \u003ch3 align=\"center\"\u003e🏃‍♀️ pgsql-ast-parser is a Postgres SQL syntax parser. It produces a typed AST (Abstract Syntax Tree), covering the most common syntaxes of pgsql.\u003c/h3\u003e\n\n\u003cp align=\"center\"\u003e\n❤ It works both in node or in browser.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n⚠ This parser does not support (yet) PL/pgSQL. It might not even cover some funky syntaxes.\n\u003c/p\u003e\n\n\n\u003cp align=\"center\"\u003e\n❤ Open an issue if you find an bug or unsupported syntax !\n \u003c/p\u003e\n\n\n\u003cp align=\"center\"\u003e\n  🔗 This parser has been created to implement \u003ca href=\"https://github.com/oguimbal/pg-mem\"\u003epg-mem\u003c/a\u003e, an in-memory postgres db emulator. 👉  \u003ca href=\"https://oguimbal.github.io/pg-mem-playground/\"\u003eplay with it here\u003c/a\u003e\n \u003c/p\u003e\n\n\n\u003cp align=\"center\"\u003e\n⭐ this repo if you like this package, it helps to motivate me :)\n\u003c/p\u003e\n\n# 📐 Installation\n\n## With NodeJS\n\n```bash\nnpm i pgsql-ast-parser\n```\n\n## With Deno\n\nJust reference it like that:\n\n```typescript\nimport { /* imports here */ } from 'https://deno.land/x/pgsql_ast_parser/mod.ts';\n```\n\n# 📖 Parsing SQL\n\n⚠ I strongly recommend NOT using this parser without Typescript. It will work, but types are awesome.\n\nParse sql to an AST like this:\n\n```typescript\nimport { parse, Statement } from 'pgsql-ast-parser';\n\n// parse multiple statements\nconst ast: Statement[] = parse(`BEGIN TRANSACTION;\n                                insert into my_table values (1, 'two')`);\n\n// parse a single statement\nconst ast: Statement = parseFirst(`SELECT * FROM \"my_table\";`);\n```\n\n\n# 🔍 Inspecting SQL AST\n\nOnce you have parsed an AST, you might want to traverse it easily to know what's in it.\n\nThere is a helper for that: [astVisitor](/src/ast-visitor.ts).\n\nHere is an example which lists all the tables used in a request, and which counts how many joins it contains:\n\n```typescript\n\nimport { astVisitor, parse } from 'pgsql-ast-parser';\n\nconst tables = new Set();\nlet joins = 0;\nconst visitor = astVisitor(map =\u003e ({\n\n    // implement here AST parts you want to hook\n\n    tableRef: t =\u003e tables.add(t.name),\n    join: t =\u003e {\n        joins++;\n        // call the default implementation of 'join'\n        // this will ensure that the subtree is also traversed.\n        map.super().join(t);\n    }\n}))\n\n// start traversing a statement\nvisitor.statement(parseFirst(`select * from ta left join tb on ta.id=tb.id`));\n\n// print result\nconsole.log(`Used tables ${[...tables].join(', ')} with ${joins} joins !`)\n\n```\n\nYou'll find that AST visitors (that's the name of this pattern) are quite flexible and powerful once you get used to them !\n\n👉 Here is the implementation of [toSql](/src/to-sql.ts) which uses an astVisitor to reconstitude SQL from an AST (see below).\n\n\n\n# 🖨 Converting an AST to SQL\n\nThat's super easy:\n\n```typescript\nimport { toSql } from 'pgsql-ast-parser';\n\nconst sql: string = toSql.statement(myAst);\n\n```\n\nLike with `astVisitor()` or `astModifier()`, you can also convert subparts of AST to SQL (not necessarily a whole statement) by calling other methods of toSql.\n\n\n\n# 📝 Modifying SQL AST\n\n\nThere is a special kind of visitor, which I called [astMapper](/src/ast-mapper.ts), which allows you to traverse \u0026 modify ASTs on the fly.\n\nFor instance, you could rename a table in a request like this:\n\n```typescript\nimport { toSql, parseFirst, astMapper } from 'pgsql-ast-parser';\n\n// create a mapper\nconst mapper = astMapper(map =\u003e ({\n    tableRef: t =\u003e {\n        if (t.name === 'foo') {\n            return {\n                 // Dont do that... see below\n                 // (I wrote this like that for the sake of explainability)\n                ...t,\n                name: 'bar',\n            }\n        }\n\n        // call the default implementation of 'tableRef'\n        // this will ensure that the subtree is also traversed.\n        return map.super().tableRef(t);\n    }\n}))\n\n// parse + map + reconvert to sql\nconst modified = mapper.statement(parseFirst('select * from foo'));\n\nconsole.log(toSql.statement(modified!)); //  =\u003e  SELECT * FROM \"bar\"\n\n```\n\nGood to know: If you use Typescript, return types will force  you to return something compatible with a valid AST.\n\nHowever, if you wish to remove a node from a tree, you can return null. For instance, this sample removes all references to column `'foo'`:\n\n```typescript\n// create a mapper\nconst mapper = astMapper(map =\u003e ({\n    ref: c =\u003e c.name === 'foo' ? null : c,\n}))\n\n// process sql\nconst result = mapper.statement(parseFirst('select foo, bar from test'));\n\n// Prints: SELECT \"bar\" FROM \"test\"\nconsole.log(toSql.statement(result!));\n```\n\nIf no valid AST can be produced after having removed it, `result` will be null.\n\n\n## A note on `astMapper` performance:\n\nThe AST default modifier tries to be as efficient as possible:\nIt does not copy AST parts as long as they do not have changed.\n\nIf you wan to avoid unnecessary copies, try to return the original argument\nas much as possible when nothing has changed.\n\nFor instance, instead of writing this:\n\n```typescript\n    member(val: a.ExprMember) {\n        const operand = someOperandTransformation(val.operand);\n        if (!operand) {\n            return null;\n        }\n        return {\n            ...val,\n            operand,\n        }\n    }\n```\n\nPrefer an implement that checks that nothing has changed, for instance by using the `assignChanged()` helper.\n\n```typescript\n    member(val: a.ExprMember) {\n        const operand = someOperandTransformation(val.operand);\n        if (!operand) {\n            return null;\n        }\n        return assignChanged(val, {\n            operand,\n        });\n    }\n```\n\nIt's pretty easy to implement.\nTo deal with this kind optimization with arrays, there is a `arrayNilMap()` helper exposed:\n\n```typescript\nconst newArray = arrayNilMap(array, elem =\u003e transform(elem));\nif (newArray === array) {\n    // transform() has not changed any element in the array !\n}\n```\n\n# Parsing literal values\n\nPostgres implements several literal syntaxes (string-to-something converters), whiches parsers are exposed as helper functions by this pgsql-ast-parser:\n\n- `parseArrayLiteral()` parses arrays literals syntaxes (for instance `{a,b,c}`)\n- `parseGeometricLiteral()` parses [geometric types](https://www.postgresql.org/docs/current/datatype-geometric.html)  (for instance, things like `(1,2)` or `\u003c(1,2),3\u003e`)\n- `parseIntervalLiteral()` parses [interval inputs](https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT) literals (such as `P1Y2DT1H` or `1 yr 2 days 1 hr`)\n\n\n# FAQ\n\n- **How to parse named parameters like `:name` ?** 👉 _See [here](https://github.com/oguimbal/pgsql-ast-parser/issues/8#issuecomment-774280514) ([TLDR](https://runkit.com/oguimbal/pgsql-ast-parser.circumvent-named-arguments))_\n- **Can I get detailed a location for each AST node ?** 👉 _Yes. Pass the option `{locationTracking: true}` to `parse()`, and use the `locationOf(node)` function._\n- **Can I get the comments that the parser has ignored ?** 👉 _Yes. Use `parseWithComments()` instead of `parse()`_\n\n# Development\n\nPull requests are welcome :)\n\nTo start hacking this lib, you'll have to:\n- Use vscode\n- Install [mocha test explorer with HMR support](https://marketplace.visualstudio.com/items?itemName=oguimbal.vscode-mocha-test-adapter) extension\n- `npm start`\n- Reload unit tests in vscode\n\n... once done, tests should appear. HMR is on, which means that changes in your code are instantly propagated to unit tests.\nThis allows for ultra fast development cycles (running tests takes less than 1 sec).\n\nTo debug tests: Just hit \"run\" (F5, or whatever)... vscode should attach the mocha worker. Then run the test you want to debug.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foguimbal%2Fpgsql-ast-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foguimbal%2Fpgsql-ast-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foguimbal%2Fpgsql-ast-parser/lists"}