Skip to content

Rhai (evalexpr_rhai)

Run Rhai scripts inside DuckDB SQL.

32,906
extension loads · last 90 days
On this page

Technical Overview

A sandboxed script for the rows SQL can't express

What it is

How it works

  • Single function, union return shape: evalexpr_rhai(expression) and evalexpr_rhai(expression, context) — one name, two overloads. Read .ok on success, .error on failure. There is no separate evalexpr_rhai_safe / evalexpr_rhai_strict split; the UNION(ok JSON, error VARCHAR) return type is the entire error-handling story.
  • Per-row context as JSON: The second argument is a JSON object DuckDB builds from row columns ({ 'salary': salary }). Inside the script it appears as context.<field>. Anything DuckDB can encode to JSON crosses the boundary — numbers, strings, booleans, arrays, nested objects.
  • Compile-and-cache for constant scripts: When the script string is a literal (or otherwise constant within a query), the parser runs once and per-row cost after warmup is interpreter execution only. When the script comes from a column (the rules-from-a-column pattern), each distinct script string parses on first encounter and caches by content — a handful of distinct rules is cheap; thousands per query is where parse cost starts to bite.
  • Errors don't kill the query: A script that throws — runtime error, type error, divide-by-zero — produces the error arm of the union for that row, and the query keeps running. Read the failure with .error if you need to surface it.

Performance and limits

  • Slower than vectorized SQL: Row-by-row interpretation is fundamentally slower than DuckDB's vectorized execution. Expect interpreter execution to dominate at high row counts — for hot OLAP paths over millions of rows, prefer native SQL or a compiled DuckDB extension function.
  • Best fit: moderate volumes: Where flexibility wins over peak throughput — config-driven rules, ad-hoc transforms, audits, exploratory analysis, batches in the thousands-to-low-millions of rows. Beyond that, profile.
  • Sandbox is intentional: Rhai has no module loading, no file I/O, and no networking — module loading is explicitly disabled in this extension. That's a feature here: you can evaluate untrusted script text from a column without granting it access to anything outside context. If you need those capabilities, this is the wrong tool. See the Rhai Book — Safety chapter.
  • Language is constrained: Rhai is smaller than full JavaScript: no package ecosystem, no async / await, no regex unless you reach for Rhai-specific helpers. The full feature set is documented in the Rhai Book.

Rhai vs. QuickJS — pick by need

  • Pick Rhai when safety matters more than expressiveness: Sandboxed by default, no module loading, no host I/O, smaller language surface. The right choice for evaluating script text supplied by users or stored in a config table — there's nothing inside Rhai to reach the host with.
  • Pick QuickJS when you need full JavaScript: Full ECMAScript semantics, regex, JSON ergonomics, and the idioms developers already know. The right choice when you're porting expressions from a JavaScript codebase or need a richer standard library — at the cost of a larger attack surface and a heavier interpreter.
  • Both are row-by-row: Neither vectorizes. The choice is about language and sandboxing, not throughput. If raw throughput dominates, push the logic into SQL or a compiled UDF instead.

Deep Dive

Technical Details

Install

INSTALL evalexpr_rhai FROM community;
LOAD evalexpr_rhai;

Quick Start

Quick expression

SELECT evalexpr_rhai('5 + 6').ok;

Evaluate per-row with a context object

SELECT name,
       evalexpr_rhai('context.salary > 100000', { 'salary': salary }).ok AS high_earner
FROM employees;

Full script with a defined function

SELECT evalexpr_rhai('fn sq(x) { x * x }  sq(context.n)', { 'n': 7 }).ok;

Reference

Extension Contents

Quick reference to all available functions and settings organized by category.

Name Description
Execute
evalexpr_rhai() Evaluate a Rhai expression or full script and return the result as a DuckDB UNION of ok (the JSON-encoded result on success) or error (the error message on failure).

API Reference

Function Documentation

Practical Examples

Cookbook

Real-world recipes and patterns for common use cases.

Platform Support

Compatibility

Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.

Quick Facts

Release status Stable
Software License MIT
Pricing Free
Written In Rust
Source Available Yes
View on GitHub
Usage
32,906
loads · last 90 days

Platforms

  • Linux x86_64 aarch64
  • Linux (musl) Not available
  • macOS Intel Apple Silicon
  • Windows x86_64
  • WASM eh mvp threads
Compiled binary sizes
Platform Architecture Size
Linux x86_64 5.85 MB
Linux aarch64 5.37 MB
macOS Intel 3.16 MB
macOS Apple Silicon 2.92 MB
Windows x86_64 9.04 MB
WASM eh 973.3 KB
WASM mvp 1.16 MB
WASM threads 971.6 KB

Compressed download size from the Haybarn extension repository.

DuckDB & Haybarn

Release calendar