Skip to content
Open
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
84 changes: 53 additions & 31 deletions packages/pglite/src/pglite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,31 @@ export class PGlite
implements PGliteInterface, AsyncDisposable
{
fs?: Filesystem
protected mod?: PostgresMod
protected _mod?: PostgresMod

/**
* The Postgres Emscripten Module.
* Throws if the module is not yet loaded, or has been released by `close()` -
* the Emscripten runtime is force-exited on close and cannot be revived.
*/
protected get mod(): PostgresMod {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to tidy the many mod!

if (!this._mod) {
throw new Error(
this.#closed
? 'PGlite is closed'
: 'PGlite is not ready, await .waitReady before use',
)
}
return this._mod
}

// we handle Postgres' main longjmp manually, by intercepting it and exiting with this error code
// keep in sync with pglitec.c->POSTGRES_MAIN_LONGJMP
private readonly POSTGRES_MAIN_LONGJMP = 100
#onData: ((bytes: Uint8Array) => number) | undefined

get ENV(): any {
return this.mod?.ENV
return this._mod?.ENV
}

readonly dataDir?: string
Expand Down Expand Up @@ -281,9 +297,9 @@ export class PGlite

handleExternalCmd(cmd: string, mode: string) {
if (cmd.startsWith('locale -a') && mode === 'r') {
const filePath = this.mod!.stringToUTF8OnStack('/pglite/locale-a')
const smode = this.mod!.stringToUTF8OnStack(mode)
return this.mod!._fopen(filePath, smode)
const filePath = this.mod.stringToUTF8OnStack('/pglite/locale-a')
const smode = this.mod.stringToUTF8OnStack(mode)
return this.mod._fopen(filePath, smode)
}
throw new Error('Unhandled cmd')
}
Expand Down Expand Up @@ -527,7 +543,7 @@ export class PGlite
await fsBundleBufferPromise

// Load the database engine
this.mod = await PostgresModFactory(emscriptenOpts)
this._mod = await PostgresModFactory(emscriptenOpts)

// Sync the filesystem from any previous store
await this.fs!.initialSyncFs()
Expand Down Expand Up @@ -595,7 +611,7 @@ export class PGlite

this.#handlePostgresqlConf(extSharedPreloadLibraries, options)

this.mod!._pgl_setPGliteActive(1)
this.mod._pgl_setPGliteActive(1)
this.#startInSingleMode({
pgDataFolder: PGDATA,
startParams: [
Expand Down Expand Up @@ -650,7 +666,7 @@ export class PGlite
}
}
copyToFS(
this.mod!.FS,
this.mod.FS,
`${PGDATA}/postgresql.conf`,
new TextEncoder().encode(conf),
)
Expand All @@ -661,10 +677,10 @@ export class PGlite
this.#log(
`pglite: icuDataDir specified, removing default icu data dir at ${ICU_DATA_PATH}`,
)
pglUtils.rmdirRecursive(this.mod!.FS, ICU_DATA_PATH)
pglUtils.rmdirRecursive(this.mod.FS, ICU_DATA_PATH)
this.#log(`pglite: loading icu data from tarball ${icuDataDir}`)
this.mod!.FS.mkdirTree(ICU_DATA_PATH)
await loadTar(this.mod!.FS, icuDataDir, ICU_DATA_PATH)
this.mod.FS.mkdirTree(ICU_DATA_PATH)
await loadTar(this.mod.FS, icuDataDir, ICU_DATA_PATH)
}

#onRuntimeInitialized(mod: PostgresMod) {
Expand All @@ -689,7 +705,7 @@ export class PGlite

