1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
6 ;;;; Transaction support
8 ;;;; This file is part of CLSQL.
10 ;;;; CLSQL users are granted the rights to distribute and use this software
11 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
12 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
13 ;;;; *************************************************************************
15 (in-package #:clsql-base-sys)
17 (defclass transaction ()
18 ((commit-hooks :initform () :accessor commit-hooks)
19 (rollback-hooks :initform () :accessor rollback-hooks)
20 (status :initform nil :accessor transaction-status))) ; nil or :committed
22 (defun commit-transaction (database)
23 (when (and (transaction database)
24 (not (transaction-status (transaction database))))
25 (setf (transaction-status (transaction database)) :committed)))
27 (defun add-transaction-commit-hook (database commit-hook)
28 (when (transaction database)
29 (push commit-hook (commit-hooks (transaction database)))))
31 (defun add-transaction-rollback-hook (database rollback-hook)
32 (when (transaction database)
33 (push rollback-hook (rollback-hooks (transaction database)))))
35 (defmethod database-start-transaction (database)
36 (unless database (error 'clsql-no-database-error))
37 (unless (transaction database)
38 (setf (transaction database) (make-instance 'transaction)))
39 (when (= (incf (transaction-level database) 1))
40 (let ((transaction (transaction database)))
41 (setf (commit-hooks transaction) nil
42 (rollback-hooks transaction) nil
43 (transaction-status transaction) nil)
44 (execute-command "BEGIN" :database database))))
46 (defmethod database-commit-transaction (database)
47 (if (> (transaction-level database) 0)
48 (when (zerop (decf (transaction-level database)))
49 (execute-command "COMMIT" :database database)
50 (map nil #'funcall (commit-hooks (transaction database))))
51 (warn 'clsql-simple-warning
52 :format-control "Cannot commit transaction against ~A because there is no transaction in progress."
53 :format-arguments (list database))))
55 (defmethod database-abort-transaction (database)
56 (if (> (transaction-level database) 0)
57 (when (zerop (decf (transaction-level database)))
59 (execute-command "ROLLBACK" :database database)
60 (map nil #'funcall (rollback-hooks (transaction database)))))
61 (warn 'clsql-simple-warning
62 :format-control "Cannot abort transaction against ~A because there is no transaction in progress."
63 :format-arguments (list database))))
66 (defmacro with-transaction ((&key (database '*default-database*)) &rest body)
67 "Executes BODY within a transaction for DATABASE (which defaults to
68 *DEFAULT-DATABASE*). The transaction is committed if the body finishes
69 successfully (without aborting or throwing), otherwise the database is
71 (let ((db (gensym "db-")))
72 `(let ((,db ,database))
75 (database-start-transaction ,db)
77 (commit-transaction ,db))
78 (if (eq (transaction-status (transaction ,db)) :committed)
79 (database-commit-transaction ,db)
80 (database-abort-transaction ,db))))))
82 (defun commit (&key (database *default-database*))
83 "Commits changes made to DATABASE which defaults to *DEFAULT-DATABASE*."
84 (database-commit-transaction database))
86 (defun rollback (&key (database *default-database*))
87 "Rolls back changes made in DATABASE, which defaults to
88 *DEFAULT-DATABASE* since the last commit, that is changes made since
89 the last commit are not recorded."
90 (database-abort-transaction database))
92 (defun start-transaction (&key (database *default-database*))
93 "Starts a transaction block on DATABASE which defaults to
94 *default-database* and which continues until ROLLBACK or COMMIT are
96 (unless (in-transaction-p :database database)
97 (database-start-transaction database)))
99 (defun in-transaction-p (&key (database *default-database*))
100 "A predicate to test whether we are currently within the scope of a
101 transaction in DATABASE."
102 (and database (transaction database) (= (transaction-level database) 1)))