diff options
| author | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-09-22 17:32:44 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2026-02-13 09:57:39 +0100 |
| commit | 78bf2fe4a5bf37514f6dfd203ef969da0bf40c2e (patch) | |
| tree | 33f9440b8ee0fa7a3b3ad033616d722d2101bb4d /op.c | |
| parent | 107a2e43d54f9a42fb85b00b83cb0d9abb194680 (diff) | |
Diffstat (limited to 'op.c')
| -rw-r--r-- | op.c | 183 |
1 files changed, 183 insertions, 0 deletions
@@ -0,0 +1,183 @@ +/* + * nanobbs, a tiny forums software. + * Copyright (C) 2025-2026 Xavier Del Campo Romero + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "op.h" +#include <libweb/http.h> +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +struct op +{ + sqlite3_stmt *stmt; + struct op_cfg cfg; +}; + +static int teardown(struct op *const op) +{ + int ret = 0, error; + + if (!op) + return 0; + else if ((error = sqlite3_finalize(op->stmt)) != SQLITE_OK) + { + fprintf(stderr, "%s: sqlite3_finalize: %s\n", __func__, + sqlite3_errstr(error)); + ret = -1; + } + + const struct op_cfg *const cfg = &op->cfg; + + if (cfg->error) + cfg->error(cfg->args); + + free(op); + return ret; +} + +static void run_error(void *const p) +{ + teardown(p); +} + +static int run(const struct http_payload *const pl, + struct http_response *const r, void *const user, void *const args) +{ + struct op *const op = args; + sqlite3_stmt *const stmt = op->stmt; + const struct op_cfg *const cfg = &op->cfg; + int ret = -1, error = sqlite3_step(stmt); + + switch (error) + { + case SQLITE_BUSY: + break; + + case SQLITE_ROW: + if (!cfg->row) + { + fprintf(stderr, "%s: expected row callback\n", __func__); + goto failure; + } + else if ((ret = cfg->row(op->stmt, pl, r, user, cfg->args))) + goto failure; + + break; + + case SQLITE_CONSTRAINT: + sqlite3_reset(op->stmt); + + if (!cfg->constraint) + { + fprintf(stderr, "%s: expected constraint callback\n", __func__); + goto failure; + } + else if ((ret = cfg->constraint(cfg->args))) + goto failure; + + /* Fall through. */ + case SQLITE_DONE: + { + const int error = sqlite3_finalize(op->stmt); + + if (error != SQLITE_OK) + { + fprintf(stderr, "%s: sqlite3_finalize: %s\n", __func__, + sqlite3_errstr(error)); + ret = -1; + goto failure; + } + else if (cfg->end && (ret = cfg->end(pl, r, user, cfg->args))) + goto failure; + + free(op); + } + break; + + default: + fprintf(stderr, "%s: sqlite3_step: %s\n", __func__, + sqlite3_errstr(error)); + sqlite3_reset(stmt); + goto failure; + } + + return 0; + +failure: + + if (teardown(op)) + { + fprintf(stderr, "%s: teardown failed\n", __func__); + ret = -1; + } + + return ret; +} + +void op_resume(struct op *const op, struct http_response *const r) +{ + *r = (const struct http_response) + { + .step.payload = run, + .free = run_error, + .args = op + }; +} + +struct op *op_run(sqlite3 *const db, const char *const query, + const struct op_cfg *const cfg, struct http_response *const r) +{ + sqlite3_stmt *stmt = NULL; + struct op *const ret = malloc(sizeof *ret); + + if (!ret) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto failure; + } + + const int error = sqlite3_prepare_v2(db, query, -1, &stmt, NULL); + + if (error != SQLITE_OK && error != SQLITE_BUSY) + { + fprintf(stderr, "%s: sqlite3_prepare_v2 \"%s\": %s\n", __func__, + query, sqlite3_errstr(error)); + goto failure; + } + + *r = (const struct http_response) + { + .step.payload = run, + .free = run_error, + .args = ret + }; + + *ret = (const struct op) + { + .stmt = stmt, + .cfg = *cfg + }; + + return ret; + +failure: + free(ret); + sqlite3_finalize(stmt); + return NULL; +} |
