f7ad73357ed9 default tip

Add some documentation
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Thu, 11 Apr 2024 08:39:29 -0400
parents 99142dcb072d
children (none)
branches/tags default tip
files docs/01-usage.markdown docs/02-reference.markdown docs/03-changelog.markdown src/main.lisp

Changes

--- a/docs/01-usage.markdown	Tue Apr 09 14:01:57 2024 -0400
+++ b/docs/01-usage.markdown	Thu Apr 11 08:39:29 2024 -0400
@@ -6,3 +6,92 @@
 round-trip, and DBvolve intentionally does not support backwards migrations.
 
 [TOC]
+
+## Overview
+
+DBvolve is a relatively light-weight schema update mechanism for SQL databases.
+Updates ("evolutions") are raw SQL files stored in a single directory with
+a particular naming scheme, e.g.:
+
+    evolutions/
+        000-initial-tables.sql
+        001-add-products-table.sql
+        002-insert-more-product-types.sql
+        003-merge-name-fields.sql
+
+When run, DBvolve will ensure that a table called `dbvolve` exists in your
+database to track which evolutions have been applied. This table will be locked
+before running any evolutions to ensure that multiple clients can attempt to run
+evolutions at the same time, e.g. when restarting a web server.
+
+DBvolve will compare the list of already-run evolutions from the `dbvolve` table
+to the evolution files found in the directory and run any with a higher ID than
+the last currently-applied evolution.
+
+A transaction will be used when running these evolutions.  If any evolutions
+fail to apply, the transaction will be rolled back.  Note that this is *one*
+transaction for *all* pending evolutions, so if you have three evolutions to run
+and the second evolution fails, *none* of the three will be applied.
+
+You must not begin, commit, or rollback transactions in your evolutions.  This
+is not checked.
+
+There is intentionally no support for backwards evolutions.  If you need to undo
+something, write a new evolution and roll forward.
+
+## Installation/Loading
+
+The core `dbvolve` system contains only the main API.  You will also need to
+load a second `dbvolve/…` system that adds support for your particular database
+client.  Currently the following database clients are supported:
+
+* `dbvolve/postmodern`
+* `dbvolve/sqlite`
+
+So if you want to e.g. use DBvolve to run schema evolutions against a Postgres
+database, your project's `.asd` might look something like this:
+
+    :depends-on (… :dbvolve :dbvolve/postmodern)
+
+## Evolution Files
+
+All schema evolutions must be kept in a single directory.  Any files ending in
+`sql` or `dbvolve` (reserved for future use) will be considered to be
+evolutions, and must have names of the form `<integer>-<name>.sql`, e.g.:
+
+    evolutions/
+        000-initial-tables.sql
+        001-add-products-table.sql
+        002-insert-more-product-types.sql
+        003-merge-name-fields.sql
+
+Evolution IDs must start from 0 and there must be no gaps or duplicates (DBvolve
+will verify this before running).
+
+## API
+
+The main API is the `dbvolve:evolve` function, which takes a database client
+object and a path to an evolutions directory:
+
+    (defvar *db* (sqlite:connect "db.sqlite"))
+
+    (dbvolve:evolve *db* "path/to/evolutions/")
+
+`evolve` is a generic function that will dispatch to the appropriate method
+based on the class of the database client object it receives — you must ensure
+that the appropriate DBvolve client support system (e.g. `dbvolve/sqlite`) has
+been loaded in advance.
+
+This single function is pretty much the extent of the API.  How you want to use
+it is up to you.  One way might be to write a little wrapper script to invoke it
+manually when you need to.  Or if you're writing a web application server, you
+could have the server run `evolve` every time it starts up — the table locking
+will ensure that servers will see a consistent database state even if you
+restart many of them at the same time.
+
+## New Clients
+
+If you want to add support for another database client, look at the code in
+`src/postmodern.lisp` and `src/sqlite.lisp` to get an idea of what you need to
+do.  If it's a relatively common database client that others might find useful
+please consider sending a pull request.
--- a/docs/02-reference.markdown	Tue Apr 09 14:01:57 2024 -0400
+++ b/docs/02-reference.markdown	Thu Apr 11 08:39:29 2024 -0400
@@ -12,3 +12,20 @@
 
 ## Package `DBVOLVE`
 
+### `EVOLVE` (function)
+
+    (EVOLVE DATABASE EVOLUTIONS-PATH)
+
+Run evolutions at `evolutions-path` against `database`.
+
+  `database` must be a database client object for a supported database client
+  system.  Supporting systems (e.g. `dbvolve/sqlite`) must already be loaded.
+
+  Examples:
+
+    (ql:quickload '(:dbvolve :dbvolve/sqlite))
+    (defvar *db* (sqlite:connect "foo.sqlite"))
+    (evolve *db* "path/to/evolutions"))
+
+  
+
--- a/docs/03-changelog.markdown	Tue Apr 09 14:01:57 2024 -0400
+++ b/docs/03-changelog.markdown	Thu Apr 11 08:39:29 2024 -0400
@@ -5,3 +5,6 @@
 
 [TOC]
 
+## 1.0.0
+
+Initial version with sqlite and Postgres support.
--- a/src/main.lisp	Tue Apr 09 14:01:57 2024 -0400
+++ b/src/main.lisp	Thu Apr 11 08:39:29 2024 -0400
@@ -121,6 +121,18 @@
        evolutions))
 
 (defun evolve (database evolutions-path)
+  "Run evolutions at `evolutions-path` against `database`.
+
+  `database` must be a database client object for a supported database client
+  system.  Supporting systems (e.g. `dbvolve/sqlite`) must already be loaded.
+
+  Examples:
+
+    (ql:quickload '(:dbvolve :dbvolve/sqlite))
+    (defvar *db* (sqlite:connect \"foo.sqlite\"))
+    (evolve *db* \"path/to/evolutions\"))
+
+  "
   (let* ((path (uiop:parse-native-namestring evolutions-path :ensure-directory t))
          (evolutions (find-evolutions path)))
     (let ((n (length evolutions)))