diff options
| author | Anders Betts <anders.betts@gmail.com> | 2026-09-21 09:47:50 +0200 |
|---|---|---|
| committer | Anders Betts <anders.betts@gmail.com> | 2026-09-21 11:01:07 +0200 |
| commit | b1956aa73a533702ccbc3d7713ea90dd5ae44b9b (patch) | |
| tree | 7d3d4c8db28cbe15cfdf916f74ea7d77a5666414 /src/cmd_util.c | |
| parent | e25a8c9ae70e5a310c1a82be55d0dbf58ba71250 (diff) | |
| download | bokf-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.c | 132 |
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]; |
