Skip to content

Wasmtime host implementation for a SQL component WIT interface. Enables Wasm components to interact with SQL databases via the WebAssembly Component Model.

License

Notifications You must be signed in to change notification settings

ashitikov/wasm-sql

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wasm-sql

Wasmtime host capability for SQL database access from WebAssembly components. Uses sqlx as the underlying database driver.

Note: This library uses experimental async features from wasmtime component model. Required versions: wasmtime 0.41+, wit-bindgen 0.51+

Features

  • Connection pooling — efficient connection management via sqlx
  • Transactions — full support for begin/commit/rollback with automatic rollback on drop
  • Async support — fully async API using wasmtime component model async

Database Support

PostgreSQL — Supported

  • Codecs: int16/32/64, float32/64, string, bool, json, uuid, hstore, date, time, timestamp, timestamptz, interval, inet, cidr, macaddr, numeric

SQLite — Supported

  • Codecs: int64, float64, string, blob, bool, json, uuid, date, time, datetime, datetime-utc

MySQL — Planned

  • Driver ready, codecs pending

Missing a codec? Please create an issue with your use case!

Usage

Examples

Host Side (Rust)

Add dependency:

[dependencies]
wasm-sql = { version = "0.1.5", features = ["postgres"] }  # or "sqlite"

Set up the host:

use std::sync::Arc;
use wasm_sql::{SqlHostState, SqlHostStateView};
use wasm_sql::sqldb::SqlDB;
use wasm_sql::sqldb::sqlx::postgres::PgPoolOptions;
use wasmtime::component::Linker;

// Your state struct
struct HostState {
    sql: SqlHostState,
    // ... other fields (e.g., WasiCtx if needed)
}

impl SqlHostStateView for HostState {
    fn sql_host_state(&mut self) -> &mut SqlHostState {
        &mut self.sql
    }
}

// Create database pool
let pool = PgPoolOptions::new()
    .max_connections(5)
    .connect("postgres://user:pass@localhost/db")
    .await?;
let sql_db = Arc::new(SqlDB::new(pool));

// Add wasm-sql imports to linker
let mut linker: Linker<HostState> = Linker::new(&engine);
wasm_sql::add_to_linker(&mut linker)?;

// Create state
let state = HostState {
    sql: SqlHostState::new(sql_db),
};

See full working example in examples/host.rs.

Guest Side (WASM Component)

See full example in examples/guest-app/.

Add dependency:

[dependencies]
wit-bindgen = "0.51"

Generate bindings and use:

use bindings::wasm_sql::{
    core::query::{self, QueryExecutor},
    core::query_types::{SqlArguments, SqlQuery},
    core::util_types::Error as SqlError,
    postgres::codecs,
};
use bindings::wasm_sql::core::{pool, transaction::Transaction};

mod bindings {
    wit_bindgen::generate!({
        world: "your-app-world",
        path: ["path/to/wasm-sql/wit", "path/to/wasm-sql/wit/postgres", "./wit"],
    });
}

// Execute a parameterized query
async fn insert_user(name: &str, age: i32) -> Result<(), SqlError> {
    let args = SqlArguments::new();
    codecs::push_string(Some(name), &args)?;
    codecs::push_int32(Some(age), &args)?;

    let query = SqlQuery {
        sql: "INSERT INTO users (name, age) VALUES ($1, $2)".to_string(),
        args: Some(args),
        persistent: Some(true),
    };

    query::execute(query, QueryExecutor::Pool).await?;
    Ok(())
}

// Fetch data and decode results using codecs
async fn get_user(id: i64) -> Result<(String, i32, String), SqlError> {
    use bindings::wasm_sql::core::codecs::ColumnIndex;

    let args = SqlArguments::new();
    codecs::push_int64(Some(id), &args)?;

    let query = SqlQuery {
        sql: "SELECT name, age, email FROM users WHERE id = $1".to_string(),
        args: Some(args),
        persistent: Some(true),
    };

    let result = query::fetch_all(query, QueryExecutor::Pool).await?;

    // Decode values from row 0 using get_* functions
    let name = codecs::get_string(&result, &(0, ColumnIndex::ColumnName("name")))?
        .ok_or(SqlError::Decode("name is null".to_string()))?;
    let age = codecs::get_int32(&result, &(0, ColumnIndex::ColumnName("age")))?
        .ok_or(SqlError::Decode("age is null".to_string()))?;
    let email = codecs::get_string(&result, &(0, ColumnIndex::ColumnName("email")))?
        .ok_or(SqlError::Decode("email is null".to_string()))?;

    Ok((name, age, email))
}

Architecture

┌─────────────────────┐
│   WASM Component    │
└─────────┬───────────┘
          │ WIT imports
          ▼
┌─────────────────────┐
│   wasm-sql (host)   │
└─────────┬───────────┘
          │ sqlx
          ▼
┌─────────────────────┐
│      Database       │
│ (PostgreSQL/MySQL/  │
│      SQLite)        │
└─────────────────────┘

WIT Worlds

  • wasm-sql:core/host — Core SQL operations (pool, connections, transactions, queries)
  • wasm-sql:postgres/host — PostgreSQL-specific type codecs
  • wasm-sql:sqlite/host — SQLite-specific type codecs

License

MIT

About

Wasmtime host implementation for a SQL component WIT interface. Enables Wasm components to interact with SQL databases via the WebAssembly Component Model.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages