Chapter 14 · Full-Text Search, R-Tree, Virtual Tables, and Extensions

Loadable Extensions, Built-In Extensions, and Supply-Chain Safety

Treat SQLite extensions as executable capabilities with explicit trust boundaries, detect built-in modules before depending on them, and design a safe, portable extension policy for applications and deployments.

Beginner100–120 minutesCapability + extension trust reviewSQLite 3.53.4 baselineNo untrusted binariesLast reviewed: August 2026

Learning outcomes

SQLite can be extended with functions, collations, virtual-table modules, and even VFS implementations. That power means extension loading belongs in the software supply-chain and security model. The final lesson separates capabilities compiled into your SQLite library from native code loaded at runtime, then builds a repeatable capability checklist before Chapter 15 moves into application drivers.

01

Distinguish built-in, statically linked, registered, and runtime-loadable SQLite capabilities.

02

Explain why native extension loading is disabled by default in the core application API.

03

Use .load and application-level loading only as explicitly trusted deployment operations.

04

Inventory FTS5, RTree, dbstat, session, CSV/other extension capabilities without promising they exist everywhere.

05

Identify portability and packaging costs introduced by non-standard extensions.

06

Produce a capability-detection and supply-chain checklist suitable for an application startup/deployment review.

“Extension” can mean several deployment shapes

Capability shape How code reaches the process Example Operational implication
Core/built into SQLite library Compiled into the library JSON support in modern SQLite Travels with that library build.
Optional feature compiled in Compile-time option or source integration FTS5/RTree/dbstat/session depending on build Must verify the actual host build.
Statically linked extension Application/library link step Official extension source integrated into product Versioned and reviewed with the application binary.
Runtime loadable extension Shared library/DLL loaded after connection opens Approved custom module Native code executes in-process; packaging/signing/provenance matter.

Runtime extension loading is code execution

A SQLite loadable extension is a native shared library: .dll on Windows, usually .so on Linux/Unix, and .dylib on macOS. Its initialization routine can register SQL functions, collations, virtual tables, or VFSes. If the binary is malicious or compromised, SQL sandboxing cannot make its native machine code harmless.

Supply-chain boundary

Never teach or automate “download whatever DLL fixes no such module.” Use an approved source/build pipeline, pin versions/hashes or signatures where your organization supports them, match CPU/OS/runtime architecture, and treat extension updates like application dependencies.

Why application APIs disable loading by default

SQLite's core documentation says extension loading is turned off by default for security. An application must explicitly enable it before using the C loading interface or SQL load_extension() function. This default reduces the chance that untrusted SQL can cause the process to execute arbitrary native libraries.

c · C-level intent, not a complete program
/* Preferred application decision point: enable extension loading   only when the deployment policy explicitly requires it. */sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);/* Then load only an approved extension path/entry point. */sqlite3_load_extension(db, approved_path, NULL, &errmsg);/* Disable again when the loading window is over. */sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL);

Higher-level drivers expose different wrappers—or none at all. Chapter 15 will treat driver behavior as its own contract rather than assuming the C API surface is available unchanged.

The sqlite3 CLI intentionally behaves differently

The official command-line shell enables extension loading as part of its setup, so its .load dot-command can load a trusted extension without an extra enable step. That convenience is for an interactive tool; do not infer that an application connection should enable loading globally.

text · CLI syntax — trusted local file only
-- Dot-command, not SQL:.load ./approved_extension-- Optional non-default entry point:.load ./approved_extension custom_entry_point

The course intentionally does not provide an arbitrary extension download URL. The safe lesson is the loading mechanism and trust process, not “run this unknown binary.”

Capability detection comes before loading

Many useful features may already be available in the exact SQLite library. Loading duplicate or unnecessary native code expands deployment risk without benefit.

sql · capability checklist SQL
SELECT sqlite_version() AS sqlite_version;SELECT nameFROM pragma_module_listWHERE name IN (  'fts5','fts5vocab','rtree','rtree_i32','dbstat',  'json_each','json_tree')ORDER BY name;SELECT compile_optionsFROM pragma_compile_optionsWHERE compile_options LIKE '%FTS%'   OR compile_options LIKE '%RTREE%'   OR compile_options LIKE '%DBSTAT%'   OR compile_options LIKE '%SESSION%'   OR compile_options LIKE '%OMIT_LOAD_EXTENSION%'ORDER BY compile_options;

Survey official/bundled capabilities without promising universality

