aboutsummaryrefslogtreecommitdiff
path: root/src/cmd_util.c
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-21 09:47:50 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-21 11:01:07 +0200
commitb1956aa73a533702ccbc3d7713ea90dd5ae44b9b (patch)
tree7d3d4c8db28cbe15cfdf916f74ea7d77a5666414 /src/cmd_util.c
parente25a8c9ae70e5a310c1a82be55d0dbf58ba71250 (diff)
downloadbokf-b1956aa73a533702ccbc3d7713ea90dd5ae44b9b.tar.gz
bokf-b1956aa73a533702ccbc3d7713ea90dd5ae44b9b.zip
commands: db_row_json and db_exec_bound helpers
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/cmd_util.c')
-rw-r--r--src/cmd_util.c132
1 files changed, 132 insertions, 0 deletions
diff --git a/src/cmd_util.c b/src/cmd_util.c
index 5347d48..abe56d6 100644
--- a/src/cmd_util.c
+++ b/src/cmd_util.c
@@ -216,6 +216,138 @@ void bind_text_or_null(sqlite3_stmt *st, int idx, const char *s)
sqlite3_bind_null(st, idx);
}
+yyjson_mut_val *db_row_json(yyjson_mut_doc *doc, sqlite3_stmt *st,
+ const char *spec)
+{
+ yyjson_mut_val *o = yyjson_mut_obj(doc);
+ const char *p = spec;
+ for (int col = 0; *p; col++) {
+ while (*p == ',' || *p == ' ')
+ p++;
+ if (!*p)
+ break;
+ const char *name = p;
+ while (*p && *p != ':')
+ p++;
+ size_t nlen = (size_t)(p - name);
+ char type = *p == ':' ? p[1] : 's';
+ while (*p && *p != ',' && *p != ' ')
+ p++;
+ yyjson_mut_val *key = yyjson_mut_strncpy(doc, name, nlen);
+ yyjson_mut_val *val;
+ int is_null = sqlite3_column_type(st, col) == SQLITE_NULL;
+ switch (type) {
+ case 'i':
+ val = yyjson_mut_int(doc, sqlite3_column_int64(st, col));
+ break;
+ case 'b':
+ val = yyjson_mut_bool(doc, sqlite3_column_int(st, col) != 0);
+ break;
+ case 'n':
+ val = is_null ? yyjson_mut_null(doc)
+ : yyjson_mut_strcpy(doc,
+ sq(sqlite3_column_text(st, col)));
+ break;
+ case 'r':
+ val = is_null ? yyjson_mut_null(doc)
+ : yyjson_mut_int(doc, sqlite3_column_int64(st, col));
+ break;
+ default:
+ val = yyjson_mut_strcpy(doc, sq(sqlite3_column_text(st, col)));
+ break;
+ }
+ yyjson_mut_obj_add(o, key, val);
+ }
+ return o;
+}
+
+static sqlite3_stmt *prepare_boundv(sqlite3 *db, const char *sql,
+ const char *types, va_list ap)
+{
+ sqlite3_stmt *st = NULL;
+ if (sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK)
+ return NULL;
+ for (int i = 0; types[i]; i++) {
+ switch (types[i]) {
+ case 'i':
+ sqlite3_bind_int64(st, i + 1, va_arg(ap, int64_t));
+ break;
+ case 's':
+ sqlite3_bind_text(st, i + 1, va_arg(ap, const char *), -1,
+ SQLITE_TRANSIENT);
+ break;
+ case 'n':
+ bind_text_or_null(st, i + 1, va_arg(ap, const char *));
+ break;
+ default:
+ sqlite3_finalize(st);
+ return NULL;
+ }
+ }
+ return st;
+}
+
+sqlite3_stmt *db_prepare_bound(sqlite3 *db, const char *sql, const char *types,
+ ...)
+{
+ va_list ap;
+ va_start(ap, types);
+ sqlite3_stmt *st = prepare_boundv(db, sql, types, ap);
+ va_end(ap);
+ return st;
+}
+
+static int exec_boundv(sqlite3 *db, const char *sql, const char *types,
+ va_list ap)
+{
+ sqlite3_stmt *st = prepare_boundv(db, sql, types, ap);
+ if (!st)
+ return -1;
+ int rc = sqlite3_step(st);
+ sqlite3_finalize(st);
+ return rc == SQLITE_DONE ? 0 : rc;
+}
+
+int db_exec_bound(sqlite3 *db, const char *sql, const char *types, ...)
+{
+ va_list ap;
+ va_start(ap, types);
+ int rc = exec_boundv(db, sql, types, ap);
+ va_end(ap);
+ return rc;
+}
+
+int req_exec(struct req *r, const char *sql, const char *types, ...)
+{
+ va_list ap;
+ va_start(ap, types);
+ int rc = exec_boundv(r->db, sql, types, ap);
+ va_end(ap);
+ if (rc == -1)
+ db_error(r);
+ else if (rc != 0)
+ db_sqlite_error(r);
+ return rc == 0 ? 0 : -1;
+}
+
+yyjson_mut_val *req_list(struct req *r, const char *sql, const char *spec,
+ const char *types, ...)
+{
+ va_list ap;
+ va_start(ap, types);
+ sqlite3_stmt *st = prepare_boundv(r->db, sql, types, ap);
+ va_end(ap);
+ if (!st)
+ return db_error(r);
+ yyjson_mut_val *items = yyjson_mut_arr(r->rdoc);
+ while (sqlite3_step(st) == SQLITE_ROW)
+ yyjson_mut_arr_add_val(items, db_row_json(r->rdoc, st, spec));
+ sqlite3_finalize(st);
+ yyjson_mut_val *o = yyjson_mut_obj(r->rdoc);
+ yyjson_mut_obj_add_val(r->rdoc, o, "items", items);
+ return o;
+}
+
int mkdir_p(const char *path, mode_t mode)
{
char tmp[4096];