comparison scid/db.c @ 18:600204c31bf0

misc: refactor
author David Demelier <markand@malikania.fr>
date Tue, 12 Jul 2022 20:20:51 +0200
parents db.c@3ef8128e244f
children
comparison
equal deleted inserted replaced
17:40fe70256fb0 18:600204c31bf0
1 /*
2 * db.c -- scid database access
3 *
4 * Copyright (c) 2021 David Demelier <markand@malikania.fr>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/queue.h>
20 #include <assert.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <sqlite3.h>
25
26 #include "db.h"
27 #include "log.h"
28 #include "types.h"
29 #include "util.h"
30
31 #include "sql/init.h"
32 #include "sql/job-add.h"
33 #include "sql/job-todo.h"
34 #include "sql/jobresult-add.h"
35 #include "sql/project-add.h"
36 #include "sql/project-update.h"
37 #include "sql/project-find.h"
38 #include "sql/project-find-id.h"
39 #include "sql/project-list.h"
40 #include "sql/worker-add.h"
41 #include "sql/worker-find.h"
42 #include "sql/worker-find-id.h"
43 #include "sql/worker-list.h"
44
45 #define CHAR(v) (const char *)(v)
46
47 static sqlite3 *db;
48
49 typedef void (*unpacker)(sqlite3_stmt *, struct db_ctx *, void *);
50
51 struct str {
52 char *str;
53 SLIST_ENTRY(str) link;
54 };
55
56 struct list {
57 unpacker unpack;
58 void *data;
59 size_t datasz;
60 size_t elemwidth;
61 struct db_ctx *ctx;
62 };
63
64 SLIST_HEAD(strlist, str);
65
66 static struct strlist *
67 strlist_new(void)
68 {
69 struct strlist *l;
70
71 l = util_calloc(1, sizeof (*l));
72 SLIST_INIT(l);
73
74 return l;
75 }
76
77 static const char *
78 strlist_add(struct strlist *l, const char *text)
79 {
80 struct str *s;
81
82 s = util_calloc(1, sizeof (*s));
83 s->str = util_strdup(text);
84
85 SLIST_INSERT_HEAD(l, s, link);
86
87 return s->str;
88 }
89
90 static void
91 strlist_free(struct strlist *l)
92 {
93 struct str *s, *tmp;
94
95 SLIST_FOREACH_SAFE(s, l, link, tmp) {
96 free(s->str);
97 free(s);
98 }
99
100 free(l);
101 }
102
103 static void
104 project_unpacker(sqlite3_stmt *stmt, struct db_ctx *ctx, struct project *project)
105 {
106 project->id = sqlite3_column_int(stmt, 0);
107 project->name = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 1)));
108 project->desc = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 2)));
109 project->url = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 3)));
110 project->script = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 4)));
111 }
112
113 static void
114 worker_unpacker(sqlite3_stmt *stmt, struct db_ctx *ctx, struct worker *w)
115 {
116 w->id = sqlite3_column_int(stmt, 0);
117 w->name = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 1)));
118 w->desc = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 2)));
119 }
120
121 static void
122 job_unpacker(sqlite3_stmt *stmt, struct db_ctx *ctx, struct job *job)
123 {
124 job->id = sqlite3_column_int(stmt, 0);
125 job->tag = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 1)));
126 job->project_id = sqlite3_column_int(stmt, 2);
127 }
128
129 static void
130 vbind(sqlite3_stmt *stmt, const char *fmt, va_list ap)
131 {
132 for (int index = 1; *fmt; ++fmt) {
133 switch (*fmt) {
134 case 'i':
135 sqlite3_bind_int(stmt, index++, va_arg(ap, int));
136 break;
137 case 's':
138 sqlite3_bind_text(stmt, index++, va_arg(ap, const char *), -1, SQLITE_STATIC);
139 break;
140 case 'z':
141 sqlite3_bind_int64(stmt, index++, va_arg(ap, size_t));
142 break;
143 default:
144 break;
145 }
146 }
147 }
148
149 static int
150 insert(const char *sql, const char *fmt, ...)
151 {
152 assert(sql);
153 assert(fmt);
154
155 sqlite3_stmt *stmt = NULL;
156 va_list ap;
157 int ret = -1;
158
159 if (sqlite3_prepare(db, sql, -1, &stmt, NULL) != SQLITE_OK)
160 return log_warn("db: %s", sqlite3_errmsg(db)), -1;
161
162 va_start(ap, fmt);
163 vbind(stmt, fmt, ap);
164 va_end(ap);
165
166 if (sqlite3_step(stmt) != SQLITE_DONE)
167 log_warn("db: %s", sqlite3_errmsg(db));
168 else
169 ret = sqlite3_last_insert_rowid(db);
170
171 sqlite3_finalize(stmt);
172
173 return ret;
174 }
175
176 static int
177 update(const char *sql, const char *fmt, ...)
178 {
179 assert(sql);
180 assert(fmt);
181
182 sqlite3_stmt *stmt = NULL;
183 va_list ap;
184 int ret = 1;
185
186 if (sqlite3_prepare(db, sql, -1, &stmt, NULL) != SQLITE_OK)
187 return log_warn("db: %s", sqlite3_errmsg(db)), -1;
188
189 va_start(ap, fmt);
190 vbind(stmt, fmt, ap);
191 va_end(ap);
192
193 if (sqlite3_step(stmt) != SQLITE_DONE)
194 log_warn("db: %s", sqlite3_errmsg(db));
195 else
196 ret = 0;
197
198 sqlite3_finalize(stmt);
199
200 return ret;
201 }
202
203 static ssize_t
204 list(struct list *sel, const char *sql, const char *args, ...)
205 {
206 sqlite3_stmt *stmt = NULL;
207
208 va_list ap;
209 int step;
210 ssize_t ret = -1;
211 size_t tot = 0;
212
213 sel->ctx->handle = NULL;
214
215 if (sqlite3_prepare(db, sql, -1, &stmt, NULL) != SQLITE_OK)
216 return log_warn("db: %s", sqlite3_errmsg(db)), -1;
217
218 va_start(ap, args);
219 vbind(stmt, args, ap);
220 va_end(ap);
221
222 sel->ctx->handle = strlist_new();
223
224 while (tot < sel->datasz && (step = sqlite3_step(stmt)) == SQLITE_ROW)
225 sel->unpack(stmt, sel->ctx, (unsigned char *)sel->data + (tot++ * sel->elemwidth));
226
227 if (step == SQLITE_OK || step == SQLITE_DONE || step == SQLITE_ROW)
228 ret = tot;
229 else {
230 memset(sel->data, 0, sel->datasz * sel->elemwidth);
231 strlist_free(sel->ctx->handle);
232 sel->ctx->handle = NULL;
233 }
234
235 sqlite3_finalize(stmt);
236
237 return ret;
238 }
239
240 int
241 db_open(const char *path)
242 {
243 assert(path);
244
245 if (sqlite3_open(path, &db) != SQLITE_OK)
246 return log_warn("db: open error: %s", sqlite3_errmsg(db)), -1;
247
248 /* Wait for 30 seconds to lock the database. */
249 sqlite3_busy_timeout(db, 30000);
250
251 if (sqlite3_exec(db, CHAR(sql_init), NULL, NULL, NULL) != SQLITE_OK)
252 return log_warn("db: initialization error: %s", sqlite3_errmsg(db)), -1;
253
254 return 0;
255 }
256
257 int
258 db_project_add(struct project *p)
259 {
260 return (p->id = insert(CHAR(sql_project_add), "ssss", p->name, p->desc,
261 p->url, p->script)) < 0 ? -1 : 0;
262 }
263
264 int
265 db_project_update(const struct project *p)
266 {
267 assert(p);
268
269 return update(CHAR(sql_project_update), "ssssi", p->name, p->desc,
270 p->url, p->script, p->id);
271 }
272
273 ssize_t
274 db_project_list(struct db_ctx *ctx, struct project *projects, size_t projectsz)
275 {
276 struct list sel = {
277 .unpack = (unpacker)project_unpacker,
278 .data = projects,
279 .datasz = projectsz,
280 .elemwidth = sizeof (*projects),
281 .ctx = ctx
282 };
283
284 return list(&sel, CHAR(sql_project_list), "z", projectsz);
285 }
286
287 int
288 db_project_find(struct db_ctx *ctx, struct project *project)
289 {
290 struct list sel = {
291 .unpack = (unpacker)project_unpacker,
292 .data = project,
293 .datasz = 1,
294 .elemwidth = sizeof (*project),
295 .ctx = ctx
296 };
297
298 return list(&sel, CHAR(sql_project_find), "s", project->name) == 1 ? 0 : -1;
299 }
300
301 int
302 db_project_find_id(struct db_ctx *ctx, struct project *project)
303 {
304 struct list sel = {
305 .unpack = (unpacker)project_unpacker,
306 .data = project,
307 .datasz = 1,
308 .elemwidth = sizeof (*project),
309 .ctx = ctx
310 };
311
312 return list(&sel, CHAR(sql_project_find_id), "i", project->id) == 1 ? 0 : -1;
313 }
314
315 int
316 db_worker_add(struct worker *wk)
317 {
318 assert(wk);
319
320 return (wk->id = insert(CHAR(sql_worker_add), "ss", wk->name, wk->desc)) < 0 ? -1 : 0;
321 }
322
323 ssize_t
324 db_worker_list(struct db_ctx *ctx, struct worker *wk, size_t wksz)
325 {
326 assert(ctx);
327 assert(wk);
328
329 struct list sel = {
330 .unpack = (unpacker)worker_unpacker,
331 .data = wk,
332 .datasz = wksz,
333 .elemwidth = sizeof (*wk),
334 .ctx = ctx
335 };
336
337 return list(&sel, CHAR(sql_worker_list), "z", wksz);
338 }
339
340 int
341 db_worker_find(struct db_ctx *ctx, struct worker *wk)
342 {
343 struct list sel = {
344 .unpack = (unpacker)worker_unpacker,
345 .data = wk,
346 .datasz = 1,
347 .elemwidth = sizeof (*wk),
348 .ctx = ctx
349 };
350
351 return list(&sel, CHAR(sql_worker_find), "s", wk->name) == 1 ? 0 : -1;
352 }
353
354 int
355 db_worker_find_id(struct db_ctx *ctx, struct worker *wk)
356 {
357 struct list sel = {
358 .unpack = (unpacker)worker_unpacker,
359 .data = wk,
360 .datasz = 1,
361 .elemwidth = sizeof (*wk),
362 .ctx = ctx
363 };
364
365 return list(&sel, CHAR(sql_worker_find_id), "i", wk->id) == 1 ? 0 : -1;
366 }
367
368 int
369 db_job_add(struct job *job)
370 {
371 assert(job);
372
373 return (job->id = insert(CHAR(sql_job_add),
374 "si", job->tag, job->project_id)) < 0 ? -1 : 0;
375 }
376
377 ssize_t
378 db_job_todo(struct db_ctx *ctx, struct job *jobs, size_t jobsz, int worker_id)
379 {
380 assert(ctx);
381 assert(jobs);
382
383 struct list sel = {
384 .unpack = (unpacker)job_unpacker,
385 .data = jobs,
386 .datasz = jobsz,
387 .elemwidth = sizeof (*jobs),
388 .ctx = ctx
389 };
390
391 return list(&sel, CHAR(sql_job_todo), "iiz", worker_id, worker_id, jobsz);
392 }
393
394 int
395 db_jobresult_add(struct jobresult *r)
396 {
397 assert(r);
398
399 return (r->id = insert(CHAR(sql_jobresult_add), "iiis", r->job_id,
400 r->worker_id, r->exitcode, r->log)) < 0 ? -1 : 0;
401 }
402
403 void
404 db_finish(void)
405 {
406 if (db) {
407 sqlite3_close(db);
408 db = NULL;
409 }
410 }
411
412 void
413 db_ctx_finish(struct db_ctx *ctx)
414 {
415 if (ctx->handle) {
416 strlist_free(ctx->handle);
417 ctx->handle = NULL;
418 }
419 }