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;
 }