Skip to content

Commit 6547374

Browse files
authored
feat(pglite-tools): pg_dump (#425)
* initial import of easywasi.js code * initial port of easywasi to typescript * Fix stylecheck * Add wasi to the build and as a package export * Fix types * WIP code for pg_dump (not working) * WIP * WIP * Refactor into seporate package * Add WASM loading for node * Update wasi shim * Node example * Working pg_dump! * FIx args and style * Readme * Tweaks and fix packaging * Add tests for pg_dump * Changeset * Increase test timeout * Fix test timeout on CI * Fix foratting and typos * Remove incorrect export from the pglite package
1 parent d1dd12b commit 6547374

25 files changed

+2603
-0
lines changed

.changeset/angry-scissors-grin.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@electric-sql/pglite': patch
3+
---
4+
5+
New `runExclusive` method on PGlite that allows you to hold an exclusive lock on the database, for use with `execProtocol*` methods

.changeset/cold-bears-return.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@electric-sql/pglite': patch
3+
---
4+
5+
A new `execProtocolRawSync` method that can execute a postgres wire protocol synchronously
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@electric-sql/pglite-tools': patch
3+
---
4+
5+
Alpha version of pg_dump support in the browser and Node using a WASM build of pg_dump

packages/pglite-sync/vitest.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export default defineConfig({
66
dir: './test',
77
watch: false,
88
typecheck: { enabled: true },
9+
testTimeout: 30000,
10+
hookTimeout: 30000,
911
restoreMocks: true,
1012
testTransformMode: {
1113
ssr: ['**/*'],

packages/pglite-tools/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
release/

packages/pglite-tools/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# pglite-tools
2+
3+
A selection of tools for working with [PGlite](https://github.com/electric-sql/pglite) databases, including pg_dump.
4+
5+
Install with:
6+
7+
```bash
8+
npm install @electric-sql/pglite-tools
9+
```
10+
11+
## `pgDump`
12+
13+
pg_dump is a tool for dumping a PGlite database to a SQL file, this is a WASM build of pg_dump that can be used in a browser or other JavaScript environments. You can read more about pg_dump [in the Postgres docs](https://www.postgresql.org/docs/current/app-pgdump.html).
14+
15+
### Options
16+
17+
- `pg`: A PGlite instance.
18+
- `args`: An array of arguments to pass to pg_dump - see [pg_dump docs](https://www.postgresql.org/docs/current/app-pgdump.html) for more details.
19+
- `fileName`: The name of the file to write the dump to, defaults to `dump.sql`.
20+
21+
There are a number of arguments that are automatically added to the end of the command, these are:
22+
23+
- `--inserts` - use inserts format for the output, this ensures that the dump can be restored by simply passing the output to `pg.exec()`.
24+
- `-j 1` - concurrency level, set to 1 as multithreading isn't supported.
25+
- `-f /tmp/out.sql` - the output file is always written to `/tmp/out.sql` in the virtual file system.
26+
- `-U postgres` - use the postgres user is hard coded.
27+
28+
### Returns
29+
30+
- A `File` object containing the dump.
31+
32+
### Example
33+
34+
```typescript
35+
import { PGlite } from '@electric-sql/pglite'
36+
import { pgDump } from '@electric-sql/pglite-tools/pg_dump'
37+
38+
const pg = await PGlite.create()
39+
40+
// Create a table and insert some data
41+
await pg.exec(`
42+
CREATE TABLE test (
43+
id SERIAL PRIMARY KEY,
44+
name TEXT
45+
);
46+
`)
47+
await pg.exec(`
48+
INSERT INTO test (name) VALUES ('test');
49+
`)
50+
51+
// Dump the database to a file
52+
const dump = await pgDump({ pg })
53+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import globals from 'globals'
2+
import rootConfig from '../../eslint.config.js'
3+
4+
export default [
5+
...rootConfig,
6+
{
7+
ignores: ['release/**/*', 'examples/**/*', 'dist/**/*'],
8+
},
9+
{
10+
languageOptions: {
11+
globals: {
12+
...globals.browser,
13+
...globals.node,
14+
},
15+
},
16+
rules: {
17+
...rootConfig.rules,
18+
'@typescript-eslint/no-explicit-any': 'off',
19+
},
20+
},
21+
{
22+
files: ['tests/targets/deno/**/*.js'],
23+
languageOptions: {
24+
globals: {
25+
Deno: false,
26+
},
27+
},
28+
},
29+
]

packages/pglite-tools/package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "@electric-sql/pglite-tools",
3+
"version": "0.2.0",
4+
"description": "Tools for working with PGlite databases",
5+
"author": "Electric DB Limited",
6+
"homepage": "https://pglite.dev",
7+
"license": "Apache-2.0",
8+
"keywords": [
9+
"postgres",
10+
"sql",
11+
"database",
12+
"wasm",
13+
"pglite",
14+
"pg_dump",
15+
"pg_restore"
16+
],
17+
"type": "module",
18+
"main": "index.js",
19+
"exports": {
20+
"./pg_dump": {
21+
"types": "./dist/pg_dump.d.ts",
22+
"import": "./dist/pg_dump.js",
23+
"require": "./dist/pg_dump.cjs"
24+
}
25+
},
26+
"scripts": {
27+
"build": "tsup && tsx ./scripts/bundle-wasm.ts",
28+
"lint": "eslint ./src ./tests --report-unused-disable-directives --max-warnings 0",
29+
"format": "prettier --write ./src ./tests",
30+
"typecheck": "tsc",
31+
"stylecheck": "pnpm lint && prettier --check ./src ./tests",
32+
"test": "vitest"
33+
},
34+
"devDependencies": {
35+
"@electric-sql/pglite": "workspace:*",
36+
"@types/emscripten": "^1.39.13",
37+
"@types/node": "^20.16.11",
38+
"tsx": "^4.19.2",
39+
"vitest": "^1.3.1"
40+
}
41+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as fs from "fs/promises";
2+
import * as path from "path";
3+
4+
const copyFiles = async (srcDir: string, destDir: string) => {
5+
await fs.mkdir(destDir, { recursive: true });
6+
const files = await fs.readdir(srcDir);
7+
for (const file of files) {
8+
if (file.startsWith(".")) {
9+
continue;
10+
}
11+
const srcFile = path.join(srcDir, file);
12+
const destFile = path.join(destDir, file);
13+
const stat = await fs.stat(srcFile);
14+
if (stat.isFile()) {
15+
await fs.copyFile(srcFile, destFile);
16+
console.log(`Copied ${srcFile} to ${destFile}`);
17+
}
18+
}
19+
};
20+
21+
async function main() {
22+
// pg_dump is not yet available from CI, so we download as precompiled
23+
try {
24+
await fs.access("./release");
25+
} catch {
26+
await fs.mkdir("./release", { recursive: true });
27+
}
28+
29+
try {
30+
await fs.access("./release/pg_dump.wasm");
31+
console.log("pg_dump.wasm already exists in release directory");
32+
} catch {
33+
const response = await fetch("https://static.pglite.dev/pg_tools/pg_dump_26-11-24.wasm");
34+
const wasmBuffer = await response.arrayBuffer();
35+
36+
await fs.writeFile("./release/pg_dump.wasm", new Uint8Array(wasmBuffer));
37+
console.log("Downloaded pg_dump.wasm to release directory");
38+
}
39+
40+
await copyFiles("./release", "./dist");
41+
}
42+
43+
main();

packages/pglite-tools/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './pg_dump'

0 commit comments

Comments
 (0)