Skip to content

Commit 8d0ff03

Browse files
committed
fix: deparsing with proto.js
1 parent dc0905f commit 8d0ff03

File tree

3 files changed

+125
-126
lines changed

3 files changed

+125
-126
lines changed

test/deparsing.test.js

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,70 @@ describe("Query Deparsing", () => {
88

99
describe("Sync Deparsing", () => {
1010
it("should deparse a simple query", () => {
11-
const parsed = query.parseQuerySync("select 1");
12-
const deparsed = query.deparseSync(parsed);
13-
expect(deparsed).to.eq("SELECT 1");
11+
const sql = 'SELECT * FROM users';
12+
const parseTree = query.parseQuerySync(sql);
13+
const deparsed = query.deparseSync(parseTree);
14+
expect(deparsed).to.equal(sql);
1415
});
1516

1617
it("should deparse a complex query", () => {
17-
const parsed = query.parseQuerySync("select a, b from users where id = 123");
18-
const deparsed = query.deparseSync(parsed);
19-
expect(deparsed).to.include("SELECT");
20-
expect(deparsed).to.include("FROM");
21-
expect(deparsed).to.include("WHERE");
18+
const sql = 'SELECT a, b, c FROM t1 JOIN t2 ON t1.id = t2.id WHERE t1.x > 10';
19+
const parseTree = query.parseQuerySync(sql);
20+
const deparsed = query.deparseSync(parseTree);
21+
expect(deparsed).to.equal(sql);
2222
});
2323

2424
it("should fail to deparse without protobuf data", () => {
25-
expect(() => query.deparseSync({})).to.throw(/No protobuf data found/);
25+
expect(() => query.deparseSync({})).to.throw('No parseTree provided');
2626
});
2727
});
2828

2929
describe("Async Deparsing", () => {
3030
it("should return a promise resolving to same result", async () => {
31-
const parsed = await query.parseQuery("select 1");
32-
const deparsedPromise = query.deparse(parsed);
33-
const deparsed = await deparsedPromise;
34-
35-
expect(deparsedPromise).to.be.instanceof(Promise);
36-
expect(deparsed).to.eq(query.deparseSync(parsed));
31+
const sql = 'SELECT * FROM users';
32+
const parseTree = await query.parseQuery(sql);
33+
const deparsed = await query.deparse(parseTree);
34+
expect(deparsed).to.equal(sql);
3735
});
3836

3937
it("should reject when no protobuf data", async () => {
40-
return query.deparse({}).then(
41-
() => {
42-
throw new Error("should have rejected");
43-
},
44-
(e) => {
45-
expect(e).instanceof(Error);
46-
expect(e.message).to.match(/No protobuf data found/);
47-
}
48-
);
38+
try {
39+
await query.deparse({});
40+
throw new Error('should have rejected');
41+
} catch (err) {
42+
expect(err.message).to.equal('No parseTree provided');
43+
}
4944
});
5045
});
5146

5247
describe("Round-trip parsing and deparsing", () => {
5348
it("should maintain query semantics through round-trip", async () => {
54-
const originalQuery = "SELECT id, name FROM users WHERE active = true ORDER BY name";
55-
const parsed = await query.parseQuery(originalQuery);
56-
const deparsed = await query.deparse(parsed);
57-
58-
expect(deparsed).to.include("SELECT");
59-
expect(deparsed).to.include("FROM users");
60-
expect(deparsed).to.include("WHERE");
61-
expect(deparsed).to.include("ORDER BY");
49+
const sql = 'SELECT a, b, c FROM t1 JOIN t2 ON t1.id = t2.id WHERE t1.x > 10';
50+
const parseTree = await query.parseQuery(sql);
51+
const deparsed = await query.deparse(parseTree);
52+
expect(deparsed).to.equal(sql);
6253
});
6354
});
55+
56+
it('should deparse a parse tree', async () => {
57+
const sql = 'SELECT * FROM users';
58+
const parseTree = await query.parseQuery(sql);
59+
console.log('Parse Tree:', parseTree);
60+
61+
const deparsed = await query.deparse(parseTree);
62+
console.log('Deparsed:', deparsed);
63+
64+
expect(deparsed).to.equal(sql);
65+
});
66+
67+
it('should throw on invalid parse tree', () => {
68+
console.log('Testing empty object...');
69+
try {
70+
query.deparseSync({});
71+
console.log('No error thrown!');
72+
} catch (err) {
73+
console.log('Error caught:', err.message);
74+
}
75+
expect(() => query.deparseSync({})).to.throw('No parseTree provided');
76+
});
6477
});

