---
title: "WASM Procedures: Host Functions"
---

# WASM Procedures: Host Functions

**Part of**: [WASM Procedures User Guide](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_user_guide/)

:::caution[Not yet available in the server]
The current `heliosdb-full` server does not run WASM procedures. "WASM
functions" are on its list of subsystems that are not wired, so SQL that
would create or call one fails with SQLSTATE `0A000`. This page documents the
ABI of the WASM host in the HeliosDB Full source, which procedures will use once
the server enables it.
:::

---

## Module requirements

- **Imports come from the `env` module.** The host's linker defines only the
  functions below. It does not provide WASI, so a module that imports
  `wasi_snapshot_preview1` fails to instantiate with an unknown-import error.
- **The module exports its linear memory as `memory`.** The host reads and
  writes arguments through it.
- **Procedures are exported functions with scalar parameters and results**
  (`i32`, `i64`, `f32`, `f64`). The host calls them directly and does not run
  `_start` or `_initialize`.
- **Strings are passed as a pointer and a byte length** into the module's
  memory, and must be valid UTF-8.

## Database and storage functions

| Import (`env`) | Signature | Returns |
|----------------|-----------|---------|
| `heliosdb_execute` | `(stmt_ptr i32, stmt_len i32) -> i64` | Rows affected (`>= 0`). `-1` invalid UTF-8, `-2` execution failed, `-3` memory access error. |
| `heliosdb_begin_tx` | `() -> i64` | Transaction id (`> 0`). `-1` could not start, `-2` too many concurrent transactions. |
| `heliosdb_commit_tx` | `(tx_id i64) -> i32` | `0` success. `1` unknown id, `2` already committed or rolled back, `3` commit failed. |
| `heliosdb_rollback_tx` | `(tx_id i64) -> i32` | `0` success. `1` unknown id, `2` already committed or rolled back. |
| `heliosdb_storage_write` | `(key_ptr i32, key_len i32, value_ptr i32, value_len i32) -> i32` | `0` success. `1` invalid UTF-8 key, `2` write failed, `3` memory access error. |
| `heliosdb_log` | `(level i32, msg_ptr i32, msg_len i32) -> i32` | `0` success. `1` invalid UTF-8, `2` level outside 0 (trace) to 4 (error), `3` memory access error. |

### Functions that return data

| Import (`env`) | Signature | Notes |
|----------------|-----------|-------|
| `heliosdb_query` | `(query_ptr i32, query_len i32, result_ptr_out i32, result_len_out i32) -> i32` | Runs a query and serialises the result as JSON (`{"columns": [...], "rows": [[...]], "row_count": N}`). It writes the result's address and length to the two `*_out` locations. |
| `heliosdb_storage_read` | `(key_ptr i32, key_len i32, value_ptr_out i32, value_len_out i32) -> i32` | Reads a key-value entry the same way. `2` means the key was not found. |
| `heliosdb_fetch_rows` | `(result_handle i64, buffer_ptr i32, buffer_len i32, rows_fetched_ptr i32) -> i32` | Cursor-style row fetch. |
| `heliosdb_emit_event` | `(type_ptr i32, type_len i32, payload_ptr i32, payload_len i32) -> i32` | Validates a JSON event payload. Events are not yet delivered to triggers. |

:::danger[Do not use heliosdb_query or heliosdb_storage_read from compiled modules]
These functions do not write into a buffer the module supplies. The host
copies the result to an address it picks itself, using a bump allocator that
starts at 64 KiB of the module's memory. Compilers such as TinyGo put the
module's data segment at that address, so the call overwrites the module's
own globals. The Go SDK does not expose these functions.
:::

## Calling the functions from Go

The [Go SDK](https://github.com/HeliosDatabase/HeliosDB-SDKs/tree/main/sdks/go/wasm)
wraps the safe functions:

| Go API | Host import |
|--------|-------------|
| `heliosdb.Exec(sql) (int64, error)` | `heliosdb_execute` |
| `heliosdb.Begin()`, `Tx.Commit()`, `Tx.Rollback()`, `heliosdb.WithTx(fn)` | `heliosdb_begin_tx`, `heliosdb_commit_tx`, `heliosdb_rollback_tx` |
| `heliosdb.StorageWrite(key, value)` | `heliosdb_storage_write` |
| `heliosdb.Log(level, msg)`, `LogInfo`, `LogWarn`, `LogError`, ... | `heliosdb_log` |

```go
//export archive_order
func archiveOrder(orderID int64) int32 {
    err := heliosdb.WithTx(func(heliosdb.Tx) error {
        id := heliosdb.Int64(orderID)
        if _, err := heliosdb.Exec("INSERT INTO orders_archive SELECT * FROM orders WHERE id = " + id); err != nil {
            return err
        }
        _, err := heliosdb.Exec("DELETE FROM orders WHERE id = " + id)
        return err
    })
    if err != nil {
        heliosdb.LogError("archive_order: " + err.Error())
        return -1
    }
    return 0
}
```

## Calling the functions from other languages

Declare the imports with the signatures above. For example, in Rust:

```rust
#[link(wasm_import_module = "env")]
extern "C" {
    fn heliosdb_execute(stmt_ptr: i32, stmt_len: i32) -> i64;
    fn heliosdb_log(level: i32, msg_ptr: i32, msg_len: i32) -> i32;
}

#[no_mangle]
pub extern "C" fn touch_user(id: i64) -> i32 {
    let sql = format!("UPDATE users SET seen_at = NOW() WHERE id = {id}");
    let n = unsafe { heliosdb_execute(sql.as_ptr() as i32, sql.len() as i32) };
    if n < 0 { -1 } else { 0 }
}
```

Build such modules for `wasm32-unknown-unknown`, not a WASI target.

---

**Navigation:**
- **Previous**: [Language SDKs](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_language_sdks/)
- **Next**: [Security Model](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_security_model/)
- **Index**: [WASM Procedures User Guide](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_user_guide/)
