Directory: | src/ |
---|---|
File: | src/storage_sqlite.c |
Date: | 2023-08-28 07:33:56 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 197 | 303 | 65.0% |
Branches: | 54 | 146 | 37.0% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2022 Egor Tensin <Egor.Tensin@gmail.com> | ||
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 "storage_sqlite.h" | ||
9 | #include "log.h" | ||
10 | #include "process.h" | ||
11 | #include "run_queue.h" | ||
12 | #include "sql/sqlite_sql.h" | ||
13 | #include "sqlite.h" | ||
14 | #include "storage.h" | ||
15 | |||
16 | #include <sqlite3.h> | ||
17 | |||
18 | #include <pthread.h> | ||
19 | #include <stdio.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <string.h> | ||
22 | |||
23 | struct storage_sqlite_settings { | ||
24 | char *path; | ||
25 | }; | ||
26 | |||
27 | 29 | int storage_sqlite_settings_create(struct storage_settings *settings, const char *path) | |
28 | { | ||
29 | 29 | struct storage_sqlite_settings *sqlite = malloc(sizeof(struct storage_sqlite_settings)); | |
30 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (!sqlite) { |
31 | ✗ | log_errno("malloc"); | |
32 | ✗ | return -1; | |
33 | } | ||
34 | |||
35 | 29 | sqlite->path = strdup(path); | |
36 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (!sqlite->path) { |
37 | ✗ | log_errno("strdup"); | |
38 | ✗ | goto free; | |
39 | } | ||
40 | |||
41 | 29 | settings->type = STORAGE_TYPE_SQLITE; | |
42 | 29 | settings->sqlite = sqlite; | |
43 | 29 | return 0; | |
44 | |||
45 | ✗ | free: | |
46 | ✗ | free(sqlite); | |
47 | |||
48 | ✗ | return -1; | |
49 | } | ||
50 | |||
51 | 29 | void storage_sqlite_settings_destroy(const struct storage_settings *settings) | |
52 | { | ||
53 | 29 | free(settings->sqlite->path); | |
54 | 29 | free(settings->sqlite); | |
55 | 29 | } | |
56 | |||
57 | enum run_status { | ||
58 | RUN_STATUS_CREATED = 1, | ||
59 | RUN_STATUS_FINISHED = 2, | ||
60 | }; | ||
61 | |||
62 | struct prepared_stmt { | ||
63 | pthread_mutex_t mtx; | ||
64 | sqlite3_stmt *impl; | ||
65 | }; | ||
66 | |||
67 | 116 | static int prepared_stmt_init(struct prepared_stmt *stmt, sqlite3 *db, const char *sql) | |
68 | { | ||
69 | 116 | int ret = 0; | |
70 | |||
71 | 116 | ret = pthread_mutex_init(&stmt->mtx, NULL); | |
72 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
|
116 | if (ret) { |
73 | ✗ | pthread_errno(ret, "pthread_mutex_init"); | |
74 | ✗ | return ret; | |
75 | } | ||
76 | |||
77 | 116 | ret = sqlite_prepare(db, sql, &stmt->impl); | |
78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
|
116 | if (ret < 0) |
79 | ✗ | goto destroy_mtx; | |
80 | |||
81 | 116 | return ret; | |
82 | |||
83 | ✗ | destroy_mtx: | |
84 | ✗ | pthread_errno_if(pthread_mutex_destroy(&stmt->mtx), "pthread_mutex_destroy"); | |
85 | |||
86 | ✗ | return ret; | |
87 | } | ||
88 | |||
89 | 116 | static void prepared_stmt_destroy(struct prepared_stmt *stmt) | |
90 | { | ||
91 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 116 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
116 | pthread_errno_if(pthread_mutex_destroy(&stmt->mtx), "pthread_mutex_destroy"); |
92 | 116 | sqlite_finalize(stmt->impl); | |
93 | 116 | } | |
94 | |||
95 | 36720 | static int prepared_stmt_lock(struct prepared_stmt *stmt) | |
96 | { | ||
97 | 36720 | int ret = pthread_mutex_lock(&stmt->mtx); | |
98 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36720 times.
|
36720 | if (ret) { |
99 | ✗ | pthread_errno(ret, "pthread_mutex_unlock"); | |
100 | ✗ | return ret; | |
101 | } | ||
102 | 36720 | return ret; | |
103 | } | ||
104 | |||
105 | 36720 | static void prepared_stmt_unlock(struct prepared_stmt *stmt) | |
106 | { | ||
107 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 36720 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
36720 | pthread_errno_if(pthread_mutex_unlock(&stmt->mtx), "pthread_mutex_unlock"); |
108 | 36720 | } | |
109 | |||
110 | struct storage_sqlite { | ||
111 | sqlite3 *db; | ||
112 | |||
113 | struct prepared_stmt stmt_repo_find; | ||
114 | struct prepared_stmt stmt_repo_insert; | ||
115 | struct prepared_stmt stmt_run_insert; | ||
116 | struct prepared_stmt stmt_run_finished; | ||
117 | }; | ||
118 | |||
119 | 29 | static int storage_sqlite_upgrade_to(struct storage_sqlite *storage, size_t version) | |
120 | { | ||
121 | static const char *const fmt = "%s PRAGMA user_version = %zu;"; | ||
122 | |||
123 | 29 | const char *script = sqlite_schemas[version]; | |
124 | 29 | int ret = 0; | |
125 | |||
126 | 29 | ret = snprintf(NULL, 0, fmt, script, version + 1); | |
127 | 29 | size_t nb = (size_t)ret + 1; | |
128 | 29 | ret = 0; | |
129 | |||
130 | 29 | char *full_script = malloc(nb); | |
131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (!full_script) { |
132 | ✗ | log_errno("malloc"); | |
133 | ✗ | return -1; | |
134 | } | ||
135 | 29 | snprintf(full_script, nb, fmt, script, version + 1); | |
136 | |||
137 | 29 | ret = sqlite_exec_as_transaction(storage->db, full_script); | |
138 | 29 | goto free; | |
139 | |||
140 | 29 | free: | |
141 | 29 | free(full_script); | |
142 | |||
143 | 29 | return ret; | |
144 | } | ||
145 | |||
146 | 29 | static int storage_sqlite_upgrade_from_to(struct storage_sqlite *storage, size_t from, size_t to) | |
147 | { | ||
148 | 29 | int ret = 0; | |
149 | |||
150 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 29 times.
|
58 | for (size_t i = from; i < to; ++i) { |
151 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
|
29 | log("Upgrading SQLite database from version %zu to version %zu\n", i, i + 1); |
152 | 29 | ret = storage_sqlite_upgrade_to(storage, i); | |
153 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) { |
154 | ✗ | log_err("Failed to upgrade to version %zu\n", i + 1); | |
155 | ✗ | return ret; | |
156 | } | ||
157 | } | ||
158 | |||
159 | 29 | return ret; | |
160 | } | ||
161 | |||
162 | 29 | static int storage_sqlite_upgrade(struct storage_sqlite *storage) | |
163 | { | ||
164 | 29 | unsigned int current_version = 0; | |
165 | 29 | int ret = 0; | |
166 | |||
167 | 29 | ret = sqlite_get_user_version(storage->db, ¤t_version); | |
168 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
169 | ✗ | return ret; | |
170 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
|
29 | log("SQLite database version: %u\n", current_version); |
171 | |||
172 | 29 | size_t newest_version = sizeof(sqlite_schemas) / sizeof(sqlite_schemas[0]); | |
173 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
|
29 | log("Newest database version: %zu\n", newest_version); |
174 | |||
175 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (current_version > newest_version) { |
176 | ✗ | log_err("Unknown database version: %u\n", current_version); | |
177 | ✗ | return -1; | |
178 | } | ||
179 | |||
180 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (current_version == newest_version) { |
181 | ✗ | log("SQLite database already at the newest version\n"); | |
182 | ✗ | return 0; | |
183 | } | ||
184 | |||
185 | 29 | return storage_sqlite_upgrade_from_to(storage, current_version, newest_version); | |
186 | } | ||
187 | |||
188 | 29 | static int storage_sqlite_setup(struct storage_sqlite *storage) | |
189 | { | ||
190 | 29 | int ret = 0; | |
191 | |||
192 | 29 | ret = sqlite_set_foreign_keys(storage->db); | |
193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
194 | ✗ | return ret; | |
195 | |||
196 | 29 | ret = storage_sqlite_upgrade(storage); | |
197 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
198 | ✗ | return ret; | |
199 | |||
200 | 29 | return ret; | |
201 | } | ||
202 | |||
203 | 29 | static int storage_sqlite_prepare_statements(struct storage_sqlite *storage) | |
204 | { | ||
205 | static const char *const fmt_repo_find = "SELECT id FROM cimple_repos WHERE url = ?;"; | ||
206 | static const char *const fmt_repo_insert = | ||
207 | "INSERT INTO cimple_repos(url) VALUES (?) ON CONFLICT(url) DO NOTHING;"; | ||
208 | static const char *const fmt_run_insert = | ||
209 | "INSERT INTO cimple_runs(status, exit_code, output, repo_id, repo_rev) VALUES (?, -1, x'', ?, ?) RETURNING id;"; | ||
210 | static const char *const fmt_run_finished = | ||
211 | "UPDATE cimple_runs SET status = ?, exit_code = ?, output = ? WHERE id = ?;"; | ||
212 | |||
213 | 29 | int ret = 0; | |
214 | |||
215 | 29 | ret = prepared_stmt_init(&storage->stmt_repo_find, storage->db, fmt_repo_find); | |
216 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
217 | ✗ | return ret; | |
218 | 29 | ret = prepared_stmt_init(&storage->stmt_repo_insert, storage->db, fmt_repo_insert); | |
219 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
220 | ✗ | goto finalize_repo_find; | |
221 | 29 | ret = prepared_stmt_init(&storage->stmt_run_insert, storage->db, fmt_run_insert); | |
222 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
223 | ✗ | goto finalize_repo_insert; | |
224 | 29 | ret = prepared_stmt_init(&storage->stmt_run_finished, storage->db, fmt_run_finished); | |
225 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
226 | ✗ | goto finalize_run_insert; | |
227 | |||
228 | 29 | return ret; | |
229 | |||
230 | ✗ | finalize_run_insert: | |
231 | ✗ | prepared_stmt_destroy(&storage->stmt_run_insert); | |
232 | ✗ | finalize_repo_insert: | |
233 | ✗ | prepared_stmt_destroy(&storage->stmt_repo_insert); | |
234 | ✗ | finalize_repo_find: | |
235 | ✗ | prepared_stmt_destroy(&storage->stmt_repo_find); | |
236 | |||
237 | ✗ | return ret; | |
238 | } | ||
239 | |||
240 | 29 | static void storage_sqlite_finalize_statements(struct storage_sqlite *storage) | |
241 | { | ||
242 | 29 | prepared_stmt_destroy(&storage->stmt_run_finished); | |
243 | 29 | prepared_stmt_destroy(&storage->stmt_run_insert); | |
244 | 29 | prepared_stmt_destroy(&storage->stmt_repo_insert); | |
245 | 29 | prepared_stmt_destroy(&storage->stmt_repo_find); | |
246 | 29 | } | |
247 | |||
248 | 29 | int storage_sqlite_create(struct storage *storage, const struct storage_settings *settings) | |
249 | { | ||
250 | 29 | int ret = 0; | |
251 | |||
252 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
|
29 | log("Using SQLite database at %s\n", settings->sqlite->path); |
253 | |||
254 | 29 | struct storage_sqlite *sqlite = malloc(sizeof(struct storage_sqlite)); | |
255 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (!sqlite) { |
256 | ✗ | log_errno("malloc"); | |
257 | ✗ | return -1; | |
258 | } | ||
259 | |||
260 | 29 | ret = sqlite_init(); | |
261 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
262 | ✗ | goto free; | |
263 | 29 | ret = sqlite_open_rw(settings->sqlite->path, &sqlite->db); | |
264 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
265 | ✗ | goto destroy; | |
266 | 29 | ret = storage_sqlite_setup(sqlite); | |
267 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
268 | ✗ | goto close; | |
269 | 29 | ret = storage_sqlite_prepare_statements(sqlite); | |
270 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
271 | ✗ | goto close; | |
272 | |||
273 | 29 | storage->sqlite = sqlite; | |
274 | 29 | return ret; | |
275 | |||
276 | ✗ | close: | |
277 | ✗ | sqlite_close(storage->sqlite->db); | |
278 | ✗ | destroy: | |
279 | ✗ | sqlite_destroy(); | |
280 | ✗ | free: | |
281 | ✗ | free(sqlite); | |
282 | |||
283 | ✗ | return ret; | |
284 | } | ||
285 | |||
286 | 29 | void storage_sqlite_destroy(struct storage *storage) | |
287 | { | ||
288 | 29 | storage_sqlite_finalize_statements(storage->sqlite); | |
289 | 29 | sqlite_close(storage->sqlite->db); | |
290 | 29 | sqlite_destroy(); | |
291 | 29 | free(storage->sqlite); | |
292 | 29 | } | |
293 | |||
294 | 9180 | static int storage_sqlite_find_repo(struct storage_sqlite *storage, const char *url) | |
295 | { | ||
296 | 9180 | struct prepared_stmt *stmt = &storage->stmt_repo_find; | |
297 | 9180 | int ret = 0; | |
298 | |||
299 | 9180 | ret = prepared_stmt_lock(stmt); | |
300 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
301 | ✗ | return ret; | |
302 | 9180 | ret = sqlite_bind_text(stmt->impl, 1, url); | |
303 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
304 | ✗ | goto reset; | |
305 | 9180 | ret = sqlite_step(stmt->impl); | |
306 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
307 | ✗ | goto reset; | |
308 | |||
309 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (!ret) |
310 | ✗ | goto reset; | |
311 | |||
312 | 9180 | ret = sqlite_column_int(stmt->impl, 0); | |
313 | 9180 | goto reset; | |
314 | |||
315 | 9180 | reset: | |
316 | 9180 | sqlite_reset(stmt->impl); | |
317 | 9180 | prepared_stmt_unlock(stmt); | |
318 | |||
319 | 9180 | return ret; | |
320 | } | ||
321 | |||
322 | 9180 | static int storage_sqlite_insert_repo(struct storage_sqlite *storage, const char *url) | |
323 | { | ||
324 | 9180 | struct prepared_stmt *stmt = &storage->stmt_repo_insert; | |
325 | 9180 | int ret = 0; | |
326 | |||
327 | 9180 | ret = prepared_stmt_lock(stmt); | |
328 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
329 | ✗ | return ret; | |
330 | 9180 | ret = sqlite_bind_text(stmt->impl, 1, url); | |
331 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
332 | ✗ | goto reset; | |
333 | 9180 | ret = sqlite_step(stmt->impl); | |
334 |
1/2✓ Branch 0 taken 9180 times.
✗ Branch 1 not taken.
|
9180 | if (ret < 0) |
335 | ✗ | goto reset; | |
336 | |||
337 | 9180 | reset: | |
338 | 9180 | sqlite_reset(stmt->impl); | |
339 | 9180 | prepared_stmt_unlock(stmt); | |
340 | |||
341 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
342 | ✗ | return ret; | |
343 | |||
344 | 9180 | return storage_sqlite_find_repo(storage, url); | |
345 | } | ||
346 | |||
347 | 9180 | static int storage_sqlite_insert_run(struct storage_sqlite *storage, int repo_id, const char *rev) | |
348 | { | ||
349 | 9180 | struct prepared_stmt *stmt = &storage->stmt_run_insert; | |
350 | 9180 | int ret = 0; | |
351 | |||
352 | 9180 | ret = prepared_stmt_lock(stmt); | |
353 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
354 | ✗ | return ret; | |
355 | 9180 | ret = sqlite_bind_int(stmt->impl, 1, RUN_STATUS_CREATED); | |
356 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
357 | ✗ | goto reset; | |
358 | 9180 | ret = sqlite_bind_int(stmt->impl, 2, repo_id); | |
359 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
360 | ✗ | goto reset; | |
361 | 9180 | ret = sqlite_bind_text(stmt->impl, 3, rev); | |
362 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
363 | ✗ | goto reset; | |
364 | 9180 | ret = sqlite_step(stmt->impl); | |
365 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
366 | ✗ | goto reset; | |
367 | |||
368 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (!ret) { |
369 | ✗ | ret = -1; | |
370 | ✗ | log_err("Failed to insert a run\n"); | |
371 | ✗ | goto reset; | |
372 | } | ||
373 | |||
374 | 9180 | ret = sqlite_column_int(stmt->impl, 0); | |
375 | 9180 | goto reset; | |
376 | |||
377 | 9180 | reset: | |
378 | 9180 | sqlite_reset(stmt->impl); | |
379 | 9180 | prepared_stmt_unlock(stmt); | |
380 | |||
381 | 9180 | return ret; | |
382 | } | ||
383 | |||
384 | 9180 | int storage_sqlite_run_create(struct storage *storage, const char *repo_url, const char *rev) | |
385 | { | ||
386 | 9180 | int ret = 0; | |
387 | |||
388 | 9180 | ret = storage_sqlite_insert_repo(storage->sqlite, repo_url); | |
389 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
390 | ✗ | return ret; | |
391 | |||
392 | 9180 | ret = storage_sqlite_insert_run(storage->sqlite, ret, rev); | |
393 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
394 | ✗ | return ret; | |
395 | |||
396 | 9180 | return ret; | |
397 | } | ||
398 | |||
399 | 9180 | int storage_sqlite_run_finished(struct storage *storage, int run_id, | |
400 | const struct proc_output *output) | ||
401 | { | ||
402 | 9180 | struct prepared_stmt *stmt = &storage->sqlite->stmt_run_finished; | |
403 | 9180 | int ret = 0; | |
404 | |||
405 | 9180 | ret = prepared_stmt_lock(stmt); | |
406 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
407 | ✗ | return ret; | |
408 | 9180 | ret = sqlite_bind_int(stmt->impl, 1, RUN_STATUS_FINISHED); | |
409 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
410 | ✗ | goto reset; | |
411 | 9180 | ret = sqlite_bind_int(stmt->impl, 2, output->ec); | |
412 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
413 | ✗ | goto reset; | |
414 | 9180 | ret = sqlite_bind_blob(stmt->impl, 3, output->data, output->data_size); | |
415 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
416 | ✗ | goto reset; | |
417 | 9180 | ret = sqlite_bind_int(stmt->impl, 4, run_id); | |
418 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9180 times.
|
9180 | if (ret < 0) |
419 | ✗ | goto reset; | |
420 | 9180 | ret = sqlite_step(stmt->impl); | |
421 |
1/2✓ Branch 0 taken 9180 times.
✗ Branch 1 not taken.
|
9180 | if (ret < 0) |
422 | ✗ | goto reset; | |
423 | |||
424 | 9180 | reset: | |
425 | 9180 | sqlite_reset(stmt->impl); | |
426 | 9180 | prepared_stmt_unlock(stmt); | |
427 | |||
428 | 9180 | return ret; | |
429 | } | ||
430 | |||
431 | ✗ | static int storage_sqlite_row_to_run(struct sqlite3_stmt *stmt, struct run **run) | |
432 | { | ||
433 | ✗ | int ret = 0; | |
434 | |||
435 | ✗ | int id = sqlite_column_int(stmt, 0); | |
436 | |||
437 | ✗ | char *url = NULL; | |
438 | ✗ | ret = sqlite_column_text(stmt, 1, &url); | |
439 | ✗ | if (ret < 0) | |
440 | ✗ | return ret; | |
441 | |||
442 | ✗ | char *rev = NULL; | |
443 | ✗ | ret = sqlite_column_text(stmt, 2, &rev); | |
444 | ✗ | if (ret < 0) | |
445 | ✗ | goto free_url; | |
446 | |||
447 | ✗ | ret = run_create(run, id, url, rev); | |
448 | ✗ | if (ret < 0) | |
449 | ✗ | goto free_rev; | |
450 | |||
451 | ✗ | log("Adding a run %d for repository %s to the queue\n", id, url); | |
452 | |||
453 | ✗ | free_rev: | |
454 | ✗ | free(rev); | |
455 | |||
456 | ✗ | free_url: | |
457 | ✗ | free(url); | |
458 | |||
459 | ✗ | return ret; | |
460 | } | ||
461 | |||
462 | 29 | int storage_sqlite_get_run_queue(struct storage *storage, struct run_queue *queue) | |
463 | { | ||
464 | static const char *const fmt = | ||
465 | "SELECT id, repo_url, repo_rev FROM cimple_runs_view WHERE status = ?;"; | ||
466 | |||
467 | sqlite3_stmt *stmt; | ||
468 | 29 | int ret = 0; | |
469 | |||
470 | 29 | ret = sqlite_prepare(storage->sqlite->db, fmt, &stmt); | |
471 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
472 | ✗ | return ret; | |
473 | 29 | ret = sqlite_bind_int(stmt, 1, RUN_STATUS_CREATED); | |
474 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | if (ret < 0) |
475 | ✗ | goto finalize; | |
476 | |||
477 | 29 | run_queue_create(queue); | |
478 | |||
479 | ✗ | while (1) { | |
480 | 29 | ret = sqlite_step(stmt); | |
481 |
1/2✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
|
29 | if (!ret) |
482 | 29 | break; | |
483 | ✗ | if (ret < 0) | |
484 | ✗ | goto run_queue_destroy; | |
485 | |||
486 | ✗ | struct run *run = NULL; | |
487 | |||
488 | ✗ | ret = storage_sqlite_row_to_run(stmt, &run); | |
489 | ✗ | if (ret < 0) | |
490 | ✗ | goto run_queue_destroy; | |
491 | |||
492 | ✗ | run_queue_add_last(queue, run); | |
493 | } | ||
494 | |||
495 | 29 | goto finalize; | |
496 | |||
497 | ✗ | run_queue_destroy: | |
498 | ✗ | run_queue_destroy(queue); | |
499 | |||
500 | 29 | finalize: | |
501 | 29 | sqlite_finalize(stmt); | |
502 | |||
503 | 29 | return ret; | |
504 | } | ||
505 |