cl-cookbook icon indicating copy to clipboard operation
cl-cookbook copied to clipboard

Database Access and Persistence: How to handle deletion of joins?

Open foretspaisibles opened this issue 7 years ago • 1 comments

I am currently learning CLSQL and wonder what is the best way to handle deletions of joins. As an example, assume a user account is to be deleted in some random application and we want to delete all the data connected to it. I see the description on mito does not say anything about this and bridging this gap could be a very interesting improvement.

Assume the account has a one-to-many relationship to users within an account and messages within an account. The account implementation should not be aware of the details of message and users (as other data records can be there or added).

It seems that the object system is not useful here because decorating the deletion of account objects does not combine. (?) For instance if “message” code says (defmethod database-package:delete :before ((a account)) (delete-all-messages-related-to account)) and the “user” code (defmethod database-package:delete :before ((a account)) (delete-all-users-related-to account)) then we get only one of the effects. It also makes it hard or impossible to correctly order that additional behaviour.

A natural approach seems to use hooks

;;;
;;; Event Hooks
;;;

(defun make-hook ()
  "Create a fesh new hook holding callbacks."
  nil)

(defmacro hook-add (hook keyword callback &key after before)
  "Add or replace a CALLBACK identified by KEYWORD to CALLBACK."
  `(access:set-plist-val!
    (list :callback ,callback
          :after ,after
          :before ,before)
    ,keyword ,hook))

(defmacro hook-rem (hook keyword)
  "Remove the CALLBACK identified by KEYWORD from CALLBACK.

It is a no-op if KEYWORD is not bound in CALLBACK."
  `(access:rem-plist-val! ,keyword ,hook))


(defun hook-run/order (hook)
  "Sort hooks in the right order before executing them."
  (loop
    :for (callback-name callback-spec . rest) :on hook :by #'cddr
    :when (or (getf callback-spec :after) (getf callback-spec :before))
    :do (error "hook-run/order: Real ordering not implemented.")
    :collect (getf callback-spec :callback)))

(defun hook-run (hook)
  "Run the given HOOK."
  (loop :for callback :in (hook-run/order hook)
        :do (funcall callback)))

It is then easy to define a hook account-delete-hook (or maybe before/after hooks for that event) and just extend the deletion account method to run that hook. If this is a good approach, it could be worth mentioning it in the paragraph about object deletions.

foretspaisibles avatar Jun 14 '18 05:06 foretspaisibles

the deletion of account objects does not combine. (?)

Can you not do both actions in the same :before method ?

Anyway +1 for deletion of joins. I will do it the simple way, a user function that deletes what's needed step by step, nothing fancy, magic nor difficult. That may be a feature request for Mito.

vindarel avatar Jun 14 '18 13:06 vindarel