From 9e71deb4638255c71a332b38fa7f8acefd131b10 Mon Sep 17 00:00:00 2001 From: Lars Mennen <1162951+larsmennen@users.noreply.github.com> Date: Tue, 24 Feb 2026 08:38:48 +0000 Subject: [PATCH 1/3] Fix PostGIS loading to use async WASM precompilation --- packages/pglite/src/extensionUtils.ts | 64 ++++++++++++++++++++------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/packages/pglite/src/extensionUtils.ts b/packages/pglite/src/extensionUtils.ts index ea225566d..37e6285e6 100644 --- a/packages/pglite/src/extensionUtils.ts +++ b/packages/pglite/src/extensionUtils.ts @@ -64,7 +64,9 @@ export async function loadExtensions( } if (blob) { const bytes = new Uint8Array(await blob.arrayBuffer()) - loadExtension(mod, ext, bytes, log) + // Await all async WASM precompilations so preloadedWasm is populated + // before dlopen is called during postgres startup. + await Promise.all(loadExtension(mod, ext, bytes, log)) } else { console.error('Could not get binary data for extension:', ext) } @@ -76,28 +78,55 @@ function loadExtension( _ext: string, bytes: Uint8Array, log: (...args: any[]) => void, -) { +): Promise[] { + const soPreloadPromises: Promise[] = [] const data = tinyTar.untar(bytes) data.forEach((file: any) => { if (!file.name.startsWith('.')) { const filePath = mod.WASM_PREFIX + '/' + file.name if (file.name.endsWith('.so')) { - const extOk = (...args: any[]) => { - log('pgfs:ext OK', filePath, args) - } - const extFail = (...args: any[]) => { - log('pgfs:ext FAIL', filePath, args) + const nameWithSo = file.name.split('/').pop()! // e.g. 'postgis-3.so' + const nameWithoutSo = nameWithSo.slice(0, -3) // e.g. 'postgis-3' + const dirPath = dirname(filePath) + // Wrap createPreloadedFile in a Promise so loadExtensions can await the + // async WASM compilation done by Emscripten's wasm preload plugin. + // The plugin calls extOk only after preloadedWasm[path] is set, so + // awaiting this ensures dlopen finds the pre-compiled module. + const soPreload = new Promise((resolve, reject) => { + const extOk = (...args: any[]) => { + log('pgfs:ext OK', filePath, args) + resolve() + } + const extFail = (...args: any[]) => { + log('pgfs:ext FAIL', filePath, args) + reject(new Error(`Failed to preload ${filePath}`)) + } + // Keep the .so suffix so Emscripten's wasm preload plugin canHandle() matches, + // triggering async WebAssembly.instantiate. The compiled module is stored in + // preloadedWasm under the path with .so. + mod.FS.createPreloadedFile( + dirPath, + nameWithSo, + file.data as any, // There is a type error in Emscripten's FS.createPreloadedFile, this excepts a Uint8Array, but the type is defined as any + true, + true, + extOk, + extFail, + false, + ) + }) + soPreloadPromises.push(soPreload) + // Also write to the path without .so so PostgreSQL's dlopen() can find the file + // as a fallback if the preloadedWasm lookup misses. + const filePathWithoutSo = dirPath + '/' + nameWithoutSo + try { + if (mod.FS.analyzePath(dirPath).exists === false) { + mod.FS.mkdirTree(dirPath) + } + mod.FS.writeFile(filePathWithoutSo, file.data) + } catch (e) { + console.error(`Error writing file ${filePathWithoutSo}`, e) } - mod.FS.createPreloadedFile( - dirname(filePath), - file.name.split('/').pop()!.slice(0, -3), - file.data as any, // There is a type error in Emscripten's FS.createPreloadedFile, this excepts a Uint8Array, but the type is defined as any - true, - true, - extOk, - extFail, - false, - ) } else { try { const dirPath = filePath.substring(0, filePath.lastIndexOf('/')) @@ -111,6 +140,7 @@ function loadExtension( } } }) + return soPreloadPromises } function dirname(path: string) { From 9599ad8a6ddcf39a750f2274fb0c6c73377fc364 Mon Sep 17 00:00:00 2001 From: Lars Mennen <1162951+larsmennen@users.noreply.github.com> Date: Tue, 24 Feb 2026 13:39:45 +0000 Subject: [PATCH 2/3] revert stripping of the extension --- packages/pglite/src/extensionUtils.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/pglite/src/extensionUtils.ts b/packages/pglite/src/extensionUtils.ts index 37e6285e6..d9f92c533 100644 --- a/packages/pglite/src/extensionUtils.ts +++ b/packages/pglite/src/extensionUtils.ts @@ -85,8 +85,7 @@ function loadExtension( if (!file.name.startsWith('.')) { const filePath = mod.WASM_PREFIX + '/' + file.name if (file.name.endsWith('.so')) { - const nameWithSo = file.name.split('/').pop()! // e.g. 'postgis-3.so' - const nameWithoutSo = nameWithSo.slice(0, -3) // e.g. 'postgis-3' + const soName = file.name.split('/').pop()! // e.g. 'postgis-3.so' const dirPath = dirname(filePath) // Wrap createPreloadedFile in a Promise so loadExtensions can await the // async WASM compilation done by Emscripten's wasm preload plugin. @@ -106,7 +105,7 @@ function loadExtension( // preloadedWasm under the path with .so. mod.FS.createPreloadedFile( dirPath, - nameWithSo, + soName, file.data as any, // There is a type error in Emscripten's FS.createPreloadedFile, this excepts a Uint8Array, but the type is defined as any true, true, @@ -116,17 +115,6 @@ function loadExtension( ) }) soPreloadPromises.push(soPreload) - // Also write to the path without .so so PostgreSQL's dlopen() can find the file - // as a fallback if the preloadedWasm lookup misses. - const filePathWithoutSo = dirPath + '/' + nameWithoutSo - try { - if (mod.FS.analyzePath(dirPath).exists === false) { - mod.FS.mkdirTree(dirPath) - } - mod.FS.writeFile(filePathWithoutSo, file.data) - } catch (e) { - console.error(`Error writing file ${filePathWithoutSo}`, e) - } } else { try { const dirPath = filePath.substring(0, filePath.lastIndexOf('/')) From 969018411d41023d2f13635649f7584d2c8a7e84 Mon Sep 17 00:00:00 2001 From: Lars Mennen <1162951+larsmennen@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:47:33 +0000 Subject: [PATCH 3/3] stylecheck --- packages/pglite/src/extensionUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pglite/src/extensionUtils.ts b/packages/pglite/src/extensionUtils.ts index d9f92c533..ca2aeb243 100644 --- a/packages/pglite/src/extensionUtils.ts +++ b/packages/pglite/src/extensionUtils.ts @@ -85,7 +85,7 @@ function loadExtension( if (!file.name.startsWith('.')) { const filePath = mod.WASM_PREFIX + '/' + file.name if (file.name.endsWith('.so')) { - const soName = file.name.split('/').pop()! // e.g. 'postgis-3.so' + const soName = file.name.split('/').pop()! // e.g. 'postgis-3.so' const dirPath = dirname(filePath) // Wrap createPreloadedFile in a Promise so loadExtensions can await the // async WASM compilation done by Emscripten's wasm preload plugin.