Capability How it may be delivered What it is for Detection idea
FTS5 Built/compiled into many SQLite libraries; can also be built as extension Full-text search module_list + controlled CREATE VIRTUAL TABLE probe
RTree Compile-time extension in many builds Multidimensional range/bounding boxes module_list for rtree/rtree_i32
dbstat Requires SQLITE_ENABLE_DBSTAT_VTAB Read-only page/storage introspection module_list / compile_options
session Optional extension/compile capability Changesets/session tracking compile_options/API capability; not exposed as a normal SQL table by default
CSV Official extension source exists, often not built into generic host libraries Expose CSV as virtual table Do not assume present; package explicitly if your application needs it

Optional local lab: prove loading is disabled before trusting anything

Python's standard sqlite3 wrapper may expose enable_load_extension() when its underlying library supports loadable extensions. This lab does not load a binary. It only records runtime capabilities and demonstrates the application-side control surface.

python · safe Python capability probe
import sqlite3con = sqlite3.connect(":memory:")print("SQLite:", sqlite3.sqlite_version)print("modules:", [r[0] for r in con.execute("PRAGMA module_list")])print("has enable_load_extension:", hasattr(con, "enable_load_extension"))# Do not call load_extension() with an unreviewed path.con.close()

On the generation environment, Python is linked to SQLite 3.46.1 and exposes enable_load_extension(); its module list already includes FTS5, RTree, dbstat, JSON traversal modules, and others, so no external binary is needed for this chapter's labs.

Portability cost: the database file can outlive the extension environment

An ordinary SQLite database file is famously portable, but a schema can contain virtual tables whose modules are unavailable on another host. Opening the file may still be possible, yet statements touching those objects can fail because the module is missing. A deployment package therefore needs both the database schema and the executable capabilities it depends on.

Dependency question What to record
Which SQLite library actually runs? Runtime version/source id and driver package version.
Which modules must exist? FTS5/RTree/custom module names and feature options.
How are they supplied? Built-in, statically linked, or approved runtime shared library.
Which platforms are supported? OS, CPU architecture, ABI/toolchain constraints.
How is integrity/provenance verified? Controlled build source, package signature/hash policy, release process.
What is the fallback? Disable optional feature, migrate schema, or fail startup clearly—never silently corrupt semantics.

Do not let SQL choose arbitrary filesystem paths

If application users can submit SQL, leaving the SQL load_extension() function enabled can turn SQL input into native-library loading. SQLite's explicit enablement model exists for a reason. Keep extension policy in trusted application/deployment code, not in an arbitrary user query channel.

Production policy

Enable runtime loading only for the narrowest trusted initialization window, use allowlisted paths/artifacts, load what is required, then disable loading again where the driver/API permits. If runtime loading is unnecessary, leave it disabled.

Failure cases and diagnosis

Symptom Likely class of cause Safe next step
no such module: fts5 Host library lacks/has not registered FTS5 Inspect sqlite_version, module_list, compile options, and driver bundling.
not authorized / extension loading disabled API deliberately blocks loading Decide whether product policy actually permits native extensions; do not bypass casually.
cannot open shared object / DLL Path, packaging, architecture, dependency issue Verify approved artifact path, CPU/OS ABI, dependent libraries, and permissions.
undefined symbol / entry point Extension/runtime ABI or entry-point mismatch Use the extension's documented build/entry point for the target SQLite/platform.
Works in CLI, fails in app Different SQLite library/build or loading policy Compare runtime versions/modules inside each process, not installed file names.

Production capability checklist

Extension readiness review

Use this as a deployment gate before Chapter 15 application integration.

  1. Have we recorded the SQLite library version that the application process actually uses?
  2. Are every required virtual-table/function capabilities detected at startup or during compatibility tests?
  3. Is runtime extension loading actually necessary, or can approved capabilities be built/static-linked instead?
  4. If loading is necessary, are artifacts provenance-controlled, versioned, architecture-matched, and allowlisted?
  5. Does untrusted SQL remain unable to select arbitrary extension paths?
  6. Have CI/release tests opened representative databases on every supported runtime and exercised extension-dependent features?
Review the answers

A production-ready design knows the exact runtime, required capabilities, delivery method, trust policy, and compatibility test matrix. Runtime loading should be a deliberate deployment decision, not a query-time convenience. If a feature is optional, failure behavior should be explicit; if it is required, startup should fail clearly rather than operating with changed semantics.

Chapter recap: SQLite's extension model has three responsibilities

Semantic responsibility: know what the module actually indexes or exposes. Operational responsibility: maintain structures such as external-content FTS indexes and benchmark their costs. Security responsibility: treat native extensions as executable dependencies with a supply chain. With those boundaries established, Chapter 15 can safely move from shell labs into application drivers, connections, prepared statements, and binding.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.