Skip to content

16 deparse #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions .github/workflows/build-wasm-linux.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build Wasm Linux 🧪
name: Build Wasm 🛠

on:
workflow_dispatch:
@@ -16,16 +16,34 @@ jobs:
node-version: '20.x'
cache: 'yarn'

- name: Install Linux Deps 🧶
- name: Cache Emscripten 📇
uses: actions/cache@v2
with:
path: |
emsdk/upstream
emsdk/node
key: ${{ runner.os }}-emsdk-${{ hashFiles('emsdk/emsdk_manifest.json') }}
restore-keys: |
${{ runner.os }}-emsdk-
- name: Install Emscripten ✍🏻
if: steps.cache-emsdk.outputs.cache-hit != 'true'
run: |
sudo apt-get update
sudo apt-get install -y make
sudo apt-get install cmake python3 python3-pip
git clone --branch 3.1.59 --depth 1 https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install 3.1.59
./emsdk activate 3.1.59
source ./emsdk_env.sh
- name: Install Dependencies 🧶
- name: Build with Emscripten
run: |
yarn
source ./emsdk/emsdk_env.sh
emmake make
- name: Test 🔍
run: |
yarn make:wasm
yarn make build:wasm
- name: Archive production artifacts
uses: actions/upload-artifact@v2
with:
name: compiled-files
path: wasm
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ libs/
npm-debug.log
libpg_query/**/*.a
libpg_query/**/*.h
libpg_query/**/*.proto
wasm/libpg-query.js
*.wasm
.cache
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export function parseQuery(sql: string): Promise<any>;
export function deparse(parseTree: any): Promise<string>;
export function parsePlPgSQL(funcsSql: string): Promise<any>;
export function parseQuerySync(sql: string): any;
export function deparseSync(parseTree: any): any;
export function parsePlPgSQLSync(funcsSql: string): any;
export function fingerprint(sql: string): Promise<string>;
export function fingerprintSync(sql: string): string;
20 changes: 19 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const PgQuery = require('./build/Release/queryparser.node');
const PgQuery = require('./build/Release/queryparser');
const { pg_query } = require('./proto');


module.exports = {
parseQuery(query) {
@@ -9,6 +11,16 @@ module.exports = {
});
},

deparse(parseTree) {
const msg = pg_query.ParseResult.fromObject(parseTree);
const data = pg_query.ParseResult.encode(msg).finish();
return new Promise((resolve, reject) => {
PgQuery.deparseAsync(data, (err, result) => {
err ? reject(err) : resolve(result);
});
});
},

parsePlPgSQL(query) {
return new Promise((resolve, reject) => {
PgQuery.parsePlPgSQLAsync(query, (err, result) => {
@@ -21,6 +33,12 @@ module.exports = {
return JSON.parse(PgQuery.parseQuerySync(query));
},

deparseSync(parseTree) {
const msg = pg_query.ParseResult.fromObject(parseTree);
const data = pg_query.ParseResult.encode(msg).finish();
return PgQuery.deparseSync(data);
},

parsePlPgSQLSync(query) {
return JSON.parse(PgQuery.parsePlPgSQLSync(query));
},
Empty file added libpg_query/protobuf/.gitkeep
Empty file.
24 changes: 14 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -30,34 +30,38 @@
}
},
"scripts": {
"protogen": "node ./script/protogen.js 16-latest",
"clean": "rimraf build",
"configure": "node-pre-gyp configure",
"install": "node-pre-gyp install --fallback-to-build --loglevel verbose",
"rebuild": "node-pre-gyp rebuild --loglevel verbose",
"make:wasm": "docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) emscripten/emsdk emmake make",
"build:wasm": "yarn make:wasm build",
"rebuild:wasm": "yarn make:wasm rebuild",
"clean:wasm": "yarn make:wasm clean",
"clean-cache:wasm": "yarn make:wasm clean-cache",
"wasm:make": "docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) emscripten/emsdk emmake make",
"wasm:build": "npm run wasm:make build",
"wasm:rebuild": "npm run wasm:make rebuild",
"wasm:clean": "npm run wasm:make clean",
"wasm:clean-cache": "npm run wasm:make clean-cache",
"test": "mocha --timeout 5000",
"binary:build": "node-pre-gyp rebuild package",
"binary:publish": "AWS_PROFILE=supabase-dev node-pre-gyp publish"
},
"binary:publish": "AWS_PROFILE=supabase-dev node-pre-gyp publish",
"clean:all": "npm run clean && npm run wasm:clean && npm run wasm:clean-cache"
},
"author": "Dan Lynch <pyramation@gmail.com> (http://github.com/pyramation)",
"license": "LICENSE IN LICENSE",
"repository": {
"type": "git",
"url": "git://github.com/launchql/libpg-query-node.git"
},
"devDependencies": {
"rimraf": "5.0.0",
"@launchql/proto-cli": "1.25.0",
"chai": "^3.5.0",
"emnapi": "^0.43.1",
"lodash": "^4.17.15",
"mocha": "^5.2.0"
"mocha": "^5.2.0",
"rimraf": "5.0.0"
},
"dependencies": {
"@emnapi/runtime": "^0.43.1",
"@launchql/protobufjs": "7.2.6",
"@mapbox/node-pre-gyp": "^1.0.8",
"node-addon-api": "^7.0.0",
"node-gyp": "^10.0.1"
@@ -77,4 +81,4 @@
"host": "https://supabase-public-artifacts-bucket.s3.amazonaws.com",
"remote_path": "./libpg-query-node/"
}
}
}
106,287 changes: 106,287 additions & 0 deletions proto.js

