Bugfix: make it possible to close the DB connection

This commit is contained in:
Tomek Kurcz 2017-11-28 14:05:40 +01:00
parent 19b9f1baa3
commit 67f6d4dc9e

View file

@ -19,8 +19,16 @@
:remove-session)) :remove-session))
(in-package :lack.middleware.session.store.dbi) (in-package :lack.middleware.session.store.dbi)
(defmacro with-db-connection (connection store &body body)
`(let ((,connection (funcall (dbi-store-connector ,store))))
(unwind-protect
(progn ,@body)
(when (dbi-store-disconnector ,store)
(funcall (dbi-store-disconnector ,store) ,connection)))))
(defstruct (dbi-store (:include store)) (defstruct (dbi-store (:include store))
(connector nil :type function) (connector nil :type function)
(disconnector nil)
(serializer (lambda (data) (serializer (lambda (data)
(usb8-array-to-base64-string (usb8-array-to-base64-string
(string-to-utf-8-bytes (prin1-to-string (marshal data)))))) (string-to-utf-8-bytes (prin1-to-string (marshal data))))))
@ -31,20 +39,20 @@
(table-name "sessions")) (table-name "sessions"))
(defmethod fetch-session ((store dbi-store) sid) (defmethod fetch-session ((store dbi-store) sid)
(let* ((conn (funcall (dbi-store-connector store))) (with-db-connection conn store
(query (dbi:prepare conn (let* ((query (dbi:prepare conn
(format nil "SELECT session_data FROM ~A WHERE id = ?" (format nil "SELECT session_data FROM ~A WHERE id = ?"
(dbi-store-table-name store)))) (dbi-store-table-name store))))
(result (dbi:fetch (dbi:execute query sid)))) (result (dbi:fetch (dbi:execute query sid))))
(if result (if result
(handler-case (funcall (dbi-store-deserializer store) (getf result :|session_data|)) (handler-case (funcall (dbi-store-deserializer store) (getf result :|session_data|))
(error (e) (error (e)
(warn "Error (~A) occured while deserializing a session. Ignoring.~2% Data:~% ~A~2% Error:~% ~A" (warn "Error (~A) occured while deserializing a session. Ignoring.~2% Data:~% ~A~2% Error:~% ~A"
(class-name (class-of e)) (class-name (class-of e))
(getf result :|session_data|) (getf result :|session_data|)
e) e)
nil)) nil))
nil))) nil))))
(defun current-timestamp () (defun current-timestamp ()
(multiple-value-bind (sec min hour date month year) (multiple-value-bind (sec min hour date month year)
@ -54,36 +62,37 @@
hour min sec))) hour min sec)))
(defmethod store-session ((store dbi-store) sid session) (defmethod store-session ((store dbi-store) sid session)
(let ((conn (funcall (dbi-store-connector store))) (with-db-connection conn store
(serialized-session (funcall (dbi-store-serializer store) session))) (let ((serialized-session (funcall (dbi-store-serializer store) session)))
(dbi:with-transaction conn (dbi:with-transaction conn
(let* ((query (dbi:prepare conn (let* ((query (dbi:prepare conn
(format nil "SELECT session_data FROM ~A WHERE id = ?" (format nil "SELECT session_data FROM ~A WHERE id = ?"
(dbi-store-table-name store)))) (dbi-store-table-name store))))
(current-session (getf (dbi:fetch (dbi:execute query sid)) :|session_data|))) (current-session (getf (dbi:fetch (dbi:execute query sid)) :|session_data|)))
(cond (cond
;; Session exists but not changed ;; Session exists but not changed
((equal current-session serialized-session)) ((equal current-session serialized-session))
;; Session exists and is going to be changed ;; Session exists and is going to be changed
(current-session (current-session
(dbi:do-sql conn (dbi:do-sql conn
(format nil "UPDATE ~A SET session_data = ?~:[~*~;, updated_at = '~A'~] WHERE id = ?" (format nil "UPDATE ~A SET session_data = ?~:[~*~;, updated_at = '~A'~] WHERE id = ?"
(dbi-store-table-name store) (dbi-store-table-name store)
(dbi-store-record-timestamps store) (dbi-store-record-timestamps store)
(current-timestamp)) (current-timestamp))
serialized-session serialized-session
sid)) sid))
;; New session ;; New session
(t (t
(dbi:do-sql conn (format nil "INSERT INTO ~A (id, session_data~:[~;, created_at, updated_at~]) VALUES (?, ?~:*~:[~*~;, '~A', ~:*'~A'~])" (dbi:do-sql conn (format nil "INSERT INTO ~A (id, session_data~:[~;, created_at, updated_at~]) VALUES (?, ?~:*~:[~*~;, '~A', ~:*'~A'~])"
(dbi-store-table-name store) (dbi-store-table-name store)
(dbi-store-record-timestamps store) (dbi-store-record-timestamps store)
(current-timestamp)) (current-timestamp))
sid sid
serialized-session))))))) serialized-session))))))))
(defmethod remove-session ((store dbi-store) sid) (defmethod remove-session ((store dbi-store) sid)
(dbi:do-sql (funcall (dbi-store-connector store)) (with-db-connection conn store
(format nil "DELETE FROM ~A WHERE id = ?" (dbi:do-sql conn
(dbi-store-table-name store)) (format nil "DELETE FROM ~A WHERE id = ?"
sid)) (dbi-store-table-name store))
sid)))