-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsqlite3.carp
More file actions
224 lines (185 loc) · 6.78 KB
/
sqlite3.carp
File metadata and controls
224 lines (185 loc) · 6.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
(relative-include "sqlite3_helper.h")
(add-cflag "-lsqlite3")
(doc SQLite3 "is a simple high-level wrapper around SQLite3. It doesn’t intend
to wrap everything, but it tries to be useful.
## Installation
```clojure
(load \"git@veitheller.de:git/carpentry/sqlite3.git@0.0.6\")
```
## Usage
The module `SQLite3` provides facilities for opening, closing, and querying
databases.
```clojure
(load \"git@veitheller.de:git/carpentry/sqlite3.git@0.0.6\")
; opening DBs can fail, for the purposes of this example we
; ignore that
(defn main []
(let-do [db (Result.unsafe-from-success (SQLite3.open \"db\"))]
; Let's make sure our table is there
(ignore
(SQLite3.query &db
\"CREATE TABLE IF NOT EXISTS mytable (name TEXT, age INT)\"
&[]))
; we can prepare statements
(ignore
(SQLite3.query &db
\"INSERT INTO mytable VALUES (?1, ?2);\"
&[(to-sqlite3 @\"Carp\") (to-sqlite3 4)]))
; and query things
(println* &(SQLite3.query &db \"SELECT * from mytable;\" &[]))
(SQLite3.close db)))
```
Because `open` and `query` return `Result` types, we could also use
combinators!")
(defmodule SQLite3
(private sql_ok)
(hidden sql_ok)
(register sql_ok Int "SQLITE_OK")
(private sql_int)
(hidden sql_int)
(register sql_int Int "SQLITE_INTEGER")
(private sql_double)
(hidden sql_double)
(register sql_double Int "SQLITE_FLOAT")
(private sql_text)
(hidden sql_text)
(register sql_text Int "SQLITE_TEXT")
(private sql_blob)
(hidden sql_blob)
(register sql_blob Int "SQLITE_BLOB")
(doc SQLite "is the opaque database type. You’ll need one of those to query
anything.
It can be obtained by using [open](#open).")
(register-type SQLite)
(doc Type "represent all the SQLite types we can represent.
The constructors are `Null`, `Integer`, `Floating`, `Text`, and `Blob`. Most
primitive Carp types can be casted to appropriate SQLite types by using the
`to-sqlite3` interface.")
(deftype Type
(Null [])
(Integer [Long])
(Floating [Double])
(Text [String])
(Blob [String]))
(private SQLiteColumn)
(hidden SQLiteColumn)
(register-type SQLiteColumn)
(defmodule Type
(defmodule SQLiteColumn
(register nil (Fn [] SQLiteColumn) "SQLiteColumn_nil")
(register int (Fn [Long] SQLiteColumn) "SQLiteColumn_int")
(register float (Fn [Double] SQLiteColumn) "SQLiteColumn_float")
(register text (Fn [String] SQLiteColumn) "SQLiteColumn_text")
(register blob (Fn [String] SQLiteColumn) "SQLiteColumn_blob"))
(defn = [a b]
(match-ref a
(Null) (match-ref b (Null) true _ false)
(Integer ai) (match-ref b (Integer bi) (= ai bi) _ false)
(Floating af) (match-ref b (Floating bf) (= af bf) _ false)
(Text as) (match-ref b (Text bs) (= as bs) _ false)
(Blob ab) (match-ref b (Blob bb) (= ab bb) _ false)))
(implements = SQLite3.Type.=)
(defn prn [s] (SQLite3.Type.str s))
(implements prn SQLite3.Type.prn)
(defn to-sqlite3-internal [x]
(match x
(Null) (SQLiteColumn.nil)
(Integer i) (SQLiteColumn.int i)
(Floating f) (SQLiteColumn.float f)
(Text s) (SQLiteColumn.text s)
(Blob s) (SQLiteColumn.blob s))))
(defmodule SQLiteColumn
(register tag (Fn [&SQLiteColumn] Int) "SQLiteColumn_tag")
(register from-integer (Fn [SQLiteColumn] Long) "SQLiteColumn_from_int")
(register from-floating (Fn [SQLiteColumn] Double) "SQLiteColumn_from_float")
(register from-text (Fn [SQLiteColumn] String) "SQLiteColumn_from_str")
(defn to-carp [c]
(case (tag &c)
sql_int
(Type.Integer (from-integer c))
sql_double
(Type.Floating (from-floating c))
sql_text
(Type.Text (from-text c))
sql_blob
(Type.Blob (from-text c))
(Type.Null))))
(private SQLiteRow)
(hidden SQLiteRow)
(register-type SQLiteRow)
(defmodule SQLiteRow
(register length (Fn [&SQLiteRow] Int) "SQLiteRow_length")
(register nth (Fn [&SQLiteRow Int] SQLiteColumn) "SQLiteRow_nth")
(defn to-carp [r]
(let-do [l (length &r)
a (Array.allocate l)]
(for [i 0 l]
(Array.aset-uninitialized! &a i (SQLiteColumn.to-carp (nth &r i))))
a)))
(private SQLiteRes)
(hidden SQLiteRes)
(register-type SQLiteRes)
(defmodule SQLiteRes
(register ok? (Fn [&SQLiteRes] Bool) "SQLiteRes_is_ok")
(register length (Fn [&SQLiteRes] Int) "SQLiteRes_length")
(register nth (Fn [&SQLiteRes Int] SQLiteRow) "SQLiteRes_nth")
(register error (Fn [SQLiteRes] (Ptr CChar)) "SQLiteRes_error")
(defn to-array [r]
(let-do [l (length &r)
a (Array.allocate l)]
(for [i 0 l]
(Array.aset-uninitialized! &a i (SQLiteRow.to-carp (nth &r i))))
a)))
(private init)
(hidden init)
(register init (Fn [] SQLite))
(private open-)
(hidden open-)
(register open- (Fn [&SQLite (Ptr CChar)] Int) "SQLite3_open_c")
(private exec-)
(hidden exec-)
(register exec-
(Fn [&SQLite (Ptr CChar) &(Array SQLiteColumn)] SQLiteRes)
"SQLite3_exec_c")
(private error-)
(hidden error-)
(register error- (Fn [SQLite] (Ptr CChar)) "SQLite3_error")
(doc open "opens a database with the filename `s`.
If it fails, we return an error message using `Result.Error`.")
(defn open [s]
(let [db (SQLite3.init)
res (open- &db (cstr s))]
(if (= res sql_ok)
(Result.Success db)
(Result.Error (from-cstr (error- db))))))
(doc query "queries the database `db` using the query `s` and the parameters
`p`.
If it fails, we return an error message using `Result.Error`.")
(defn query [db s p]
(let [r (exec- db
(cstr s)
&(Array.copy-map &(fn [x] (Type.to-sqlite3-internal @x)) p))]
(if (SQLiteRes.ok? &r)
(Result.Success (SQLiteRes.to-array r))
(Result.Error (from-cstr (SQLiteRes.error r))))))
(doc close "closes a database.")
(register close (Fn [SQLite] ()) "SQLite3_close_c"))
(definterface to-sqlite3 (Fn [a] SQLite3.Type))
(defmodule Bool
(defn to-sqlite3 [b] (SQLite3.Type.Integer (if b 1l 0l)))
(implements to-sqlite3 Bool.to-sqlite3))
(defmodule Int
(defn to-sqlite3 [i] (SQLite3.Type.Integer (Long.from-int i)))
(implements to-sqlite3 Int.to-sqlite3))
(defmodule Long
(defn to-sqlite3 [l] (SQLite3.Type.Integer l))
(implements to-sqlite3 Long.to-sqlite3))
(defmodule Float
(defn to-sqlite3 [f] (SQLite3.Type.Floating (Double.from-float f)))
(implements to-sqlite3 Float.to-sqlite3))
(defmodule Double
(defn to-sqlite3 [d] (SQLite3.Type.Floating d))
(implements to-sqlite3 Double.to-sqlite3))
(defmodule String
(defn to-sqlite3 [s] (SQLite3.Type.Text s))
(implements to-sqlite3 String.to-sqlite3))