Skip to content
Open
Show file tree
Hide file tree
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
8 changes: 7 additions & 1 deletion templates/full/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,20 @@ function ptrToString(ptr: number): string {
return wasmModule.UTF8ToString(ptr);
}

const NO_RESULT_OUT_OF_MEMORY = 'libpg_query returned no result: out of WASM memory (heap limit is WASM_MAXIMUM_MEMORY, 1 GiB); parse large inputs in smaller pieces';

function readDetailedResult(resultPtr: number): string {
if (!resultPtr) {
throw new Error('libpg_query returned no result: out of WASM memory (heap limit is WASM_MAXIMUM_MEMORY, 1 GiB); parse large inputs in smaller pieces');
throw new Error(NO_RESULT_OUT_OF_MEMORY);
}
const ptrAt = (offset: number) => wasmModule.getValue(resultPtr + offset, 'i32');
const str = (ptr: number) => (ptr ? wasmModule.UTF8ToString(ptr) : undefined);
if (ptrAt(0)) {
const message = str(ptrAt(4)) ?? 'Unknown error';
// libpg_query reports a failed malloc as a plain "out of memory" error.
if (message === 'out of memory') {
throw new Error(NO_RESULT_OUT_OF_MEMORY);
}
const lineno = ptrAt(16);
const cursorpos = ptrAt(20);
throw new SqlError(message, {
Expand Down
2 changes: 1 addition & 1 deletion versions/18/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ WASM_STACK_SIZE ?= 33554432
# byte-for-byte. Set in package.json `x-publish` and applied by copy:templates.
LIBPG_QUERY_REPO := https://github.com/constructive-io/libpg_query.git
LIBPG_QUERY_TAG := 18-constructive
LIBPG_QUERY_COMMIT := b0f5f331dc50d69597ec610de7b21afa47a66808
LIBPG_QUERY_COMMIT := 8076f09aecc655a717e4a038e182a9dd41cc3eb8

CACHE_DIR := .cache

Expand Down
2 changes: 1 addition & 1 deletion versions/18/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"distTag": "pg18",
"libpgQueryRepo": "https://github.com/constructive-io/libpg_query.git",
"libpgQueryTag": "18-constructive",
"libpgQueryCommit": "b0f5f331dc50d69597ec610de7b21afa47a66808",
"libpgQueryCommit": "8076f09aecc655a717e4a038e182a9dd41cc3eb8",
"fullApi": true
},
"files": [
Expand Down
8 changes: 7 additions & 1 deletion versions/18/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,14 +252,20 @@ function ptrToString(ptr: number): string {
return wasmModule.UTF8ToString(ptr);
}

const NO_RESULT_OUT_OF_MEMORY = 'libpg_query returned no result: out of WASM memory (heap limit is WASM_MAXIMUM_MEMORY, 1 GiB); parse large inputs in smaller pieces';

function readDetailedResult(resultPtr: number): string {
if (!resultPtr) {
throw new Error('libpg_query returned no result: out of WASM memory (heap limit is WASM_MAXIMUM_MEMORY, 1 GiB); parse large inputs in smaller pieces');
throw new Error(NO_RESULT_OUT_OF_MEMORY);
}
const ptrAt = (offset: number) => wasmModule.getValue(resultPtr + offset, 'i32');
const str = (ptr: number) => (ptr ? wasmModule.UTF8ToString(ptr) : undefined);
if (ptrAt(0)) {
const message = str(ptrAt(4)) ?? 'Unknown error';
// libpg_query reports a failed malloc as a plain "out of memory" error.
if (message === 'out of memory') {
throw new Error(NO_RESULT_OUT_OF_MEMORY);
}
const lineno = ptrAt(16);
const cursorpos = ptrAt(20);
throw new SqlError(message, {
Expand Down
29 changes: 29 additions & 0 deletions versions/18/test/scan.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const query = require("../");
const { describe, it, before, after, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const { execFileSync } = require('node:child_process');

describe("Query Scanning", () => {
before(async () => {
Expand Down Expand Up @@ -364,6 +365,34 @@ describe("scan input limits", () => {
assert.equal(query.scanSync("SELECT 1;\n".repeat(1000000)).tokens.length, 3000000);
assert.throws(() => query.scanSync("SELECT 1;\n".repeat(2000000)), /out of WASM memory/);
});

// About 50 MB (18.6M tokens) used to make libpg_query write the token protobuf
// through a NULL pointer, abort, and break the next sync call. Runs in a child
// process because the abort corrupts the instance.
it("throws out of WASM memory at 50 MB, then sync and async calls work", () => {
const script = `const query = require(${JSON.stringify(require.resolve("../"))});
(async () => {
await query.parse("SELECT 1");
let message = "no error";
try {
query.scanSync("SELECT a, b FROM t WHERE c = 1;\\n".repeat(1690000));
} catch (error) {
message = error.message;
}
const sync = query.scanSync("SELECT 1").tokens.length;
const async = (await query.scan("SELECT 1")).tokens.length;
process.stdout.write("\\nresult=" + JSON.stringify({ message, sync, async }));
})();`;
const output = execFileSync(process.execPath, ["-e", script], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
maxBuffer: 256 * 1024 * 1024,
});
const result = JSON.parse(output.match(/^result=(.*)$/m)[1]);
assert.match(result.message, /out of WASM memory/);
assert.equal(result.sync, 2);
assert.equal(result.async, 2);
});
});

describe("scan control characters", () => {
Expand Down
Loading