r8936: merged classic-tests into tests
[clsql.git] / db-sqlite / sqlite-api-uffi.lisp
1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          sqlite-api-uffi.lisp
6 ;;;; Purpose:       Low-level SQLite interface using UFFI
7 ;;;; Programmers:   Aurelio Bignoli and Kevin Rosenberg
8 ;;;; Date Started:  Nov 2003
9 ;;;;
10 ;;;; $Id$
11 ;;;;
12 ;;;; This file, part of CLSQL, is Copyright (c) 2003 by Aurelio Bignoli
13 ;;;;
14 ;;;; CLSQL users are granted the rights to distribute and use this software
15 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
16 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
17 ;;;; *************************************************************************
18
19 (in-package #:cl-user)
20
21 (defpackage #:sqlite
22   (:use #:common-lisp #:uffi)
23     (:export
24            ;;; Conditions
25            #:sqlite-error
26            #:sqlite-error-code
27            #:sqlite-error-message
28            
29            ;;; Core API.
30            #:sqlite-open
31            #:sqlite-close
32
33            ;;; New API.
34            #:sqlite-compile
35            #:sqlite-step
36            #:sqlite-finalize
37            
38            ;;; Extended API.
39            #:sqlite-get-table
40            #:sqlite-free-table
41            #:sqlite-version             ; Defined as constant.
42            #:sqlite-encoding            ; Defined as constant.
43            #:sqlite-last-insert-rowid
44
45            ;;; Utility functions.
46            #:make-null-row
47            #:make-null-vm
48            #:null-row-p
49            #:sqlite-aref
50            #:sqlite-free-row
51            
52            ;;; Types.
53            #:sqlite-row
54            #:sqlite-row-pointer
55            #:sqlite-row-pointer-type
56            #:sqlite-vm-pointer))
57
58 (in-package #:sqlite)
59
60 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
61 ;;;;
62 ;;;; Return values for sqlite_exec() and sqlite_step()
63 ;;;;
64 (defconstant SQLITE-OK            0  "Successful result")
65 (defconstant SQLITE-ERROR         1  "SQL error or missing database")
66 (defconstant SQLITE-ROW         100  "sqlite_step() has another row ready")
67 (defconstant SQLITE-DONE        101  "sqlite_step() has finished executing")
68
69 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
70 ;;;;
71 ;;;; Conditions.
72 ;;;;
73 (define-condition sqlite-error ()
74   ((message :initarg :message :reader sqlite-error-message :initform "")
75    (code :initarg :code :reader sqlite-error-code))
76   (:report (lambda (condition stream)
77              (let ((code (sqlite-error-code condition)))
78                (format stream "SQLite error [~A]: ~A"
79                        code (sqlite-error-message condition))))))
80
81 (defun signal-sqlite-error (code &optional message)
82   (let ((condition
83          (make-condition 'sqlite-error
84                          :code code
85                          :message (if message
86                                       message
87                                       (sqlite-error-string code)))))
88     (unless (signal condition)
89       (invoke-debugger condition))))
90
91 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
92 ;;;;
93 ;;;; Foreign types definitions.
94 ;;;;
95 (def-foreign-type errmsg (* :char))
96 (def-foreign-type sqlite-db :pointer-void)
97 (def-foreign-type sqlite-vm :pointer-void)
98 (def-foreign-type string-pointer (* (* :char)))
99 (def-foreign-type sqlite-row-pointer (* string-pointer))
100
101 (defvar +null-errmsg-pointer+ (make-null-pointer 'errmsg))
102 (defvar +null-string-pointer-pointer+ (make-null-pointer 'string-pointer))
103
104
105 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
106 ;;;
107 ;;; Lisp types used in declarations.
108 ;;;;
109 (def-type sqlite-db-type sqlite-db)
110 (def-type sqlite-row string-pointer)
111 (def-type sqlite-row-pointer-type (* string-pointer))
112 (def-type sqlite-vm-pointer (* sqlite-vm))
113
114 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
115 ;;;;
116 ;;;; Library functions.
117 ;;;;
118 (defmacro def-sqlite-function (name args &key (returning :void))
119   `(def-function ,name ,args
120     :module "sqlite"
121     :returning ,returning))
122
123 (def-sqlite-function
124     "sqlite_error_string"
125     ((error-code :int))
126   :returning :cstring)
127
128 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
129 ;;;;
130 ;;;; Core API.
131 ;;;;
132 (declaim (inline %open))
133 (def-sqlite-function
134     ("sqlite_open" %open)
135     ((dbname :cstring)
136      (mode :int)
137      (error-message (* errmsg)))
138   :returning sqlite-db)
139
140 (declaim (inline sqlite-close))
141 (def-sqlite-function
142     "sqlite_close"
143     ((db sqlite-db)))
144
145 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
146 ;;;;
147 ;;;; New API.
148 ;;;;
149 (declaim (inline %compile))
150 (def-sqlite-function
151     ("sqlite_compile" %compile)
152     ((db sqlite-db)
153      (sql :cstring)
154      (sql-tail (* (* :char)))
155      (vm (* sqlite-vm))
156      (error-message (* errmsg)))
157   :returning :int)
158
159 (declaim (inline %step))
160 (def-sqlite-function
161     ("sqlite_step" %step)
162     ((vm sqlite-vm)
163      (cols-n (* :int))
164      (cols (* (* (* :char))))
165      (col-names (* (* (* :char)))))
166   :returning :int)
167
168 (declaim (inline %finalize))
169 (def-sqlite-function
170     ("sqlite_finalize" %finalize)
171     ((vm sqlite-vm)
172      (error-message (* errmsg)))
173   :returning :int)
174
175 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
176 ;;;;
177 ;;;; Extended API.
178 ;;;;
179 (declaim (inline sqlite-last-insert-rowid))
180 (def-sqlite-function
181     "sqlite_last_insert_rowid"
182     ((db sqlite-db))
183   :returning :int)
184
185 (declaim (inline %get-table))
186 (def-sqlite-function
187     ("sqlite_get_table" %get-table)
188     ((db sqlite-db)
189      (sql :cstring)
190      (result (* (* (* :char))))
191      (rows-n (* :int))
192      (cols-n (* :int))
193      (error-message (* errmsg)))
194   :returning :int)
195
196 (declaim (inline %free-table))
197 (def-sqlite-function
198     ("sqlite_free_table" %free-table)
199     ((rows :pointer-void)))
200
201 (declaim (inline sqlite-libversion))
202 (def-sqlite-function
203     "sqlite_libversion"
204     ()
205   :returning :cstring)
206
207 (declaim (inline sqlite-libencoding))
208 (def-sqlite-function
209     "sqlite_libencoding"
210     ()
211   :returning :cstring)
212
213 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
214 ;;;;
215 ;;;; Wrapper functions.
216 ;;;;
217 (defparameter sqlite-version (sqlite-libversion))
218 (defparameter sqlite-encoding (sqlite-libencoding))
219
220 (defun sqlite-open (db-name &optional (mode 0))
221   (with-cstring (db-name-native db-name) 
222     (let ((db (%open db-name-native mode +null-errmsg-pointer+)))
223       (if (null-pointer-p db)
224           (signal-sqlite-error SQLITE-ERROR
225                                (format nil "unable to open ~A" db-name))
226           db))))
227
228 (defun sqlite-compile (db sql)
229   (with-cstring (sql-native sql)
230     (let ((vm (allocate-foreign-object 'sqlite-vm)))
231       (with-foreign-object (sql-tail '(* :char))
232         (let ((result (%compile db sql-native sql-tail vm +null-errmsg-pointer+)))
233           (if (= result SQLITE-OK)
234               vm
235               (progn
236                 (free-foreign-object vm)
237                 (signal-sqlite-error result))))))))
238
239 (defun sqlite-step (vm)
240   (declare (type sqlite-vm-pointer vm))
241   (with-foreign-object (cols-n :int)
242     (let ((cols (allocate-foreign-object '(* (* :char))))
243           (col-names (allocate-foreign-object '(* (* :char)))))
244       (declare (type sqlite-row-pointer-type cols col-names))
245       (let ((result (%step (deref-pointer vm 'sqlite-vm)
246                            cols-n cols col-names)))
247         (cond
248           ((= result SQLITE-ROW)
249            (let ((n (deref-pointer cols-n :int)))
250              (values n cols col-names)))
251           ((= result SQLITE-DONE)
252            (free-foreign-object cols)
253            (free-foreign-object col-names)
254            (values 0 +null-string-pointer-pointer+ +null-string-pointer-pointer+))
255           (t
256            (free-foreign-object cols)
257            (free-foreign-object col-names)
258            (signal-sqlite-error result)))))))
259
260 (defun sqlite-finalize (vm)
261   (declare (type sqlite-vm-pointer vm))
262   (let ((result (%finalize (deref-pointer vm 'sqlite-vm) +null-errmsg-pointer+)))
263     (if (= result SQLITE-OK)
264         (progn
265           (free-foreign-object vm)
266           t)
267         (signal-sqlite-error result))))
268
269 (defun sqlite-get-table (db sql)
270   (declare (type sqlite-db-type db))
271   (with-cstring (sql-native sql)
272     (let ((rows (allocate-foreign-object '(* (* :char)))))
273       (declare (type sqlite-row-pointer-type rows))
274       (with-foreign-object (rows-n :int)
275         (with-foreign-object (cols-n :int)
276           (let ((result (%get-table db sql-native rows rows-n cols-n +null-errmsg-pointer+)))
277             (if (= result SQLITE-OK)
278                 (let ((cn (deref-pointer cols-n :int))
279                       (rn (deref-pointer rows-n :int)))
280                   (values rows rn cn))
281                 (progn
282                   (free-foreign-object rows)
283                   (signal-sqlite-error result)))))))))
284
285 (declaim (inline sqlite-free-table))
286 (defun sqlite-free-table (table)
287   (declare (type sqlite-row-pointer-type table))
288   (%free-table (deref-pointer table 'sqlite-row-pointer)))
289
290 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
291 ;;;;
292 ;;;; Utility functions.
293 ;;;;
294 (declaim (inline make-null-row))
295 (defun make-null-row ()
296   +null-string-pointer-pointer+)
297
298 (declaim (inline make-null-vm))
299 (defun make-null-vm ()
300   (uffi:make-null-pointer 'sqlite-vm))
301
302 (declaim (inline null-row-p))
303 (defun null-row-p (row)
304   (null-pointer-p row))
305
306 (declaim (inline sqlite-aref))
307 (defun sqlite-aref (a n)
308   (declare (type sqlite-row-pointer-type a))
309   (convert-from-foreign-string
310    (deref-array (deref-pointer a 'sqlite-row-pointer) '(:array (* :char)) n)))
311
312 (declaim (inline sqlite-free-row))
313 (defun sqlite-free-row (row)
314   (declare (type sqlite-row-pointer-type row))
315   (free-foreign-object row))