1
|
;;;
|
2
|
;;;
|
3
|
;;;
|
4
|
(define-module dbd.sqlite3
|
5
|
(use dbi)
|
6
|
(use gauche.uvector)
|
7
|
(use util.list)
|
8
|
(use util.match)
|
9
|
(use gauche.collection)
|
10
|
(use gauche.sequence)
|
11
|
(use util.relation)
|
12
|
(export <sqlite3-driver>
|
13
|
<sqlite3-connection>
|
14
|
<sqlite3-result-set>
|
15
|
))
|
16
|
|
17
|
(select-module dbd.sqlite3)
|
18
|
(dynamic-load "dbd_sqlite3")
|
19
|
|
20
|
|
21
|
(define-class <sqlite3-driver> (<dbi-driver>)
|
22
|
())
|
23
|
|
24
|
(define-class <sqlite3-connection> (<dbi-connection>)
|
25
|
((%handle :init-keyword :handle :init-value #f)))
|
26
|
|
27
|
(define-class <sqlite3-result-set> (<relation> <collection>)
|
28
|
((%handle :init-keyword :handle :init-value #f)
|
29
|
(%prev :init-keyword :prev :init-value #f)
|
30
|
(field-names :init-keyword :field-names :init-value #f)
|
31
|
(rows :init-form #f)))
|
32
|
|
33
|
(define-condition-type <sqlite3-error> <dbi-error> #f
|
34
|
(error-code))
|
35
|
|
36
|
|
37
|
(define (sqlite3-step result-set)
|
38
|
(let1 v (sqlite-c-stmt-step (slot-ref result-set '%handle))
|
39
|
(slot-set! result-set '%prev v)
|
40
|
v))
|
41
|
|
42
|
(define (sqlite3-execute db query)
|
43
|
(let ((stmt (make-sqlite-stmt))
|
44
|
(result-set (make <sqlite3-result-set>)))
|
45
|
(if (sqlite-c-execute db stmt query)
|
46
|
(begin (slot-set! result-set '%handle stmt)
|
47
|
(slot-set! result-set 'field-names (sqlite-c-stmt-column-names stmt))
|
48
|
result-set)
|
49
|
#f)))
|
50
|
|
51
|
(define-method dbi-make-connection ((d <sqlite3-driver>)
|
52
|
(options <string>)
|
53
|
(option-alist <list>)
|
54
|
. args)
|
55
|
(let* ((db-name (match option-alist
|
56
|
(((maybe-db . #t) . rest) maybe-db)
|
57
|
(else (assoc-ref option-alist "db" #f))))
|
58
|
(conn (make <sqlite3-connection>)))
|
59
|
(with-error-handler (lambda (e) (error <dbi-error> :message "SQLite3 open failed"))
|
60
|
(lambda () (slot-set! conn '%handle (sqlite-c-open db-name))))
|
61
|
conn))
|
62
|
|
63
|
|
64
|
(define-method dbi-execute-using-connection ((c <sqlite3-connection>)
|
65
|
(q <dbi-query>)
|
66
|
params)
|
67
|
(let* ((handle (slot-ref c '%handle))
|
68
|
(query-string (apply (slot-ref q 'prepared) params))
|
69
|
(result #f))
|
70
|
; (print query-string)
|
71
|
(with-error-handler (lambda (e) (error <dbi-error> :message (slot-ref e 'message)))
|
72
|
(lambda () (set! result (sqlite3-execute handle query-string))))
|
73
|
(if result
|
74
|
(begin (sqlite3-step result)
|
75
|
result)
|
76
|
(errorf <dbi-error> :error-message (sqlite-c-error-message handle)
|
77
|
"SQLite3 query failed: ~a" (sqlite-c-error-message handle)))))
|
78
|
|
79
|
|
80
|
(define-method dbi-close ((result-set <sqlite3-result-set>))
|
81
|
(sqlite-c-stmt-finish (slot-ref result-set '%handle)))
|
82
|
|
83
|
(define-method dbi-close ((c <sqlite3-connection>))
|
84
|
(with-error-handler
|
85
|
(lambda (e) (error <dbi-error> :message (slot-ref e 'message)))
|
86
|
(cut sqlite-c-close (slot-ref c '%handle))))
|
87
|
|
88
|
|
89
|
(define-method dbi-open? ((c <sqlite3-connection>))
|
90
|
(not (sqlite-c-closed-p (slot-ref c '%handle))))
|
91
|
|
92
|
(define-method dbi-escape-sql ((c <sqlite3-connection>) str)
|
93
|
(sqlite-c-escape-string str))
|
94
|
|
95
|
;; (define-method call-with-iterator ((r <sqlite3-result-set>) proc . option)
|
96
|
;; (let* ((prev #f)
|
97
|
;; (end? (cut sqlite-c-stmt-end-p (slot-ref r '%handle)))
|
98
|
;; (next (lambda ()
|
99
|
;; (set! prev (slot-ref r '%prev))
|
100
|
;; (with-error-handler (lambda (e)
|
101
|
;; (error <dbi-error> :message (slot-ref e 'message)))
|
102
|
;; (cut sqlite3-step r))
|
103
|
;; prev)))
|
104
|
;; (proc end? next)))
|
105
|
|
106
|
(define (get-all-rows r)
|
107
|
(let loop ((rows (list (slot-ref r '%prev))))
|
108
|
(let1 row (sqlite3-step r)
|
109
|
(if row
|
110
|
(loop (cons row rows))
|
111
|
(reverse! rows)))))
|
112
|
|
113
|
(define-method call-with-iterator ((r <sqlite3-result-set>) proc . option)
|
114
|
(unless (dbi-open? r)
|
115
|
(error <dbi-error> "<sqlite3-result> already closed:" r))
|
116
|
(unless (ref r 'rows)
|
117
|
(set! (ref r 'rows) (get-all-rows r)))
|
118
|
(call-with-iterator (ref r 'rows) proc))
|
119
|
|
120
|
(define-method relation-rows ((r <sqlite3-result-set>))
|
121
|
(slot-ref r 'rows))
|
122
|
|
123
|
(define-method relation-column-names ((result-set <sqlite3-result-set>))
|
124
|
(ref result-set 'field-names))
|
125
|
|
126
|
(define-method relation-accessor ((result-set <sqlite3-result-set>))
|
127
|
(let1 columns (ref result-set 'field-names)
|
128
|
(lambda (row column . maybe-default)
|
129
|
(cond ((find-index (cut string=? <> column) columns)
|
130
|
=> (cut vector-ref row <>))
|
131
|
((pair? maybe-default) (car maybe-default))
|
132
|
(else (error "invalud column name:" column))))))
|
133
|
|
134
|
(provide "dbd/sqlite3")
|