![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
pgsql-ast-parser
Advanced tools
🏃♀️ pgsql-ast-parser
is a Postgres SQL syntax parser. It produces a typed AST (Abstract Syntax Tree), covering the most common syntaxes of pgsql.
⚠ This parser does not support (yet) PL/pgSQL. It might not even cover some funky syntaxes.
❤ Open an issue if you find an bug or unsupported syntax !
🔗 This parser has been created to implement pg-mem, an in-memory postgres db emulator. 👉 Play with it here
👉 Dont forget to ⭐ this repo if you like this package :)
npm i pgsql-ast-parser
Just reference it like that:
import { /* imports here */ } from 'https://deno.land/x/pgsql_ast_parser/mod.ts';
⚠ I strongly recommand NOT using this parser without Typescript. It will work, but types are awesome.
Parse sql to an AST like this:
import { parse, Statement } from 'pgsql-ast-parser';
// parse multiple statements
const ast: Statement[] = parse(`BEGIN TRANSACTION;
insert into my_table values (1, 'two')`);
// parse a single statement
const ast: Statement = parseFirst(`SELECT * FROM "my_table";`);
Once you have parsed an AST, you might want to traverse it easily to know what's in it.
There is a helper for that: astVisitor.
Here is an example which lists all the tables used in a request, and which counts how many joins it contains:
import { astVisitor, parse } from 'pgsql-ast-parser';
const tables = new Set();
let joins = 0;
const visitor = astVisitor(map => ({
// implement here AST parts you want to hook
tableRef: t => tables.add(t.name),
join: t => {
joins++;
// call the default implementation of 'join'
// this will ensure that the subtree is also traversed.
map.super().join(t);
}
}))
// start traversing a statement
visitor.statement(parseFirst(`select * from ta left join tb on ta.id=tb.id`));
// print result
console.log(`Used tables ${[...tables].join(', ')} with ${joins} joins !`)
You'll find that AST visitors (that's the name of this pattern) are quite flexible and powerful once you get used to them !
👉 Here is the implementation of toSql which uses an astVisitor to reconstitude SQL from an AST (see below).
That's super easy:
import { toSql } from 'pgsql-ast-parser';
const sql: string = toSql.statement(myAst);
Like with astVisitor()
or astModifier()
, you can also convert subparts of AST to SQL (not necessarily a whole statement) by calling other methods of toSql.
There is a special kind of visitor, which I called astMapper, which allows you to traverse & modify ASTs on the fly.
For instance, you could rename a table in a request like this:
import { toSql, parseFirst, astMapper } from 'pgsql-ast-parser';
// create a mapper
const mapper = astMapper(map => ({
tableRef: t => {
if (t.name === 'foo') {
return {
// Dont do that... see below
// (I wrote this like that for the sake of explainability)
...t,
name: 'bar',
}
}
// call the default implementation of 'tableRef'
// this will ensure that the subtree is also traversed.
return map.super().tableRef(t);
}
}))
// parse + map + reconvert to sql
const modified = mapper.statement(parseFirst('select * from foo'));
console.log(toSql.statement(modified!)); // => SELECT * FROM "bar"
Good to know: If you use Typescript, return types will force you to return something compatible with a valid AST.
However, if you wish to remove a node from a tree, you can return null. For instance, this sample removes all references to column 'foo'
:
// create a mapper
const mapper = astMapper(map => ({
ref: c => c.name === 'foo' ? null : c,
}))
// process sql
const result = mapper.statement(parseFirst('select foo, bar from test'));
// Prints: SELECT "bar" FROM "test"
console.log(toSql.statement(result!));
If no valid AST can be produced after having removed it, result
will be null.
astMapper
performance:The AST default modifier tries to be as efficient as possible: It does not copy AST parts as long as they do not have changed.
If you wan to avoid unnecessary copies, try to return the original argument as much as possible when nothing has changed.
For instance, instead of writing this:
member(val: a.ExprMember) {
const operand = someOperandTransformation(val.operand);
if (!operand) {
return null;
}
return {
...val,
operand,
}
}
Prefer an implement that checks that nothing has changed, for instance by using the assignChanged()
helper.
member(val: a.ExprMember) {
const operand = someOperandTransformation(val.operand);
if (!operand) {
return null;
}
return assignChanged(val, {
operand,
});
}
It's pretty easy to implement.
To deal with this kind optimization with arrays, there is a arrayNilMap()
helper exposed:
const newArray = arrayNilMap(array, elem => transform(elem));
if (newArray === array) {
// transform() has not changed any element in the array !
}
Pull requests are welcome :)
To start hacking this lib, you'll have to:
npm start
... once done, tests should appear. HMR is on, which means that changes in your code are instantly propagated to unit tests. This allows for ultra fast development cycles (running tests takes less than 1 sec).
To debug tests: Just hit "run" (F5, or whatever)... vscode should attach the mocha worker. Then run the test you want to debug.
FAQs
Yet another simple Postgres SQL parser/modifier
We found that pgsql-ast-parser demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.