fixed bug causing some failing sqlite3 tests (database-identifiers are not string=)
[clsql.git] / db-sqlite3 / sqlite3-sql.lisp
1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:     sqlite-sql.lisp
6 ;;;; Purpose:  High-level SQLite3 interface
7 ;;;; Authors:  Aurelio Bignoli & Kevin Rosenberg
8 ;;;; Created:  Oct 2004
9 ;;;;
10 ;;;; This file, part of CLSQL, is Copyright (c) 2004-2010 by Aurelio Bignoli & Kevin Rosenberg
11 ;;;;
12 ;;;; CLSQL users are granted the rights to distribute and use this software
13 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
14 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
15 ;;;; *************************************************************************
16
17 (in-package #:clsql-sqlite3)
18
19 (defclass sqlite3-database (database)
20   ((sqlite3-db :initarg :sqlite3-db :accessor sqlite3-db)))
21
22 (defmethod database-type ((database sqlite3-database))
23   :sqlite3)
24
25 (defmethod database-initialize-database-type ((database-type (eql :sqlite3)))
26   t)
27
28 (defun check-sqlite3-connection-spec (connection-spec)
29   (check-connection-spec connection-spec :sqlite3 (name &optional init-foreign-func)))
30
31 (defmethod database-name-from-spec (connection-spec
32                                     (database-type (eql :sqlite3)))
33   (check-sqlite3-connection-spec connection-spec)
34   (princ-to-string (first connection-spec)))
35
36 (defmethod database-connect (connection-spec (database-type (eql :sqlite3)))
37   (check-sqlite3-connection-spec connection-spec)
38     (handler-case
39         (let ((db (sqlite3:sqlite3-open (first connection-spec)))
40               (init-foreign-func (second connection-spec)))
41           (declare (type sqlite3:sqlite3-db-type db))
42           (when init-foreign-func
43             (handler-case
44                 (funcall init-foreign-func db)
45               (condition (c)
46                 (progn
47                   (sqlite3:sqlite3-close db)
48                   (error c)))))
49           (make-instance 'sqlite3-database
50                          :name (database-name-from-spec connection-spec :sqlite3)
51                          :database-type :sqlite3
52                          :connection-spec connection-spec
53                          :sqlite3-db db))
54       (sqlite3:sqlite3-error (err)
55         (error 'sql-connection-error
56                :database-type database-type
57                :connection-spec connection-spec
58                :error-id (sqlite3:sqlite3-error-code err)
59                :message (sqlite3:sqlite3-error-message err)))))
60
61 (defmethod database-disconnect ((database sqlite3-database))
62   (sqlite3:sqlite3-close (sqlite3-db database))
63   (setf (sqlite3-db database) nil)
64   t)
65
66 (defmethod database-execute-command (sql-expression (database sqlite3-database))
67   (handler-case
68       (let ((stmt (sqlite3:sqlite3-prepare (sqlite3-db database) sql-expression)))
69         (declare (type sqlite3:sqlite3-stmt-type stmt))
70         (when stmt
71           (unwind-protect
72                (sqlite3:sqlite3-step stmt)
73             (sqlite3:sqlite3-finalize stmt))))
74     (sqlite3:sqlite3-error (err)
75       (error 'sql-database-data-error
76              :database database
77              :expression sql-expression
78              :error-id (sqlite3:sqlite3-error-code err)
79              :message (sqlite3:sqlite3-error-message err))))
80   t)
81
82 (defstruct sqlite3-result-set
83   (stmt sqlite3:null-stmt
84         :type sqlite3:sqlite3-stmt-type)
85   (n-col 0 :type fixnum)
86   (col-names '())
87   (result-types '()))
88
89 (declaim (ftype (function (sqlite3:sqlite3-stmt-type fixnum t) list) get-result-types))
90 (defun get-result-types (stmt n-col result-types)
91   (declare (type sqlite3:sqlite3-stmt-type stmt) (type fixnum n-col))
92   (if (eq :auto result-types)
93       (loop for n from 0 below n-col
94             collect (let ((column-type (sqlite3:sqlite3-column-type stmt n)))
95                       (cond
96                         ((= column-type sqlite3:SQLITE-INTEGER) :int64)
97                         ((= column-type sqlite3:SQLITE-FLOAT) :double)
98                         ((= column-type sqlite3:SQLITE-TEXT) :string)
99                         ((= column-type sqlite3:SQLITE-BLOB) :blob)
100                         ((= column-type sqlite3:SQLITE-NULL) :string)
101                         (t :string))))
102       (loop for type in result-types
103             collect (case type
104                       ((:int :integer :tinyint) :int32)
105                       (:long #+(or x86-64 64bit) :int64 #-(or x86-64 64bit) :int32)
106                       (:bigint :int64)
107                       ((:float :double) :double)
108                       ((:numeric) :number)
109                       (otherwise :string)))))
110
111 (defmethod database-query-result-set ((query-expression string)
112                                       (database sqlite3-database)
113                                       &key result-types full-set)
114   (let ((stmt sqlite3:null-stmt))
115     (declare (type sqlite3:sqlite3-stmt-type stmt))
116     (handler-case
117         (progn
118           (setf stmt (sqlite3:sqlite3-prepare (sqlite3-db database)
119                                               query-expression))
120           (let* ((n-col (if (sqlite3:sqlite3-step stmt)
121                             ;; Non empty result set.
122                             (sqlite3:sqlite3-column-count stmt)
123                             ;; Empty result set.
124                             0))
125                  (result-set (make-sqlite3-result-set
126                               :stmt stmt
127                               :n-col n-col
128                               :col-names (loop for n from 0 below n-col
129                                                collect (sqlite3:sqlite3-column-name stmt n))
130                               :result-types (when (> n-col 0)
131                                               (get-result-types stmt n-col result-types)))))
132             (if full-set
133                 (values result-set n-col nil)
134                 (values result-set n-col))))
135     (sqlite3:sqlite3-error (err)
136         (progn
137           (unless (eq stmt sqlite3:null-stmt)
138             (ignore-errors
139               (sqlite3:sqlite3-finalize stmt)))
140           (error 'sql-database-data-error
141                  :database database
142                  :expression query-expression
143                  :error-id (sqlite3:sqlite3-error-code err)
144                  :message (sqlite3:sqlite3-error-message err)))))))
145
146 (defmethod database-dump-result-set (result-set (database sqlite3-database))
147   (handler-case
148       (sqlite3:sqlite3-finalize (sqlite3-result-set-stmt result-set))
149     (sqlite3:sqlite3-error (err)
150       (error 'sql-database-error
151              :message
152              (format nil "Error finalizing SQLite3 statement: ~A"
153                      (sqlite3:sqlite3-error-message err))))))
154
155 (defmethod database-store-next-row (result-set (database sqlite3-database) list)
156   (let ((n-col (sqlite3-result-set-n-col result-set)))
157     (if (= n-col 0)
158         ;; empty result set.
159         nil
160         ;; Non-empty set.
161         (let ((stmt (sqlite3-result-set-stmt result-set)))
162           (declare (type sqlite3:sqlite3-stmt-type stmt))
163           ;; Store row in list.
164           (loop for i = 0 then (1+ i)
165                 for rest on list
166                 for types = (sqlite3-result-set-result-types result-set) then (rest types)
167                 do (setf (car rest)
168                          (if (eq (first types) :blob)
169                              (clsql-uffi:convert-raw-field
170                               (sqlite3:sqlite3-column-blob stmt i)
171                               (car types)
172                               :length (sqlite3:sqlite3-column-bytes stmt i)
173                               :encoding (encoding database))
174                              (clsql-uffi:convert-raw-field
175                               (sqlite3:sqlite3-column-text stmt i)
176                               (car types)
177                               :encoding (encoding database)))))
178           ;; Advance result set cursor.
179           (handler-case
180               (unless (sqlite3:sqlite3-step stmt)
181                 (setf (sqlite3-result-set-n-col result-set) 0))
182             (sqlite3:sqlite3-error (err)
183               (error 'sql-database-error
184                      :message (format nil "Error in sqlite3-step: ~A"
185                                       (sqlite3:sqlite3-error-message err)))))
186           t))))
187
188
189 (defmethod database-query (query-expression (database sqlite3-database) result-types field-names)
190   (declare (optimize (speed 3) (safety 0) (debug 0) (space 0)))
191   (handler-case
192       (let ((stmt (sqlite3:sqlite3-prepare (sqlite3-db database)
193                                            query-expression))
194             (rows '())
195             (col-names '()))
196         (declare (type sqlite3:sqlite3-stmt-type stmt))
197         (unwind-protect
198              (when (sqlite3:sqlite3-step stmt)
199                (let ((n-col (sqlite3:sqlite3-column-count stmt)))
200                  (flet ((extract-row-data ()
201                           (loop for i from 0 below n-col
202                                 for types = (get-result-types stmt n-col result-types) then (rest types)
203                                 collect (if (eq (first types) :blob)
204                                             (clsql-uffi:convert-raw-field
205                                              (sqlite3:sqlite3-column-blob stmt i)
206                                              (car types)
207                                              :length (sqlite3:sqlite3-column-bytes stmt i)
208                                              :encoding (encoding database))
209                                             (clsql-uffi:convert-raw-field
210                                              (sqlite3:sqlite3-column-text stmt i)
211                                              (car types)
212                                              :encoding (encoding database))))))
213                    (when field-names
214                      (setf col-names (loop for n from 0 below n-col
215                                            collect (sqlite3:sqlite3-column-name stmt n))))
216                    (push (extract-row-data) rows)
217                    (do* () (nil)
218                      (if (sqlite3:sqlite3-step stmt)
219                          (push (extract-row-data) rows)
220                          (return))))))
221                (sqlite3:sqlite3-finalize stmt))
222         (values (nreverse rows) col-names))
223     (sqlite3:sqlite3-error (err)
224       (error 'sql-database-data-error
225              :database database
226              :expression query-expression
227              :error-id (sqlite3:sqlite3-error-code err)
228              :message (sqlite3:sqlite3-error-message err)))))
229
230 ;;; Object listing
231
232 (defmethod database-list-tables-and-sequences ((database sqlite3-database) &key owner)
233   (declare (ignore owner))
234   ;; Query is copied from .table command of sqlite3 command line utility.
235   (mapcar #'car (database-query
236                  "SELECT name FROM sqlite_master WHERE type='table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='table' ORDER BY name"
237                  database nil nil)))
238
239 (defmethod database-list-tables ((database sqlite3-database) &key owner)
240   (remove-if #'(lambda (s)
241                  (and (>= (length s) 11)
242                       (string-equal (subseq s 0 11) "_CLSQL_SEQ_")))
243              (database-list-tables-and-sequences database :owner owner)))
244
245 (defmethod database-list-views ((database sqlite3-database)
246                                 &key (owner nil))
247   (declare (ignore owner))
248   (mapcar #'car (database-query
249                  "SELECT name FROM sqlite_master WHERE type='view' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='view' ORDER BY name"
250                  database nil nil)))
251
252 (defmethod database-list-indexes ((database sqlite3-database)
253                                   &key (owner nil))
254   (declare (ignore owner))
255   (mapcar #'car (database-query
256                  "SELECT name FROM sqlite_master WHERE type='index' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='index' ORDER BY name"
257                  database nil nil)))
258
259 (defmethod database-list-table-indexes (table (database sqlite3-database)
260                                         &key (owner nil))
261   (declare (ignore owner))
262   (let ((*print-circle* nil))
263     (mapcar #'car
264             (database-query
265              (format
266               nil
267               "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='~A' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='index' AND tbl_name='~A' ORDER BY name"
268               table table)
269              database nil nil))))
270
271 (declaim (inline sqlite3-table-info))
272 (defun sqlite3-table-info (table database)
273   (let ((sql (format nil "PRAGMA table_info('~A')"
274                      (clsql-sys::unescaped-database-identifier table))))
275     (database-query sql database nil nil)))
276
277 (defmethod database-list-attributes (table (database sqlite3-database)
278                                            &key (owner nil))
279   (declare (ignore owner))
280   (mapcar #'(lambda (table-info) (second table-info))
281           (sqlite3-table-info table database)))
282
283 (defmethod database-attribute-type (attribute table
284                                     (database sqlite3-database)
285                                     &key (owner nil))
286   (declare (ignore owner))
287   
288   (loop for field-info in (sqlite3-table-info table database)
289       when (string= (clsql-sys::unescaped-database-identifier attribute)
290                     (second field-info))
291       return
292         (let* ((raw-type (third field-info))
293                (start-length (position #\( raw-type))
294                (type (string-trim clsql-sys::+whitespace-chars+
295                                   (if start-length
296                                       (subseq raw-type 0 start-length)
297                                       raw-type)))
298                (length (if start-length
299                            (parse-integer (subseq raw-type (1+ start-length))
300                                           :junk-allowed t)
301                          nil)))
302           (values (when type (ensure-keyword type))
303                   length
304                   nil
305                   (if (string-equal (fourth field-info) "0")
306                       1 0)))))
307
308 (defmethod database-last-auto-increment-id ((database sqlite3-database) table column)
309   (declare (ignore table column))
310   (car (query "SELECT LAST_INSERT_ROWID();"
311               :flatp t :field-names nil
312               :database database)))
313
314 (defmethod database-create (connection-spec (type (eql :sqlite3)))
315   (declare (ignore connection-spec))
316   ;; databases are created automatically by Sqlite3
317   t)
318
319 (defmethod database-destroy (connection-spec (type (eql :sqlite3)))
320   (destructuring-bind (name) connection-spec
321     (if (probe-file name)
322         (delete-file name)
323         nil)))
324
325 (defmethod database-probe (connection-spec (type (eql :sqlite3)))
326   (destructuring-bind (name) connection-spec
327     ;; TODO: Add a test that this file is a real sqlite3 database
328     (or (string-equal ":memory:" name)
329         (and (probe-file name) t))))
330
331 (defmethod database-get-type-specifier ((type (eql 'integer))
332                                         args database
333                                         (db-type (eql :sqlite3)))
334   (declare (ignore database))
335   (if args
336       (format nil "INTEGER(~A)" (car args))
337       "INTEGER"))
338
339 (defmethod database-get-type-specifier ((type (eql 'integer))
340                                         args database
341                                         (db-type (eql :sqlite3)))
342   (declare (ignore database))
343   (if args
344       (format nil "INTEGER(~A)" (car args))
345       "INTEGER"))
346
347 ;;; Database capabilities
348
349 (defmethod db-type-has-boolean-where? ((db-type (eql :sqlite3)))
350   nil)
351
352 (defmethod db-type-has-auto-increment? ((db-type (eql :sqlite3)))
353   t)