Friday, July 31, 2026

Announcing cl-firebird v1.0.0: Pure Common Lisp Driver with Full node-firebird Parity & Multi-Version CI Matrix

We are excited to announce the release of cl-firebird v1.0.0 — a pure Common Lisp database driver for Firebird 3.0, 4.0, 5.0, and 6.0+. Featuring full 1:1 feature parity with node-firebird, this release brings 12-factor connection URIs, thread-safe connection pooling, named placeholder parameter binding, custom type parsers (type-cast), streaming cursors, database events (POST_EVENT), Service Manager support, Firebird 6.0 tablespaces/schemas, and an automated GitHub Actions testing matrix.

Key Highlights

1. Pure Common Lisp Wire Protocol (Zero C / FFI Dependencies)

cl-firebird communicates directly over TCP sockets using Firebird's remote protocol (negotiating up to Protocol 20). It runs natively across ANSI Common Lisp implementations (SBCL, CCL, etc.) without requiring external C libraries or native shared objects (libfbclient).

2. 12-Factor Connection URIs & attach-or-create

Configure connections using standard URIs or traditional connection strings:

  • URI syntax: firebird://user:pass@host:port/database?pageSize=8192&lowercase-keys=true (including IPv6 [::1])
  • Traditional DSN: host/port:path
  • attach-or-create: Connects to an existing database or automatically creates it if it does not yet exist.

3. Named Placeholders & SQL Injection Protection

  • Named parameters (:name): Bind SQL parameters using property lists ((:name "Val")), association lists ((("name" . "Val"))), or hash-tables.
  • SQL Escaping (escape / escape-string): Protects against SQL injection across strings, numbers, booleans, dates, octet vectors, and NULLs.

4. Built-in Thread-Safe Connection Pooling

Manage connection lifecycles with pool.lisp:

  • Auto-reaping idle connections after configurable timeouts.
  • Safe queueing and slot recovery under heavy concurrency.
  • Live metrics getters: pool-total-count, pool-idle-count, pool-active-count, pool-waiting-count.

5. Custom Type Parsers (type-cast) & Statement Caching

  • type-cast: Pass a custom decoder function (lambda (col default-fn) ...) to format values per column type (e.g., converting INT64 or DATE fields into custom representations).
  • statement-cache-size: Transparently reuses prepared server-side statements per connection to eliminate redundant prepare round-trips.

6. High-Performance Streaming & Bulk Batch Execution

  • sequentially: Stream large query results row-by-row with minimal memory footprint.
  • execute-batch: Bulk parameter execution for high-throughput batch inserts and updates.

7. Firebird 6.0 Features & Protocol 20

  • Native support for Firebird 6.0 physical tablespaces (create-tablespace, alter-tablespace, drop-tablespace) and schemas (create-schema).
  • Session search paths (searchPath), default schemas (defaultSchema), and custom database ownership.

8. Database Events (POST_EVENT) & Service Manager

  • Database Events: attach-event / detach-event listener for PSQL POST_EVENT signals.
  • Service Manager: Administration API for backup/restore (.fbk), user management (service-add-user, service-get-users, etc.), trace sessions, and server diagnostics.

9. Comprehensive Testing & CI Matrix

Includes a 66-check FiveAM test suite and an automated GitHub Actions CI Matrix testing across Firebird 3.0, 4.0, 5.0, and 6.0-snapshot.

Code Examples

1. Connecting & Named Placeholder Queries

(use-package :cl-firebird)

(with-connection ("firebird://SYSDBA:masterkey@localhost:3050/employee?named-placeholders=true&lowercase-keys=true")
  (let ((users (query "SELECT id, name FROM users WHERE role = :role AND age > :age"
                      '(:role "admin" :age 25))))
    (dolist (u users)
      (format t "User: ~a (ID: ~a)~%" (getf u :name) (getf u :id)))))

2. Connection Pooling & Real-time Metrics

;; Create pool of up to 10 connections (minimum 2 idle, 30s timeout)
(defvar *pool* (create-pool 10 "firebird://SYSDBA:masterkey@localhost:3050/employee?min=2&idleTimeoutMillis=30000"))

(with-pooled-connection (conn *pool*)
  (query "SELECT * FROM employee"))

;; Inspect pool status
(format t "Active: ~a, Idle: ~a, Total: ~a~%"
        (pool-active-count *pool*)
        (pool-idle-count *pool*)
        (pool-total-count *pool*))

3. Custom Type Decoding (type-cast)

(defvar *conn*
  (connect "firebird://SYSDBA:masterkey@localhost:3050/employee"
           :type-cast (lambda (col default-fn)
                        (cond
                          ((eq (getf col :type-name) :int64)
                           (format nil "BIGINT-~a" (funcall default-fn)))
                          (t (funcall default-fn))))))

Links & Installation

  • GitHub Repository: mariuz/cl-firebird
  • Release Tag: v1.0.0
  • Quicklisp Load: (ql:quickload :cl-firebird)
  • Run Test Suite: sbcl --load test/run-matrix.lisp --quit

No comments: