pgwasm runs WebAssembly components inside PostgreSQL and exposes
their exports as strongly-typed SQL functions. A component's WIT world is
mapped automatically to PostgreSQL types (records, enums, variants, flags,
domains, lists), its bytes are compiled once and cached on disk, and every
invocation runs inside a Wasmtime sandbox whose
capabilities are configured by the database administrator.
- Why pgwasm
- Quick start
- A minimal component
- Calling it from SQL
- Sandbox and policy
- Documentation
- Development
- License
- Components first. Write your UDF in any language that can target a
WebAssembly component
and ship its WIT world.
pgwasmturns each exported function into a regularpg_procrow with real PostgreSQL parameter and return types. - Typed, not buffered. Records become composite types, enums become PG
enums, variants and flags become composite / domain types, lists become
arrays or
bytea. You do not marshal JSON on either side. - Run once, call many. Modules are compiled at
pgwasm.pgwasm_loadtime and their AOT artifacts live under$PGDATA/pgwasm/<module_id>/. Per-call overhead is a pool'd component instance plus argument marshaling. - Strong sandbox. WASI filesystem, sockets, HTTP, and environment
access are all off by default. Administrators widen the ceiling with
pgwasm.*GUCs; per-module overrides can only narrow that ceiling. - Durable, observable. Module metadata is in catalog tables; counters
and gauges live in shared memory;
pgwasm_modules,pgwasm_functions,pgwasm_stats, and related SRFs expose both.
# Build and install the extension into the pgrx-managed Postgres (v13 by default).
cargo pgrx install --release
# Start the pgrx-managed cluster if you are not running your own.
cargo pgrx startIn your database:
CREATE EXTENSION pgwasm;
-- Optional: make sure the extension is enabled (this is the default).
SHOW pgwasm.enabled;The roles
pgwasm_loader(may load / unload / reload / reconfigure modules) andpgwasm_reader(may readpgwasm_stats) are created byCREATE EXTENSION. Grant membership deliberately; loading a module is a privileged operation.
arith.wit:
package example:arith;
world arith {
export add: func(a: s32, b: s32) -> s32;
}Compile your language of choice to a WebAssembly component that
implements this world (for example, cargo component build --release
for Rust). You should end up with arith.component.wasm.
-- Load the component from bytes (bytea overload).
SELECT pgwasm.pgwasm_load(
wasm => pg_read_binary_file('arith.component.wasm'),
name => 'arith',
options => '{}'::jsonb
) AS module_id;
-- The loader creates one pg_proc row per WIT export, named <prefix>_<export>.
SELECT arith_add(2, 3); --> 5
-- Inspect everything the extension knows about this module.
SELECT module_id, name, abi, generation FROM pgwasm.pgwasm_modules();
SELECT export_id, sql_name, wasm_name, signature FROM pgwasm.pgwasm_functions();
-- Tear it down when you are done.
SELECT pgwasm.pgwasm_unload(module_id) FROM pgwasm.pgwasm_modules() WHERE name = 'arith';The text overload pgwasm.pgwasm_load(path text, ...) is available for file
system loads, subject to pgwasm.allow_load_from_file,
pgwasm.module_path, and pgwasm.allowed_path_prefixes. See
docs/guc.md for the full list.
Every pgwasm.* GUC defaults to the safe option: all WASI surfaces are
off, SPI access is off, fuel is off, and the invocation deadline is 5 s.
To let a module reach the network, an administrator must set both the
master toggle (pgwasm.allow_wasi) and the specific capability
(pgwasm.allow_wasi_net, pgwasm.allow_wasi_http), and populate
pgwasm.allowed_hosts. A module author can still opt to narrow further
-- see the policy / limits keys in the options JSON accepted by
pgwasm.pgwasm_load and pgwasm.pgwasm_reconfigure.
See docs/guc.md for every GUC, default, scope, and hot/cold reconfiguration semantics.
- docs/architecture.md — the full v2 design: catalog, shared memory, runtime, trampoline, type mapping, policy, metrics.
- docs/guc.md — every
pgwasm.*GUC with type, default, scope (USERSET/SUSET/POSTMASTER), and whether it can be changed live or requires a restart. - docs/wit-mapping.md — the canonical WIT → PostgreSQL type table with WIT and SQL examples for every kind (primitives, composites, generics, resources).
This is a pgrx extension
pinned to pgrx 0.18 with Wasmtime 44. The repository is a Cargo
workspace; the extension crate is pgwasm/.
Useful commands (see AGENTS.md for the authoritative testing guide):
# Fast host-only type / borrow check.
cargo check -p pgwasm
# Regress tests: deterministic SQL goldens. pgrx installs the extension.
cd pgwasm && cargo pgrx regress --features pg_test
# In-backend unit tests (`#[pg_test]`).
cargo pgrx test -p pgwasmcargo testis a host-only lane and must stay free of direct Postgres backend symbol dependencies.cargo pgrx test -p pgwasmis the backend lane for#[pg_test]and any code paths that require pgrx/Postgres runtime symbols.- For CI, run both lanes explicitly (do not treat one as a substitute for the
other):
cargo testcargo pgrx test pg17 -p pgwasm
When adding or modifying Rust code, follow
.cursor/rules/rust-coding-standards.mdc
(alphabetical #[derive(...)], three-block use layout, alphabetical
Cargo dependency keys, no unwrap() outside tests).
pgwasm is licensed under the terms of the LICENSE file at
the root of this repository.