wasm/index.cjs

Lines changed: 41 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const PgQueryModule = require('./libpg-query.js');
2+
const { pg_query } = require('../proto.js');
23

34
let wasmModule;
45

@@ -48,9 +49,7 @@ const parseQuery = awaitInit(async (query) => {
4849
throw new Error(resultStr);
4950
}
5051

51-
const parsed = JSON.parse(resultStr);
52-
parsed.query = query;
53-
return parsed;
52+
return JSON.parse(resultStr);
5453
} finally {
5554
wasmModule._free(queryPtr);
5655
if (resultPtr) {
@@ -60,37 +59,35 @@ const parseQuery = awaitInit(async (query) => {
6059
});
6160

6261
const deparse = awaitInit(async (parseTree) => {
63-
if (!parseTree || !parseTree.query) {
64-
throw new Error('No protobuf data found in parse tree - original query is required for deparse');
62+
if (
63+
!parseTree ||
64+
typeof parseTree !== 'object' ||
65+
!Array.isArray(parseTree.stmts) ||
66+
parseTree.stmts.length === 0
67+
) {
68+
throw new Error('No parseTree provided');
6569
}
70+
const msg = pg_query.ParseResult.fromObject(parseTree);
71+
const data = pg_query.ParseResult.encode(msg).finish();
6672

67-
const queryPtr = stringToPtr(parseTree.query);
68-
const lengthPtr = wasmModule._malloc(4);
69-
70-
try {
71-
const protobufPtr = wasmModule._wasm_parse_query_protobuf(queryPtr, lengthPtr);
72-
const protobufLength = wasmModule.HEAPU32[lengthPtr >> 2];
73-
74-
if (!protobufPtr || protobufLength <= 0) {
75-
const errorMsg = ptrToString(protobufPtr);
76-
wasmModule._wasm_free_string(protobufPtr);
77-
throw new Error(errorMsg || 'Failed to generate protobuf data');
78-
}
73+
const dataPtr = wasmModule._malloc(data.length);
74+
let resultPtr;
7975

80-
const resultPtr = wasmModule._wasm_deparse_protobuf(protobufPtr, protobufLength);
76+
try {
77+
wasmModule.HEAPU8.set(data, dataPtr);
78+
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, data.length);
8179
const resultStr = ptrToString(resultPtr);
82-
83-
wasmModule._wasm_free_string(protobufPtr);
84-
wasmModule._wasm_free_string(resultPtr);
85-
80+
8681
if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) {
8782
throw new Error(resultStr);
8883
}
89-
84+
9085
return resultStr;
9186
} finally {
92-
wasmModule._free(queryPtr);
93-
wasmModule._free(lengthPtr);
87+
wasmModule._free(dataPtr);
88+
if (resultPtr) {
89+
wasmModule._wasm_free_string(resultPtr);
90+
}
9491
}
9592
});
9693

@@ -212,9 +209,7 @@ function parseQuerySync(query) {
212209
throw new Error(resultStr);
213210
}
214211

215-
const parsed = JSON.parse(resultStr);
216-
parsed.query = query;
217-
return parsed;
212+
return JSON.parse(resultStr);
218213
} finally {
219214
wasmModule._free(queryPtr);
220215
if (resultPtr) {
@@ -227,37 +222,35 @@ function deparseSync(parseTree) {
227222
if (!wasmModule) {
228223
throw new Error('WASM module not initialized. Call loadModule() first.');
229224
}
230-
if (!parseTree || !parseTree.query) {
231-
throw new Error('No protobuf data found in parse tree - original query is required for deparse');
225+
if (
226+
!parseTree ||
227+
typeof parseTree !== 'object' ||
228+
!Array.isArray(parseTree.stmts) ||
229+
parseTree.stmts.length === 0
230+
) {
231+
throw new Error('No parseTree provided');
232232
}
233-
234-
const queryPtr = stringToPtr(parseTree.query);
235-
const lengthPtr = wasmModule._malloc(4);
233+
const msg = pg_query.ParseResult.fromObject(parseTree);
234+
const data = pg_query.ParseResult.encode(msg).finish();
235+
236+
const dataPtr = wasmModule._malloc(data.length);
237+
let resultPtr;
236238

237239
try {
238-
const protobufPtr = wasmModule._wasm_parse_query_protobuf(queryPtr, lengthPtr);
239-
const protobufLength = wasmModule.HEAPU32[lengthPtr >> 2];
240-
241-
if (!protobufPtr || protobufLength <= 0) {
242-
const errorMsg = ptrToString(protobufPtr);
243-
wasmModule._wasm_free_string(protobufPtr);
244-
throw new Error(errorMsg || 'Failed to generate protobuf data');
245-
}
246-
247-
const resultPtr = wasmModule._wasm_deparse_protobuf(protobufPtr, protobufLength);
240+
wasmModule.HEAPU8.set(data, dataPtr);
241+
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, data.length);
248242
const resultStr = ptrToString(resultPtr);
249243

250-
wasmModule._wasm_free_string(protobufPtr);
251-
wasmModule._wasm_free_string(resultPtr);
252-
253244
if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) {
254245
throw new Error(resultStr);
255246
}
256247

257248
return resultStr;
258249
} finally {
259-
wasmModule._free(queryPtr);
260-
wasmModule._free(lengthPtr);
250+
wasmModule._free(dataPtr);
251+
if (resultPtr) {
252+
wasmModule._wasm_free_string(resultPtr);
253+
}
261254
}
262255
}
263256

wasm/index.js

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import PgQueryModule from './libpg-query.js';
2+
import { pg_query } from '../proto.js';
23

34
let wasmModule;
45

@@ -48,9 +49,7 @@ export const parseQuery = awaitInit(async (query) => {
4849
throw new Error(resultStr);
4950
}
5051

51-
const parsed = JSON.parse(resultStr);
52-
parsed.query = query;
53-
return parsed;
52+
return JSON.parse(resultStr);
5453
} finally {
5554
wasmModule._free(queryPtr);
5655
if (resultPtr) {
@@ -60,37 +59,35 @@ export const parseQuery = awaitInit(async (query) => {
6059
});
6160

6261
export const deparse = awaitInit(async (parseTree) => {
63-
if (!parseTree || !parseTree.query) {
64-
throw new Error('No protobuf data found in parse tree - original query is required for deparse');
62+
if (
63+
!parseTree ||
64+
typeof parseTree !== 'object' ||
65+
!Array.isArray(parseTree.stmts) ||
66+
parseTree.stmts.length === 0
67+
) {
68+
throw new Error('No parseTree provided');
6569
}
66-
67-
const queryPtr = stringToPtr(parseTree.query);
68-
const lengthPtr = wasmModule._malloc(4);
70+
const msg = pg_query.ParseResult.fromObject(parseTree);
71+
const data = pg_query.ParseResult.encode(msg).finish();
72+
73+
const dataPtr = wasmModule._malloc(data.length);
74+
let resultPtr;
6975

7076
try {
71-
const protobufPtr = wasmModule._wasm_parse_query_protobuf(queryPtr, lengthPtr);
72-
const protobufLength = wasmModule.HEAPU32[lengthPtr >> 2];
73-
74-
if (!protobufPtr || protobufLength <= 0) {
75-
const errorMsg = ptrToString(protobufPtr);
76-
wasmModule._wasm_free_string(protobufPtr);
77-
throw new Error(errorMsg || 'Failed to generate protobuf data');
78-
}
79-
80-
const resultPtr = wasmModule._wasm_deparse_protobuf(protobufPtr, protobufLength);
77+
wasmModule.HEAPU8.set(data, dataPtr);
78+
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, data.length);
8179
const resultStr = ptrToString(resultPtr);
8280

83-
wasmModule._wasm_free_string(protobufPtr);
84-
wasmModule._wasm_free_string(resultPtr);
85-
8681
if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) {
8782
throw new Error(resultStr);
8883
}
8984

9085
return resultStr;
9186
} finally {
92-
wasmModule._free(queryPtr);
93-
wasmModule._free(lengthPtr);
87+
wasmModule._free(dataPtr);
88+
if (resultPtr) {
89+
wasmModule._wasm_free_string(resultPtr);
90+
}
9491
}
9592
});
9693

@@ -212,9 +209,7 @@ export function parseQuerySync(query) {
212209
throw new Error(resultStr);
213210
}
214211

215-
const parsed = JSON.parse(resultStr);
216-
parsed.query = query;
217-
return parsed;
212+
return JSON.parse(resultStr);
218213
} finally {
219214
wasmModule._free(queryPtr);
220215
if (resultPtr) {
@@ -227,37 +222,35 @@ export function deparseSync(parseTree) {
227222
if (!wasmModule) {
228223
throw new Error('WASM module not initialized. Call loadModule() first.');
229224
}
230-
if (!parseTree || !parseTree.query) {
231-
throw new Error('No protobuf data found in parse tree - original query is required for deparse');
225+
if (
226+
!parseTree ||
227+
typeof parseTree !== 'object' ||
228+
!Array.isArray(parseTree.stmts) ||
229+
parseTree.stmts.length === 0
230+
) {
231+
throw new Error('No parseTree provided');
232232
}
233-
234-
const queryPtr = stringToPtr(parseTree.query);
235-
const lengthPtr = wasmModule._malloc(4);
233+
const msg = pg_query.ParseResult.fromObject(parseTree);
234+
const data = pg_query.ParseResult.encode(msg).finish();
235+
236+
const dataPtr = wasmModule._malloc(data.length);
237+
let resultPtr;
236238

237239
try {
238-
const protobufPtr = wasmModule._wasm_parse_query_protobuf(queryPtr, lengthPtr);
239-
const protobufLength = wasmModule.HEAPU32[lengthPtr >> 2];
240-
241-
if (!protobufPtr || protobufLength <= 0) {
242-
const errorMsg = ptrToString(protobufPtr);
243-
wasmModule._wasm_free_string(protobufPtr);
244-
throw new Error(errorMsg || 'Failed to generate protobuf data');
245-
}
246-
247-
const resultPtr = wasmModule._wasm_deparse_protobuf(protobufPtr, protobufLength);
240+
wasmModule.HEAPU8.set(data, dataPtr);
241+
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, data.length);
248242
const resultStr = ptrToString(resultPtr);
249243

250-
wasmModule._wasm_free_string(protobufPtr);
251-
wasmModule._wasm_free_string(resultPtr);
252-
253244
if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) {
254245
throw new Error(resultStr);
255246
}
256247

257248
return resultStr;
258249
} finally {
259-
wasmModule._free(queryPtr);
260-
wasmModule._free(lengthPtr);
250+
wasmModule._free(dataPtr);
251+
if (resultPtr) {
252+
wasmModule._wasm_free_string(resultPtr);
253+
}
261254
}
262255
}
263256

0 commit comments

Comments
 (0)