Sélectionner une révision Git
open.c 7,18 Kio
#define _POSIX_C_SOURCE 200809L
#include <common/common.h>
#include <lektor/database.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
/* Some notes:
- There's one and only row.
- `paused` 0 = play, 1 = paused
- `random` whether the queue is played randomly
- `repeat` whether the queue loops
- `single` whether only one kara loops
- `current` the position in the queue of the kara being played
- `elapsed` the number of seconds from the beginning of the current kara
- `duration` the total duration of the playing kara
This schema is used to initialize the in-memory database. */
static const char *const SQL_MEM_SCHEM =
"CREATE TABLE IF NOT EXISTS queue_state"
" ( id INTEGER PRIMARY KEY DEFAULT 42 CHECK(id = 42)"
" , volume INTEGER NOT NULL DEFAULT 100 CHECK(0 <= volume AND volume <= 100)"
" , paused INTEGER NOT NULL DEFAULT 1"
" , random INTEGER NOT NULL DEFAULT 0"
" , repeat INTEGER NOT NULL DEFAULT 0"
" , single INTEGER NOT NULL DEFAULT 0"
" , consume INTEGER NOT NULL DEFAULT 0"
" , current INTEGER CHECK(current > 0)"
" , duration INTEGER CHECK(duration >= 0)"
" , elapsed INTEGER CHECK(elapsed >= 0)"
" );\n"
"INSERT INTO queue_state (id) VALUES (42);\n"
"CREATE TABLE IF NOT EXISTS config"
" ( section TEXT NOT NULL"
" , key TEXT NOT NULL"
" , value TEXT"
" , PRIMARY KEY (section, key)"
" ) WITHOUT ROWID;\n";
/* Should be, defined scripts_init_sql and its length scripts_init_sql_len */
extern unsigned char ___scripts_init_sql[];
extern int ___scripts_init_sql_len;
#define INVALID_CHARS_DBPATH ":?!'\""
#define HEAP_LIMIT_SOFT 100 * 1024 * 1024
#define HEAP_LIMIT_HARD 150 * 1024 * 1024
int
is_sql_str_invalid(const char *str)
{
size_t len = strlen(str);
return strcspn(str, INVALID_CHARS_DBPATH) != len;
}
bool
database_new(volatile sqlite3 **db)
{
static int flags = SQLITE_OPEN_READWRITE | /* Open in RW mode */
SQLITE_OPEN_SHAREDCACHE | /* hHared cache for databases */
SQLITE_OPEN_NOFOLLOW | /* Don't follow symlinks */
SQLITE_OPEN_FULLMUTEX; /* Serialized */
RETURN_IF(SQLITE_OK != sqlite3_enable_shared_cache(1), "Failed to enable shared cache", false);
RETURN_IF(sqlite3_soft_heap_limit64(HEAP_LIMIT_SOFT) < 0, "Failed to set soft heap limit", false);
RETURN_IF(sqlite3_hard_heap_limit64(HEAP_LIMIT_HARD) < 0, "Failed to set soft heap limit", false);
RETURN_IF(SQLITE_OK != sqlite3_open_v2(":memory:", (sqlite3 **) db, flags, NULL), "Failed to open :memory:", false);
SQLITE_EXEC(*db, SQL_MEM_SCHEM, err_not_init);
return true;
err_not_init:
*db = NULL;
return false;
}
static inline bool
__attach(volatile sqlite3 *db, const char *name, const char *path)
{
static const char SQL_ATTACH_TEMPLATE[] = "ATTACH '%s' AS '%s';";
char SQL_ATTACH[LKT_MAX_SQLITE_STATEMENT];
bool ret = false;
safe_snprintf(SQL_ATTACH, LKT_MAX_SQLITE_STATEMENT, SQL_ATTACH_TEMPLATE, path, name);
SQLITE_EXEC(db, SQL_ATTACH, err_no_attach);
LOG_INFO_SCT("DB", "Attached database '%s' with path '%s'", name, path);
ret = true;
err_no_attach:
return ret;
}
static inline bool
__detach(volatile sqlite3 *db, const char *name)
{
static const char SQL_DETACH_TEMPLATE[] = "DETACH '%s';\n";
char SQL_DETACH[LKT_MAX_SQLITE_STATEMENT];
bool ret = false;
safe_snprintf(SQL_DETACH, LKT_MAX_SQLITE_STATEMENT, SQL_DETACH_TEMPLATE, name);
SQLITE_EXEC(db, SQL_DETACH, err_no_detach);
LOG_INFO_SCT("DB", "Detached database '%s'", name);
ret = true;
err_no_detach:
return ret;
}
static inline bool
__is_attached(volatile sqlite3 *db, const char *name)
{
static const char *SQL_STMT = "SELECT name FROM pragma_database_list WHERE name = ?;\n";
sqlite3_stmt *stmt = 0;
bool ret = false;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
SQLITE_BIND_TEXT(db, stmt, 1, name, error);
SQLITE_STEP_ROW(db, stmt, error);
ret = true;
error:
sqlite3_finalize(stmt);
return ret;
}
bool
database_open(volatile sqlite3 *db, const char *dbpath)
{
if (is_sql_str_invalid(dbpath)) {
LOG_ERROR("The database path '%s' is invalid", dbpath);
return false;
}
return __attach(db, PROTECTED_DATABASE, dbpath);
}
bool
database_attach(volatile sqlite3 *db, const char *name, const char *dbpath)
{
RETURN_UNLESS(strcasecmp(PROTECTED_DATABASE, name), " The database "PROTECTED_DATABASE
" is protected, can't attach a database with the same name", false);
if (is_sql_str_invalid(name)) {
LOG_ERROR("The database name '%s' is invalid", name);
return false;
}
if (__is_attached(db, name)) {
LOG_ERROR("The database '%s' is already attached", name);
return false;
}
if (!__attach(db, name, dbpath)) {
LOG_ERROR_SCT("DB", "Failed to attach database named '%s' with path '%s'", name, dbpath);
return false;
}
LOG_INFO_SCT("DB", "Database '%s' attached", name);
return true;
}
bool
database_detach(volatile sqlite3 *db, const char *name)
{
RETURN_UNLESS(strcasecmp(PROTECTED_DATABASE, name), " The database "PROTECTED_DATABASE
" is protected, can't detach it", false);
if (is_sql_str_invalid(name)) {
LOG_ERROR("The database name '%s' is invalid", name);
return false;
}
if (!__is_attached(db, name)) {
LOG_ERROR("The database '%s' is not attached", name);
return false;
}
if (!__detach(db, name)) {
LOG_ERROR_SCT("DB", "Failed to detach database named %s", name);
return false;
}
LOG_INFO_SCT("DB", "Database '%s' detached", name);
return true;
}
bool
database_init(const char *dbpath)
{
sqlite3 *db;
GOTO_IF(SQLITE_OK != sqlite3_open(dbpath, &db), "Failed to open the database", error);
SQLITE_EXEC(db, (const char *) ___scripts_init_sql, error);
LOG_INFO_SCT("DB", "Initialized the 'disk' database successfully, path was '%s'", dbpath);
return true;
error:
LOG_ERROR_SCT("DB", "Failed to init the 'disk' database, path was '%s'", dbpath);
return false;
}
void
database_get_update(volatile sqlite3 *db, long *timestamp, long *job, int *current)
{
static const char *SQL = "SELECT last_update, update_job, last_update > last_end_update FROM misc WHERE id = 42;";
sqlite3_stmt *stmt;
SQLITE_PREPARE(db, stmt, SQL, error);
SQLITE_STEP_ROW(db, stmt, error);
if (timestamp)
*timestamp = sqlite3_column_int(stmt, 0);
if (job)
*job = sqlite3_column_int(stmt, 1);
if (current)
*current = sqlite3_column_int(stmt, 2);
return;
error:
LOG_WARN_SCT("DB", "Failed to get informations about the last update: %s", sqlite3_errmsg((sqlite3 *) db));
}
void
database_stamp(volatile sqlite3 *db)
{
SQLITE_EXEC(db, "UPDATE misc SET last_update = strftime('%s','now');", error);
error:
return;
}
void
database_updated(volatile sqlite3 *db)
{
SQLITE_EXEC(db, "UPDATE misc SET last_end_update = strftime('%s','now'), update_job = update_job + 1;", error);
error:
return;
}