diff --git a/inc/lektor/database.h b/inc/lektor/database.h index 303d8499032baad782b4f1355b3539902ac480c8..ea4d079bf18a1973a928e3a665760e0e204e9cfc 100644 --- a/inc/lektor/database.h +++ b/inc/lektor/database.h @@ -34,6 +34,8 @@ struct lkt_queue_state { /* Open correctly a database for lektor. */ bool database_new(sqlite3 **db); bool database_open(sqlite3 *db, const char *dbpath); +bool database_attach(sqlite3 *db, const char *name, const char *dbpath); +bool database_detach(sqlite3 *db, const char *name); /* Get information on the queue and currently playing kara. */ bool database_queue_state(sqlite3 *db, struct lkt_queue_state *res); diff --git a/src/database/open.c b/src/database/open.c index b493bf2612155e06d2ec03e0222e73a92068c1b4..0e93589d5508a1d187286fbef100129a19328593 100644 --- a/src/database/open.c +++ b/src/database/open.c @@ -4,6 +4,9 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <strings.h> + +#define PROTECTED_DATABASE "disk" /* Some notes: - There's one and only row. @@ -87,6 +90,79 @@ err_not_init: return false; } +static inline bool +__attach(sqlite3 *db, const char *name, const char *path) +{ + static const char SQL_ATTACH_TEMPLATE[] = "ATTACH '%s' AS '%s';"; + size_t len = strlen(path) + strlen(name) + (sizeof(SQL_ATTACH_TEMPLATE) / sizeof(char)); + char *SQL_ATTACH = (char *) calloc(len, sizeof(char)); + bool ret = false; + snprintf(SQL_ATTACH, len - 1, SQL_ATTACH_TEMPLATE, path, name); + + if (SQLITE_OK != sqlite3_exec(db, SQL_ATTACH, 0, 0, 0)) { + fprintf(stderr, " * Failed to attach database named %s with path %s: %s\n", + name, path, sqlite3_errmsg(db)); + goto err_no_attach; + } + + fprintf(stderr, " * Attached database '%s' with path '%s'\n", name, path); + ret = true; +err_no_attach: + free(SQL_ATTACH); + return ret; +} + +static inline bool +__detach(sqlite3 *db, const char *name) +{ + static const char SQL_DETACH_TEMPLATE[] = "DETACH '%s';\n"; + size_t len = strlen(name) + (sizeof(SQL_DETACH_TEMPLATE) / sizeof(char)); + char *SQL_DETACH = (char *) calloc(len, sizeof(char)); + bool ret = false; + snprintf(SQL_DETACH, len - 1, SQL_DETACH_TEMPLATE, name); + + if (SQLITE_OK != sqlite3_exec(db, SQL_DETACH, 0, 0, 0)) { + fprintf(stderr, " * Failed to detach database named %s: %s\n", + name, sqlite3_errmsg(db)); + goto err_no_detach; + } + + fprintf(stderr, " * Detached database '%s'\n", name); + ret = true; +err_no_detach: + free(SQL_DETACH); + return ret; +} + +static inline bool +__is_attached(sqlite3 *db, const char *name) +{ + static const char *SQL_STMT = "SELECT name FROM pragma_database_list WHERE name = ?;\n"; + sqlite3_stmt *stmt = 0; + int code = 0; + bool ret = false; + + if (SQLITE_OK != sqlite3_prepare_v2(db, SQL_STMT, -1, &stmt, 0)) { + fprintf(stderr, " . __is_attached: Failed to prepare statement: %s\n", + sqlite3_errmsg(db)); + goto error; + } + + if (SQLITE_OK != sqlite3_bind_text(stmt, 1, name, -1, 0)) { + fprintf(stderr, " . __is_attached: Failed to bind name: %s\n", + sqlite3_errmsg(db)); + goto error; + } + + code = sqlite3_step(stmt); + if (code == SQLITE_ROW) + ret = true; + +error: + sqlite3_finalize(stmt); + return ret; +} + bool database_open(sqlite3 *db, const char *dbpath) { @@ -95,20 +171,62 @@ database_open(sqlite3 *db, const char *dbpath) return false; } - static const char SQL_ATTACH_TEMPLATE[] = "ATTACH '%s' AS disk;"; - size_t len = strlen(dbpath) + (sizeof(SQL_ATTACH_TEMPLATE) / sizeof(char)); - char *SQL_ATTACH = (char *) calloc(len, sizeof(char)); - snprintf(SQL_ATTACH, len - 1, SQL_ATTACH_TEMPLATE, dbpath); + return __attach(db, PROTECTED_DATABASE, dbpath); +} - if (SQLITE_OK != sqlite3_exec(db, SQL_ATTACH, 0, 0, 0)) { - fprintf(stderr, " ! database_open: Failed to attach disk database %s: %s\n", - dbpath, sqlite3_errmsg(db)); - goto err_no_attach; +bool +database_attach(sqlite3 *db, const char *name, const char *dbpath) +{ + if (!strcasecmp(PROTECTED_DATABASE, name)) { + fprintf(stderr, " ! database_attach: The database " PROTECTED_DATABASE + " is protected, can't attach a database with the same name\n"); + return false; + } + + if (is_sql_str_invalid(name)) { + fprintf(stderr, " ! database_attach: The database name %s is invalid\n", name); + return false; + } + + if (__is_attached(db, name)) { + fprintf(stderr, " ! database_detach: Database named %s is already attached\n", name); + return false; } + if (!__attach(db, name, dbpath)) { + fprintf(stderr, " ! database_attach: Failed to attach database named '%s' with path '%s'\n", + name, dbpath); + return false; + } + + fprintf(stderr, " * Database %s attached\n", name); return true; +} -err_no_attach: - free(SQL_ATTACH); - return false; +bool +database_detach(sqlite3 *db, const char *name) +{ + if (!strcasecmp(PROTECTED_DATABASE, name)) { + fprintf(stderr, " ! database_detach: The database " PROTECTED_DATABASE + " is protected, can't detach it\n"); + return false; + } + + if (is_sql_str_invalid(name)) { + fprintf(stderr, " ! database_detach: The database name %s is invalid\n", name); + return false; + } + + if (!__is_attached(db, name)) { + fprintf(stderr, " ! database_detach: Database named %s is not attached\n", name); + return false; + } + + if (!__detach(db, name)) { + fprintf(stderr, " ! database_detach: Failed to detach database named %s\n", name); + return false; + } + + fprintf(stderr, " * Database %s detached\n", name); + return true; }