this.#pclose_fn = mod.addFunction((stream: number) => {
if (stream === this.externalCommandStreamFd) {
this.mod!._fclose(this.externalCommandStreamFd!)
this.mod._fclose(this.externalCommandStreamFd!)
this.externalCommandStreamFd = null
} else {
throw `Unhandled pclose ${stream}`
Expand All @@ -703,7 +719,7 @@ export class PGlite
this.#pglite_socket_write = mod.addFunction((ptr: any, length: number) => {
let bytes
try {
bytes = this.mod!.HEAPU8.subarray(ptr, ptr + length)
bytes = this.mod.HEAPU8.subarray(ptr, ptr + length)
} catch (e: any) {
console.error('error', e)
throw e
Expand All @@ -719,7 +735,7 @@ export class PGlite
if (length > max_length) {
length = max_length
}
this.mod!.HEAP8.set(
this.mod.HEAP8.set(
(this.#outputData as Uint8Array).subarray(
this.#readOffset,
this.#readOffset + length,
Expand Down Expand Up @@ -768,7 +784,7 @@ export class PGlite
* The Postgres Emscripten Module
*/
get Module() {
return this.mod!
return this.mod
}

/**
Expand Down Expand Up @@ -800,9 +816,9 @@ export class PGlite

// Close the database
try {
this.mod!._pgl_setPGliteActive(0)
this.mod._pgl_setPGliteActive(0)
await this.execProtocol(serialize.end())
this.mod!._pgl_run_atexit_funcs()
this.mod._pgl_run_atexit_funcs()
} catch (e: any) {
const err = e as { name: string; status: number }
if (err.name === 'ExitStatus' && err.status === 0) {
Expand All @@ -813,8 +829,8 @@ export class PGlite
this.#log(`An error occured while closing the db`, e.toString())
}
} finally {
this.mod!.removeFunction(this.#pglite_socket_read)
this.mod!.removeFunction(this.#pglite_socket_write)
this.mod.removeFunction(this.#pglite_socket_read)
this.mod.removeFunction(this.#pglite_socket_write)
}

// Close the filesystem
Expand All @@ -832,14 +848,20 @@ export class PGlite
try {
// exit the runtime. since we're using `noExitRuntime: true` on our module,
// we need to do this explicitly
this.mod!._emscripten_force_exit(/* exit code */ 0)
this.mod._emscripten_force_exit(/* exit code */ 0)
} catch (e: any) {
this.#log(e)
if (e.status !== 0) {
this.#log('Error when exiting', e.toString())
}
} finally {
pglUtils.pgliteProc.exitCode = prevExitCode

// Drop our reference to the Emscripten module. The runtime has been
// force-exited above and cannot be revived, so holding on to it only
// pins its heap (which can be hundreds of MB) for as long as this
// instance is referenced. Any later access throws via the `mod` getter.
this._mod = undefined

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the motivation for the change.

}
}

Expand Down Expand Up @@ -903,7 +925,7 @@ export class PGlite
* @returns The direct message data response produced by Postgres
*/
execProtocolRawSync(message: Uint8Array) {
const mod = this.mod!
const mod = this.mod

this.#readOffset = 0
this.#writeOffset = 0
Expand Down Expand Up @@ -1132,7 +1154,7 @@ export class PGlite
* @returns True if the database is in a transaction, false otherwise
*/
isInTransaction() {
const result = this.mod!._IsTransactionBlock()
const result = this.mod._IsTransactionBlock()
return result !== 0
}

Expand Down Expand Up @@ -1308,15 +1330,15 @@ export class PGlite
}

callMain(args: string[]): number {
return this.mod!.callMain(args)
return this.mod.callMain(args)
}

#setPGliteActive(): void {
if (this.#running) {
throw new Error('PGlite single mode already running')
}

this.mod!._pgl_startPGlite()
this.mod._pgl_startPGlite()
this.#running = true
}

Expand All @@ -1328,9 +1350,9 @@ export class PGlite
...opts.startParams,
'-D',
opts.pgDataFolder,
this.mod!.ENV.PGDATABASE,
this.mod.ENV.PGDATABASE,
]
const result = this.mod!.callMain(singleModeArgs)
const result = this.mod.callMain(singleModeArgs)
if (result !== 99) {
throw new Error('PGlite failed to initialize properly')
}
Expand All @@ -1340,22 +1362,22 @@ export class PGlite
this.#readOffset = 0
this.#writeOffset = 0
this.#outputData = message
const myProcPort = this.mod!._pgl_getMyProcPort()
const result = this.mod!._ProcessStartupPacket(myProcPort, true, true)
const myProcPort = this.mod._pgl_getMyProcPort()
const result = this.mod._ProcessStartupPacket(myProcPort, true, true)
if (result !== 0) {
throw new Error(`Cannot process startup packet + ${message.toString()}`)
}

this.mod!._pgl_sendConnData()
this.mod._pgl_sendConnData()

this.mod!._pgl_pq_flush()
this.mod._pgl_pq_flush()
this.#outputData = []

if (this.#writeOffset) return this.#inputData.subarray(0, this.#writeOffset)
return new Uint8Array(0)
}

copyToFS(filePath: string, data: Uint8Array, mode?: number) {
copyToFS(this.mod!.FS, filePath, data, mode)
copyToFS(this.mod.FS, filePath, data, mode)
}
}