Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Backends

Ferrule speaks five database protocols. Four are on by default; Oracle is opt-in. Each is implemented in a feature-gated module under ferrule-core/src/backends/, and they all expose results through the unified Value type.

Backend matrix

BackendURL schemesFeature flagDriver
PostgreSQLpostgres, postgresqlpostgres (default)tokio-postgres + rustls
MySQLmysql, mariadbmysql (default)mysql_async
MSSQLmssql, sqlserver, tdsmssql (default)tiberius
SQLitesqlitesqlite (default)rusqlite (bundled)
Oracleoracleoracle (opt-in)oracle crate (ODPI-C)

To opt into Oracle, build with cargo install ferrule --features oracle and arrange Instant Client at runtime — see Troubleshooting.

C-free embedding (the ferrule-sql core)

The query core lives in the embeddable ferrule-sql crate (default = [], every backend opt-in). Embedders under a no-C-build-dependency policy can depend on ferrule-sql with Postgres + MySQL and link zero C system libraries:

ferrule-sql = { version = "*", default-features = false, features = ["postgres", "mysql"] }

On that feature set, TLS is rustls pinned to the pure-Rust ring crypto provider — rustls, tokio-rustls, and mysql_async are all configured default-features = false so the aws-lc-rs provider (and its cmake/cc aws-lc-sys build) never enters the graph; mysql_async uses default-rustls-ring, which also selects flate2’s pure-Rust miniz_oxide backend in place of libz-sys.

