Directory: | src/ |
---|---|
File: | src/sqlite.c |
Date: | 2024-04-25 03:45:42 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 101 | 162 | 62.3% |
Branches: | 21 | 95 | 22.1% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2022 Egor Tensin <egor@tensin.name> | ||
3 | * This file is part of the "cimple" project. | ||
4 | * For details, see https://github.com/egor-tensin/cimple. | ||
5 | * Distributed under the MIT License. | ||
6 | */ | ||
7 | |||
8 | #include "sqlite.h" | ||
9 | #include "compiler.h" | ||
10 | #include "log.h" | ||
11 | |||
12 | #include <sqlite3.h> | ||
13 | |||
14 | #include <stddef.h> | ||
15 | #include <stdio.h> | ||
16 | #include <stdlib.h> | ||
17 | #include <string.h> | ||
18 | |||
19 | #define sqlite_errno(var, fn) \ | ||
20 | do { \ | ||
21 | log_err("%s: %s\n", fn, sqlite3_errstr(var)); \ | ||
22 | var = -var; \ | ||
23 | } while (0) | ||
24 | |||
25 | #define sqlite_errno_if(expr, fn) \ | ||
26 | do { \ | ||
27 | int CONCAT(ret, __LINE__) = expr; \ | ||
28 | if (CONCAT(ret, __LINE__)) \ | ||
29 | sqlite_errno(CONCAT(ret, __LINE__), fn); \ | ||
30 | } while (0) | ||
31 | |||
32 | 29 | int sqlite_init(void) | |
33 | { | ||
34 | 29 | int ret = 0; | |
35 | |||
36 | 29 | ret = sqlite3_initialize(); | |
37 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret) { |
38 | ✗ | sqlite_errno(ret, "sqlite3_initialize"); | |
39 | ✗ | return ret; | |
40 | } | ||
41 | |||
42 | 29 | return ret; | |
43 | } | ||
44 | |||
45 | 29 | void sqlite_destroy(void) | |
46 | { | ||
47 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
29 | sqlite_errno_if(sqlite3_shutdown(), "sqlite3_shutdown"); |
48 | 29 | } | |
49 | |||
50 | 29 | static int sqlite_open(const char *path, sqlite3 **db, int flags) | |
51 | { | ||
52 | 29 | int ret = 0; | |
53 | |||
54 | 29 | ret = sqlite3_open_v2(path, db, flags, NULL); | |
55 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret) { |
56 | ✗ | sqlite_errno(ret, "sqlite3_open_v2"); | |
57 | ✗ | return ret; | |
58 | } | ||
59 | |||
60 | 29 | return ret; | |
61 | } | ||
62 | |||
63 | 29 | int sqlite_open_rw(const char *path, sqlite3 **db) | |
64 | { | ||
65 | static const int flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; | ||
66 | 29 | return sqlite_open(path, db, flags); | |
67 | } | ||
68 | |||
69 | ✗ | int sqlite_open_ro(const char *path, sqlite3 **db) | |
70 | { | ||
71 | static const int flags = SQLITE_OPEN_READONLY; | ||
72 | ✗ | return sqlite_open(path, db, flags); | |
73 | } | ||
74 | |||
75 | 29 | void sqlite_close(sqlite3 *db) | |
76 | { | ||
77 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
29 | sqlite_errno_if(sqlite3_close(db), "sqlite3_close"); |
78 | 29 | } | |
79 | |||
80 | 58 | int sqlite_exec(sqlite3 *db, const char *stmt, sqlite3_callback callback, void *arg) | |
81 | { | ||
82 | 58 | int ret = 0; | |
83 | |||
84 | 58 | ret = sqlite3_exec(db, stmt, callback, arg, NULL); | |
85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
|
58 | if (ret) { |
86 | ✗ | sqlite_errno(ret, "sqlite3_exec"); | |
87 | ✗ | return ret; | |
88 | } | ||
89 | |||
90 | 58 | return ret; | |
91 | } | ||
92 | |||
93 | ✗ | int sqlite_exec_log_result(UNUSED void *arg, int numof_columns, char **values, char **column_names) | |
94 | { | ||
95 | ✗ | log("Row:\n"); | |
96 | ✗ | for (int i = 0; i < numof_columns; ++i) { | |
97 | ✗ | log("\t%s: %s\n", column_names[i], values[i]); | |
98 | } | ||
99 | ✗ | return 0; | |
100 | } | ||
101 | |||
102 | 203 | int sqlite_prepare(sqlite3 *db, const char *stmt, sqlite3_stmt **result) | |
103 | { | ||
104 | 203 | int ret = 0; | |
105 | |||
106 | 203 | ret = sqlite3_prepare_v2(db, stmt, -1, result, NULL); | |
107 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 203 times.
|
203 | if (ret) { |
108 | ✗ | sqlite_errno(ret, "sqlite3_prepare_v2"); | |
109 | ✗ | return ret; | |
110 | } | ||
111 | |||
112 | 203 | return ret; | |
113 | } | ||
114 | |||
115 | 36775 | void sqlite_reset(sqlite3_stmt *stmt) | |
116 | { | ||
117 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 36775 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
36775 | sqlite_errno_if(sqlite3_reset(stmt), "sqlite3_reset"); |
118 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 36775 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
36775 | sqlite_errno_if(sqlite3_clear_bindings(stmt), "sqlite3_clear_bindings"); |
119 | 36775 | } | |
120 | |||
121 | 203 | void sqlite_finalize(sqlite3_stmt *stmt) | |
122 | { | ||
123 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 203 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
203 | sqlite_errno_if(sqlite3_finalize(stmt), "sqlite3_finalize"); |
124 | 203 | } | |
125 | |||
126 | 45984 | int sqlite_step(sqlite3_stmt *stmt) | |
127 | { | ||
128 | 45984 | int ret = 0; | |
129 | |||
130 | 45984 | ret = sqlite3_step(stmt); | |
131 | |||
132 |
2/3✓ Branch 0 taken 27569 times.
✓ Branch 1 taken 18415 times.
✗ Branch 2 not taken.
|
45984 | switch (ret) { |
133 | 27569 | case SQLITE_ROW: | |
134 | 27569 | return 1; | |
135 | 18415 | case SQLITE_DONE: | |
136 | 18415 | return 0; | |
137 | |||
138 | ✗ | default: | |
139 | ✗ | sqlite_errno(ret, "sqlite3_step"); | |
140 | ✗ | return ret; | |
141 | } | ||
142 | } | ||
143 | |||
144 | 45929 | int sqlite_column_int(sqlite3_stmt *stmt, int index) | |
145 | { | ||
146 | 45929 | return sqlite3_column_int(stmt, index); | |
147 | } | ||
148 | |||
149 | 18360 | int sqlite_column_text(sqlite3_stmt *stmt, int index, char **_result) | |
150 | { | ||
151 | 18360 | int ret = 0; | |
152 | |||
153 | 18360 | const unsigned char *value = sqlite3_column_text(stmt, index); | |
154 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18360 times.
|
18360 | if (!value) { |
155 | ✗ | ret = sqlite3_errcode(sqlite3_db_handle(stmt)); | |
156 | ✗ | if (ret) { | |
157 | ✗ | sqlite_errno(ret, "sqlite3_column_text"); | |
158 | ✗ | return ret; | |
159 | } | ||
160 | |||
161 | ✗ | *_result = NULL; | |
162 | ✗ | return 0; | |
163 | } | ||
164 | |||
165 | 18360 | ret = sqlite3_column_bytes(stmt, index); | |
166 | 18360 | size_t nb = (size_t)ret; | |
167 | |||
168 | 18360 | char *result = calloc(nb + 1, 1); | |
169 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18360 times.
|
18360 | if (!result) { |
170 | ✗ | log_errno("calloc"); | |
171 | ✗ | return -1; | |
172 | } | ||
173 | 18360 | memcpy(result, value, nb); | |
174 | |||
175 | 18360 | *_result = result; | |
176 | 18360 | return 0; | |
177 | } | ||
178 | |||
179 | ✗ | int sqlite_column_blob(sqlite3_stmt *stmt, int index, unsigned char **_result) | |
180 | { | ||
181 | ✗ | int ret = 0; | |
182 | |||
183 | ✗ | const unsigned char *value = sqlite3_column_blob(stmt, index); | |
184 | ✗ | if (!value) { | |
185 | ✗ | ret = sqlite3_errcode(sqlite3_db_handle(stmt)); | |
186 | ✗ | if (ret) { | |
187 | ✗ | sqlite_errno(ret, "sqlite3_column_text"); | |
188 | ✗ | return ret; | |
189 | } | ||
190 | |||
191 | ✗ | *_result = NULL; | |
192 | ✗ | return 0; | |
193 | } | ||
194 | |||
195 | ✗ | ret = sqlite3_column_bytes(stmt, index); | |
196 | ✗ | size_t nb = (size_t)ret; | |
197 | |||
198 | ✗ | unsigned char *result = malloc(nb); | |
199 | ✗ | if (!result) { | |
200 | ✗ | log_errno("malloc"); | |
201 | ✗ | return -1; | |
202 | } | ||
203 | ✗ | memcpy(result, value, nb); | |
204 | |||
205 | ✗ | *_result = result; | |
206 | ✗ | return 0; | |
207 | } | ||
208 | |||
209 | 45929 | int sqlite_bind_int(sqlite3_stmt *stmt, int index, int value) | |
210 | { | ||
211 | 45929 | int ret = 0; | |
212 | |||
213 | 45929 | ret = sqlite3_bind_int(stmt, index, value); | |
214 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 45929 times.
|
45929 | if (ret) { |
215 | ✗ | sqlite_errno(ret, "sqlite3_bind_int"); | |
216 | ✗ | return ret; | |
217 | } | ||
218 | |||
219 | 45929 | return ret; | |
220 | } | ||
221 | |||
222 | 27540 | int sqlite_bind_text(sqlite3_stmt *stmt, int index, const char *value) | |
223 | { | ||
224 | 27540 | int ret = 0; | |
225 | |||
226 | 27540 | ret = sqlite3_bind_text(stmt, index, value, -1, SQLITE_STATIC); | |
227 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27540 times.
|
27540 | if (ret) { |
228 | ✗ | sqlite_errno(ret, "sqlite3_bind_text"); | |
229 | ✗ | return ret; | |
230 | } | ||
231 | |||
232 | 27540 | return ret; | |
233 | } | ||
234 | |||
235 | 9180 | int sqlite_bind_blob(sqlite3_stmt *stmt, int index, unsigned char *value, size_t nb) | |
236 | { | ||
237 | 9180 | int ret = 0; | |
238 | |||
239 | 9180 | ret = sqlite3_bind_blob64(stmt, index, value, nb, SQLITE_STATIC); | |
240 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret) { |
241 | ✗ | sqlite_errno(ret, "sqlite3_bind_blob64"); | |
242 | ✗ | return ret; | |
243 | } | ||
244 | |||
245 | 9180 | return ret; | |
246 | } | ||
247 | |||
248 | 29 | int sqlite_exec_as_transaction(sqlite3 *db, const char *stmt) | |
249 | { | ||
250 | static const char *const fmt = "BEGIN; %s COMMIT;"; | ||
251 | 29 | int ret = 0; | |
252 | |||
253 | 29 | ret = snprintf(NULL, 0, fmt, stmt); | |
254 | 29 | size_t nb = (size_t)ret + 1; | |
255 | 29 | ret = 0; | |
256 | |||
257 | 29 | char *full_stmt = malloc(nb); | |
258 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (!full_stmt) { |
259 | ✗ | log_errno("malloc"); | |
260 | ✗ | return -1; | |
261 | } | ||
262 | 29 | snprintf(full_stmt, nb, fmt, stmt); | |
263 | |||
264 | 29 | ret = sqlite_exec(db, stmt, NULL, NULL); | |
265 | 29 | goto free; | |
266 | |||
267 | 29 | free: | |
268 | 29 | free(full_stmt); | |
269 | |||
270 | 29 | return ret; | |
271 | } | ||
272 | |||
273 | 29 | int sqlite_get_user_version(sqlite3 *db, unsigned int *output) | |
274 | { | ||
275 | static const char *const sql = "PRAGMA user_version;"; | ||
276 | |||
277 | 29 | sqlite3_stmt *stmt = NULL; | |
278 | 29 | int result = -1, ret = 0; | |
279 | |||
280 | 29 | ret = sqlite_prepare(db, sql, &stmt); | |
281 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
282 | ✗ | return ret; | |
283 | 29 | ret = sqlite_step(stmt); | |
284 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
285 | ✗ | goto finalize; | |
286 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (!ret) { |
287 | ✗ | ret = -1; | |
288 | ✗ | log_err("Failed to read database version\n"); | |
289 | ✗ | goto finalize; | |
290 | } | ||
291 | |||
292 | 29 | result = sqlite_column_int(stmt, 0); | |
293 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (result < 0) { |
294 | ✗ | log_err("Invalid database version: %d\n", result); | |
295 | ✗ | ret = -1; | |
296 | ✗ | goto finalize; | |
297 | } | ||
298 | 29 | *output = (unsigned int)result; | |
299 | |||
300 | 29 | goto finalize; | |
301 | |||
302 | 29 | finalize: | |
303 | 29 | sqlite_finalize(stmt); | |
304 | |||
305 | 29 | return ret; | |
306 | } | ||
307 | |||
308 | 29 | int sqlite_set_foreign_keys(sqlite3 *db) | |
309 | { | ||
310 | static const char *const sql = "PRAGMA foreign_keys = ON;"; | ||
311 | 29 | return sqlite_exec(db, sql, NULL, NULL); | |
312 | } | ||
313 |