These functions must be implemented or stubbed by all data stores.
These are supporting functions and variables for implementing transactions.
;; Designer considerations: ;; - with-transaction passes *current-transaction* or the user parameter to execute-transaction ;; in the parent keyword argument. Backends allowing nested transactions can treat the transaction ;; as a parent, otherwise they can reuse the current transaction by ignoring it (inheriting the dynamic ;; value of *current-transaction*) or rebinding the dynamic context (whatever makes coding easier). ;; - ensure-transaction uses *current-transaction* to determine if there is a current transaction ;; in progress (not null). If so, it jumps to the body directly. Otherwise it executes the body in a ;; new transaction by calling ... ;; - execute-transaction contract: ;; - Backends must dynamically bind *current-transaction* to a meaningful identifier for the ;; transaction in progress and execute the provided closure in that context ;; - All non-local exists result in an abort; only regular return values result in a commit ;; - If a transaction is aborted due to a deadlock or read conflict, execute-transaction should ;; automatically retry with an appropriate default amount ;; - execute-transaction can take any number of backend-defined keywords, although designers should ;; make sure there are no semantic conflicts if there is a name overlap with existing backends ;; - A typical design approach is to make sure that the most primitive interfaces to the backend ;; database look at *current-transaction* to determine whether a transaction is active. Users code can also ;; access this parameter to check whether a transaction is active.