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 shapeHow code reaches the processExampleOperational implication
Core/built into SQLite libraryCompiled into the libraryJSON support in modern SQLiteTravels with that library build.
Optional feature compiled inCompile-time option or source integrationFTS5/RTree/dbstat/session depending on buildMust verify the actual host build.
Statically linked extensionApplication/library link stepOfficial extension source integrated into productVersioned and reviewed with the application binary.
Runtime loadable extensionShared library/DLL loaded after connection opensApproved custom moduleNative 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

CapabilityHow it may be deliveredWhat it is forDetection idea
FTS5Built/compiled into many SQLite libraries; can also be built as extensionFull-text searchmodule_list + controlled CREATE VIRTUAL TABLE probe
RTreeCompile-time extension in many buildsMultidimensional range/bounding boxesmodule_list for rtree/rtree_i32
dbstatRequires SQLITE_ENABLE_DBSTAT_VTABRead-only page/storage introspectionmodule_list / compile_options
sessionOptional extension/compile capabilityChangesets/session trackingcompile_options/API capability; not exposed as a normal SQL table by default
CSVOfficial extension source exists, often not built into generic host librariesExpose CSV as virtual tableDo 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 questionWhat 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

SymptomLikely class of causeSafe next step
no such module: fts5Host library lacks/has not registered FTS5Inspect sqlite_version, module_list, compile options, and driver bundling.
not authorized / extension loading disabledAPI deliberately blocks loadingDecide whether product policy actually permits native extensions; do not bypass casually.
cannot open shared object / DLLPath, packaging, architecture, dependency issueVerify approved artifact path, CPU/OS ABI, dependent libraries, and permissions.
undefined symbol / entry pointExtension/runtime ABI or entry-point mismatchUse the extension's documented build/entry point for the target SQLite/platform.
Works in CLI, fails in appDifferent SQLite library/build or loading policyCompare 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.