Mercurial > sci
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scid/db.c Tue Jul 12 20:20:51 2022 +0200 @@ -0,0 +1,419 @@ +/* + * db.c -- scid database access + * + * Copyright (c) 2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/queue.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <sqlite3.h> + +#include "db.h" +#include "log.h" +#include "types.h" +#include "util.h" + +#include "sql/init.h" +#include "sql/job-add.h" +#include "sql/job-todo.h" +#include "sql/jobresult-add.h" +#include "sql/project-add.h" +#include "sql/project-update.h" +#include "sql/project-find.h" +#include "sql/project-find-id.h" +#include "sql/project-list.h" +#include "sql/worker-add.h" +#include "sql/worker-find.h" +#include "sql/worker-find-id.h" +#include "sql/worker-list.h" + +#define CHAR(v) (const char *)(v) + +static sqlite3 *db; + +typedef void (*unpacker)(sqlite3_stmt *, struct db_ctx *, void *); + +struct str { + char *str; + SLIST_ENTRY(str) link; +}; + +struct list { + unpacker unpack; + void *data; + size_t datasz; + size_t elemwidth; + struct db_ctx *ctx; +}; + +SLIST_HEAD(strlist, str); + +static struct strlist * +strlist_new(void) +{ + struct strlist *l; + + l = util_calloc(1, sizeof (*l)); + SLIST_INIT(l); + + return l; +} + +static const char * +strlist_add(struct strlist *l, const char *text) +{ + struct str *s; + + s = util_calloc(1, sizeof (*s)); + s->str = util_strdup(text); + + SLIST_INSERT_HEAD(l, s, link); + + return s->str; +} + +static void +strlist_free(struct strlist *l) +{ + struct str *s, *tmp; + + SLIST_FOREACH_SAFE(s, l, link, tmp) { + free(s->str); + free(s); + } + + free(l); +} + +static void +project_unpacker(sqlite3_stmt *stmt, struct db_ctx *ctx, struct project *project) +{ + project->id = sqlite3_column_int(stmt, 0); + project->name = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 1))); + project->desc = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 2))); + project->url = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 3))); + project->script = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 4))); +} + +static void +worker_unpacker(sqlite3_stmt *stmt, struct db_ctx *ctx, struct worker *w) +{ + w->id = sqlite3_column_int(stmt, 0); + w->name = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 1))); + w->desc = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 2))); +} + +static void +job_unpacker(sqlite3_stmt *stmt, struct db_ctx *ctx, struct job *job) +{ + job->id = sqlite3_column_int(stmt, 0); + job->tag = strlist_add(ctx->handle, CHAR(sqlite3_column_text(stmt, 1))); + job->project_id = sqlite3_column_int(stmt, 2); +} + +static void +vbind(sqlite3_stmt *stmt, const char *fmt, va_list ap) +{ + for (int index = 1; *fmt; ++fmt) { + switch (*fmt) { + case 'i': + sqlite3_bind_int(stmt, index++, va_arg(ap, int)); + break; + case 's': + sqlite3_bind_text(stmt, index++, va_arg(ap, const char *), -1, SQLITE_STATIC); + break; + case 'z': + sqlite3_bind_int64(stmt, index++, va_arg(ap, size_t)); + break; + default: + break; + } + } +} + +static int +insert(const char *sql, const char *fmt, ...) +{ + assert(sql); + assert(fmt); + + sqlite3_stmt *stmt = NULL; + va_list ap; + int ret = -1; + + if (sqlite3_prepare(db, sql, -1, &stmt, NULL) != SQLITE_OK) + return log_warn("db: %s", sqlite3_errmsg(db)), -1; + + va_start(ap, fmt); + vbind(stmt, fmt, ap); + va_end(ap); + + if (sqlite3_step(stmt) != SQLITE_DONE) + log_warn("db: %s", sqlite3_errmsg(db)); + else + ret = sqlite3_last_insert_rowid(db); + + sqlite3_finalize(stmt); + + return ret; +} + +static int +update(const char *sql, const char *fmt, ...) +{ + assert(sql); + assert(fmt); + + sqlite3_stmt *stmt = NULL; + va_list ap; + int ret = 1; + + if (sqlite3_prepare(db, sql, -1, &stmt, NULL) != SQLITE_OK) + return log_warn("db: %s", sqlite3_errmsg(db)), -1; + + va_start(ap, fmt); + vbind(stmt, fmt, ap); + va_end(ap); + + if (sqlite3_step(stmt) != SQLITE_DONE) + log_warn("db: %s", sqlite3_errmsg(db)); + else + ret = 0; + + sqlite3_finalize(stmt); + + return ret; +} + +static ssize_t +list(struct list *sel, const char *sql, const char *args, ...) +{ + sqlite3_stmt *stmt = NULL; + + va_list ap; + int step; + ssize_t ret = -1; + size_t tot = 0; + + sel->ctx->handle = NULL; + + if (sqlite3_prepare(db, sql, -1, &stmt, NULL) != SQLITE_OK) + return log_warn("db: %s", sqlite3_errmsg(db)), -1; + + va_start(ap, args); + vbind(stmt, args, ap); + va_end(ap); + + sel->ctx->handle = strlist_new(); + + while (tot < sel->datasz && (step = sqlite3_step(stmt)) == SQLITE_ROW) + sel->unpack(stmt, sel->ctx, (unsigned char *)sel->data + (tot++ * sel->elemwidth)); + + if (step == SQLITE_OK || step == SQLITE_DONE || step == SQLITE_ROW) + ret = tot; + else { + memset(sel->data, 0, sel->datasz * sel->elemwidth); + strlist_free(sel->ctx->handle); + sel->ctx->handle = NULL; + } + + sqlite3_finalize(stmt); + + return ret; +} + +int +db_open(const char *path) +{ + assert(path); + + if (sqlite3_open(path, &db) != SQLITE_OK) + return log_warn("db: open error: %s", sqlite3_errmsg(db)), -1; + + /* Wait for 30 seconds to lock the database. */ + sqlite3_busy_timeout(db, 30000); + + if (sqlite3_exec(db, CHAR(sql_init), NULL, NULL, NULL) != SQLITE_OK) + return log_warn("db: initialization error: %s", sqlite3_errmsg(db)), -1; + + return 0; +} + +int +db_project_add(struct project *p) +{ + return (p->id = insert(CHAR(sql_project_add), "ssss", p->name, p->desc, + p->url, p->script)) < 0 ? -1 : 0; +} + +int +db_project_update(const struct project *p) +{ + assert(p); + + return update(CHAR(sql_project_update), "ssssi", p->name, p->desc, + p->url, p->script, p->id); +} + +ssize_t +db_project_list(struct db_ctx *ctx, struct project *projects, size_t projectsz) +{ + struct list sel = { + .unpack = (unpacker)project_unpacker, + .data = projects, + .datasz = projectsz, + .elemwidth = sizeof (*projects), + .ctx = ctx + }; + + return list(&sel, CHAR(sql_project_list), "z", projectsz); +} + +int +db_project_find(struct db_ctx *ctx, struct project *project) +{ + struct list sel = { + .unpack = (unpacker)project_unpacker, + .data = project, + .datasz = 1, + .elemwidth = sizeof (*project), + .ctx = ctx + }; + + return list(&sel, CHAR(sql_project_find), "s", project->name) == 1 ? 0 : -1; +} + +int +db_project_find_id(struct db_ctx *ctx, struct project *project) +{ + struct list sel = { + .unpack = (unpacker)project_unpacker, + .data = project, + .datasz = 1, + .elemwidth = sizeof (*project), + .ctx = ctx + }; + + return list(&sel, CHAR(sql_project_find_id), "i", project->id) == 1 ? 0 : -1; +} + +int +db_worker_add(struct worker *wk) +{ + assert(wk); + + return (wk->id = insert(CHAR(sql_worker_add), "ss", wk->name, wk->desc)) < 0 ? -1 : 0; +} + +ssize_t +db_worker_list(struct db_ctx *ctx, struct worker *wk, size_t wksz) +{ + assert(ctx); + assert(wk); + + struct list sel = { + .unpack = (unpacker)worker_unpacker, + .data = wk, + .datasz = wksz, + .elemwidth = sizeof (*wk), + .ctx = ctx + }; + + return list(&sel, CHAR(sql_worker_list), "z", wksz); +} + +int +db_worker_find(struct db_ctx *ctx, struct worker *wk) +{ + struct list sel = { + .unpack = (unpacker)worker_unpacker, + .data = wk, + .datasz = 1, + .elemwidth = sizeof (*wk), + .ctx = ctx + }; + + return list(&sel, CHAR(sql_worker_find), "s", wk->name) == 1 ? 0 : -1; +} + +int +db_worker_find_id(struct db_ctx *ctx, struct worker *wk) +{ + struct list sel = { + .unpack = (unpacker)worker_unpacker, + .data = wk, + .datasz = 1, + .elemwidth = sizeof (*wk), + .ctx = ctx + }; + + return list(&sel, CHAR(sql_worker_find_id), "i", wk->id) == 1 ? 0 : -1; +} + +int +db_job_add(struct job *job) +{ + assert(job); + + return (job->id = insert(CHAR(sql_job_add), + "si", job->tag, job->project_id)) < 0 ? -1 : 0; +} + +ssize_t +db_job_todo(struct db_ctx *ctx, struct job *jobs, size_t jobsz, int worker_id) +{ + assert(ctx); + assert(jobs); + + struct list sel = { + .unpack = (unpacker)job_unpacker, + .data = jobs, + .datasz = jobsz, + .elemwidth = sizeof (*jobs), + .ctx = ctx + }; + + return list(&sel, CHAR(sql_job_todo), "iiz", worker_id, worker_id, jobsz); +} + +int +db_jobresult_add(struct jobresult *r) +{ + assert(r); + + return (r->id = insert(CHAR(sql_jobresult_add), "iiis", r->job_id, + r->worker_id, r->exitcode, r->log)) < 0 ? -1 : 0; +} + +void +db_finish(void) +{ + if (db) { + sqlite3_close(db); + db = NULL; + } +} + +void +db_ctx_finish(struct db_ctx *ctx) +{ + if (ctx->handle) { + strlist_free(ctx->handle); + ctx->handle = NULL; + } +}