clsqlがとまる

clsqlについては
http://d.hatena.ne.jp/yuki_neko_nyan/searchdiary?word=clsql
がすごくよくまとまっている。参考になる。


CLOSを用いた簡易O/R Mapperのような機能をつかい、セレクタマクロを書いて、dbからレコードを取得していた。

(defmacro db-select-1 (db slot target)
  `(car (clsql:select ,db :where (clsql:sql-= (sql-slot-value ,db ,slot) ,target) :flatp t)))
(defmacro $d (db slot target) `(db-select-1 ,db ,slot ,target))
($d 'user 'id 0)
-> <USER>

しかし、長時間アクセスがないと、dbがとまってしまうようだ。

($d 'user 'id 0)
; Error 2006 / MySQL server has gone away
;  has occurred.
;  [Condition of type CLSQL-SYS:SQL-DATABASE-DATA-ERROR]

これでは、webなどつくったとき、すぐ止まってしまってつかいものにならない。


とりあえず以下のようにした。

(defun retry-sql-access (condition)
  (let ((*connect-if-exists* :new))
    (when (= (clsql:sql-error-error-id condition) 2006)
      (clsql:reconnect :database (clsql:sql-error-database condition))
      (invoke-restart 'retry-sql-access))))

(defmacro db-select-1 (db slot target)
  (let ((sel `(clsql:select ,db :where (clsql:sql-= (sql-slot-value ,db ,slot) ,target) :flatp t)))
    `(car (handler-bind ((clsql-sys:sql-database-data-error #'retry-sql-access))
	    (restart-case ,sel (retry-sql-access () ,sel))))))

(defmacro $d (db slot target) `(db-select-1 ,db ,slot ,target))

sqlエラーが出た際、スタックを巻き戻し、再接続してから処理を再起動する。
もっとよい方法もあるのだろうか。


ちなみにほかのマクロ

  • 条件に合う複数のdbレコードを取得
(defmacro db-select (db slot target)
  (let ((sel `(clsql:select ,db :where (clsql:sql-= (sql-slot-value ,db ,slot) ,target) :flatp t)))
    `(handler-bind ((clsql-sys:sql-database-data-error #'retry-sql-access))
       (restart-case ,sel (retry-sql-access () ,sel)))))
(defmacro $ds (db slot target) `(db-select ,db ,slot ,target))

;($ds 'user 'age 25)
  • スロットの値が唯一であるか検索
(defmacro unique-value? (db slot target)
  (let ((sel `(clsql:select ,db :where (clsql:sql-= (sql-slot-value ,db ,slot) ,target) :flatp t)))
    `(null (handler-bind ((clsql-sys:sql-database-data-error #'retry-sql-access))
	     (restart-case ,sel (retry-sql-access () ,sel))))))
(defmacro $u? (db slot target) `(unique-value? ,db ,slot ,target))

;($u? 'user 'age 150)
(defmacro make-new-instance-and-save (db &rest initargs)
  (let ((sel `(clsql:update-records-from-instance (make-instance ,db ,@initargs))))
    `(handler-bind ((clsql-sys:sql-database-data-error #'retry-sql-access))
       (restart-case ,sel (retry-sql-access () ,sel)))))
(defmacro $c! (db &rest initargs) `(make-new-instance-and-save ,db ,@initargs))

;($c! 'user :id 100 :name "nagayoru" :age 21)

こんなかんじ。
それにしても、Commonlispのデザインパターンはまだいまいちわからない。