comparison lib/db.c @ 19:de4bf839b565

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