HeliosDB Stored Procedures - Function Development Guide
Table of Contents
- Introduction
- Getting Started
- Supported Languages
- Creating Functions
- Type System
- Security and Sandboxing
- Performance Optimization
- Best Practices
- Debugging
- Examples
Introduction
HeliosDB’s polyglot stored procedure system supports these programming languages:
- JavaScript (via QuickJS)
- Python (via RustPython)
- Rust (compiled to WebAssembly)
- WebAssembly (any language that compiles to WASM)
All functions run in a secure, sandboxed environment with resource limits and capability-based access control.
Getting Started
Basic Function Creation
Create a simple JavaScript function:
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.CREATE FUNCTION add_numbers(a INTEGER, b INTEGER)RETURNS INTEGERLANGUAGE javascriptAS $$ function add_numbers(a, b) { return a + b; }$$;Call the function:
SELECT add_numbers(10, 32); -- Returns 42Python Function
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.CREATE FUNCTION calculate_discount(price REAL, rate REAL)RETURNS REALLANGUAGE pythonAS $$ def calculate_discount(price, rate): return price * (1 - rate)$$;Rust/WASM Function
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.CREATE FUNCTION fibonacci(n INTEGER)RETURNS INTEGERLANGUAGE rustAS $$ -- Binary WASM module content (base64 encoded or binary)$$;Supported Languages
JavaScript
Features:
- ES6+ syntax support
- Async/await operations
- JSON manipulation
- Array and object methods
- Math operations
Runtime: QuickJS with 16MB memory limit by default
Example:
function process_data(items) { return items .filter(x => x > 0) .map(x => x * 2) .reduce((sum, x) => sum + x, 0);}Python
Features:
- Python 3.x syntax
- Standard library support
- List comprehensions
- Dictionary operations
- String manipulation
Runtime: RustPython with 32MB memory limit by default
Example:
def process_users(users): return [ { 'id': user['id'], 'name': user['name'].upper(), 'active': user.get('active', False) } for user in users if user.get('age', 0) >= 18 ]Rust (WebAssembly)
Features:
- Zero-cost abstractions
- Memory safety
- High performance
- Direct WASM compilation
Example:
#[no_mangle]pub extern "C" fn factorial(n: i32) -> i32 { if n <= 1 { 1 } else { n * factorial(n - 1) }}Creating Functions
SQL Syntax
CREATE FUNCTION function_name(param1 type1, param2 type2, ...)RETURNS return_typeLANGUAGE language_name[VOLATILE | STABLE | IMMUTABLE][STRICT]AS $$ function_body$$;Function Attributes
- VOLATILE: Function has side effects or returns different values with same inputs (default)
- STABLE: Function returns same value for same inputs within a transaction
- IMMUTABLE: Function always returns same value for same inputs (pure function)
- STRICT: Function returns NULL if any input is NULL
Example with Attributes
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.CREATE FUNCTION calculate_hash(data TEXT)RETURNS TEXTLANGUAGE javascriptIMMUTABLESTRICTAS $$ function calculate_hash(data) { // Deterministic hash function let hash = 0; for (let i = 0; i < data.length; i++) { hash = ((hash << 5) - hash) + data.charCodeAt(i); hash = hash & hash; } return hash.toString(16); }$$;Type System
Supported SQL Types
| SQL Type | JavaScript | Python | Rust/WASM |
|---|---|---|---|
| INTEGER | number | int | i32 |
| BIGINT | number | int | i64 |
| REAL | number | float | f32 |
| DOUBLE | number | float | f64 |
| BOOLEAN | boolean | bool | i32 (0/1) |
| TEXT | string | str | String |
| JSON | object | dict | - |
| ARRAY[] | Array | list | Vec |
Type Conversion
Values are automatically converted between languages:
// JavaScript function receiving JSONfunction process_order(order) { return { order_id: order.id, total: order.items.reduce((sum, item) => sum + item.price, 0), item_count: order.items.length };}# Python function with array inputdef sum_array(numbers): return sum(numbers)Security and Sandboxing
Capability-Based Security
Functions run in a sandboxed environment with explicit permissions:
-- Grant database read permissionALTER FUNCTION my_functionGRANT CAPABILITY DATABASE_READ('users');
-- Grant database write permissionALTER FUNCTION my_functionGRANT CAPABILITY DATABASE_WRITE('audit_log');
-- Grant network accessALTER FUNCTION my_functionGRANT CAPABILITY NETWORK_ACCESS('api.example.com');Available Capabilities
DATABASE_READ(pattern)- Read from tables matching patternDATABASE_WRITE(pattern)- Write to tables matching patternDATABASE_EXECUTE- Execute DDL/admin operationsFILE_READ(pattern)- Read files matching patternFILE_WRITE(pattern)- Write files matching patternNETWORK_ACCESS(pattern)- Access network hosts matching patternCALL_FUNCTION(pattern)- Call other functionsENV_ACCESS- Access environment variablesTIME_ACCESS- Access system timeRANDOM_ACCESS- Generate random numbersCRYPTO_ACCESS- Cryptographic operationsHTTP_CLIENT- HTTP client access
Resource Limits
Default limits per function execution:
- Memory: 16MB (JavaScript), 32MB (Python)
- CPU Time: 5 seconds
- Wall Time: 10 seconds
- Instructions: 10 billion
- Stack Size: 2MB
Customize limits:
ALTER FUNCTION expensive_calculationSET max_memory_bytes = 67108864; -- 64MB
ALTER FUNCTION expensive_calculationSET max_cpu_time_ms = 30000; -- 30 secondsStrict Security Example
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.CREATE FUNCTION secure_function(input TEXT)RETURNS TEXTLANGUAGE javascriptAS $$ function secure_function(input) { // Runs with minimal permissions // No file, network, or database access return input.toUpperCase(); }$$;
-- Apply strict security policyALTER FUNCTION secure_functionSET SECURITY POLICY STRICT;Performance Optimization
Function Result Caching
Enable caching for expensive, deterministic functions:
ALTER FUNCTION expensive_calculationSET CACHE_TTL = 300; -- Cache results for 5 minutes
ALTER FUNCTION expensive_calculationSET CACHE_SIZE = 1000; -- Cache up to 1000 resultsJIT Optimization
Configure JIT compilation settings:
-- Fast JIT (lower latency, less optimization)ALTER FUNCTION quick_functionSET JIT_MODE = 'low_latency';
-- Aggressive optimization (higher latency, better performance)ALTER FUNCTION heavy_computationSET JIT_MODE = 'fast';Parallel Execution
Execute multiple function calls in parallel:
// Batch execution in application codeconst results = await db.executeParallel([ { function: 'process_user', args: [user1] }, { function: 'process_user', args: [user2] }, { function: 'process_user', args: [user3] }]);Performance Tips
- Use IMMUTABLE for pure functions - Enables better caching
- Minimize memory allocations - Reuse arrays/objects
- Batch database operations - Use array inputs instead of loops
- Enable caching for read-heavy workloads
- Use appropriate data types - INTEGER faster than TEXT for numbers
- Avoid deep nesting - Flatten data structures when possible
Best Practices
1. Error Handling
function safe_divide(a, b) { if (b === 0) { throw new Error('Division by zero'); } return a / b;}def safe_parse_json(text): try: return heliosdb.json.parse(text) except Exception as e: heliosdb.log(f'JSON parse error: {e}') return None2. Input Validation
function process_email(email) { if (typeof email !== 'string' || !email.includes('@')) { throw new Error('Invalid email address'); } return email.toLowerCase().trim();}3. Logging
function debug_calculation(x, y) { heliosdb.log('Calculating with inputs:', x, y); const result = x * y + x / y; heliosdb.log('Result:', result); return result;}4. Transaction Handling
function transfer_funds(from_id, to_id, amount) { heliosdb.transaction(function(tx) { const from_balance = heliosdb.query( 'SELECT balance FROM accounts WHERE id = ?', [from_id] ).rows[0].balance;
if (from_balance < amount) { tx.rollback(); throw new Error('Insufficient funds'); }
heliosdb.execute( 'UPDATE accounts SET balance = balance - ? WHERE id = ?', [amount, from_id] );
heliosdb.execute( 'UPDATE accounts SET balance = balance + ? WHERE id = ?', [amount, to_id] );
tx.commit(); });}5. Testing Functions
-- Test basic functionalitySELECT add_numbers(5, 3); -- Expected: 8
-- Test edge casesSELECT add_numbers(0, 0); -- Expected: 0SELECT add_numbers(-5, 5); -- Expected: 0
-- Test NULL handling (if STRICT)SELECT add_numbers(NULL, 5); -- Expected: NULL
-- Test error handlingSELECT safe_divide(10, 0); -- Should throw errorDebugging
Debug Mode
Enable debug logging:
ALTER FUNCTION my_functionSET DEBUG = true;Stack Traces
Errors include stack traces:
ExecutionError: Division by zero at safe_divide (line 3, column 15) at calculate_average (line 12, column 20)Performance Profiling
SELECT * FROM heliosdb.function_stats('my_function');Returns:
- Total calls
- Average execution time
- Cache hit rate
- Memory usage
- Error count
Examples
Example 1: Data Validation
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.CREATE FUNCTION validate_user_data(user JSON)RETURNS JSONLANGUAGE javascriptAS $$ function validate_user_data(user) { const errors = [];
if (!user.email || !user.email.includes('@')) { errors.push('Invalid email'); }
if (!user.name || user.name.length < 2) { errors.push('Name must be at least 2 characters'); }
if (user.age && (user.age < 0 || user.age > 150)) { errors.push('Invalid age'); }
return { valid: errors.length === 0, errors: errors }; }$$;Example 2: Data Transformation
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.CREATE FUNCTION transform_orders(orders JSON)RETURNS JSONLANGUAGE javascriptAS $$ function transform_orders(orders) { return orders.map(order => ({ id: order.id, customer: order.customer_name, total: order.items.reduce((sum, item) => sum + item.quantity * item.price, 0 ), items: order.items.length, status: order.shipped ? 'shipped' : 'pending' })); }$$;Example 3: Complex Calculation
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.CREATE FUNCTION calculate_stats(numbers DOUBLE[])RETURNS JSONLANGUAGE pythonAS $$ def calculate_stats(numbers): if not numbers: return None
n = len(numbers) mean = sum(numbers) / n variance = sum((x - mean) ** 2 for x in numbers) / n std_dev = variance ** 0.5
sorted_nums = sorted(numbers) median = sorted_nums[n // 2] if n % 2 == 1 else \ (sorted_nums[n // 2 - 1] + sorted_nums[n // 2]) / 2
return { 'count': n, 'mean': mean, 'median': median, 'std_dev': std_dev, 'min': min(numbers), 'max': max(numbers) }$$;Example 4: Cross-Language Interoperability
-- Planned syntax for the upcoming WASM procedures feature; the current server does not accept it.-- JavaScript functionCREATE FUNCTION format_currency(amount REAL)RETURNS TEXTLANGUAGE javascriptAS $$ function format_currency(amount) { return '$' + amount.toFixed(2); }$$;
-- Python function that calls JavaScript functionCREATE FUNCTION generate_invoice(items JSON)RETURNS TEXTLANGUAGE pythonAS $$ def generate_invoice(items): total = sum(item['price'] * item['quantity'] for item in items) formatted_total = heliosdb.call_function('format_currency', [total])
return f"Invoice Total: {formatted_total}"$$;Conclusion
HeliosDB’s polyglot stored procedure system runs business logic close to your data in a sandbox. Follow these guidelines to keep database functions maintainable as your application grows.
For more information: