omicron/src/db.c

544 lines
16 KiB
C

#include <string.h>
#include "db.h"
#include "ring.h"
#include "sqlite3.h"
#include "tinycthread.h"
static int db_enabled = 0;
static sqlite3 *db;
static sqlite3_stmt *insert_block_stmt;
static sqlite3_stmt *insert_light_stmt;
static sqlite3_stmt *insert_sign_stmt;
static sqlite3_stmt *delete_sign_stmt;
static sqlite3_stmt *delete_signs_stmt;
static sqlite3_stmt *load_blocks_stmt;
static sqlite3_stmt *load_lights_stmt;
static sqlite3_stmt *load_signs_stmt;
static sqlite3_stmt *get_key_stmt;
static sqlite3_stmt *set_key_stmt;
static Ring ring;
static thrd_t thrd;
static mtx_t mtx;
static cnd_t cnd;
static mtx_t load_mtx;
void db_enable() {
db_enabled = 1;
}
void db_disable() {
db_enabled = 0;
}
int get_db_enabled() {
return db_enabled;
}
int db_init(char *path) {
if (!db_enabled) {
return 0;
}
static const char *create_query =
"attach database 'auth.db' as auth;"
"create table if not exists auth.identity_token ("
" username text not null,"
" token text not null,"
" selected int not null"
");"
"create unique index if not exists auth.identity_token_username_idx"
" on identity_token (username);"
"create table if not exists state ("
" x float not null,"
" y float not null,"
" z float not null,"
" rx float not null,"
" ry float not null"
");"
"create table if not exists block ("
" p int not null,"
" q int not null,"
" x int not null,"
" y int not null,"
" z int not null,"
" w int not null"
");"
"create table if not exists light ("
" p int not null,"
" q int not null,"
" x int not null,"
" y int not null,"
" z int not null,"
" w int not null"
");"
"create table if not exists key ("
" p int not null,"
" q int not null,"
" key int not null"
");"
"create table if not exists sign ("
" p int not null,"
" q int not null,"
" x int not null,"
" y int not null,"
" z int not null,"
" face int not null,"
" text text not null"
");"
"create unique index if not exists block_pqxyz_idx on block (p, q, x, y, z);"
"create unique index if not exists light_pqxyz_idx on light (p, q, x, y, z);"
"create unique index if not exists key_pq_idx on key (p, q);"
"create unique index if not exists sign_xyzface_idx on sign (x, y, z, face);"
"create index if not exists sign_pq_idx on sign (p, q);";
static const char *insert_block_query =
"insert or replace into block (p, q, x, y, z, w) "
"values (?, ?, ?, ?, ?, ?);";
static const char *insert_light_query =
"insert or replace into light (p, q, x, y, z, w) "
"values (?, ?, ?, ?, ?, ?);";
static const char *insert_sign_query =
"insert or replace into sign (p, q, x, y, z, face, text) "
"values (?, ?, ?, ?, ?, ?, ?);";
static const char *delete_sign_query =
"delete from sign where x = ? and y = ? and z = ? and face = ?;";
static const char *delete_signs_query =
"delete from sign where x = ? and y = ? and z = ?;";
static const char *load_blocks_query =
"select x, y, z, w from block where p = ? and q = ?;";
static const char *load_lights_query =
"select x, y, z, w from light where p = ? and q = ?;";
static const char *load_signs_query =
"select x, y, z, face, text from sign where p = ? and q = ?;";
static const char *get_key_query =
"select key from key where p = ? and q = ?;";
static const char *set_key_query =
"insert or replace into key (p, q, key) "
"values (?, ?, ?);";
int rc;
rc = sqlite3_open(path, &db);
if (rc) return rc;
rc = sqlite3_exec(db, create_query, NULL, NULL, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(
db, insert_block_query, -1, &insert_block_stmt, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(
db, insert_light_query, -1, &insert_light_stmt, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(
db, insert_sign_query, -1, &insert_sign_stmt, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(
db, delete_sign_query, -1, &delete_sign_stmt, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(
db, delete_signs_query, -1, &delete_signs_stmt, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(db, load_blocks_query, -1, &load_blocks_stmt, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(db, load_lights_query, -1, &load_lights_stmt, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(db, load_signs_query, -1, &load_signs_stmt, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(db, get_key_query, -1, &get_key_stmt, NULL);
if (rc) return rc;
rc = sqlite3_prepare_v2(db, set_key_query, -1, &set_key_stmt, NULL);
if (rc) return rc;
sqlite3_exec(db, "begin;", NULL, NULL, NULL);
db_worker_start();
return 0;
}
void db_close() {
if (!db_enabled) {
return;
}
db_worker_stop();
sqlite3_exec(db, "commit;", NULL, NULL, NULL);
sqlite3_finalize(insert_block_stmt);
sqlite3_finalize(insert_light_stmt);
sqlite3_finalize(insert_sign_stmt);
sqlite3_finalize(delete_sign_stmt);
sqlite3_finalize(delete_signs_stmt);
sqlite3_finalize(load_blocks_stmt);
sqlite3_finalize(load_lights_stmt);
sqlite3_finalize(load_signs_stmt);
sqlite3_finalize(get_key_stmt);
sqlite3_finalize(set_key_stmt);
sqlite3_close(db);
}
void db_commit() {
if (!db_enabled) {
return;
}
mtx_lock(&mtx);
ring_put_commit(&ring);
cnd_signal(&cnd);
mtx_unlock(&mtx);
}
void _db_commit() {
sqlite3_exec(db, "commit; begin;", NULL, NULL, NULL);
}
void db_auth_set(char *username, char *identity_token) {
if (!db_enabled) {
return;
}
static const char *query =
"insert or replace into auth.identity_token "
"(username, token, selected) values (?, ?, ?);";
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, username, -1, NULL);
sqlite3_bind_text(stmt, 2, identity_token, -1, NULL);
sqlite3_bind_int(stmt, 3, 1);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
db_auth_select(username);
}
int db_auth_select(char *username) {
if (!db_enabled) {
return 0;
}
db_auth_select_none();
static const char *query =
"update auth.identity_token set selected = 1 where username = ?;";
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, username, -1, NULL);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
return sqlite3_changes(db);
}
void db_auth_select_none() {
if (!db_enabled) {
return;
}
sqlite3_exec(db, "update auth.identity_token set selected = 0;",
NULL, NULL, NULL);
}
int db_auth_get(
char *username,
char *identity_token, int identity_token_length)
{
if (!db_enabled) {
return 0;
}
static const char *query =
"select token from auth.identity_token "
"where username = ?;";
int result = 0;
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, username, -1, NULL);
if (sqlite3_step(stmt) == SQLITE_ROW) {
const char *a = (const char *)sqlite3_column_text(stmt, 0);
strncpy(identity_token, a, identity_token_length - 1);
identity_token[identity_token_length - 1] = '\0';
result = 1;
}
sqlite3_finalize(stmt);
return result;
}
int db_auth_get_selected(
char *username, int username_length,
char *identity_token, int identity_token_length)
{
if (!db_enabled) {
return 0;
}
static const char *query =
"select username, token from auth.identity_token "
"where selected = 1;";
int result = 0;
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
if (sqlite3_step(stmt) == SQLITE_ROW) {
const char *a = (const char *)sqlite3_column_text(stmt, 0);
const char *b = (const char *)sqlite3_column_text(stmt, 1);
strncpy(username, a, username_length - 1);
username[username_length - 1] = '\0';
strncpy(identity_token, b, identity_token_length - 1);
identity_token[identity_token_length - 1] = '\0';
result = 1;
}
sqlite3_finalize(stmt);
return result;
}
void db_save_state(float x, float y, float z, float rx, float ry) {
if (!db_enabled) {
return;
}
static const char *query =
"insert into state (x, y, z, rx, ry) values (?, ?, ?, ?, ?);";
sqlite3_stmt *stmt;
sqlite3_exec(db, "delete from state;", NULL, NULL, NULL);
sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
sqlite3_bind_double(stmt, 1, x);
sqlite3_bind_double(stmt, 2, y);
sqlite3_bind_double(stmt, 3, z);
sqlite3_bind_double(stmt, 4, rx);
sqlite3_bind_double(stmt, 5, ry);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
int db_load_state(float *x, float *y, float *z, float *rx, float *ry) {
if (!db_enabled) {
return 0;
}
static const char *query =
"select x, y, z, rx, ry from state;";
int result = 0;
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
if (sqlite3_step(stmt) == SQLITE_ROW) {
*x = sqlite3_column_double(stmt, 0);
*y = sqlite3_column_double(stmt, 1);
*z = sqlite3_column_double(stmt, 2);
*rx = sqlite3_column_double(stmt, 3);
*ry = sqlite3_column_double(stmt, 4);
result = 1;
}
sqlite3_finalize(stmt);
return result;
}
void db_insert_block(int p, int q, int x, int y, int z, int w) {
if (!db_enabled) {
return;
}
mtx_lock(&mtx);
ring_put_block(&ring, p, q, x, y, z, w);
cnd_signal(&cnd);
mtx_unlock(&mtx);
}
void _db_insert_block(int p, int q, int x, int y, int z, int w) {
sqlite3_reset(insert_block_stmt);
sqlite3_bind_int(insert_block_stmt, 1, p);
sqlite3_bind_int(insert_block_stmt, 2, q);
sqlite3_bind_int(insert_block_stmt, 3, x);
sqlite3_bind_int(insert_block_stmt, 4, y);
sqlite3_bind_int(insert_block_stmt, 5, z);
sqlite3_bind_int(insert_block_stmt, 6, w);
sqlite3_step(insert_block_stmt);
}
void db_insert_light(int p, int q, int x, int y, int z, int w) {
if (!db_enabled) {
return;
}
mtx_lock(&mtx);
ring_put_light(&ring, p, q, x, y, z, w);
cnd_signal(&cnd);
mtx_unlock(&mtx);
}
void _db_insert_light(int p, int q, int x, int y, int z, int w) {
sqlite3_reset(insert_light_stmt);
sqlite3_bind_int(insert_light_stmt, 1, p);
sqlite3_bind_int(insert_light_stmt, 2, q);
sqlite3_bind_int(insert_light_stmt, 3, x);
sqlite3_bind_int(insert_light_stmt, 4, y);
sqlite3_bind_int(insert_light_stmt, 5, z);
sqlite3_bind_int(insert_light_stmt, 6, w);
sqlite3_step(insert_light_stmt);
}
void db_insert_sign(
int p, int q, int x, int y, int z, int face, const char *text)
{
if (!db_enabled) {
return;
}
sqlite3_reset(insert_sign_stmt);
sqlite3_bind_int(insert_sign_stmt, 1, p);
sqlite3_bind_int(insert_sign_stmt, 2, q);
sqlite3_bind_int(insert_sign_stmt, 3, x);
sqlite3_bind_int(insert_sign_stmt, 4, y);
sqlite3_bind_int(insert_sign_stmt, 5, z);
sqlite3_bind_int(insert_sign_stmt, 6, face);
sqlite3_bind_text(insert_sign_stmt, 7, text, -1, NULL);
sqlite3_step(insert_sign_stmt);
}
void db_delete_sign(int x, int y, int z, int face) {
if (!db_enabled) {
return;
}
sqlite3_reset(delete_sign_stmt);
sqlite3_bind_int(delete_sign_stmt, 1, x);
sqlite3_bind_int(delete_sign_stmt, 2, y);
sqlite3_bind_int(delete_sign_stmt, 3, z);
sqlite3_bind_int(delete_sign_stmt, 4, face);
sqlite3_step(delete_sign_stmt);
}
void db_delete_signs(int x, int y, int z) {
if (!db_enabled) {
return;
}
sqlite3_reset(delete_signs_stmt);
sqlite3_bind_int(delete_signs_stmt, 1, x);
sqlite3_bind_int(delete_signs_stmt, 2, y);
sqlite3_bind_int(delete_signs_stmt, 3, z);
sqlite3_step(delete_signs_stmt);
}
void db_delete_all_signs() {
if (!db_enabled) {
return;
}
sqlite3_exec(db, "delete from sign;", NULL, NULL, NULL);
}
void db_load_blocks(Map *map, int p, int q) {
if (!db_enabled) {
return;
}
mtx_lock(&load_mtx);
sqlite3_reset(load_blocks_stmt);
sqlite3_bind_int(load_blocks_stmt, 1, p);
sqlite3_bind_int(load_blocks_stmt, 2, q);
while (sqlite3_step(load_blocks_stmt) == SQLITE_ROW) {
int x = sqlite3_column_int(load_blocks_stmt, 0);
int y = sqlite3_column_int(load_blocks_stmt, 1);
int z = sqlite3_column_int(load_blocks_stmt, 2);
int w = sqlite3_column_int(load_blocks_stmt, 3);
map_set(map, x, y, z, w);
}
mtx_unlock(&load_mtx);
}
void db_load_lights(Map *map, int p, int q) {
if (!db_enabled) {
return;
}
mtx_lock(&load_mtx);
sqlite3_reset(load_lights_stmt);
sqlite3_bind_int(load_lights_stmt, 1, p);
sqlite3_bind_int(load_lights_stmt, 2, q);
while (sqlite3_step(load_lights_stmt) == SQLITE_ROW) {
int x = sqlite3_column_int(load_lights_stmt, 0);
int y = sqlite3_column_int(load_lights_stmt, 1);
int z = sqlite3_column_int(load_lights_stmt, 2);
int w = sqlite3_column_int(load_lights_stmt, 3);
map_set(map, x, y, z, w);
}
mtx_unlock(&load_mtx);
}
void db_load_signs(SignList *list, int p, int q) {
if (!db_enabled) {
return;
}
sqlite3_reset(load_signs_stmt);
sqlite3_bind_int(load_signs_stmt, 1, p);
sqlite3_bind_int(load_signs_stmt, 2, q);
while (sqlite3_step(load_signs_stmt) == SQLITE_ROW) {
int x = sqlite3_column_int(load_signs_stmt, 0);
int y = sqlite3_column_int(load_signs_stmt, 1);
int z = sqlite3_column_int(load_signs_stmt, 2);
int face = sqlite3_column_int(load_signs_stmt, 3);
const char *text = (const char *)sqlite3_column_text(
load_signs_stmt, 4);
sign_list_add(list, x, y, z, face, text);
}
}
int db_get_key(int p, int q) {
if (!db_enabled) {
return 0;
}
sqlite3_reset(get_key_stmt);
sqlite3_bind_int(get_key_stmt, 1, p);
sqlite3_bind_int(get_key_stmt, 2, q);
if (sqlite3_step(get_key_stmt) == SQLITE_ROW) {
return sqlite3_column_int(get_key_stmt, 0);
}
return 0;
}
void db_set_key(int p, int q, int key) {
if (!db_enabled) {
return;
}
mtx_lock(&mtx);
ring_put_key(&ring, p, q, key);
cnd_signal(&cnd);
mtx_unlock(&mtx);
}
void _db_set_key(int p, int q, int key) {
sqlite3_reset(set_key_stmt);
sqlite3_bind_int(set_key_stmt, 1, p);
sqlite3_bind_int(set_key_stmt, 2, q);
sqlite3_bind_int(set_key_stmt, 3, key);
sqlite3_step(set_key_stmt);
}
void db_worker_start(char *path) {
if (!db_enabled) {
return;
}
ring_alloc(&ring, 1024);
mtx_init(&mtx, mtx_plain);
mtx_init(&load_mtx, mtx_plain);
cnd_init(&cnd);
thrd_create(&thrd, db_worker_run, path);
}
void db_worker_stop() {
if (!db_enabled) {
return;
}
mtx_lock(&mtx);
ring_put_exit(&ring);
cnd_signal(&cnd);
mtx_unlock(&mtx);
thrd_join(thrd, NULL);
cnd_destroy(&cnd);
mtx_destroy(&load_mtx);
mtx_destroy(&mtx);
ring_free(&ring);
}
int db_worker_run(void *arg) {
int running = 1;
while (running) {
RingEntry e;
mtx_lock(&mtx);
while (!ring_get(&ring, &e)) {
cnd_wait(&cnd, &mtx);
}
mtx_unlock(&mtx);
switch (e.type) {
case BLOCK:
_db_insert_block(e.p, e.q, e.x, e.y, e.z, e.w);
break;
case LIGHT:
_db_insert_light(e.p, e.q, e.x, e.y, e.z, e.w);
break;
case KEY:
_db_set_key(e.p, e.q, e.key);
break;
case COMMIT:
_db_commit();
break;
case EXIT:
running = 0;
break;
}
}
return 0;
}