/sqlite.lisp

http://github.com/dmitryvk/cl-sqlite · Lisp · 507 lines · 439 code · 67 blank · 1 comment · 24 complexity · b556d686eef5c94a06b99cfd07d05451 MD5 · raw file

  1. (defpackage :sqlite
  2. (:use :cl :iter)
  3. (:export :sqlite-error
  4. :sqlite-constraint-error
  5. :sqlite-error-db-handle
  6. :sqlite-error-code
  7. :sqlite-error-message
  8. :sqlite-error-sql
  9. :sqlite-handle
  10. :connect
  11. :set-busy-timeout
  12. :disconnect
  13. :sqlite-statement
  14. :prepare-statement
  15. :finalize-statement
  16. :step-statement
  17. :reset-statement
  18. :clear-statement-bindings
  19. :statement-column-value
  20. :statement-column-names
  21. :statement-bind-parameter-names
  22. :bind-parameter
  23. :execute-non-query
  24. :execute-to-list
  25. :execute-single
  26. :execute-single/named
  27. :execute-one-row-m-v/named
  28. :execute-to-list/named
  29. :execute-non-query/named
  30. :execute-one-row-m-v
  31. :last-insert-rowid
  32. :with-transaction
  33. :with-open-database))
  34. (in-package :sqlite)
  35. (define-condition sqlite-error (simple-error)
  36. ((handle :initform nil :initarg :db-handle
  37. :reader sqlite-error-db-handle)
  38. (error-code :initform nil :initarg :error-code
  39. :reader sqlite-error-code)
  40. (error-msg :initform nil :initarg :error-msg
  41. :reader sqlite-error-message)
  42. (statement :initform nil :initarg :statement
  43. :reader sqlite-error-statement)
  44. (sql :initform nil :initarg :sql
  45. :reader sqlite-error-sql)))
  46. (define-condition sqlite-constraint-error (sqlite-error)
  47. ())
  48. (defun sqlite-error (error-code message &key
  49. statement
  50. (db-handle (if statement (db statement)))
  51. (sql-text (if statement (sql statement))))
  52. (error (if (eq error-code :constraint)
  53. 'sqlite-constraint-error
  54. 'sqlite-error)
  55. :format-control (if (listp message) (first message) message)
  56. :format-arguments (if (listp message) (rest message))
  57. :db-handle db-handle
  58. :error-code error-code
  59. :error-msg (if (and db-handle error-code)
  60. (sqlite-ffi:sqlite3-errmsg (handle db-handle)))
  61. :statement statement
  62. :sql sql-text))
  63. (defmethod print-object :after ((obj sqlite-error) stream)
  64. (unless *print-escape*
  65. (when (or (and (sqlite-error-code obj)
  66. (not (eq (sqlite-error-code obj) :ok)))
  67. (sqlite-error-message obj))
  68. (format stream "~&Code ~A: ~A."
  69. (or (sqlite-error-code obj) :OK)
  70. (or (sqlite-error-message obj) "no message")))
  71. (when (sqlite-error-db-handle obj)
  72. (format stream "~&Database: ~A"
  73. (database-path (sqlite-error-db-handle obj))))
  74. (when (sqlite-error-sql obj)
  75. (format stream "~&SQL: ~A" (sqlite-error-sql obj)))))
  76. ;(declaim (optimize (speed 3) (safety 0) (debug 0)))
  77. (defclass sqlite-handle ()
  78. ((handle :accessor handle)
  79. (database-path :accessor database-path)
  80. (cache :accessor cache)
  81. (statements :initform nil :accessor sqlite-handle-statements))
  82. (:documentation "Class that encapsulates the connection to the database. Use connect and disconnect."))
  83. (defmethod initialize-instance :after ((object sqlite-handle) &key (database-path ":memory:") &allow-other-keys)
  84. (cffi:with-foreign-object (ppdb 'sqlite-ffi:p-sqlite3)
  85. (let ((error-code (sqlite-ffi:sqlite3-open database-path ppdb)))
  86. (if (eq error-code :ok)
  87. (setf (handle object) (cffi:mem-ref ppdb 'sqlite-ffi:p-sqlite3)
  88. (database-path object) database-path)
  89. (sqlite-error error-code (list "Could not open sqlite3 database ~A" database-path)))))
  90. (setf (cache object) (make-instance 'sqlite.cache:mru-cache :cache-size 16 :destructor #'really-finalize-statement)))
  91. (defun connect (database-path &key busy-timeout)
  92. "Connect to the sqlite database at the given DATABASE-PATH. Returns the SQLITE-HANDLE connected to the database. Use DISCONNECT to disconnect.
  93. Operations will wait for locked databases for up to BUSY-TIMEOUT milliseconds; if BUSY-TIMEOUT is NIL, then operations on locked databases will fail immediately."
  94. (let ((db (make-instance 'sqlite-handle
  95. :database-path (etypecase database-path
  96. (string database-path)
  97. (pathname (namestring database-path))))))
  98. (when busy-timeout
  99. (set-busy-timeout db busy-timeout))
  100. db))
  101. (defun set-busy-timeout (db milliseconds)
  102. "Sets the maximum amount of time to wait for a locked database."
  103. (sqlite-ffi:sqlite3-busy-timeout (handle db) milliseconds))
  104. (defun disconnect (handle)
  105. "Disconnects the given HANDLE from the database. All further operations on the handle are invalid."
  106. (sqlite.cache:purge-cache (cache handle))
  107. (iter (with statements = (copy-list (sqlite-handle-statements handle)))
  108. (declare (dynamic-extent statements))
  109. (for statement in statements)
  110. (really-finalize-statement statement))
  111. (let ((error-code (sqlite-ffi:sqlite3-close (handle handle))))
  112. (unless (eq error-code :ok)
  113. (sqlite-error error-code "Could not close sqlite3 database." :db-handle handle))
  114. (slot-makunbound handle 'handle)))
  115. (defclass sqlite-statement ()
  116. ((db :reader db :initarg :db)
  117. (handle :accessor handle)
  118. (sql :reader sql :initarg :sql)
  119. (columns-count :accessor resultset-columns-count)
  120. (columns-names :accessor resultset-columns-names :reader statement-column-names)
  121. (parameters-count :accessor parameters-count)
  122. (parameters-names :accessor parameters-names :reader statement-bind-parameter-names))
  123. (:documentation "Class that represents the prepared statement."))
  124. (defmethod initialize-instance :after ((object sqlite-statement) &key &allow-other-keys)
  125. (cffi:with-foreign-object (p-statement 'sqlite-ffi:p-sqlite3-stmt)
  126. (cffi:with-foreign-object (p-tail '(:pointer :char))
  127. (cffi:with-foreign-string (sql (sql object))
  128. (let ((error-code (sqlite-ffi:sqlite3-prepare (handle (db object)) sql -1 p-statement p-tail)))
  129. (unless (eq error-code :ok)
  130. (sqlite-error error-code "Could not prepare an sqlite statement."
  131. :db-handle (db object) :sql-text (sql object)))
  132. (unless (zerop (cffi:mem-ref (cffi:mem-ref p-tail '(:pointer :char)) :uchar))
  133. (sqlite-error nil "SQL string contains more than one SQL statement." :sql-text (sql object)))
  134. (setf (handle object) (cffi:mem-ref p-statement 'sqlite-ffi:p-sqlite3-stmt)
  135. (resultset-columns-count object) (sqlite-ffi:sqlite3-column-count (handle object))
  136. (resultset-columns-names object) (loop
  137. for i below (resultset-columns-count object)
  138. collect (sqlite-ffi:sqlite3-column-name (handle object) i))
  139. (parameters-count object) (sqlite-ffi:sqlite3-bind-parameter-count (handle object))
  140. (parameters-names object) (loop
  141. for i from 1 to (parameters-count object)
  142. collect (sqlite-ffi:sqlite3-bind-parameter-name (handle object) i))))))))
  143. (defun prepare-statement (db sql)
  144. "Prepare the statement to the DB that will execute the commands that are in SQL.
  145. Returns the SQLITE-STATEMENT.
  146. SQL must contain exactly one statement.
  147. SQL may have some positional (not named) parameters specified with question marks.
  148. Example:
  149. select name from users where id = ?"
  150. (or (let ((statement (sqlite.cache:get-from-cache (cache db) sql)))
  151. (when statement
  152. (clear-statement-bindings statement))
  153. statement)
  154. (let ((statement (make-instance 'sqlite-statement :db db :sql sql)))
  155. (push statement (sqlite-handle-statements db))
  156. statement)))
  157. (defun really-finalize-statement (statement)
  158. (setf (sqlite-handle-statements (db statement))
  159. (delete statement (sqlite-handle-statements (db statement))))
  160. (sqlite-ffi:sqlite3-finalize (handle statement))
  161. (slot-makunbound statement 'handle))
  162. (defun finalize-statement (statement)
  163. "Finalizes the statement and signals that associated resources may be released.
  164. Note: does not immediately release resources because statements are cached."
  165. (reset-statement statement)
  166. (sqlite.cache:put-to-cache (cache (db statement)) (sql statement) statement))
  167. (defun step-statement (statement)
  168. "Steps to the next row of the resultset of STATEMENT.
  169. Returns T is successfully advanced to the next row and NIL if there are no more rows."
  170. (let ((error-code (sqlite-ffi:sqlite3-step (handle statement))))
  171. (case error-code
  172. (:done nil)
  173. (:row t)
  174. (t
  175. (sqlite-error error-code "Error while stepping an sqlite statement." :statement statement)))))
  176. (defun reset-statement (statement)
  177. "Resets the STATEMENT and prepare it to be called again."
  178. (let ((error-code (sqlite-ffi:sqlite3-reset (handle statement))))
  179. (unless (eq error-code :ok)
  180. (sqlite-error error-code "Error while resetting an sqlite statement." :statement statement))))
  181. (defun clear-statement-bindings (statement)
  182. "Sets all binding values to NULL."
  183. (let ((error-code (sqlite-ffi:sqlite3-clear-bindings (handle statement))))
  184. (unless (eq error-code :ok)
  185. (sqlite-error error-code "Error while clearing bindings of an sqlite statement."
  186. :statement statement))))
  187. (defun statement-column-value (statement column-number)
  188. "Returns the COLUMN-NUMBER-th column's value of the current row of the STATEMENT. Columns are numbered from zero.
  189. Returns:
  190. * NIL for NULL
  191. * INTEGER for integers
  192. * DOUBLE-FLOAT for floats
  193. * STRING for text
  194. * (SIMPLE-ARRAY (UNSIGNED-BYTE 8)) for BLOBs"
  195. (let ((type (sqlite-ffi:sqlite3-column-type (handle statement) column-number)))
  196. (ecase type
  197. (:null nil)
  198. (:text (sqlite-ffi:sqlite3-column-text (handle statement) column-number))
  199. (:integer (sqlite-ffi:sqlite3-column-int64 (handle statement) column-number))
  200. (:float (sqlite-ffi:sqlite3-column-double (handle statement) column-number))
  201. (:blob (let* ((blob-length (sqlite-ffi:sqlite3-column-bytes (handle statement) column-number))
  202. (result (make-array (the fixnum blob-length) :element-type '(unsigned-byte 8)))
  203. (blob (sqlite-ffi:sqlite3-column-blob (handle statement) column-number)))
  204. (loop
  205. for i below blob-length
  206. do (setf (aref result i) (cffi:mem-aref blob :unsigned-char i)))
  207. result)))))
  208. (defmacro with-prepared-statement (statement-var (db sql parameters-var) &body body)
  209. (let ((i-var (gensym "I"))
  210. (value-var (gensym "VALUE")))
  211. `(let ((,statement-var (prepare-statement ,db ,sql)))
  212. (unwind-protect
  213. (progn
  214. (iter (for ,i-var from 1)
  215. (declare (type fixnum ,i-var))
  216. (for ,value-var in ,parameters-var)
  217. (bind-parameter ,statement-var ,i-var ,value-var))
  218. ,@body)
  219. (finalize-statement ,statement-var)))))
  220. (defmacro with-prepared-statement/named (statement-var (db sql parameters-var) &body body)
  221. (let ((name-var (gensym "NAME"))
  222. (value-var (gensym "VALUE")))
  223. `(let ((,statement-var (prepare-statement ,db ,sql)))
  224. (unwind-protect
  225. (progn
  226. (iter (for (,name-var ,value-var) on ,parameters-var by #'cddr)
  227. (bind-parameter ,statement-var (string ,name-var) ,value-var))
  228. ,@body)
  229. (finalize-statement ,statement-var)))))
  230. (defun execute-non-query (db sql &rest parameters)
  231. "Executes the query SQL to the database DB with given PARAMETERS. Returns nothing.
  232. Example:
  233. \(execute-non-query db \"insert into users (user_name, real_name) values (?, ?)\" \"joe\" \"Joe the User\")
  234. See BIND-PARAMETER for the list of supported parameter types."
  235. (declare (dynamic-extent parameters))
  236. (with-prepared-statement statement (db sql parameters)
  237. (step-statement statement)))
  238. (defun execute-non-query/named (db sql &rest parameters)
  239. "Executes the query SQL to the database DB with given PARAMETERS. Returns nothing.
  240. PARAMETERS is a list of alternating parameter names and values.
  241. Example:
  242. \(execute-non-query db \"insert into users (user_name, real_name) values (:name, :real_name)\" \":name\" \"joe\" \":real_name\" \"Joe the User\")
  243. See BIND-PARAMETER for the list of supported parameter types."
  244. (declare (dynamic-extent parameters))
  245. (with-prepared-statement/named statement (db sql parameters)
  246. (step-statement statement)))
  247. (defun execute-to-list (db sql &rest parameters)
  248. "Executes the query SQL to the database DB with given PARAMETERS. Returns the results as list of lists.
  249. Example:
  250. \(execute-to-list db \"select id, user_name, real_name from users where user_name = ?\" \"joe\")
  251. =>
  252. \((1 \"joe\" \"Joe the User\")
  253. (2 \"joe\" \"Another Joe\"))
  254. See BIND-PARAMETER for the list of supported parameter types."
  255. (declare (dynamic-extent parameters))
  256. (with-prepared-statement stmt (db sql parameters)
  257. (let (result)
  258. (loop (if (step-statement stmt)
  259. (push (iter (for i from 0 below (the fixnum (sqlite-ffi:sqlite3-column-count (handle stmt))))
  260. (declare (type fixnum i))
  261. (collect (statement-column-value stmt i)))
  262. result)
  263. (return)))
  264. (nreverse result))))
  265. (defun execute-to-list/named (db sql &rest parameters)
  266. "Executes the query SQL to the database DB with given PARAMETERS. Returns the results as list of lists.
  267. PARAMETERS is a list of alternating parameters names and values.
  268. Example:
  269. \(execute-to-list db \"select id, user_name, real_name from users where user_name = :user_name\" \":user_name\" \"joe\")
  270. =>
  271. \((1 \"joe\" \"Joe the User\")
  272. (2 \"joe\" \"Another Joe\"))
  273. See BIND-PARAMETER for the list of supported parameter types."
  274. (declare (dynamic-extent parameters))
  275. (with-prepared-statement/named stmt (db sql parameters)
  276. (let (result)
  277. (loop (if (step-statement stmt)
  278. (push (iter (for i from 0 below (the fixnum (sqlite-ffi:sqlite3-column-count (handle stmt))))
  279. (declare (type fixnum i))
  280. (collect (statement-column-value stmt i)))
  281. result)
  282. (return)))
  283. (nreverse result))))
  284. (defun execute-one-row-m-v (db sql &rest parameters)
  285. "Executes the query SQL to the database DB with given PARAMETERS. Returns the first row as multiple values.
  286. Example:
  287. \(execute-one-row-m-v db \"select id, user_name, real_name from users where id = ?\" 1)
  288. =>
  289. \(values 1 \"joe\" \"Joe the User\")
  290. See BIND-PARAMETER for the list of supported parameter types."
  291. (with-prepared-statement stmt (db sql parameters)
  292. (if (step-statement stmt)
  293. (return-from execute-one-row-m-v
  294. (values-list (iter (for i from 0 below (the fixnum (sqlite-ffi:sqlite3-column-count (handle stmt))))
  295. (declare (type fixnum i))
  296. (collect (statement-column-value stmt i)))))
  297. (return-from execute-one-row-m-v
  298. (values-list (loop repeat (the fixnum (sqlite-ffi:sqlite3-column-count (handle stmt))) collect nil))))))
  299. (defun execute-one-row-m-v/named (db sql &rest parameters)
  300. "Executes the query SQL to the database DB with given PARAMETERS. Returns the first row as multiple values.
  301. PARAMETERS is a list of alternating parameters names and values.
  302. Example:
  303. \(execute-one-row-m-v db \"select id, user_name, real_name from users where id = :id\" \":id\" 1)
  304. =>
  305. \(values 1 \"joe\" \"Joe the User\")
  306. See BIND-PARAMETER for the list of supported parameter types."
  307. (with-prepared-statement/named stmt (db sql parameters)
  308. (if (step-statement stmt)
  309. (return-from execute-one-row-m-v/named
  310. (values-list (iter (for i from 0 below (the fixnum (sqlite-ffi:sqlite3-column-count (handle stmt))))
  311. (declare (type fixnum i))
  312. (collect (statement-column-value stmt i)))))
  313. (return-from execute-one-row-m-v/named
  314. (values-list (loop repeat (the fixnum (sqlite-ffi:sqlite3-column-count (handle stmt))) collect nil))))))
  315. (defun statement-parameter-index (statement parameter-name)
  316. (sqlite-ffi:sqlite3-bind-parameter-index (handle statement) parameter-name))
  317. (defun bind-parameter (statement parameter value)
  318. "Sets the PARAMETER-th parameter in STATEMENT to the VALUE.
  319. PARAMETER may be parameter index (starting from 1) or parameters name.
  320. Supported types:
  321. * NULL. Passed as NULL
  322. * INTEGER. Passed as an 64-bit integer
  323. * STRING. Passed as a string
  324. * FLOAT. Passed as a double
  325. * (VECTOR (UNSIGNED-BYTE 8)) and VECTOR that contains integers in range [0,256). Passed as a BLOB"
  326. (let ((index (etypecase parameter
  327. (integer parameter)
  328. (string (statement-parameter-index statement parameter)))))
  329. (declare (type fixnum index))
  330. (let ((error-code (typecase value
  331. (null (sqlite-ffi:sqlite3-bind-null (handle statement) index))
  332. (integer (sqlite-ffi:sqlite3-bind-int64 (handle statement) index value))
  333. (double-float (sqlite-ffi:sqlite3-bind-double (handle statement) index value))
  334. (real (sqlite-ffi:sqlite3-bind-double (handle statement) index (coerce value 'double-float)))
  335. (string (sqlite-ffi:sqlite3-bind-text (handle statement) index value -1 (sqlite-ffi:destructor-transient)))
  336. ((vector (unsigned-byte 8)) (cffi:with-pointer-to-vector-data (ptr value)
  337. (sqlite-ffi:sqlite3-bind-blob (handle statement) index ptr (length value) (sqlite-ffi:destructor-transient))))
  338. (vector (cffi:with-foreign-object (array :unsigned-char (length value))
  339. (loop
  340. for i from 0 below (length value)
  341. do (setf (cffi:mem-aref array :unsigned-char i) (aref value i)))
  342. (sqlite-ffi:sqlite3-bind-blob (handle statement) index array (length value) (sqlite-ffi:destructor-transient))))
  343. (t
  344. (sqlite-error nil
  345. (list "Do not know how to pass value ~A of type ~A to sqlite."
  346. value (type-of value))
  347. :statement statement)))))
  348. (unless (eq error-code :ok)
  349. (sqlite-error error-code
  350. (list "Error when binding parameter ~A to value ~A." parameter value)
  351. :statement statement)))))
  352. (defun execute-single (db sql &rest parameters)
  353. "Executes the query SQL to the database DB with given PARAMETERS. Returns the first column of the first row as single value.
  354. Example:
  355. \(execute-single db \"select user_name from users where id = ?\" 1)
  356. =>
  357. \"joe\"
  358. See BIND-PARAMETER for the list of supported parameter types."
  359. (declare (dynamic-extent parameters))
  360. (with-prepared-statement stmt (db sql parameters)
  361. (if (step-statement stmt)
  362. (statement-column-value stmt 0)
  363. nil)))
  364. (defun execute-single/named (db sql &rest parameters)
  365. "Executes the query SQL to the database DB with given PARAMETERS. Returns the first column of the first row as single value.
  366. PARAMETERS is a list of alternating parameters names and values.
  367. Example:
  368. \(execute-single db \"select user_name from users where id = :id\" \":id\" 1)
  369. =>
  370. \"joe\"
  371. See BIND-PARAMETER for the list of supported parameter types."
  372. (declare (dynamic-extent parameters))
  373. (with-prepared-statement/named stmt (db sql parameters)
  374. (if (step-statement stmt)
  375. (statement-column-value stmt 0)
  376. nil)))
  377. (defun last-insert-rowid (db)
  378. "Returns the auto-generated ID of the last inserted row on the database connection DB."
  379. (sqlite-ffi:sqlite3-last-insert-rowid (handle db)))
  380. (defmacro with-transaction (db &body body)
  381. "Wraps the BODY inside the transaction."
  382. (let ((ok (gensym "TRANSACTION-COMMIT-"))
  383. (db-var (gensym "DB-")))
  384. `(let (,ok
  385. (,db-var ,db))
  386. (execute-non-query ,db-var "begin transaction")
  387. (unwind-protect
  388. (multiple-value-prog1
  389. (progn ,@body)
  390. (setf ,ok t))
  391. (if ,ok
  392. (execute-non-query ,db-var "commit transaction")
  393. (execute-non-query ,db-var "rollback transaction"))))))
  394. (defmacro with-open-database ((db path &key busy-timeout) &body body)
  395. `(let ((,db (connect ,path :busy-timeout ,busy-timeout)))
  396. (unwind-protect
  397. (progn ,@body)
  398. (disconnect ,db))))
  399. (defmacro-driver (FOR vars IN-SQLITE-QUERY query-expression ON-DATABASE db &optional WITH-PARAMETERS parameters)
  400. (let ((statement (gensym "STATEMENT-"))
  401. (kwd (if generate 'generate 'for)))
  402. `(progn (with ,statement = (prepare-statement ,db ,query-expression))
  403. (finally-protected (when ,statement (finalize-statement ,statement)))
  404. ,@(when parameters
  405. (list `(initially ,@(iter (for i from 1)
  406. (for value in parameters)
  407. (collect `(sqlite:bind-parameter ,statement ,i ,value))))))
  408. (,kwd ,(if (symbolp vars)
  409. `(values ,vars)
  410. `(values ,@vars))
  411. next (progn (if (step-statement ,statement)
  412. (values ,@(iter (for i from 0 below (if (symbolp vars) 1 (length vars)))
  413. (collect `(statement-column-value ,statement ,i))))
  414. (terminate)))))))
  415. (defmacro-driver (FOR vars IN-SQLITE-QUERY/NAMED query-expression ON-DATABASE db &optional WITH-PARAMETERS parameters)
  416. (let ((statement (gensym "STATEMENT-"))
  417. (kwd (if generate 'generate 'for)))
  418. `(progn (with ,statement = (prepare-statement ,db ,query-expression))
  419. (finally-protected (when ,statement (finalize-statement ,statement)))
  420. ,@(when parameters
  421. (list `(initially ,@(iter (for (name value) on parameters by #'cddr)
  422. (collect `(sqlite:bind-parameter ,statement ,name ,value))))))
  423. (,kwd ,(if (symbolp vars)
  424. `(values ,vars)
  425. `(values ,@vars))
  426. next (progn (if (step-statement ,statement)
  427. (values ,@(iter (for i from 0 below (if (symbolp vars) 1 (length vars)))
  428. (collect `(statement-column-value ,statement ,i))))
  429. (terminate)))))))
  430. (defmacro-driver (FOR vars ON-SQLITE-STATEMENT statement)
  431. (let ((statement-var (gensym "STATEMENT-"))
  432. (kwd (if generate 'generate 'for)))
  433. `(progn (with ,statement-var = ,statement)
  434. (,kwd ,(if (symbolp vars)
  435. `(values ,vars)
  436. `(values ,@vars))
  437. next (progn (if (step-statement ,statement-var)
  438. (values ,@(iter (for i from 0 below (if (symbolp vars) 1 (length vars)))
  439. (collect `(statement-column-value ,statement-var ,i))))
  440. (terminate)))))))