Skip to main content

Rust API

The workadb-sys crate provides safe Rust wrappers around the wepg_* C ABI. It focuses on a small surface: configure, initialize, execute SQL, and shut down. The wrapper also decodes SQL-mode responses into a structured WorkadbFrame.

Types

WorkadbConfig

Builder for engine configuration.

let config = WorkadbConfig::new(pgdata, temp)
.flags(WEPG_FLAG_READONLY)
.max_response_bytes(2 * 1024 * 1024);

Fields and behavior:

  • pgdata_path: required path to the data directory.
  • temp_path: required temp directory path (used for temporary files).
  • flags: WEPG_FLAG_* bitmask.
  • max_response_bytes: caps the response buffer size (default 512 KiB).

Workadb

Owns a single engine handle and manages lifecycle:

  • Workadb::init(&config) initializes the engine and returns a handle.
  • initdb() explicitly creates the data directory (optional if auto-init is enabled).
  • exec_sql(sql) executes SQL and returns a decoded WorkadbFrame.
  • exec_sql_raw(sql) returns raw bytes and row count.
  • reset() clears session state without deleting data.
  • shutdown() closes the engine and releases resources.

WorkadbFrame

Decoded SQL response frame with columns and rows. Values are returned in PostgreSQL text format.

WorkadbResponse

Raw response bytes and row count. Useful if you want to parse the frame manually.

WorkadbError

Error type that includes a status code and optional error message from the engine.

Lifecycle and Drop behavior

Workadb implements Drop. If you do not call shutdown(), the Drop handler will attempt a best-effort shutdown when the value goes out of scope. Errors in Drop are ignored, so call shutdown() when you need to observe errors or guarantee a clean teardown.

If a Workadb instance is leaked (e.g., via std::mem::forget), the embedded engine will remain open until process exit.

Examples

Basic CRUD

use workadb_sys::{Workadb, WorkadbConfig};

let config = WorkadbConfig::new(pgdata, tmp);
let engine = Workadb::init(&config)?;

engine.exec_sql("CREATE TABLE issues (id INT PRIMARY KEY, title TEXT);")?;
engine.exec_sql("INSERT INTO issues VALUES (1, 'Sink leak');")?;

let frame = engine.exec_sql("SELECT id, title FROM issues ORDER BY id;")?;
assert_eq!(frame.rows[0][0].as_deref(), Some("1"));
assert_eq!(frame.rows[0][1].as_deref(), Some("Sink leak"));

engine.shutdown()?;

Transactions

engine.exec_sql("BEGIN;")?;
engine.exec_sql("INSERT INTO issues VALUES (2, 'Heater noisy');")?;
engine.exec_sql("INSERT INTO issues VALUES (3, 'Window crack');")?;
engine.exec_sql("COMMIT;")?;

CTE + join + window

let sql = r#"
WITH recent AS (
SELECT id, title, opened_at
FROM issues
WHERE opened_at >= NOW() - INTERVAL '7 days'
)
SELECT i.id, i.title, t.name,
COUNT(*) OVER (PARTITION BY t.id) AS tenant_issue_count
FROM recent i
JOIN leases l ON l.id = i.lease_id
JOIN tenants t ON t.id = l.tenant_id
ORDER BY i.id;
"#;

let frame = engine.exec_sql(sql)?;

Parameterized SQL (PREPARE/EXECUTE)

WorkaDB does not yet expose a Rust-level bind-parameter API. You can still use SQL-level parameterization via PREPARE and EXECUTE.

engine.exec_sql("PREPARE add_issue (int, text) AS INSERT INTO issues (id, title) VALUES ($1, $2);")?;
engine.exec_sql("EXECUTE add_issue(10, 'Leaky tap');")?;
engine.exec_sql("DEALLOCATE add_issue;")?;

Prepared statements live for the lifetime of the engine session and are cleared on reset() or shutdown().

Raw responses

use workadb_sys::WorkadbFrame;

let raw = engine.exec_sql_raw("SELECT 1;")?;
let frame = WorkadbFrame::decode(&raw.bytes, raw.rows)?;
assert_eq!(frame.rows[0][0].as_deref(), Some("1"));

Error handling

Check errors explicitly to inspect the underlying engine message:

match engine.exec_sql("SELECT * FROM missing_table;") {
Ok(_) => {}
Err(err) => eprintln!("WorkaDB error: {err}")
}