1 /*
2 * Copyright 2014-2017, Björn Ståhl
3 * License: 3-Clause BSD, see COPYING file in arcan source repository.
4 * Reference: http://arcan-fe.com
5 */
6
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <sqlite3.h>
12 #include <assert.h>
13 #include <string.h>
14 #include <inttypes.h>
15
16 #include <sys/types.h>
17 #include <unistd.h>
18
19 #include "arcan_math.h"
20 #include "arcan_general.h"
21 #include "arcan_db.h"
22
23 #ifdef ARCAN_DB_STANDALONE
24 static const char* ARCAN_TBL = "arcan";
25 #else
26 static const char* ARCAN_TBL =
27 #ifdef ARCAN_LWA
28 "arcan_lwa"
29 #else
30 "arcan"
31 #endif
32 ;
33 #endif
34
35 static bool db_init = false;
36
37 #define DB_VERSION_NUM "4"
38
39 #define DDL_TARGET "CREATE TABLE target ("\
40 "tgtid INTEGER PRIMARY KEY,"\
41 "tag STRING NOT NULL,"\
42 "name STRING UNIQUE NOT NULL,"\
43 "executable TEXT NOT NULL,"\
44 "user_id STRING DEFAULT NULL,"\
45 "user_group STRING DEFAULT NULL,"\
46 "bfmt INTEGER NOT NULL"\
47 ")"
48
49 #define DDL_TGT_ARGV "CREATE TABLE target_argv ("\
50 "argnum INTEGER PRIMARY KEY,"\
51 "arg STRING NOT NULL,"\
52 "target INTEGER NOT NULL,"\
53 "FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
54
55 #define DDL_CONFIG "CREATE TABLE config ("\
56 "cfgid INTEGER PRIMARY KEY,"\
57 "name STRING NOT NULL,"\
58 "passed_counter INTEGER,"\
59 "failed_counter INTEGER,"\
60 "target INTEGER NOT NULL,"\
61 "UNIQUE (name, target),"\
62 "FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
63
64 #define DDL_CFG_ARGV "CREATE TABLE config_argv ("\
65 "argnum INTEGER PRIMARY KEY,"\
66 "arg STRING NOT NULL,"\
67 "config INTEGER NOT NULL,"\
68 "FOREIGN KEY (config) REFERENCES config(cfgid) ON DELETE CASCADE )"
69
70 #define DDL_TGT_KV "CREATE TABLE target_kv ("\
71 "key STRING NOT NULL,"\
72 "val STRING NOT NULL,"\
73 "target INT NOT NULL,"\
74 "UNIQUE (key, target), "\
75 "FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
76
77 #define DDL_CFG_KV "CREATE TABLE config_kv ("\
78 "key STRING NOT NULL,"\
79 "val STRING NOT NULL,"\
80 "config INT NOT NULL,"\
81 "UNIQUE (key, config), "\
82 "FOREIGN KEY (config) REFERENCES config(cfgid) ON DELETE CASCADE )"
83
84 #define DDL_TGT_ENV "CREATE TABLE target_env ("\
85 "key STRING NOT NULL,"\
86 "val STRING NOT NULL,"\
87 "target INT NOT NULL,"\
88 "UNIQUE (key, target), "\
89 "FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
90
91 #define DDL_CFG_ENV "CREATE TABLE config_env ("\
92 "key STRING NOT NULL,"\
93 "val STRING NOT NULL,"\
94 "config INT NOT NULL,"\
95 "UNIQUE (key, config),"\
96 "FOREIGN KEY (config) REFERENCES target(tgtid) ON DELETE CASCADE )"
97
98 #define DDL_TGT_LIBS "CREATE TABLE target_libs ("\
99 "libnum INTEGER PRIMARY KEY,"\
100 "libname STRING NOT NULL,"\
101 "libnote STRING NOT NULL,"\
102 "target INT NOT NULL,"\
103 "UNIQUE (libname, target),"\
104 "FOREIGN KEY (target) REFERENCES target(tgtid) ON DELETE CASCADE )"
105
106 static const char* ddls[] = {
107 DDL_TARGET, DDL_CONFIG,
108 DDL_TGT_ARGV, DDL_CFG_ARGV,
109 DDL_TGT_KV, DDL_CFG_KV,
110 DDL_TGT_ENV, DDL_CFG_ENV,
111 DDL_TGT_LIBS
112 };
113
114 #define DI_INSARG_TARGET "INSERT INTO target_argv(target, arg) VALUES(?, ?);"
115 #define DI_INSARG_CONFIG "INSERT INTO config_argv(config, arg) VALUES(?, ?);"
116
117 #define DI_DROPKV_TARGET "DELETE FROM target_kv WHERE val=\"\";"
118
119 #define DI_INSKV_TARGET "INSERT OR REPLACE INTO "\
120 "target_kv(key, val, target) VALUES(?, ?, ?);"
121
122 #define DI_INSKV_CONFIG "INSERT OR REPLACE INTO "\
123 "config_kv(key, val, config) VALUES(?, ?, ?);"
124
125 #define DI_DROPKV_CONFIG "DELETE FROM config_kv WHERE val=\"\";"
126
127 #define DI_INSKV_CONFIG_ENV "INSERT OR REPLACE INTO "\
128 "config_env(key, val, config) VALUES(?, ?, ?);"
129
130 #define DI_INSKV_TARGET_ENV "INSERT OR REPLACE INTO "\
131 "target_env(key, val, target) VALUES(?, ?, ?);"
132
133 #define DI_INSKV_TARGET_LIBV "INSERT OR REPLACE INTO "\
134 "target_libs(libname, libnote, target) VALUES(?, ?, ?);"
135
136 struct arcan_dbh {
137 sqlite3* dbh;
138
139 /* cached appl name used for the DBHandle, although
140 * some special functions may use a different one, none outside _db.c should */
141 char* applname;
142 char* akv_update;
143 char* akv_get;
144 char* akv_clean;
145
146 size_t akv_upd_sz;
147 size_t akv_get_sz;
148 size_t akv_clean_sz;
149
150 enum DB_KVTARGET ttype;
151 union arcan_dbtrans_id trid;
152 bool trclean;
153 sqlite3_stmt* transaction;
154 };
155
156 static void setup_ddl(struct arcan_dbh* dbh);
157
158 static struct arcan_dbh* shared_handle;
arcan_db_get_shared(const char ** dappl)159 struct arcan_dbh* arcan_db_get_shared(const char** dappl)
160 {
161 if (dappl)
162 *dappl = ARCAN_TBL;
163 return shared_handle;
164 }
165
arcan_db_set_shared(struct arcan_dbh * new)166 void arcan_db_set_shared(struct arcan_dbh* new)
167 {
168 shared_handle = new;
169 }
170
171 /*
172 * any query that just returns a list of strings,
173 * pack into a dbres (or append to an existing one)
174 */
db_string_query(struct arcan_dbh * dbh,sqlite3_stmt * stmt,struct arcan_strarr * opt,off_t ofs)175 static struct arcan_strarr db_string_query(struct arcan_dbh* dbh,
176 sqlite3_stmt* stmt, struct arcan_strarr* opt, off_t ofs)
177 {
178 struct arcan_strarr res = {.data = NULL};
179
180 if (!opt) {
181 res.data = arcan_alloc_mem(sizeof(char**) * 8,
182 ARCAN_MEM_STRINGBUF, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
183 res.limit = 8;
184 res.count = ofs;
185 }
186 else
187 res = *opt;
188
189 /* we stop one step short of full capacity before
190 * resizing to have both a valid count and a NULL terminated array */
191 while (sqlite3_step(stmt) == SQLITE_ROW){
192 if (res.count+1 >= res.limit)
193 arcan_mem_growarr(&res);
194
195 const char* arg = (const char*) sqlite3_column_text(stmt, 0);
196 res.data[res.count++] = (arg ? strdup(arg) : NULL);
197 }
198
199 sqlite3_finalize(stmt);
200 return res;
201 }
202
203 /*
204 * any query where we're just interested in an integer value
205 */
db_num_query(struct arcan_dbh * dbh,const char * qry,bool * status)206 static int db_num_query(struct arcan_dbh* dbh, const char* qry, bool* status)
207 {
208 sqlite3_stmt* stmt = NULL;
209 if (status) *status = false;
210 int count = -1;
211
212 int code = sqlite3_prepare_v2(dbh->dbh, qry, strlen(qry), &stmt, NULL);
213 if (SQLITE_OK == code){
214 while (sqlite3_step(stmt) == SQLITE_ROW){
215 count = sqlite3_column_int(stmt, 0);
216 if (status) *status = true;
217 }
218
219 sqlite3_finalize(stmt);
220 }
221 else
222 count = -1;
223
224 return count;
225 }
226
227 /*
228 * query that is just silently assumed to work
229 */
db_void_query(struct arcan_dbh * dbh,const char * qry,bool suppr_err)230 static inline void db_void_query(struct arcan_dbh* dbh,
231 const char* qry, bool suppr_err)
232 {
233 sqlite3_stmt* stmt = NULL;
234 if (!qry || !dbh)
235 return;
236
237 int rc = sqlite3_prepare_v2(dbh->dbh, qry, strlen(qry), &stmt, NULL);
238 if (rc == SQLITE_OK){
239 rc = sqlite3_step(stmt);
240 }
241
242 if (rc != SQLITE_OK && rc != SQLITE_DONE && !suppr_err){
243 arcan_warning("db_void_query(failed) on %s -- reason: %d(%s)\n",
244 qry, rc, sqlite3_errmsg(dbh->dbh));
245 }
246
247 sqlite3_finalize(stmt);
248 }
249
arcan_db_dropappl(struct arcan_dbh * dbh,const char * appl)250 void arcan_db_dropappl(struct arcan_dbh* dbh, const char* appl)
251 {
252 if (!appl || !dbh)
253 return;
254
255 size_t len = strlen(appl);
256 if (0 == len)
257 return;
258
259 const char dropqry[] = "DELETE FROM appl_";
260 char dropbuf[sizeof(dropqry) + len + 1];
261 snprintf(dropbuf, sizeof(dropbuf), "%s%s;", dropqry, appl);
262
263 db_void_query(dbh, dropbuf, true);
264
265 /* special case, reset version fields etc. */
266 if (strcmp(appl, ARCAN_TBL) == 0){
267 arcan_db_appl_kv(dbh, ARCAN_TBL, "dbversion", DB_VERSION_NUM);
268 }
269 }
270
sqliteexit()271 static void sqliteexit()
272 {
273 sqlite3_shutdown();
274 }
275
276 /*
277 * will be called every time the database is opened,
278 * so we expect this to fail silently when the group already exists
279 */
create_appl_group(struct arcan_dbh * dbh,const char * applname)280 static void create_appl_group(struct arcan_dbh* dbh, const char* applname)
281 {
282 const char ddl[] = "CREATE TABLE appl_%s "
283 "(key TEXT UNIQUE, val TEXT NOT NULL);";
284 const char kv_ddl[] = "INSERT OR REPLACE INTO "
285 " appl_%s(key, val) VALUES(?, ?);";
286 const char kv_get[] = "SELECT val FROM appl_%s WHERE key = ?;";
287 const char kv_drop[] = "DELETE FROM appl_%s WHERE val = \"\";";
288
289 if (dbh->akv_update)
290 arcan_mem_free(dbh->akv_update);
291
292 size_t len = applname ? strlen(applname) : 0;
293 if (0 == len){
294 arcan_warning("create_appl_group(), missing or broken applname\n");
295 return;
296 }
297
298 /* cache key insert into app specific table */
299 assert(sizeof(kv_get) < sizeof(kv_ddl));
300 size_t ddl_sz = len + sizeof(kv_ddl);
301 dbh->akv_update = arcan_alloc_mem(
302 ddl_sz, ARCAN_MEM_STRINGBUF, 0, ARCAN_MEMALIGN_NATURAL);
303 dbh->akv_upd_sz = snprintf(dbh->akv_update, ddl_sz, kv_ddl, applname);
304
305 /* cache drop empty values from table */
306 ddl_sz = len + sizeof(kv_drop);
307 dbh->akv_clean = arcan_alloc_mem(
308 ddl_sz, ARCAN_MEM_STRINGBUF, 0, ARCAN_MEMALIGN_NATURAL);
309 dbh->akv_clean_sz = snprintf(dbh->akv_clean, ddl_sz, kv_drop, applname);
310
311 /* cache key retrieve into app specific table */
312 ddl_sz = strlen(applname) + sizeof(kv_get);
313 dbh->akv_get = arcan_alloc_mem(
314 ddl_sz, ARCAN_MEM_STRINGBUF, 0, ARCAN_MEMALIGN_NATURAL);
315 dbh->akv_get_sz = snprintf(dbh->akv_get, ddl_sz, kv_get, applname);
316
317 /* create the actual table */
318 char wbuf[ sizeof(ddl) + strlen(applname) ];
319 snprintf(wbuf, sizeof(wbuf)/sizeof(wbuf[0]), ddl, applname);
320
321 db_void_query(dbh, wbuf, true);
322 }
323
324 #ifdef ARCAN_DB_STANDALONE
arcan_db_droptarget(struct arcan_dbh * dbh,arcan_targetid id)325 bool arcan_db_droptarget(struct arcan_dbh* dbh, arcan_targetid id)
326 {
327 /* should suffice from ON DELETE CASCADE relationship */
328 static const char qry[] = "DELETE FROM target WHERE tgtid = ?;";
329
330 sqlite3_stmt* stmt;
331 sqlite3_prepare_v2(dbh->dbh, qry, sizeof(qry)-1, &stmt, NULL);
332 sqlite3_bind_int(stmt, 1, id);
333 sqlite3_step(stmt);
334 sqlite3_finalize(stmt);
335
336 return true;
337 }
338
arcan_db_dropconfig(struct arcan_dbh * dbh,arcan_configid id)339 bool arcan_db_dropconfig(struct arcan_dbh* dbh, arcan_configid id)
340 {
341 static const char qry[] = "DELETE FROM config WHERE cfgid = ?;";
342
343 sqlite3_stmt* stmt;
344 sqlite3_prepare_v2(dbh->dbh, qry, sizeof(qry)-1, &stmt, NULL);
345 sqlite3_bind_int(stmt, 1, id);
346 sqlite3_step(stmt);
347 sqlite3_finalize(stmt);
348
349 return true;
350 }
351
arcan_db_addtarget(struct arcan_dbh * dbh,const char * identifier,const char * group,const char * exec,const char * argv[],size_t sz,enum DB_BFORMAT bfmt)352 arcan_targetid arcan_db_addtarget(struct arcan_dbh* dbh,
353 const char* identifier, const char* group, const char* exec,
354 const char* argv[], size_t sz, enum DB_BFORMAT bfmt)
355 {
356 static const char ddl[] = "INSERT OR REPLACE INTO "
357 " target(tgtid, name, tag, executable, bfmt) VALUES "
358 "((select tgtid FROM target where name = ?), ?, ?, ?, ?)";
359
360 sqlite3_stmt* stmt;
361 sqlite3_prepare_v2(dbh->dbh, ddl, sizeof(ddl)-1, &stmt, NULL);
362
363 sqlite3_bind_text(stmt, 1, identifier, -1, SQLITE_STATIC);
364 sqlite3_bind_text(stmt, 2, identifier, -1, SQLITE_STATIC);
365 sqlite3_bind_text(stmt, 3, group, -1, SQLITE_STATIC);
366 sqlite3_bind_text(stmt, 4, exec, -1, SQLITE_STATIC);
367 sqlite3_bind_int(stmt, 5, bfmt);
368
369 sqlite3_step(stmt);
370 sqlite3_finalize(stmt);
371
372 arcan_targetid newid = sqlite3_last_insert_rowid(dbh->dbh);
373
374 /* delete previous arguments */
375 static const char drop_argv[] = "DELETE FROM target_argv WHERE target = ?;";
376 sqlite3_prepare_v2(dbh->dbh, drop_argv, sizeof(drop_argv) - 1, &stmt, NULL);
377 sqlite3_bind_int(stmt, 1, newid);
378 sqlite3_step(stmt);
379 sqlite3_finalize(stmt);
380
381 /* add new ones */
382 if (0 == sz)
383 return newid;
384
385 static const char add_argv[] = DI_INSARG_TARGET;
386 for (size_t i = 0; i < sz; i++){
387 sqlite3_prepare_v2(dbh->dbh, add_argv, sizeof(add_argv) - 1, &stmt, NULL);
388 sqlite3_bind_int(stmt, 1, newid);
389 sqlite3_bind_text(stmt, 2, argv[i], -1, SQLITE_STATIC);
390 sqlite3_step(stmt);
391 sqlite3_finalize(stmt);
392 }
393
394 return newid;
395 }
396
arcan_db_addconfig(struct arcan_dbh * dbh,arcan_targetid id,const char * identifier,const char * argv[],size_t sz)397 arcan_configid arcan_db_addconfig(struct arcan_dbh* dbh,
398 arcan_targetid id, const char* identifier, const char* argv[], size_t sz)
399 {
400 if (!arcan_db_verifytarget(dbh, id))
401 return BAD_CONFIG;
402
403 static const char ddl[] = "INSERT OR REPLACE INTO config(cfgid, name, "
404 "passed_counter, failed_counter, target) VALUES "
405 "(NULL, ?, ?, ?, ?)";
406
407 sqlite3_stmt* stmt;
408 sqlite3_prepare_v2(dbh->dbh, ddl, sizeof(ddl)-1, &stmt, NULL);
409
410 sqlite3_bind_text(stmt, 1, identifier, -1, SQLITE_STATIC);
411 sqlite3_bind_int(stmt, 2, 0);
412 sqlite3_bind_int(stmt, 3, 0);
413 sqlite3_bind_int(stmt, 4, id);
414
415 sqlite3_step(stmt);
416 sqlite3_finalize(stmt);
417
418 arcan_configid newid = sqlite3_last_insert_rowid(dbh->dbh);
419
420 /* delete previous arguments */
421 static const char drop_argv[] = "DELETE FROM config_argv WHERE config = ?;";
422 sqlite3_prepare_v2(dbh->dbh, drop_argv, sizeof(drop_argv) - 1, &stmt, NULL);
423 sqlite3_bind_int(stmt, 1, newid);
424 sqlite3_step(stmt);
425 sqlite3_finalize(stmt);
426
427 /* add new ones */
428 if (0 == sz)
429 return newid;
430
431 static const char add_argv[] = DI_INSARG_CONFIG;
432 for (size_t i = 0; i < sz; i++){
433 sqlite3_prepare_v2(dbh->dbh, add_argv, sizeof(add_argv) - 1, &stmt, NULL);
434 sqlite3_bind_int(stmt, 1, newid);
435 sqlite3_bind_text(stmt, 2, argv[i], -1, SQLITE_STATIC);
436 sqlite3_step(stmt);
437 sqlite3_finalize(stmt);
438 }
439
440 return newid;
441 }
442
443 #endif
444
arcan_db_verifytarget(struct arcan_dbh * dbh,arcan_targetid id)445 bool arcan_db_verifytarget(struct arcan_dbh* dbh, arcan_targetid id)
446 {
447 static const char ddl[] = "SELECT COUNT(*) FROM target WHERE tgtid = ?;";
448
449 sqlite3_stmt* stmt = NULL;
450 if (SQLITE_OK == sqlite3_prepare_v2(
451 dbh->dbh, ddl, sizeof(ddl)-1, &stmt, NULL)){
452 sqlite3_bind_int(stmt, 1, id);
453 sqlite3_step(stmt);
454 return 1 == sqlite3_column_int(stmt, 0);
455 }
456
457 return false;
458 }
459
arcan_db_targetid(struct arcan_dbh * dbh,const char * identifier,arcan_configid * defid)460 arcan_targetid arcan_db_targetid(struct arcan_dbh* dbh,
461 const char* identifier, arcan_configid* defid)
462 {
463 arcan_targetid rid = BAD_TARGET;
464 static const char dql[] = "SELECT tgtid FROM target WHERE name = ?;";
465 sqlite3_stmt* stmt;
466
467 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
468 sqlite3_bind_text(stmt, 1, identifier, -1, SQLITE_STATIC);
469
470 if (SQLITE_ROW == sqlite3_step(stmt))
471 rid = sqlite3_column_int64(stmt, 0);
472
473 sqlite3_finalize(stmt);
474 return rid;
475 }
476
arcan_db_list_appl(struct arcan_dbh * dbh)477 struct arcan_strarr arcan_db_list_appl(struct arcan_dbh* dbh)
478 {
479 static const char dql[] = "SELECT name FROM sqlite_master WHERE "
480 "type='table' and NAME like \"appl_%\"";
481 sqlite3_stmt* stmt;
482 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
483
484 return db_string_query(dbh, stmt, NULL, 0);
485 }
486
arcan_db_config_argv(struct arcan_dbh * dbh,arcan_configid id)487 struct arcan_strarr arcan_db_config_argv(struct arcan_dbh* dbh,arcan_configid id)
488 {
489 static const char dql[] = "SELECT arg FROM config_argv WHERE "
490 "config = ? ORDER BY argnum ASC;";
491 sqlite3_stmt* stmt;
492 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
493 sqlite3_bind_int(stmt, 1, id);
494
495 return db_string_query(dbh, stmt, NULL, 0);
496 }
497
arcan_db_target_argv(struct arcan_dbh * dbh,arcan_targetid id)498 struct arcan_strarr arcan_db_target_argv(struct arcan_dbh* dbh,arcan_targetid id)
499 {
500 static const char dql[] = "SELECT arg FROM target_argv WHERE "
501 "target = ? ORDER BY argnum ASC;";
502 sqlite3_stmt* stmt;
503 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
504 sqlite3_bind_int(stmt, 1, id);
505
506 return db_string_query(dbh, stmt, NULL, 0);
507 }
508
arcan_db_cfgtarget(struct arcan_dbh * dbh,arcan_configid cfg)509 arcan_targetid arcan_db_cfgtarget(struct arcan_dbh* dbh, arcan_configid cfg)
510 {
511 static const char dql[] = "SELECT target FROM config WHERE cfgid = ?;";
512 sqlite3_stmt* stmt;
513 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
514 sqlite3_bind_int(stmt, 1, cfg);
515 arcan_targetid tid = BAD_TARGET;
516
517 if (SQLITE_ROW == sqlite3_step(stmt))
518 tid = sqlite3_column_int64(stmt, 0);
519
520 sqlite3_finalize(stmt);
521 return tid;
522 }
523
arcan_db_configid(struct arcan_dbh * dbh,arcan_targetid target,const char * config)524 arcan_configid arcan_db_configid(struct arcan_dbh* dbh,
525 arcan_targetid target, const char* config)
526 {
527 static const char dql[] = "SELECT cfgid FROM config"
528 " WHERE name = ? AND target = ?;";
529 sqlite3_stmt* stmt;
530 arcan_configid cid = BAD_CONFIG;
531
532 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
533 sqlite3_bind_text(stmt, 1, config, strlen(config), SQLITE_STATIC);
534 sqlite3_bind_int(stmt, 2, target);
535
536 if (SQLITE_ROW == sqlite3_step(stmt))
537 cid = sqlite3_column_int64(stmt, 0);
538
539 sqlite3_finalize(stmt);
540 return cid;
541 }
542
arcan_db_target_tags(struct arcan_dbh * dbh)543 struct arcan_strarr arcan_db_target_tags(struct arcan_dbh* dbh)
544 {
545 sqlite3_stmt* stmt;
546 static const char dql[] = "SELECT DISTINCT tag FROM target;";
547 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
548 return db_string_query(dbh, stmt, NULL, 0);
549 }
550
arcan_db_targets(struct arcan_dbh * dbh,const char * tag)551 struct arcan_strarr arcan_db_targets(struct arcan_dbh* dbh, const char* tag)
552 {
553 sqlite3_stmt* stmt;
554 if (!tag){
555 static const char dql[] = "SELECT name FROM target;";
556 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
557 }
558 else {
559 static const char dql[] = "SELECT name FROM target WHERE tag=?;";
560 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
561 sqlite3_bind_text(stmt, 1, tag, strlen(tag), SQLITE_STATIC);
562 }
563 return db_string_query(dbh, stmt, NULL, 0);
564 }
565
arcan_db_targettag(struct arcan_dbh * dbh,arcan_targetid tid)566 char* arcan_db_targettag(struct arcan_dbh* dbh, arcan_targetid tid)
567 {
568 static const char dql[] = "SELECT tag FROM target WHERE tgtid = ?;";
569 char* resstr = NULL;
570 sqlite3_stmt* stmt;
571 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
572
573 sqlite3_bind_int(stmt, 1, tid);
574 if (sqlite3_step(stmt) == SQLITE_ROW){
575 resstr = (char*) sqlite3_column_text(stmt, 0);
576 }
577
578 if (resstr)
579 resstr = strdup(resstr);
580
581 sqlite3_finalize(stmt);
582 return resstr;
583 }
584
arcan_db_targetexec(struct arcan_dbh * dbh,arcan_configid configid,enum DB_BFORMAT * bfmt,struct arcan_strarr * argv,struct arcan_strarr * env,struct arcan_strarr * libs)585 char* arcan_db_targetexec(struct arcan_dbh* dbh,
586 arcan_configid configid, enum DB_BFORMAT* bfmt,
587 struct arcan_strarr* argv, struct arcan_strarr* env,
588 struct arcan_strarr* libs)
589 {
590 arcan_targetid tid = arcan_db_cfgtarget(dbh, configid);
591 if (tid == BAD_TARGET)
592 return NULL;
593
594 sqlite3_stmt* stmt;
595 static const char dql[] = "SELECT executable, bfmt "
596 "FROM target WHERE tgtid = ?;";
597
598 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql) - 1, &stmt, NULL);
599 sqlite3_bind_int(stmt, 1, tid);
600
601 char* execstr = NULL;
602 if (sqlite3_step(stmt) == SQLITE_ROW){
603 execstr = (char*) sqlite3_column_text(stmt, 0);
604 *bfmt = (int) sqlite3_column_int64(stmt, 1);
605 }
606
607 if (execstr)
608 execstr = strdup(execstr);
609
610 sqlite3_finalize(stmt);
611
612 static const char dql_tgt_argv[] = "SELECT arg FROM target_argv WHERE "
613 "target = ? ORDER BY argnum ASC;";
614 sqlite3_prepare_v2(dbh->dbh, dql_tgt_argv,
615 sizeof(dql_tgt_argv)-1, &stmt, NULL);
616 sqlite3_bind_int(stmt, 1, tid);
617
618 *argv = db_string_query(dbh, stmt, NULL, 1);
619 argv->data[0] = strdup(execstr ? execstr : "");
620
621 static const char dql_cfg_argv[] = "SELECT arg FROM config_argv WHERE "
622 "config = ? ORDER BY argnum ASC;";
623 sqlite3_prepare_v2(dbh->dbh, dql_cfg_argv,
624 sizeof(dql_cfg_argv)-1, &stmt, NULL);
625 sqlite3_bind_int(stmt, 1, configid);
626 *argv = db_string_query(dbh, stmt, argv, 0);
627
628 static const char dql_tgt_env[] = "SELECT key || '=' || val "
629 "FROM target_env WHERE target = ?";
630 sqlite3_prepare_v2(dbh->dbh, dql_tgt_env,
631 sizeof(dql_tgt_env)-1, &stmt, NULL);
632 sqlite3_bind_int(stmt, 1, tid);
633 *env = db_string_query(dbh, stmt, NULL, 0);
634
635 static const char dql_cfg_env[] = "SELECT key || '=' || val "
636 "FROM config_env WHERE config = ?";
637 sqlite3_prepare_v2(dbh->dbh, dql_cfg_env,
638 sizeof(dql_cfg_env)-1, &stmt, NULL);
639 sqlite3_bind_int(stmt, 1, tid);
640 db_string_query(dbh, stmt, env, 0);
641
642 static const char dql_tgt_lib[] = "SELECT libname FROM target_libs WHERE "
643 "target = ?;";
644 sqlite3_prepare_v2(dbh->dbh, dql_tgt_lib,
645 sizeof(dql_tgt_lib)-1, &stmt, NULL);
646 sqlite3_bind_int(stmt, 1, tid);
647 *libs = db_string_query(dbh, stmt, NULL, 0);
648
649 return execstr;
650 }
651
arcan_db_launch_status(struct arcan_dbh * dbh,arcan_configid cid,bool s)652 void arcan_db_launch_status(struct arcan_dbh* dbh, arcan_configid cid, bool s)
653 {
654 static const char dql_ok[] = "UPDATE passed_counter SET "
655 "passed_counter = passed_counter + 1 WHERE config = ?;";
656
657 static const char dql_fail[] = "UPDATE failed_counter SET "
658 "failed_counter = failed_counter + 1 WHERE config = ?;";
659
660 sqlite3_stmt* stmt;
661 sqlite3_prepare_v2(dbh->dbh,
662 (s ? dql_ok : dql_fail), sizeof(dql_ok)-1, &stmt, NULL);
663 sqlite3_bind_int(stmt, 1, cid);
664 sqlite3_step(stmt);
665 sqlite3_finalize(stmt);
666 }
667
arcan_db_configs(struct arcan_dbh * dbh,arcan_targetid tid)668 struct arcan_strarr arcan_db_configs(struct arcan_dbh* dbh, arcan_targetid tid)
669 {
670 static const char dql[] = "SELECT name FROM config WHERE target = ?;";
671 sqlite3_stmt* stmt;
672 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
673 sqlite3_bind_int(stmt, 1, tid);
674
675 return db_string_query(dbh, stmt, NULL, 0);
676 }
677
arcan_db_execname(struct arcan_dbh * dbh,arcan_targetid tid)678 char* arcan_db_execname(struct arcan_dbh* dbh, arcan_targetid tid)
679 {
680 static const char dql[] = "SELECT executable FROM target WHERE tgtid = ?;";
681 sqlite3_stmt* stmt;
682 sqlite3_prepare_v2(dbh->dbh, dql, sizeof(dql)-1, &stmt, NULL);
683 sqlite3_bind_int(stmt, 1, tid);
684
685 char* res = NULL;
686 if (sqlite3_step(stmt) == SQLITE_ROW) {
687 const unsigned char* arg = sqlite3_column_text(stmt, 0);
688 res = arg ? strdup((char*)arg) : NULL;
689 }
690
691 sqlite3_finalize(stmt);
692 return res;
693 }
694
arcan_db_begin_transaction(struct arcan_dbh * dbh,enum DB_KVTARGET kvt,union arcan_dbtrans_id id)695 void arcan_db_begin_transaction(struct arcan_dbh* dbh,
696 enum DB_KVTARGET kvt, union arcan_dbtrans_id id)
697 {
698 if (dbh->transaction)
699 arcan_fatal("arcan_db_begin_transaction()"
700 " called during a pending transaction\n");
701
702 sqlite3_exec(dbh->dbh, "BEGIN;", NULL, NULL, NULL);
703 int code = SQLITE_OK;
704
705 switch (kvt){
706 case DVT_APPL:
707 code = sqlite3_prepare_v2(dbh->dbh, dbh->akv_update,
708 dbh->akv_upd_sz, &dbh->transaction, NULL);
709 break;
710
711 case DVT_TARGET:
712 code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_TARGET,
713 strlen(DI_INSKV_TARGET)+1, &dbh->transaction, NULL);
714 break;
715
716 case DVT_CONFIG:
717 code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_CONFIG,
718 strlen(DI_INSKV_CONFIG)+1, &dbh->transaction, NULL);
719 break;
720
721 case DVT_CONFIG_ENV:
722 code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_CONFIG_ENV,
723 strlen(DI_INSKV_TARGET_ENV)+1, &dbh->transaction, NULL);
724 break;
725
726 case DVT_TARGET_ENV:
727 code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_TARGET_ENV,
728 strlen(DI_INSKV_TARGET_ENV)+1, &dbh->transaction, NULL);
729 break;
730
731 case DVT_TARGET_LIBV:
732 code = sqlite3_prepare_v2(dbh->dbh, DI_INSKV_TARGET_LIBV,
733 strlen(DI_INSKV_TARGET_LIBV)+1, &dbh->transaction, NULL);
734 break;
735 case DVT_ENDM:
736 break;
737 }
738
739 if (code != SQLITE_OK){
740 arcan_warning("arcan_db_begin_transaction(), failed: %s\n",
741 sqlite3_errmsg(dbh->dbh));
742 }
743
744 dbh->trid = id;
745 dbh->ttype = kvt;
746 }
747
arcan_db_getkeys(struct arcan_dbh * dbh,enum DB_KVTARGET tgt,union arcan_dbtrans_id id)748 struct arcan_strarr arcan_db_getkeys(struct arcan_dbh* dbh,
749 enum DB_KVTARGET tgt, union arcan_dbtrans_id id)
750 {
751 #define GET_KV_TGT "SELECT key || '=' || val FROM target_kv WHERE target = ?;"
752 static const char* const queries[] = {
753 GET_KV_TGT,
754 "SELECT key || '=' || val FROM config_kv WHERE config = ?"
755 };
756
757 const char* qry = NULL;
758 if (tgt >= DVT_TARGET && tgt < DVT_CONFIG)
759 qry = queries[0];
760 else
761 qry = queries[1];
762
763 sqlite3_stmt * stmt;
764 sqlite3_prepare_v2(dbh->dbh, qry, sizeof(GET_KV_TGT)-1, &stmt, NULL);
765 sqlite3_bind_int(stmt, 1, tgt>=DVT_TARGET && tgt<DVT_CONFIG ? id.tid:id.cid);
766
767 #undef GET_KV_TGT
768 return db_string_query(dbh, stmt, NULL, 0);
769 }
770
arcan_db_applkeys(struct arcan_dbh * dbh,const char * applname,const char * pattern)771 struct arcan_strarr arcan_db_applkeys(struct arcan_dbh* dbh,
772 const char* applname, const char* pattern)
773 {
774 #define MATCH_APPL "SELECT key || '=' || val FROM appl_%s WHERE key LIKE ?;"
775
776 size_t mk_sz = sizeof(MATCH_APPL) + strlen(applname);
777 char mk_buf[ mk_sz ];
778 ssize_t nw = snprintf(mk_buf, mk_sz, MATCH_APPL, applname);
779
780 sqlite3_stmt* stmt;
781 sqlite3_prepare_v2(dbh->dbh, mk_buf, mk_sz-1, &stmt, NULL);
782 sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
783
784 return db_string_query(dbh, stmt, NULL, 0);
785 #undef MATCH_KEY_APPL
786 }
787
arcan_db_matchkey(struct arcan_dbh * dbh,enum DB_KVTARGET tgt,const char * pattern)788 struct arcan_strarr arcan_db_matchkey(struct arcan_dbh* dbh,
789 enum DB_KVTARGET tgt, const char* pattern)
790 {
791 #define MATCH_KEY_TGT "SELECT tgtid ||':'|| value"\
792 " FROM target_kv WHERE key LIKE ?;"
793
794 static const char* const queries[] = {
795 MATCH_KEY_TGT,
796 "SELECT cfgid || ':' || value FROM config_kv WHERE key LIKE ?;"
797 };
798
799 const char* qry = NULL;
800 if (tgt >= DVT_TARGET && tgt < DVT_CONFIG)
801 qry = queries[0];
802 else
803 qry = queries[1];
804
805 sqlite3_stmt* stmt;
806 sqlite3_prepare_v2(dbh->dbh, qry, sizeof(MATCH_KEY_TGT)-1, &stmt, NULL);
807 sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
808
809 return db_string_query(dbh, stmt, NULL, 0);
810 }
811
arcan_db_getvalue(struct arcan_dbh * dbh,enum DB_KVTARGET tgt,int64_t id,const char * key)812 char* arcan_db_getvalue(struct arcan_dbh* dbh,
813 enum DB_KVTARGET tgt, int64_t id, const char* key)
814 {
815 char* res = NULL;
816 /* must match enum */
817 assert(DVT_ENDM == 5);
818
819 static const char* queries[] = {
820 "SELECT val FROM target_kv WHERE key = ? AND tgtid = ? LIMIT 1;",
821 "SELECT val FROM config_kv WHERE key = ? AND cfgid = ? LIMIT 1;"
822 };
823
824 const char* qry = NULL;
825 if (tgt >= DVT_TARGET && tgt < DVT_CONFIG)
826 qry = queries[0];
827 else
828 qry = queries[1];
829
830 /* assume they all are the same length */
831 static size_t qry_sz;
832 if (qry_sz == 0)
833 qry_sz = strlen(queries[1]);
834
835 sqlite3_stmt* stmt = NULL;
836
837 if (tgt == DVT_APPL){
838 sqlite3_prepare_v2(dbh->dbh, dbh->akv_get, dbh->akv_get_sz, &stmt, NULL);
839 }
840 else {
841 sqlite3_prepare_v2(dbh->dbh, qry, qry_sz, &stmt, NULL);
842 sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
843 sqlite3_bind_int(stmt, 2, id);
844 }
845
846 if (SQLITE_ROW == sqlite3_step(stmt)){
847 const char* row = (const char*) sqlite3_column_text(stmt, 0);
848 if (row)
849 res = strdup(row);
850 }
851
852 sqlite3_finalize(stmt);
853 return res;
854 }
855
arcan_db_add_kvpair(struct arcan_dbh * dbh,const char * key,const char * val)856 void arcan_db_add_kvpair(
857 struct arcan_dbh* dbh, const char* key, const char* val)
858 {
859 if (!dbh->transaction)
860 arcan_fatal("arcan_db_add_kvpair() "
861 "called without any open transaction.");
862
863 if (!val){
864 dbh->trclean = true;
865 return;
866 }
867
868 if (val[0] == 0)
869 dbh->trclean = true;
870
871 sqlite3_bind_text(dbh->transaction, 1, key, -1, SQLITE_TRANSIENT);
872 sqlite3_bind_text(dbh->transaction, 2, val, -1, SQLITE_TRANSIENT);
873
874 switch (dbh->ttype){
875 case DVT_APPL:
876 case DVT_ENDM:
877 break;
878
879 case DVT_TARGET:
880 case DVT_TARGET_ENV:
881 case DVT_TARGET_LIBV:
882 sqlite3_bind_int(dbh->transaction, 3, dbh->trid.tid);
883 break;
884
885 case DVT_CONFIG:
886 case DVT_CONFIG_ENV:
887 sqlite3_bind_int(dbh->transaction, 3, dbh->trid.cid);
888 break;
889 }
890
891 int rc = sqlite3_step(dbh->transaction);
892 if (SQLITE_DONE != rc)
893 arcan_warning("arcan_db_addkvpair(%s=%s), %d failed: %s\n",
894 key, val, rc, sqlite3_errmsg(dbh->dbh));
895 else {
896 sqlite3_clear_bindings(dbh->transaction);
897 sqlite3_reset(dbh->transaction);
898 }
899 }
900
arcan_db_end_transaction(struct arcan_dbh * dbh)901 void arcan_db_end_transaction(struct arcan_dbh* dbh)
902 {
903 if (!dbh->transaction)
904 arcan_fatal("arcan_db_end_transaction() "
905 "called without any open transaction.");
906
907 sqlite3_finalize(dbh->transaction);
908
909 if (dbh->trclean){
910 switch (dbh->ttype){
911 case DVT_APPL:
912 sqlite3_exec(dbh->dbh, dbh->akv_clean, NULL, NULL, NULL);
913 break;
914 case DVT_TARGET:
915 sqlite3_exec(dbh->dbh, DI_DROPKV_TARGET, NULL, NULL, NULL);
916 break;
917 case DVT_CONFIG:
918 sqlite3_exec(dbh->dbh, DI_DROPKV_CONFIG, NULL, NULL, NULL);
919 break;
920 default:
921 break;
922 }
923 dbh->trclean = false;
924 }
925
926 if (SQLITE_OK != sqlite3_exec(dbh->dbh, "COMMIT;", NULL, NULL, NULL)){
927 arcan_warning("arcan_db_end_transaction(), failed: %s\n",
928 sqlite3_errmsg(dbh->dbh));
929 }
930
931 dbh->transaction = NULL;
932 }
933
arcan_db_appl_kv(struct arcan_dbh * dbh,const char * applname,const char * key,const char * value)934 bool arcan_db_appl_kv(struct arcan_dbh* dbh,
935 const char* applname, const char* key, const char* value)
936 {
937 bool rv = false;
938
939 if (dbh->transaction)
940 arcan_fatal("arcan_db_appl_kv() called during a pending transaction\n");
941
942 if (!applname || !dbh || !key)
943 return rv;
944
945 const char ddl_insert[] = "INSERT OR REPLACE "
946 "INTO appl_%s(key, val) VALUES(?, ?);";
947 const char k_drop[] = "DELETE FROM appl_%s WHERE key=?;";
948
949 size_t upd_sz = 0;
950 const char* dqry = ddl_insert;
951
952 if (!value){
953 upd_sz = sizeof(k_drop) + strlen(applname);
954 dqry = k_drop;
955 }
956 else
957 upd_sz = sizeof(ddl_insert) + strlen(applname);
958
959 char upd_buf[ upd_sz ];
960 ssize_t nw = snprintf(upd_buf, upd_sz, dqry, applname);
961
962 sqlite3_stmt* stmt = NULL;
963 sqlite3_prepare_v2(dbh->dbh, upd_buf, nw, &stmt, NULL);
964 sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT);
965 sqlite3_bind_text(stmt, 2, value, -1, SQLITE_TRANSIENT);
966
967 rv = sqlite3_step(stmt) == SQLITE_DONE;
968 sqlite3_finalize(stmt);
969
970 return rv;
971 }
972
arcan_db_appl_val(struct arcan_dbh * dbh,const char * const applname,const char * const key)973 char* arcan_db_appl_val(struct arcan_dbh* dbh,
974 const char* const applname, const char* const key)
975 {
976 if (!dbh || !key)
977 return NULL;
978
979 const char qry[] = "SELECT val FROM appl_%s WHERE key = ?;";
980
981 size_t wbuf_sz = strlen(applname) + sizeof(qry);
982 char wbuf[ wbuf_sz ];
983 memset(wbuf, '\0', wbuf_sz);
984 snprintf(wbuf, wbuf_sz, qry, applname);
985
986 sqlite3_stmt* stmt = NULL;
987 sqlite3_prepare_v2(dbh->dbh, wbuf, wbuf_sz, &stmt, NULL);
988 sqlite3_bind_text(stmt, 1, (char*) key, -1, SQLITE_TRANSIENT);
989
990 char* rv = NULL;
991 int rc = sqlite3_step(stmt);
992
993 if (rc == SQLITE_ROW){
994 const unsigned char* rowt;
995
996 if ( (rowt = sqlite3_column_text(stmt, 0)) != NULL)
997 rv = strdup((const char*) rowt);
998 }
999
1000 sqlite3_finalize(stmt);
1001
1002 return rv;
1003 }
1004
setup_ddl(struct arcan_dbh * dbh)1005 static void setup_ddl(struct arcan_dbh* dbh)
1006 {
1007 create_appl_group(dbh, "arcan");
1008 create_appl_group(dbh, "arcan_lwa");
1009 arcan_db_appl_kv(dbh, "arcan", "dbversion", DB_VERSION_NUM);
1010 arcan_db_appl_kv(dbh, "arcan_lwa", "dbversion", DB_VERSION_NUM);
1011
1012 for (size_t i = 0; i < sizeof(ddls)/sizeof(ddls[0]); i++)
1013 db_void_query(dbh, ddls[i], false);
1014 }
1015
1016 /* to detect old databases and upgrade if possible */
dbh_integrity_check(struct arcan_dbh * dbh)1017 static bool dbh_integrity_check(struct arcan_dbh* dbh){
1018 int tablecount = db_num_query(dbh, "SELECT Count(*) "
1019 "FROM sqlite_master WHERE type='table';", NULL);
1020
1021 /* empty database */
1022 if (tablecount <= 0){
1023 arcan_warning("Empty database encountered, running default DDL queries.\n");
1024 setup_ddl(dbh);
1025
1026 return true;
1027 }
1028
1029 /* check for descriptor table, missing?
1030 * that means we have a first- version database, push upgrade */
1031 char* valstr = arcan_db_appl_val(dbh, ARCAN_TBL, "dbversion");
1032 unsigned vnum = valstr ? strtoul(valstr, NULL, 10) : 0;
1033
1034 switch (vnum){
1035 case 0:
1036 case 1:
1037 case 2:
1038 arcan_warning("DB version (< 3) unsupported, rebuild necessary\n");
1039 return false;
1040 case 3:
1041 arcan_warning("DB version (< 4) found, rebuild suggested\n");
1042 }
1043
1044 arcan_mem_free(valstr);
1045 return true;
1046 }
1047
arcan_db_close(struct arcan_dbh ** ctx)1048 void arcan_db_close(struct arcan_dbh** ctx)
1049 {
1050 if (!ctx)
1051 return;
1052
1053 sqlite3_close((*ctx)->dbh);
1054 arcan_mem_free((*ctx)->applname);
1055 arcan_mem_free((*ctx)->akv_update);
1056 arcan_mem_free((*ctx)->akv_get);
1057 arcan_mem_free(*ctx);
1058 *ctx = NULL;
1059 }
1060
arcan_db_open(const char * fname,const char * applname)1061 struct arcan_dbh* arcan_db_open(const char* fname, const char* applname)
1062 {
1063 sqlite3* dbh;
1064
1065 if (!fname)
1066 return NULL;
1067
1068 if (!db_init) {
1069 int rv = sqlite3_initialize();
1070 db_init = true;
1071
1072 if (rv != SQLITE_OK)
1073 return NULL;
1074
1075 atexit(sqliteexit);
1076 }
1077
1078 if (!applname)
1079 applname = "_default";
1080
1081 if (sqlite3_open_v2(fname, &dbh,
1082 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK){
1083 struct arcan_dbh* res = arcan_alloc_mem(
1084 sizeof(struct arcan_dbh), ARCAN_MEM_EXTSTRUCT,
1085 ARCAN_MEM_SENSITIVE | ARCAN_MEM_BZERO, ARCAN_MEMALIGN_PAGE
1086 );
1087
1088 res->dbh = dbh;
1089 assert(dbh);
1090
1091 if ( !dbh_integrity_check(res) ){
1092 sqlite3_close(dbh);
1093 arcan_mem_free(res);
1094 return NULL;
1095 }
1096
1097 res->applname = strdup(applname);
1098 create_appl_group(res, res->applname);
1099
1100 db_void_query(res, "PRAGMA foreign_keys=ON;", false);
1101 db_void_query(res, "PRAGMA synchronous=OFF;", false);
1102
1103 return res;
1104 }
1105 else
1106 ;
1107
1108 return NULL;
1109 }
1110