The accepted vendored-static floor is exactly two self-contained cc-built crates with no system-library linkage: ring (the rustls crypto floor) and zstd-sys (a hard, non-feature-gated dependency of mysql_common — its removal is tracked in issue #94).

The workspace deny.toml bans aws-lc-sys, openssl-sys, native-tls, and libz-sys, and cargo deny check enforces it on this surface. The two opt-in backends that fall outside the C-free floor are:

  • sqlite — bundles SQLite via a cc build (statically linked, no system library), so it is C-free in the no-system-linkage sense but is off the Postgres + MySQL floor above.
  • mssqltiberius 0.12’s stable release only offers native-tls, which links the platform OpenSSL; the mssql feature therefore links a C system library and is excluded from the deny.toml firewall graph.
  • oracle — needs the external ODPI-C Instant Client at runtime (see above); it is dlopen’d, not linked at build time.

The ssh tunnel feature is also opt-in; russh is pinned to the ring provider so it stays C-free, but it carries upstream RustSec advisories with no in-range fix, so it is excluded from the deny.toml graph (see the comments in deny.toml for the full rationale).

PostgreSQL

  • Pure Rust; no libpq required.
  • TLS via rustls (no OpenSSL dependency).
  • SSL modes: prefer, require, disable, verify-ca, verify-full.
  • Multi-statement batches supported;-separated statements in one call.
  • UUID, JSONB, arrays mapped to ferrule Value types natively.
  • Numeric / decimal preserved as Value::Decimal (string-backed) to avoid precision loss.
ferrule query "postgres://user:pass@host/db?sslmode=require" "SELECT 1;"

For TLS posture, verify-full checks the full chain and the hostname; require encrypts but doesn’t verify identity. See Security for guidance on which to use when.

MySQL

  • Pure Rust via mysql_async.
  • Works with MySQL 5.7+ and MariaDB 10.3+.
  • TLS handled by the driver; opt in via the URL or server config.
  • JSON column type maps to Value::Json.
  • ENUM columns map to Value::String (the variant name).
  • Multi-statement batches supported;-separated statements in one call via mysql_async native multi-result API.
ferrule query "mysql://root:pass@127.0.0.1:3306/mydb" "SELECT 1; SELECT 2;"

If you hit caching_sha2_password errors, see Troubleshooting.

MSSQL

  • Pure Rust via tiberius (TDS protocol — Microsoft’s wire format).
  • Supports SQL Authentication out of the box; Windows Authentication / Kerberos depends on platform support in tiberius.
  • Multi-statement batches supported.
  • DATETIMEOFFSETValue::DateTimeTz; BITValue::Bool; UNIQUEIDENTIFIERValue::Uuid.
  • No native JSON type — store JSON in NVARCHAR(MAX) and ferrule returns it as either Value::Json (if it parses) or Value::String.
ferrule query "mssql://sa:pass@host/db?trustServerCertificate=true" "SELECT 1;"

The trustServerCertificate=true query parameter accepts a self-signed cert only — that’s narrower than the global --insecure flag and the right choice for Docker test images that ship a self-signed cert.

SQLite

  • Statically linked via rusqlite with the bundled feature; no runtime library required.
  • File-based, in-memory (sqlite::memory:), or shared-cache (sqlite::memory:?cache=shared) variants supported.
  • Single-statement only at the ferrule layer; multi-statement scripts run via SQLite’s own exec from the driver, but ferrule doesn’t surface multi-statement batches.
  • Schema introspection uses pragma_table_info and sqlite_schema — see Schema Introspection.
ferrule query "sqlite::memory:" "SELECT 1;"
ferrule query "sqlite:///tmp/mydb.sqlite3" "SELECT * FROM users;"

The bundled SQLite version is whatever rusqlite ships with at build time — usually a few minor versions behind the latest.

Oracle

Opt-in only. Compiled in with cargo build --features oracle. At runtime, the oracle crate dynamically loads libclntsh.so (Linux / macOS) / oci.dll (Windows) from Oracle Instant Client. Without Instant Client present, the first connection fails with a ferrule diagnostic:

ferrule::connection
  × Oracle Instant Client (libclntsh.so) not found.
cargo install ferrule --features oracle
export LD_LIBRARY_PATH="$HOME/opt/oracle/instantclient_23_26:$LD_LIBRARY_PATH"

ferrule query "oracle://user:pass@host:1521/service" "SELECT * FROM dual;"

Schema notes:

  • No native BOOLEAN until 23c — most schemas use NUMBER(1).
  • No native UUID — use RAW(16) DEFAULT SYS_GUID().
  • JSON is CLOB with an IS JSON check constraint (12c+).

Setup details, including the libaio symlink workaround for Ubuntu 24.04+, live in Troubleshooting.

TLS posture summary

BackendTLS by default?Requires it?Self-signed dev cert
PostgreSQLNegotiated; client decides?sslmode=require (encrypt) / verify-full (full verify)?sslmode=require + --insecure
MySQLOff unless server demands; rustlsserver-side require_secure_transport--insecure
MSSQLAlways negotiated; cert often self-signed(default)?trustServerCertificate=true or --insecure
SQLiteN/A — local file or memory
OracleServer-configured (TNS)TNS listener configTNS-side, not URL

--insecure disables both certificate-chain verification and hostname verification globally for the call. The narrower MSSQL query parameter is preferred where it applies.

Multi-statement support

BackendBatch via ;Notes
PostgreSQLFirst-class; result sets and DML row counts both reported
MySQLVia mysql_async multi-result API
MSSQLVia TDS row-set framing
SQLiteUse a script via --file if needed; one statement per query call
OracleSemicolon-split with PL/SQL block awareness; BEGIN … END, IF … END IF, LOOP … END LOOP, and CASE … END CASE are kept intact

When using batches, remember --limit and --offset are not allowed — see Querying for the workaround.

Type mapping

Ferrule maps every backend’s native types to a single Value enum. This table is the canonical home — it’s referenced from Concepts, Querying, and the rest of the docs.

Ferrule ValuePostgresMySQLMSSQLSQLiteOracle
BoolBOOLEANBOOLEAN (TINYINT(1))BITINTEGERNUMBER(1)
Int64BIGINTBIGINTBIGINTINTEGERNUMBER
Float64DOUBLE PRECISIONDOUBLEFLOATREALBINARY_FLOAT
DecimalNUMERICDECIMALDECIMALNUMERICNUMBER
StringTEXT / VARCHARVARCHARNVARCHARTEXTVARCHAR2
BytesBYTEABLOBVARBINARYBLOBRAW
DateDATEDATEDATETEXT (ISO 8601)DATE
DateTimeTIMESTAMPDATETIMEDATETIME2TEXT (ISO 8601)TIMESTAMP
DateTimeTzTIMESTAMPTZTIMESTAMPDATETIMEOFFSETTEXT (ISO 8601)TIMESTAMP WITH TIME ZONE
JsonJSONB / JSONJSONNVARCHAR(MAX) (JSON-shaped)TEXTCLOB (with IS JSON)
UuidUUIDCHAR(36)UNIQUEIDENTIFIERTEXTRAW(16)
ArrayT[] (any)(n/a — pass JSON)(n/a — pass JSON)(n/a)(n/a — pass JSON)

Types that don’t have a clean Value slot (Postgres ranges, custom composite types, MySQL SET, etc.) fall back to Value::String with the driver’s native rendering.