fukamachi / cl-dbi

Database independent interface for Common Lisp
202 stars 28 forks source link
common-lisp database

CL-DBI - Database-independent interface for Common Lisp

Build Status

Usage

Connecting - MYSQL

(defvar *connection*
  (dbi:connect :mysql
               :database-name "test"
               :username "nobody"
               :password "1234"))

Connecting - SQLite

 (defvar *connection*
  (dbi:connect :sqlite3
               :database-name "/home/gt/test.sqlite3"))

Executing a query

(let* ((query (dbi:prepare *connection*
                           "SELECT * FROM somewhere WHERE flag = ? OR updated_at > ?"))
       (query (dbi:execute query (list 0 "2011-11-01"))))
  (loop for row = (dbi:fetch query)
        while row
        ;; process "row".
        ))

;; Do it all at once
(dbi:fetch-all (dbi:execute (dbi:prepare *connection* "SELECT * FROM somewhere WHERE flag = ? OR updated_at > ?")
                            (list 0 "2011-11-01")))

dbi:do-sql is another option that prepares and executes a single statement. It returns the number of rows affected. It's typically used for non-SELECT statements.

(dbi:do-sql *connection*
            "INSERT INTO somewhere (flag, updated_at) VALUES (?, NOW())"
            (list 0))
;=> 1

Breaking change warning: cl-dbi prior to march 2020 did not pass arguments with list:

(dbi:execute query 0 "2011-11-01")
;; is now:
(dbi:execute query (list 0 "2011-11-01")

The version in Quicklisp 2020-03-25 is incompatible with older code.

Using dbi:with-connection to ensure connections are closed

(dbi:with-connection (conn :sqlite3 :database-name "/home/fukamachi/test.db")
  (let* ((query (dbi:prepare conn "SELECT * FROM People"))
         (query (dbi:execute query)))
    (loop for row = (dbi:fetch query)
          while row
          do (format t "~A~%" row))))

Connection pooling

dbi:connect-cached returns a existing connection if the database is already connected. Since one cache will be created for each thread, it's safe to use in a multithread application.

Description

CL-DBI provides a uniform interface for many SQL databases, so you need not learn a separate API for each database.

This library is especially convenient when you want to use different databases in different environments. For example, you might use MySQL as a production database, but use SQLite3 on your development system. To switch database backends you need only change the arguments to dbi:connect.

Databases

Installation

This library is available on Quicklisp.

CL-USER> (ql:quickload :cl-dbi)
To load "cl-dbi":
  Load 1 ASDF system:
    cl-dbi
; Loading "cl-dbi"

(:CL-DBI)

cl-dbi will load another system on the fly depending on your database's driver:

:dbd-sqlite3
:dbd-mysql
:dbd-postgres

You must reference the required one in your system definition if you plan to build an executable (and if you plan to run it on a machine where Quicklisp is not installed).

API

User-Level API

Driver-Level API

Creating a new driver

Create a subclass of <dbi-driver> and implement following methods.

These methods can be overriden if needed.

Hook of SQL execution

CL-DBI provides dbi:*sql-execution-hooks*, a hook to run for each SQL execution, particularly used for logging.

The hook function takes these 4 values:

The row count and its execution time can be null, if those values are not available for the driver for some reason.

dbi:simple-sql-logger is also provided for printing those values directly to *standard-output*. It can be enabled as so:

(push #'dbi:simple-sql-logger dbi:*sql-execution-hooks*)

Development

Running all tests in the Docker

This will not require you to install Postgres or Mysql. All you need is Docker and Docker Compose.

To run all tests, execute this in the shell:

docker compose up tests

Running specific driver's unittests

Running tests with 'docker compose' does not allow you to debug code in SLIME or SLY. To do this, you need to start databases as separate containers and to make their ports available to the host machine.

Here is how you can start Postgres and Mysql in Docker and run unittests agains them:

Changelog

2020-03

(dbi:execute query 0 "2011-11-01")
;; is now:
(dbi:execute query (list 0 "2020-03-13")

The version in Quicklisp 2020-03-25 is incompatible with older code.

Author

Copyright

Copyright (c) 2011 Eitaro Fukamachi (e.arrows@gmail.com)

License

Licensed under the BSD 2-Clause License.