Mercurial > sci
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 } |