Mercurial > sci
view lib/db.c @ 20:f98ea578b1ef
misc: revamp database
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 19 Jul 2022 21:52:42 +0200 |
parents | de4bf839b565 |
children | dd078aea5d02 |
line wrap: on
line source
/* * 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 <assert.h> #include <stdlib.h> #include <string.h> #include <sqlite3.h> #include <utlist.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; struct list { void (*unpack)(sqlite3_stmt *, void *); void *data; size_t datasz; size_t elemwidth; }; static void project_unpacker(sqlite3_stmt *stmt, void *data) { struct project *project = data; project->id = sqlite3_column_int(stmt, 0); project->name = util_strdup(CHAR(sqlite3_column_text(stmt, 1))); project->desc = util_strdup(CHAR(sqlite3_column_text(stmt, 2))); project->url = util_strdup(CHAR(sqlite3_column_text(stmt, 3))); project->script = util_strdup(CHAR(sqlite3_column_text(stmt, 4))); } static void worker_unpacker(sqlite3_stmt *stmt, void *data) { struct worker *w = data; w->id = sqlite3_column_int(stmt, 0); w->name = util_strdup(CHAR(sqlite3_column_text(stmt, 1))); w->desc = util_strdup(CHAR(sqlite3_column_text(stmt, 2))); } static void job_unpacker(sqlite3_stmt *stmt, void *data) { struct job *job = data; job->id = sqlite3_column_int(stmt, 0); job->tag = util_strdup(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 'j': sqlite3_bind_int64(stmt, index++, va_arg(ap, intmax_t)); 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; 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); while (tot < sel->datasz && (step = sqlite3_step(stmt)) == SQLITE_ROW) sel->unpack(stmt, (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); 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 project *projects, size_t projectsz) { struct list sel = { .unpack = project_unpacker, .data = projects, .datasz = projectsz, .elemwidth = sizeof (*projects), }; return list(&sel, CHAR(sql_project_list), "z", projectsz); } int db_project_find(struct project *project, const char *name) { struct list sel = { .unpack = project_unpacker, .data = project, .datasz = 1, .elemwidth = sizeof (*project), }; return list(&sel, CHAR(sql_project_find), "s", name) == 1 ? 0 : -1; } int db_project_find_id(struct project *project, intmax_t id) { struct list sel = { .unpack = project_unpacker, .data = project, .datasz = 1, .elemwidth = sizeof (*project), }; return list(&sel, CHAR(sql_project_find_id), "i", 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 worker *wk, size_t wksz) { assert(wk); struct list sel = { .unpack = worker_unpacker, .data = wk, .datasz = wksz, .elemwidth = sizeof (*wk), }; return list(&sel, CHAR(sql_worker_list), "z", wksz); } int db_worker_find(struct worker *wk, const char *name) { struct list sel = { .unpack = worker_unpacker, .data = wk, .datasz = 1, .elemwidth = sizeof (*wk), }; return list(&sel, CHAR(sql_worker_find), "s", name) == 1 ? 0 : -1; } int db_worker_find_id(struct worker *wk, intmax_t id) { struct list sel = { .unpack = worker_unpacker, .data = wk, .datasz = 1, .elemwidth = sizeof (*wk), }; return list(&sel, CHAR(sql_worker_find_id), "i", 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 job *jobs, size_t jobsz, int worker_id) { assert(jobs); struct list sel = { .unpack = job_unpacker, .data = jobs, .datasz = jobsz, .elemwidth = sizeof (*jobs), }; 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; } }