{"id":18077596,"url":"https://github.com/fukamachi/cl-dbi","last_synced_at":"2025-07-13T04:34:58.366Z","repository":{"id":2461557,"uuid":"2635521","full_name":"fukamachi/cl-dbi","owner":"fukamachi","description":"Database independent interface for Common Lisp","archived":false,"fork":false,"pushed_at":"2025-06-18T21:15:40.000Z","size":278,"stargazers_count":220,"open_issues_count":25,"forks_count":28,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-06-18T21:33:12.873Z","etag":null,"topics":["common-lisp","database"],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fukamachi.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2011-10-24T10:42:39.000Z","updated_at":"2025-05-28T12:54:05.000Z","dependencies_parsed_at":"2023-02-16T19:00:54.580Z","dependency_job_id":"60270a36-6208-45ab-a3d5-730dc745d729","html_url":"https://github.com/fukamachi/cl-dbi","commit_stats":{"total_commits":220,"total_committers":15,"mean_commits":"14.666666666666666","dds":0.1454545454545455,"last_synced_commit":"32b97953a43ba4fe947a9260fd96a77077eab06d"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/fukamachi/cl-dbi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fukamachi%2Fcl-dbi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fukamachi%2Fcl-dbi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fukamachi%2Fcl-dbi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fukamachi%2Fcl-dbi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fukamachi","download_url":"https://codeload.github.com/fukamachi/cl-dbi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fukamachi%2Fcl-dbi/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265088991,"owners_count":23709627,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["common-lisp","database"],"created_at":"2024-10-31T11:46:03.235Z","updated_at":"2025-07-13T04:34:58.296Z","avatar_url":"https://github.com/fukamachi.png","language":"Common Lisp","funding_links":[],"categories":["Expert Systems"],"sub_categories":[],"readme":"# CL-DBI - Database-independent interface for Common Lisp\n\n[![Build Status](https://github.com/fukamachi/cl-dbi/workflows/CI/badge.svg)](https://github.com/fukamachi/cl-dbi/actions?query=workflow%3ACI)\n\n## Usage\n\n### Connecting - MYSQL\n\n```common-lisp\n(defvar *connection*\n  (dbi:connect :mysql\n               :database-name \"test\"\n               :username \"nobody\"\n               :password \"1234\"))\n```\n\n### Connecting - SQLite\n```common-lisp\n (defvar *connection*\n  (dbi:connect :sqlite3\n               :database-name \"/home/gt/test.sqlite3\"))\n```\n\n### Executing a query\n\n```common-lisp\n(let* ((query (dbi:prepare *connection*\n                           \"SELECT * FROM somewhere WHERE flag = ? OR updated_at \u003e ?\"))\n       (query (dbi:execute query (list 0 \"2011-11-01\"))))\n  (loop for row = (dbi:fetch query)\n        while row\n        ;; process \"row\".\n        ))\n\n;; Do it all at once\n(dbi:fetch-all (dbi:execute (dbi:prepare *connection* \"SELECT * FROM somewhere WHERE flag = ? OR updated_at \u003e ?\")\n                            (list 0 \"2011-11-01\")))\n```\n\n`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.\n\n```common-lisp\n(dbi:do-sql *connection*\n            \"INSERT INTO somewhere (flag, updated_at) VALUES (?, NOW())\"\n            (list 0))\n;=\u003e 1\n```\n\n**Breaking change warning**: cl-dbi prior to march 2020 did not pass arguments with `list`:\n\n    (dbi:execute query 0 \"2011-11-01\")\n    ;; is now:\n    (dbi:execute query (list 0 \"2011-11-01\")\n\nThe version in Quicklisp 2020-03-25 is incompatible with older code.\n\n\n### Using `dbi:with-connection` to ensure connections are closed\n\n```common-lisp\n(dbi:with-connection (conn :sqlite3 :database-name \"/home/fukamachi/test.db\")\n  (let* ((query (dbi:prepare conn \"SELECT * FROM People\"))\n         (query (dbi:execute query)))\n    (loop for row = (dbi:fetch query)\n          while row\n          do (format t \"~A~%\" row))))\n```\n\n### Connection pooling\n\n`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.\n\n## Description\n\nCL-DBI provides a uniform interface for many SQL databases, so you need not learn a separate API for each database.\n\nThis 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`.\n\n## Databases\n\n* SQLite3\n* PostgreSQL\n* MySQL\n\n## Installation\n\nThis library is available on [Quicklisp](https://www.quicklisp.org/).\n\n```common-lisp\nCL-USER\u003e (ql:quickload :cl-dbi)\nTo load \"cl-dbi\":\n  Load 1 ASDF system:\n    cl-dbi\n; Loading \"cl-dbi\"\n\n(:CL-DBI)\n```\n\ncl-dbi will load another system on the fly depending on your database's\ndriver:\n\n    :dbd-sqlite3\n    :dbd-mysql\n    :dbd-postgres\n\nYou must reference the required one in your system definition if you\nplan to build an executable (and if you plan to run it on a machine\nwhere Quicklisp is not installed).\n\n\n## API\n\n### User-Level API\n\n* connect [driver-name \u0026amp; params] =\u0026gt; \u0026lt;dbi-connection\u0026gt;\n* connect-cached [driver-name \u0026amp; params] =\u0026gt; \u0026lt;dbi-connection\u0026gt;\n* disconnect [\u0026lt;dbi-connection\u0026gt;] =\u0026gt; T or NIL\n* prepare [conn sql] =\u0026gt; \u0026lt;dbi-query\u0026gt;\n* prepare-cached [conn sql] =\u0026gt; \u0026lt;dbi-query\u0026gt;\n* execute [query \u0026amp;optional params] =\u0026gt; something\n* fetch [result] =\u0026gt; a row data as plist\n* fetch-all [result] =\u0026gt; a list of all row data\n* do-sql [conn sql \u0026amp;optional params]\n* list-all-drivers [] =\u0026gt; (\u0026lt;dbi-driver\u0026gt; ..)\n* find-driver [driver-name] =\u0026gt; \u0026lt;dbi-driver\u0026gt;\n* with-transaction [conn]\n* begin-transaction [conn]\n* commit [conn]\n* rollback [conn]\n* ping [conn] =\u0026gt; T or NIL\n* row-count [conn] =\u0026gt; a number of rows modified by the last executed INSERT/UPDATE/DELETE\n* with-connection [connection-variable-name \u0026body body]\n\n### Driver-Level API\n\n* \u0026lt;dbi-driver\u0026gt;\n* \u0026lt;dbi-connection\u0026gt;\n* make-connection [driver params]\n* disconnect [\u0026lt;dbi-connection\u0026gt;] =\u0026gt; T or NIL\n* prepare [conn sql] =\u0026gt; \u0026lt;dbi-query\u0026gt;\n* prepare-cached [conn sql] =\u0026gt; \u0026lt;dbi-query\u0026gt;\n* fetch-using-connection [conn result] =\u0026gt; a row data as plist\n* do-sql [conn sql \u0026amp;optional params]\n* execute-using-connection =\u0026gt; something\n* escape-sql =\u0026gt; string\n* begin-transaction [conn]\n* commit [conn]\n* rollback [conn]\n* ping [conn] =\u0026gt; T or NIL\n* row-count [conn] =\u0026gt; a number of rows modified by the last executed INSERT/UPDATE/DELETE\n* free-query-resources [query] free resources associated with a prepared query (this is required only for sqlite3 driver at the moment)\n\n## Creating a new driver\n\nCreate a subclass of \u0026lt;dbi-driver\u0026gt; and implement following methods.\n\n* make-connection\n* disconnect [\u0026lt;dbi-connection\u0026gt;] =\u0026gt; T or NIL\n* execute-using-connection\n\nThese methods can be overriden if needed.\n\n* prepare\n* fetch-using-connection\n* do-sql\n* escape-sql\n\n## Hook of SQL execution\n\nCL-DBI provides `dbi:*sql-execution-hooks*`, a hook to run for each SQL execution, particularly used for logging.\n\nThe hook function takes these 4 values:\n\n- SQL (string)\n- placeholder parameters (list)\n- Row count of the results (integer or null)\n- Took time in miliseconds (integer or null)\n\nThe row count and its execution time can be null, if those values are not available for the driver for some reason.\n\n`dbi:simple-sql-logger` is also provided for printing those values directly to `*standard-output*`. It can be enabled as so:\n\n```common-lisp\n(push #'dbi:simple-sql-logger dbi:*sql-execution-hooks*)\n```\n\n## Development\n\n### Running all tests in the Docker\n\nThis will not require you to install Postgres or Mysql.\nAll you need is Docker and Docker Compose.\n\nTo run all tests, execute this in the shell:\n\n    docker compose up tests\n\n### Running specific driver's unittests\n\nRunning tests with 'docker compose' does not allow you\nto debug code in SLIME or SLY. To do this, you need\nto start databases as separate containers and to make\ntheir ports available to the host machine.\n\nHere is how you can start Postgres and Mysql in Docker\nand run unittests agains them:\n\n* Start a docker container with the database\n\n  For example, with postgres:\n\n      docker run --rm -ti \\\n             -e POSTGRES_USER=cl-dbi \\\n             -e POSTGRES_PASSWORD=cl-dbi \\\n             -p 5432:5432 \\\n             postgres:10\n\n  Or with mysql:\n\n      docker run --rm -ti \\\n             --name cl-dbi \\\n             -e MYSQL_ROOT_PASSWORD=cl-dbi \\\n             -p 3306:3306 \\\n             mysql:8\n\n      docker exec -ti \\\n             cl-dbi \\\n             mysql -pcl-dbi \\\n                   -e 'create database if not exists `cl-dbi`'\n\n* Then in Lisp repl load the unittests:\n\n      (ql:quickload :dbi/test)\n      ;; Turn off colors if you are in the Emacs\n      (setf rove:*enable-colors* nil)\n      ;; Set this to debug failed test\n      (setf rove:*debug-on-error* t)\n\n* And start driver's unittests:\n\n  For postgres:\n\n      (dbi.test:run-driver-tests :postgres\n                                 :database-name \"postgres\"\n                                 :host \"localhost\"\n                                 :port 5432\n                                 :username \"cl-dbi\"\n                                 :password \"cl-dbi\")\n\n  For mysql:\n\n      ;; Probably you will need to load library manually if\n      ;; it was installed using Homebrew:\n      (push \"/usr/local/opt/mysql-client/lib/\" cffi:*foreign-library-directories*)\n      (cffi:load-foreign-library \"libmysqlclient.20.dylib\"\n                                 :search-path \"/usr/local/opt/mysql-client/lib/\")\n      (dbi.test:run-driver-tests :mysql\n                                 :database-name \"cl-dbi\"\n                                 :host \"127.0.0.1\"\n                                 :port 3306\n                                 :username \"root\"\n                                 :password \"cl-dbi\")\n\n  Also, you can run a single test like this:\n\n      (dbi.test:run-driver-tests :mysql\n                                 :database-name \"cl-dbi\"\n                                 :host \"127.0.0.1\"\n                                 :port 3306\n                                 :username \"root\"\n                                 :password \"cl-dbi\"\n                                 :test-name 'select-after-commit)\n\n## Changelog\n\n### 2020-03\n\n- **breaking change**: `dbi:execute` now takes its parameters as a\n  list (to avoid the call arguments limit, see [!61](https://github.com/fukamachi/cl-dbi/pull/61):\n\n```common-lisp\n(dbi:execute query 0 \"2011-11-01\")\n;; is now:\n(dbi:execute query (list 0 \"2020-03-13\")\n```\n\nThe version in Quicklisp 2020-03-25 is incompatible with older code.\n\n\n## Author\n\n* Eitaro Fukamachi (e.arrows@gmail.com)\n\n## Copyright\n\nCopyright (c) 2011 Eitaro Fukamachi (e.arrows@gmail.com)\n\n# License\n\nLicensed under the BSD 2-Clause License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffukamachi%2Fcl-dbi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffukamachi%2Fcl-dbi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffukamachi%2Fcl-dbi/lists"}