Large diffs are not rendered by default.

235 changes: 0 additions & 235 deletions queryparser.cc

This file was deleted.

1 change: 1 addition & 0 deletions script/buildAddon.sh
Original file line number Diff line number Diff line change
@@ -61,6 +61,7 @@ fi

# Copy header
cp $(pwd)/pg_query.h $rDIR/libpg_query/include/
cp $(pwd)/protobuf/*.proto $rDIR/libpg_query/protobuf/

# Cleanup: revert to original directory and remove the temp
cd "$rDIR"
44 changes: 44 additions & 0 deletions script/protogen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const https = require('https');
const fs = require('fs');
const { exec } = require('child_process');

if (typeof process.argv[2] !== 'string') {
throw new Error('branchName not provided');
}

// Configuration Variables
const branchName = process.argv[2];
const protoUrl = `https://raw.githubusercontent.com/pganalyze/libpg_query/${branchName}/protobuf/pg_query.proto`;
const inFile = 'libpg_query/protobuf/pg_query.proto';
const outFile = 'proto.js';

const protogenCmd = [
'pg-proto-parser',
'protogen',
'--protoUrl',
protoUrl,
'--inFile',
inFile,
'--outFile',
outFile,
'--originalPackageName',
'protobufjs/minimal',
'--newPackageName',
'@launchql/protobufjs/minimal'
];

// Step 2: Generate proto.js using pbjs (Assuming pbjs is installed and accessible)
function generateProtoJS(callback) {
exec(protogenCmd.join(' '), (error, stdout, stderr) => {
if (error) {
console.error(`Error during code generation: ${error.message}`);
return;
}
console.log('Generated proto.js from proto file.');
callback();
});
}

generateProtoJS(() => {
console.log('all done 🎉');
});
10 changes: 10 additions & 0 deletions src/addon.cc
Original file line number Diff line number Diff line change
@@ -14,6 +14,16 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
Napi::Function::New(env, ParseQueryAsync)
);

exports.Set(
Napi::String::New(env, "deparseSync"),
Napi::Function::New(env, DeparseSync)
);

exports.Set(
Napi::String::New(env, "deparseAsync"),
Napi::Function::New(env, DeparseAsync)
);

exports.Set(
Napi::String::New(env, "parsePlPgSQLSync"),
Napi::Function::New(env, ParsePlPgSQLSync)
47 changes: 47 additions & 0 deletions src/async.cc
Original file line number Diff line number Diff line change
@@ -34,6 +34,37 @@ class QueryWorker : public Napi::AsyncWorker {
PgQueryParseResult result;
};

class DeparseWorker : public Napi::AsyncWorker {
public:
DeparseWorker(Napi::Function& callback, PgQueryProtobuf parseTree)
: Napi::AsyncWorker(callback), parseTree(parseTree) {}
~DeparseWorker() {}

// Executed inside the worker-thread.
// It is not safe to access JS engine data structure
// here, so everything we need for input and output
// should go on `this`.
void Execute () {
result = pg_query_deparse_protobuf(parseTree);
}

// Executed when the async work is complete
// this function will be run inside the main event loop
// so it is safe to use JS engine data again
void OnOK() {
Napi::HandleScope scope(Env());
try {
Callback().Call({Env().Undefined(), DeparseResult(Env(), result) });
} catch (const Napi::Error& e) {
Callback().Call({ e.Value(), Env().Undefined() });
}
}

private:
PgQueryProtobuf parseTree;
PgQueryDeparseResult result;
};

class PgPlQSLWorker : public Napi::AsyncWorker {
public:
PgPlQSLWorker(Napi::Function& callback, const std::string& query)
@@ -105,6 +136,22 @@ Napi::Value ParseQueryAsync(const Napi::CallbackInfo& info) {
return info.Env().Undefined();
}

Napi::Value DeparseAsync(const Napi::CallbackInfo& info) {
if (info.Length() < 2 || !info[0].IsBuffer() || !info[1].IsFunction()) {
Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException();
return info.Env().Undefined();
}
Napi::Buffer<char> data = info[0].As<Napi::Buffer<char>>();
Napi::Function callback = info[1].As<Napi::Function>();
PgQueryProtobuf parseTree = {
static_cast<unsigned int>(data.Length()),
data.Data()
};
DeparseWorker* worker = new DeparseWorker(callback, parseTree);
worker->Queue();
return info.Env().Undefined();
}

Napi::Value ParsePlPgSQLAsync(const Napi::CallbackInfo& info) {
std::string query = info[0].As<Napi::String>();
Napi::Function callback = info[1].As<Napi::Function>();
1 change: 1 addition & 0 deletions src/async.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <napi.h>

Napi::Value ParseQueryAsync(const Napi::CallbackInfo& info);
Napi::Value DeparseAsync(const Napi::CallbackInfo& info);
Napi::Value ParsePlPgSQLAsync(const Napi::CallbackInfo& info);
Napi::Value FingerprintAsync(const Napi::CallbackInfo& info);
13 changes: 13 additions & 0 deletions src/helpers.cc
Original file line number Diff line number Diff line change
@@ -27,6 +27,19 @@ Napi::String QueryParseResult(Napi::Env env, const PgQueryParseResult& result)
return returnVal;
}

Napi::String DeparseResult(Napi::Env env, const PgQueryDeparseResult& result)
{
if (result.error) {
auto throwVal = CreateError(env, *result.error);
pg_query_free_deparse_result(result);
throw throwVal;
}

auto returnVal = Napi::String::New(env, result.query);
pg_query_free_deparse_result(result);
return returnVal;
}

Napi::String PlPgSQLParseResult(Napi::Env env, const PgQueryPlpgsqlParseResult& result)
{
if (result.error) {
1 change: 1 addition & 0 deletions src/helpers.h
Original file line number Diff line number Diff line change
@@ -3,5 +3,6 @@

Napi::Error CreateError(Napi::Env env, const PgQueryError& err);
Napi::String QueryParseResult(Napi::Env env, const PgQueryParseResult& result);
Napi::String DeparseResult(Napi::Env env, const PgQueryDeparseResult& result);
Napi::String PlPgSQLParseResult(Napi::Env env, const PgQueryPlpgsqlParseResult& result);
Napi::String FingerprintResult(Napi::Env env, const PgQueryFingerprintResult & result);
11 changes: 11 additions & 0 deletions src/sync.cc
Original file line number Diff line number Diff line change
@@ -10,6 +10,17 @@ Napi::String ParseQuerySync(const Napi::CallbackInfo& info) {
return QueryParseResult(info.Env(), result);
}

Napi::String DeparseSync(const Napi::CallbackInfo& info) {
Napi::Buffer<char> data = info[0].As<Napi::Buffer<char>>();
PgQueryProtobuf parseTree = {
static_cast<unsigned int>(data.Length()),
data.Data()
};
PgQueryDeparseResult result = pg_query_deparse_protobuf(parseTree);

return DeparseResult(info.Env(), result);
}

Napi::String ParsePlPgSQLSync(const Napi::CallbackInfo& info) {
std::string query = info[0].As<Napi::String>();
PgQueryPlpgsqlParseResult result = pg_query_parse_plpgsql(query.c_str());
1 change: 1 addition & 0 deletions src/sync.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <napi.h>

Napi::String ParseQuerySync(const Napi::CallbackInfo& info);
Napi::String DeparseSync(const Napi::CallbackInfo& info);
Napi::String ParsePlPgSQLSync(const Napi::CallbackInfo& info);
Napi::String FingerprintSync(const Napi::CallbackInfo& info);
28 changes: 28 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -73,6 +73,34 @@ describe("Queries", () => {
});
});

describe("Deparsing", () => {
it("async function should return a promise resolving to same SQL", async () => {
const testQuery = "select * from john;";
const parsed = query.parseQuerySync(testQuery);
const deparsed = await query.deparse(parsed);
expect(deparsed).to.eq(`SELECT * FROM john`)
});

it("sync function should return a same SQL", async () => {
const testQuery = "select * from john;";
const parsed = query.parseQuerySync(testQuery);
const deparsed = query.deparseSync(parsed);
expect(deparsed).to.eq(`SELECT * FROM john`)
});

it("should reject on bogus input", async () => {
return query.deparse({stmts: [{}]}).then(
() => {
throw new Error(`should have rejected`);
},
(e) => {
expect(e).instanceof(Error);
expect(e.message).to.match(/deparse error/);
}
);
});
});

describe("Fingerprint", () => {
context("sync", () => {
it("should not fingerprint a bogus query", () => {
929 changes: 869 additions & 60 deletions yarn.lock

Large diffs are not rendered by default.