---
title: "WASM Procedures: Migration Guide"
---

# WASM Procedures: Migration Guide

:::caution[Planned design: not yet available in the server]
The current `heliosdb-full` server does not run WASM procedures. SQL that
would create or call one fails with SQLSTATE `0A000`. This page describes the
planned design for this upcoming feature. Only the [Go SDK](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_language_sdks/#go-sdk) is
published. Rust, JavaScript and Python examples are illustrations: no SDK
package exists for those languages. See the
[Quick Start](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_quick_start/) for what works today.
:::

**Documentation Home > User Guides > Features > Migration Guide**

---

## Overview

This guide provides practical examples and performance comparisons for migrating existing database procedures to HeliosDB WASM procedures from PL/pgSQL, JavaScript UDFs, and other procedural languages.

**Related Sections:**
- [Quick Start](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_quick_start/) - Getting started with WASM procedures
- [Language SDKs](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_language_sdks/) - SDK documentation
- [Performance](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_performance/) - Performance benefits

---

## Migration Guide

### SQL Procedures to WASM

#### PL/pgSQL → Rust

**Before (PL/pgSQL):**
```sql
CREATE PROCEDURE calculate_bonus(emp_id INT)
LANGUAGE plpgsql
AS $$
DECLARE
  emp_salary DECIMAL(10,2);
  bonus DECIMAL(10,2);
BEGIN
  SELECT salary INTO emp_salary
  FROM employees
  WHERE id = emp_id;

  IF emp_salary > 100000 THEN
    bonus := emp_salary * 0.10;
  ELSE
    bonus := emp_salary * 0.05;
  END IF;

  UPDATE employees
  SET bonus = bonus
  WHERE id = emp_id;
END;
$$;
```

**After (Go/WASM, built with TinyGo):**
```go
package main

import "github.com/HeliosDatabase/HeliosDB-SDKs/sdks/go/wasm/heliosdb"

// The Go SDK runs statements and reports rows affected; it has no API for
// reading result rows yet, so the bonus rule is expressed in the UPDATE.
//
//export calculate_bonus
func calculateBonus(empID int64) int32 {
	_, err := heliosdb.Exec("UPDATE employees SET bonus = CASE " +
		"WHEN salary > 100000 THEN salary * 0.10 ELSE salary * 0.05 END " +
		"WHERE id = " + heliosdb.Int64(empID))
	if err != nil {
		heliosdb.LogError("calculate_bonus: " + err.Error())
		return -1
	}
	return 0
}

func main() {}
```

Build it with `tinygo build -target=wasm-unknown -gc=conservative -opt=z -no-debug -o bonus.wasm .`
(see the [Quick Start](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_quick_start/)). No Rust SDK crate is
published, so this example uses the Go SDK.

**Benefits:**
- Type checking at compile time
- Explicit error handling
- Testable outside the database with the SDK's `heliosdbtest` fake host

#### JavaScript UDFs → AssemblyScript

**Before (JavaScript UDF):**
```javascript
function calculateTax(amount) {
    const taxRate = 0.08;
    return amount * taxRate;
}
```

**After (AssemblyScript/WASM):**
```typescript
export function calculateTax(amount: f64): f64 {
    const taxRate: f64 = 0.08;
    return amount * taxRate;
}
```

**Benefits:**
- Type safety
- Faster execution
- Security isolation

### Performance Comparison

**Benchmark: 10,000 procedure calls** (indicative, measured on representative fixtures; reproduce on your own hardware)

| Implementation | Total Time | Avg per Call | Memory |
|----------------|------------|--------------|--------|
| PL/pgSQL | 52,000ms | 5.2ms | 250 MB |
| PL/Python | 87,000ms | 8.7ms | 450 MB |
| WASM (Rust) | 6,200ms | 0.62ms | 45 MB |
| WASM (JS) | 11,000ms | 1.1ms | 62 MB |
| WASM (Python) | 14,500ms | 1.45ms | 89 MB |
| WASM (Go) | 8,100ms | 0.81ms | 53 MB |

---

## Migration Strategies

### 1. Gradual Migration

Migrate procedures incrementally while keeping old procedures operational:

```sql
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.
-- Keep existing PL/pgSQL procedure
CREATE OR REPLACE PROCEDURE calculate_bonus_legacy(emp_id INT)
LANGUAGE plpgsql AS $$ ... $$;

-- Create new WASM version
CREATE PROCEDURE calculate_bonus_wasm(emp_id BIGINT)
LANGUAGE wasm
AS '/opt/procedures/calculate_bonus.wasm';

-- Use feature flag to switch
CREATE PROCEDURE calculate_bonus(emp_id INT)
LANGUAGE sql AS $$
  SELECT CASE
    WHEN current_setting('app.use_wasm', true) = 'true'
      THEN calculate_bonus_wasm(emp_id)
    ELSE
      calculate_bonus_legacy(emp_id)
  END;
$$;
```

### 2. A/B Testing

Test WASM procedures against existing implementations:

```sql
-- Route 10% of traffic to WASM version
CREATE PROCEDURE calculate_bonus(emp_id INT)
LANGUAGE sql AS $$
  SELECT CASE
    WHEN random() < 0.1
      THEN calculate_bonus_wasm(emp_id)
    ELSE
      calculate_bonus_legacy(emp_id)
  END;
$$;
```

### 3. Shadow Mode

Run both versions and compare results:

```sql
CREATE PROCEDURE calculate_bonus_shadow(emp_id INT)
LANGUAGE sql AS $$
  WITH legacy_result AS (
    SELECT calculate_bonus_legacy(emp_id) AS result
  ),
  wasm_result AS (
    SELECT calculate_bonus_wasm(emp_id) AS result
  )
  SELECT
    CASE WHEN l.result = w.result
      THEN l.result
      ELSE (
        -- Log discrepancy
        SELECT log_mismatch('calculate_bonus', emp_id, l.result, w.result),
               l.result  -- Return legacy result during shadow mode
      )
    END
  FROM legacy_result l, wasm_result w;
$$;
```

## Common Migration Patterns

### Database Queries

**PL/pgSQL:**
```sql
SELECT column INTO variable FROM table WHERE condition;
```

**Rust WASM:**
```rust
let result = exec_sql("SELECT column FROM table WHERE condition")?;
let variable = result.get_string(0, 0)?;
```

### Loops

**PL/pgSQL:**
```sql
FOR record IN SELECT * FROM table LOOP
  -- Process record
END LOOP;
```

**Rust WASM:**
```rust
let result = exec_sql("SELECT * FROM table")?;
for row in 0..result.row_count() {
    // Process row
}
```

### Error Handling

**PL/pgSQL:**
```sql
BEGIN
  -- Code
EXCEPTION
  WHEN OTHERS THEN
    RAISE NOTICE 'Error: %', SQLERRM;
END;
```

**Rust WASM:**
```rust
match risky_operation() {
    Ok(value) => value,
    Err(e) => {
        log_error(&format!("Error: {}", e));
        return Err(e);
    }
}
```

## Migration Checklist

- [ ] Identify procedures to migrate (prioritize high-traffic, CPU-intensive)
- [ ] Choose target language (Rust for performance, Python for ML, etc.)
- [ ] Set up development environment with SDK
- [ ] Implement WASM version
- [ ] Write unit tests
- [ ] Deploy in shadow mode
- [ ] Compare results and performance
- [ ] Gradually roll out to production
- [ ] Monitor metrics and errors
- [ ] Retire legacy procedures

## Next Steps

- **[API Reference](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_api_reference/)** - Complete API documentation
- **[Performance](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_performance/)** - Optimize migrated procedures
- **[Monitoring](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_monitoring/)** - Monitor migration success

---

**Navigation:** [← Advanced Topics](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_advanced_topics/) | [Index](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_user_guide/) | [API Reference →](https://www.heliosdb.com/docs/full/guides/features/wasm_procedures_api_reference/)
