HeliosDB Stored Procedures - Function Development Guide
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 provides a powerful polyglot stored procedure system that supports multiple 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:
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
CREATE FUNCTION calculate_discount(price REAL, rate REAL)RETURNS REALLANGUAGE pythonAS $$ def calculate_discount(price, rate): return price * (1 - rate)$$;Rust/WASM Function
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
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
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
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
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
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
-- 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 provides a powerful, secure, and performant way to implement business logic close to your data. By following these guidelines and best practices, you can build robust, maintainable database functions that scale with your application.
For more information: