Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • cc7df9f3051ee5ce29337c398f06804e64c8c0ab
  • master par défaut protégée
  • rust-playlist-sync
  • rust
  • fix-qt-deprecated-qvariant-type
  • fix-mpris-qtwindow-race-condition
  • rust-appimage-wayland
  • windows-build-rebased
  • v2.5 protégée
  • v2.4 protégée
  • v2.3-1 protégée
  • v2.3 protégée
  • v2.2 protégée
  • v2.1 protégée
  • v2.0 protégée
  • v1.8-3 protégée
  • v1.8-2 protégée
  • v1.8-1 protégée
  • v1.8 protégée
  • v1.7 protégée
  • v1.6 protégée
  • v1.5 protégée
  • v1.4 protégée
  • v1.3 protégée
  • v1.2 protégée
  • v1.1 protégée
  • v1.0 protégée
27 résultats

queue.c

Blame
  • queue.c 35,99 Kio
    #define _POSIX_C_SOURCE 200809L
    
    #include <lektor/common.h>
    #include <lektor/database.h>
    #include <lektor/internal/dbmacro.h>
    
    #define sqlite_just_exec(func, query)                                                                                  \
        bool func(lkt_db *db)                                                                                              \
        {                                                                                                                  \
            SQLITE_EXEC(db, "BEGIN TRANSACTION;" query "COMMIT;", error);                                                  \
            return true;                                                                                                   \
        error:                                                                                                             \
            SQLITE_DO_ROLLBACK(db);                                                                                        \
            return false;                                                                                                  \
        }
    // clang-format off
    sqlite_just_exec(database_queue_toggle_pause, "UPDATE queue_state SET paused = 1 - paused;");
    sqlite_just_exec(database_queue_crop, "DELETE FROM queue WHERE queue.kara_id <> (SELECT current FROM queue_state LIMIT 1);");
    sqlite_just_exec(database_queue_stop, "UPDATE queue_state SET current = NULL;");
    sqlite_just_exec(database_queue_clear, "DELETE FROM queue;"
                                           "DELETE FROM queue_tmp;"
                                           "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
                                           "DELETE FROM " LKT_PROTECTED_DATABASE ".sqlite_sequence WHERE name = 'queue';"
                                           "UPDATE queue_state SET current = NULL;");
    sqlite_just_exec(database_config_queue_default, "UPDATE queue_state SET volume = 100, paused = 1,"
                                                    " random = 0, repeat = 0, single = 0, consume = 0,"
                                                    " current = NULL, duration = 0;");
    sqlite_just_exec(database_queue_flat, "UPDATE queue SET priority = 1;");
    // clang-format on
    #undef sqlite_just_exec
    
    PRIVATE_FUNCTION void
    __queue_resequence(lkt_db *db)
    {
        static const char *SQL = "UPDATE " LKT_PROTECTED_DATABASE
                                 ".sqlite_sequence SET seq = (SELECT COUNT(*) FROM queue) WHERE name = 'queue';";
        SQLITE_EXEC(db, SQL, error);
        LOG_INFO("DB", "Re-sequenced the queue");
        return;
    error:
        LOG_ERROR("DB", "Failed to update the " LKT_PROTECTED_DATABASE ".sqlite_sequence table...");
        return;
    }
    
    bool
    database_queue_set_paused(lkt_db *db, bool paused)
    {
        const char *SQL = paused ? "UPDATE queue_state SET paused = 1;" : "UPDATE queue_state SET paused = 0;";
        SQLITE_EXEC(db, SQL, error);
        return true;
    error:
        return false;
    }
    
    bool
    database_queue_state(lkt_db *db, struct lkt_queue_state *res)
    {
        static const char *SQL_STMT = "SELECT"
                                      " volume, paused, random, repeat, single, current, duration, consume,"
                                      " (SELECT COUNT(*) FROM queue) AS length "
                                      "FROM queue_state;\n";
        sqlite3_stmt *stmt = 0;
        bool ret           = false;
    
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
        SQLITE_STEP_ROW(db, stmt, error);
    
        res->volume = sqlite3_column_int(stmt, 0);
        res->paused = sqlite3_column_int(stmt, 1);
        res->random = sqlite3_column_int(stmt, 2);
        res->repeat = sqlite3_column_int(stmt, 3);
        res->single = sqlite3_column_int(stmt, 4);
    
        if (sqlite3_column_type(stmt, 5) == SQLITE_NULL)
            res->current = -1;
        else
            res->current = sqlite3_column_int(stmt, 5);
    
        if (sqlite3_column_type(stmt, 6) == SQLITE_NULL)
            res->duration = 0;
        else
            res->duration = sqlite3_column_int(stmt, 6);
    
        res->consume = sqlite3_column_int(stmt, 7);
        res->length  = sqlite3_column_int(stmt, 8);
        ret          = true;
    error:
        sqlite3_finalize(stmt);
        return ret;
    }
    
    void
    database_queue_playtime(lkt_db *db, uint64_t *seconds)
    {
        static const char *SQL = "SELECT SUM(cached_duration) "
                                 "FROM kara "
                                 "JOIN queue ON queue.kara_id = kara.id;";
        sqlite3_stmt *stmt = NULL;
        *seconds           = 0;
        SQLITE_PREPARE(db, stmt, SQL, error);
        SQLITE_STEP_ROW(db, stmt, error);
        *seconds = sqlite3_column_int(stmt, 0);
    error:
        sqlite3_finalize(stmt);
        return;
    }
    
    void
    database_total_playtime(lkt_db *db, uint64_t *seconds)
    {
        static const char *SQL = "SELECT SUM(cached_duration) FROM kara;";
        sqlite3_stmt *stmt     = NULL;
        *seconds               = 0;
        SQLITE_PREPARE(db, stmt, SQL, error);
        SQLITE_STEP_ROW(db, stmt, error);
        *seconds = sqlite3_column_int(stmt, 0);
    error:
        sqlite3_finalize(stmt);
        return;
    }
    
    bool
    database_queue_current_kara(lkt_db *db, struct kara_metadata *res, int *id)
    {
        static const char *SQL_STMT = "SELECT song_name, source_name, category, language, author_name, "
                                      "song_type, song_number, kara_id"
                                      " FROM kara"
                                      " JOIN queue ON kara_id = kara.id"
                                      " JOIN queue_state ON current = position";
        sqlite3_stmt *stmt = NULL;
        int ret            = false;
    
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
        SQLITE_STEP_ROW(db, stmt, error);
    
        /* Here use gotos because of optimisations done by compilators.
           Most of the time it won't be NULL. */
        if (!res)
            goto no_metadata;
        // clang-format off
        strncpy(res->song_name,   sqlite3_column_chars(stmt, 0), LEKTOR_TAG_MAX - 1);
        strncpy(res->source_name, sqlite3_column_chars(stmt, 1), LEKTOR_TAG_MAX - 1);
        strncpy(res->category,    sqlite3_column_chars(stmt, 2), LEKTOR_TAG_MAX - 1);
        strncpy(res->language,    sqlite3_column_chars(stmt, 3), LEKTOR_TAG_MAX - 1);
        strncpy(res->author_name, sqlite3_column_chars(stmt, 4), LEKTOR_TAG_MAX - 1);
        strncpy(res->song_type,   sqlite3_column_chars(stmt, 5), LEKTOR_TAG_MAX - 1);
        // clang-format on
        res->song_number = sqlite3_column_int(stmt, 6);
    no_metadata:
        /* Most of the time this will be NULL. */
        if (id && sqlite3_column_type(stmt, 7) != SQLITE_NULL)
            *id = sqlite3_column_int(stmt, 7);
    
        ret = true;
    error:
        sqlite3_finalize(stmt);
        return ret;
    }
    
    #define reorder(db, prio, error)                                                                                       \
        if (prio > 1) {                                                                                                    \
            if (__queue_reorder(db)) {                                                                                     \
                LOG_INFO("DB", "Queue has been reordered");                                                                \
            } else {                                                                                                       \
                LOG_INFO("DB", "Failed to reorder");                                                                       \
                goto error;                                                                                                \
            }                                                                                                              \
        }
    
    static bool
    __queue_reorder(lkt_db *db)
    {
    #define CURRENT_POS_OR_0 /* To move karas after the current */                                                         \
        "(SELECT CASE WHEN (SELECT current FROM queue_state) IS NULL THEN 0"                                               \
        " ELSE (SELECT current FROM queue_state) END AS val LIMIT 1)"
        static const char *SQL_REORDER =
            /* Clear the TMP */
            "DELETE FROM queue_tmp;"
            "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
            /* Separate karas that are after the current one */
            "INSERT INTO queue_tmp (kara_id, priority)"
            " SELECT kara_id, priority FROM queue WHERE position > " CURRENT_POS_OR_0
            " ORDER BY priority DESC, position ASC;"
            "DELETE FROM queue WHERE position > " CURRENT_POS_OR_0 ";"
            /* Update the sqlite_sequence table */
            "UPDATE " LKT_PROTECTED_DATABASE ".sqlite_sequence SET seq = " CURRENT_POS_OR_0 " WHERE name = 'queue';"
            /* Insert back */
            "INSERT INTO queue (position, kara_id, priority)"
            " SELECT position + " CURRENT_POS_OR_0 ", kara_id, priority"
            " FROM queue_tmp;"
            /* Clear the TMP */
            "DELETE FROM queue_tmp;"
            "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';";
    #undef CURRENT_POS_OR_0
        SQLITE_EXEC(db, SQL_REORDER, error);
        return true;
    error:
        return false;
    }
    
    static bool
    queue_add_with_col_like_str(lkt_db *db, const char *col, const char *val, int priority)
    {
        static const char *SQL_STMT =
            /* Add correspondig karas into the queue */
            "INSERT INTO queue (kara_id, priority)"
            " SELECT id, ?"
            " FROM kara"
            " WHERE %s LIKE ? AND available = 1"
            " ORDER BY RANDOM();";
    
        RETURN_IF(___is_sql_str_invalid(col), "Column name is invalid", false);
        char SQL[LKT_MAX_SQLITE_STATEMENT];
        sqlite3_stmt *stmt = NULL;
    
        /* Begin, transactions are atomic so we won't be anoyed
           by multi-threading */
        SQLITE_EXEC(db, "BEGIN TRANSACTION;", error);
    
        /* Insert at the end of the queue */
        safe_snprintf(SQL, LKT_MAX_SQLITE_STATEMENT, SQL_STMT, col);
        SQLITE_PREPARE(db, stmt, SQL, error);
        SQLITE_BIND_INT(db, stmt, 1, priority, error);
        SQLITE_BIND_TEXT(db, stmt, 2, val, error);
        SQLITE_STEP_DONE(db, stmt, error);
        sqlite3_finalize(stmt);
    
        /* Reorder kara that are after the current one, do this shit only
           if priority > 1... */
        reorder(db, priority, error);
    
        /* End */
        SQLITE_EXEC(db, "COMMIT;", error);
        LOG_INFO("DB-DEBUG", "Added with col %s like %s with priority %d", col, val, priority);
        return true;
    
    error:
        sqlite3_finalize(stmt);
        SQLITE_DO_ROLLBACK(db);
        __queue_resequence(db);
        return false;
    }
    
    bool
    database_queue_add_plt(lkt_db *db, const char *plt_name, int priority)
    {
        static const char *SQL_STMT =
            /* Insert the playlist */
            "INSERT INTO queue (kara_id, priority) "
            "SELECT kara.id, ?"
            " FROM kara"
            " JOIN kara_playlist ON kara_id = kara.id AND kara.available = 1"
            " JOIN playlist ON playlist_id = playlist.id AND playlist.name = ?"
            " ORDER BY RANDOM();";
        sqlite3_stmt *stmt = NULL;
    
        /* Begin, transactions are atomic so we won't be anoyed
           by multi-threading */
        SQLITE_EXEC(db, "BEGIN TRANSACTION;", error);
    
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
        SQLITE_BIND_INT(db, stmt, 1, priority, error);
        SQLITE_BIND_TEXT(db, stmt, 2, plt_name, error);
        SQLITE_STEP_DONE(db, stmt, error);
        sqlite3_finalize(stmt);
    
        /* Do the move shit only if the priority > 1 */
        reorder(db, priority, error);
    
        SQLITE_EXEC(db, "COMMIT;", error);
        LOG_INFO("DB-DEBUG", "Added play list %s with priority %d", plt_name, priority);
        return true;
    
    error:
        sqlite3_finalize(stmt);
        SQLITE_DO_ROLLBACK(db);
        __queue_resequence(db);
        return false;
    }
    
    bool
    database_queue_add_id(lkt_db *db, int id, int priority)
    {
        static const char *SQL_STMT = "INSERT INTO queue (kara_id, priority) VALUES (?,?);";
        sqlite3_stmt *stmt          = NULL;
        SQLITE_EXEC(db, "BEGIN TRANSACTION;", error);
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
        SQLITE_BIND_INT(db, stmt, 1, id, error);
        SQLITE_BIND_INT(db, stmt, 2, priority, error);
        SQLITE_STEP_DONE(db, stmt, error);
        sqlite3_finalize(stmt);
    
        /* Do the move shit only if the priority > 1 */
        reorder(db, priority, error_no_stmt);
    
        SQLITE_EXEC(db, "COMMIT;", error);
        return true;
    error:
        LOG_ERROR("DB",
                  "Error while adding the kara id %d into the queue with the "
                  "priority %d",
                  id, priority);
        sqlite3_finalize(stmt);
    error_no_stmt:
        LOG_ERROR("DB",
                  "Need to rollback the queue because of failed insertion of id "
                  "%d with priority %d in the queue",
                  id, priority);
        SQLITE_DO_ROLLBACK(db);
        __queue_resequence(db);
        return false;
    }
    
    bool
    database_queue_add_uri(lkt_db *db, struct lkt_uri *uri, int prio)
    {
        switch (uri->type) {
        case URI_QUERY:
            return queue_add_with_col_like_str(db, LKT_DB_ALL, uri->value, prio);
    
        case URI_ID:
            return database_queue_add_id(db, uri->id, prio);
    
        case URI_TYPE:
            return queue_add_with_col_like_str(db, LKT_DB_TYPE, uri->value, prio);
    
        case URI_CATEGORY:
            return queue_add_with_col_like_str(db, LKT_DB_CAT, uri->value, prio);
    
        case URI_LANGUAGE:
            return queue_add_with_col_like_str(db, LKT_DB_LANG, uri->value, prio);
    
        case URI_AUTHOR:
            return queue_add_with_col_like_str(db, LKT_DB_AUTHOR, uri->value, prio);
    
        case URI_PLAYLIST:
            return database_queue_add_plt(db, uri->value, prio);
    
        default:
            LOG_WARN("DB", "Add to queue for uri of type %d is not implemented", uri->type);
            return false;
        }
    }
    
    bool
    database_queue_del_id(lkt_db *db, int id)
    {
        static const char *SQL_TEMPLATE =
            "BEGIN TRANSACTION;"
            /* Clear the TMP */
            "DELETE FROM queue_tmp;"
            "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
            /* Move the current 'pointer' */
            "UPDATE queue_state SET current = (SELECT NULLIF(COUNT(position), 0) FROM queue JOIN queue_state ON position <= current AND kara_id != %d);"
            "DELETE FROM queue WHERE kara_id = %d;" /* Delete any kara with the specified id */
            "INSERT INTO queue_tmp(priority, position, kara_id) SELECT priority, position, kara_id FROM queue ORDER BY position ASC;"
            "DELETE FROM queue;"
            "INSERT INTO queue(priority, position, kara_id) SELECT priority, ROW_NUMBER() OVER(ORDER BY position ASC), kara_id FROM queue_tmp;"
            "UPDATE " LKT_PROTECTED_DATABASE
            ".sqlite_sequence SET seq = (SELECT COUNT(*) FROM queue) WHERE name = 'queue';" /* Update the sqlite_sequence            */
            /* Clear the TMP */
            "DELETE FROM queue_tmp;"
            "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
            "COMMIT;";
        char SQL[LKT_MAX_SQLITE_STATEMENT];
        safe_snprintf(SQL, LKT_MAX_SQLITE_STATEMENT, SQL_TEMPLATE, id, id);
        SQLITE_EXEC(db, SQL, error);
        __queue_resequence(db);
        return true;
    error:
        SQLITE_DO_ROLLBACK(db);
        return false;
    }
    
    bool
    database_queue_del_pos(lkt_db *db, int pos)
    {
        bool sta                     = false;
        sqlite3_stmt *stmt           = NULL;
        struct lkt_queue_state queue = { .current = -1 };
        RETURN_UNLESS(database_queue_state(db, &queue), "Failed to get the queue state", false);
    
        /* Delete the current kara, can't do that because we need to skip it, and
         * send signals to the window module to do so, so we just fail here... */
        if (queue.current == pos) {
            LOG_ERROR("DB", "Can't delete the currently playing kara at position %d in queue", pos);
            return false;
        }
    
        static const char *SQL_TEMPLATE =
            /* Create temporary queue */
            "DELETE FROM queue_tmp;"
            "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
            /* -1 to all positions after the deleted kara */
            "DELETE FROM queue WHERE position = %d;"
            "INSERT INTO queue_tmp (kara_id, priority)"
            "  SELECT kara_id, priority"
            "  FROM queue;"
            /* Re-init positions in the queue */
            "DELETE FROM queue;"
            "DELETE FROM " LKT_PROTECTED_DATABASE ".sqlite_sequence WHERE name = 'queue';"
            /* Insert back all karas */
            "INSERT INTO queue (kara_id, priority, position)"
            "  SELECT kara_id, priority, position"
            "  FROM queue_tmp;"
            /* Slap correct values here */
            "UPDATE " LKT_PROTECTED_DATABASE ".sqlite_sequence"
            "  SET seq = (SELECT COUNT(position) FROM queue)"
            "  WHERE name = 'queue';"
            "DELETE FROM queue_tmp;"
            "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';";
        char SQL[LKT_MAX_SQLITE_STATEMENT];
        safe_snprintf(SQL, LKT_MAX_SQLITE_STATEMENT, SQL_TEMPLATE, pos);
    
        SQLITE_EXEC(db, "BEGIN TRANSACTION;", error_no_rollback);
        SQLITE_EXEC(db, SQL, error_rollback);
        if (pos < queue.current) {
            LOG_DEBUG("DB", "Needs to decrement current index: pos %d < current %d", pos, queue.current);
            SQLITE_EXEC(db, "UPDATE queue_state SET current = current - 1;", error_in_delete_or_update);
        }
    
        sta = true;
    error_in_delete_or_update:
        if (sta) {
            /* Commit transaction, if failed use a magnificient goto... */
            __queue_resequence(db);
            SQLITE_EXEC(db, "END TRANSACTION;", error_rollback);
        }
    
        else {
        error_rollback:
            sta = false;
            LOG_DEBUG("DB", "An error occured, needs to rollback transaction");
            SQLITE_DO_ROLLBACK(db);
        }
    
    error_no_rollback:
        if (stmt != NULL)
            sqlite3_finalize(stmt);
        return sta;
    }
    
    bool
    database_queue_next(lkt_db *db, char filepath[PATH_MAX])
    {
        static const char *SQL_STMT =
            "SELECT file_path, position, RANDOM()"
            "  FROM kara"
            "  JOIN queue ON kara.id = queue.kara_id"
            "  JOIN queue_state"
            "    ON current IS NULL"
            "    OR (CASE WHEN (SELECT single FROM queue_state LIMIT 1) = 1 THEN position = current"
            "             WHEN (SELECT random FROM queue_state LIMIT 1) = 1 THEN position <> current"
            "             ELSE position > current END)"
            "  ORDER BY CASE WHEN (SELECT random FROM queue_state LIMIT 1) = 1 THEN RANDOM() ELSE 2 END"
            "  LIMIT 1";
        char SQL_UPDATE[LKT_MAX_SQLITE_STATEMENT];
        bool status        = false;
        int code           = SQLITE_OK, id;
        sqlite3_stmt *stmt = NULL;
    
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
    
        code = sqlite3_step(stmt);
    
        if (code == SQLITE_ROW) {
            id = MAX(1, sqlite3_column_int(stmt, 1));
            safe_snprintf(SQL_UPDATE, LKT_MAX_SQLITE_STATEMENT, "UPDATE queue_state SET current = %d;", id);
    
            if (filepath != NULL)
                strncpy(filepath, sqlite3_column_chars(stmt, 0), PATH_MAX - 1);
            else {
                LOG_ERROR("DB", "Failed to get file, id was %d", id);
                goto error;
            }
        }
    
        else if (code == SQLITE_DONE) {
            LOG_ERROR("DB", "Failed to get next");
            goto error;
        }
    
        else {
            LOG_ERROR("DB", "Failed to fetch next kara: %s", sqlite3_errmsg((sqlite3 *)db));
            goto error;
        }
    
        SQLITE_EXEC(db, SQL_UPDATE, error);
        status = true;
    error:
        sqlite3_finalize(stmt);
        return status;
    }
    
    bool
    database_queue_skip_current(lkt_db *db, char filepath[PATH_MAX])
    {
        if (database_queue_next(db, filepath))
            return true;
        else if (database_queue_prev(db, filepath))
            return true;
        else {
            database_queue_stop(db);
            return false;
        }
    }
    
    bool
    database_queue_prev(lkt_db *db, char filepath[PATH_MAX])
    {
        static const char *SQL_STMT = "SELECT file_path, position "
                                      "  FROM kara"
                                      "  JOIN queue ON kara.id = queue.kara_id AND available = 1"
                                      "  JOIN queue_state ON CASE "
                                      "    WHEN (SELECT single FROM queue_state LIMIT 1) = 1 THEN position = current"
                                      "    ELSE queue.position < queue_state.current"
                                      "  END"
                                      "  ORDER BY position DESC LIMIT 1;";
        char SQL_UPDATE[LKT_MAX_SQLITE_STATEMENT];
        bool status        = false;
        int code           = SQLITE_OK, id;
        sqlite3_stmt *stmt = NULL;
    
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
        code = sqlite3_step(stmt);
    
        if (code == SQLITE_ROW) {
            id = MAX(1, sqlite3_column_int(stmt, 1));
            safe_snprintf(SQL_UPDATE, LKT_MAX_SQLITE_STATEMENT, "UPDATE queue_state SET current = %d;", id);
    
            if (filepath != NULL)
                strncpy(filepath, sqlite3_column_chars(stmt, 0), PATH_MAX - 1);
            else {
                LOG_ERROR("DB", "Failed to get file, position was %d", id);
                goto error;
            }
        }
    
        else if (code == SQLITE_DONE) {
            LOG_ERROR("DB", "Failed to get previous");
            goto error;
        }
    
        else {
            LOG_ERROR("DB", "Failed to fetch prev kara: %s", sqlite3_errmsg((sqlite3 *)db));
            goto error;
        }
    
        SQLITE_EXEC(db, SQL_UPDATE, error);
        status = true;
    error:
        sqlite3_finalize(stmt);
        return status;
    }
    
    bool
    database_queue_swap(lkt_db *db, int from, int to)
    {
        static const char *SQL_POP_ROW_1 = "SELECT position, priority, kara_id FROM queue WHERE position = ?;";
        static const char *SQL_POP_ROW_2 = "DELETE FROM queue WHERE position = ?;";
        static const char *SQL_SLAP_MAX  = "UPDATE " LKT_PROTECTED_DATABASE ".sqlite_sequence"
                                          "  SET seq = (SELECT COUNT(position) FROM queue)"
                                          "  WHERE name = 'queue';";
        static const char *SQL_PUSH_ROW = "INSERT INTO queue (position, priority, kara_id) VALUES (?, ?, ?);";
    
        struct {
            int position;
            int priority;
            int kara_id;
        } kara_from, kara_to;
        sqlite3_stmt *stmt = NULL;
        bool sta           = false;
        int tmp_kara_id;
    
        /* Get one kara from the 'queue' and delete it */
    #define POP_QUEUE(name)                                                                                                \
        SQLITE_PREPARE(db, stmt, SQL_POP_ROW_1, error);                                                                    \
        SQLITE_BIND_INT(db, stmt, 1, name, error);                                                                         \
        SQLITE_STEP_ROW(db, stmt, error);                                                                                  \
        kara_##name.position = sqlite3_column_int(stmt, 0);                                                                \
        kara_##name.priority = sqlite3_column_int(stmt, 1);                                                                \
        kara_##name.kara_id  = sqlite3_column_int(stmt, 2);                                                                \
        sqlite3_finalize(stmt);                                                                                            \
        SQLITE_PREPARE(db, stmt, SQL_POP_ROW_2, error);                                                                    \
        SQLITE_BIND_INT(db, stmt, 1, name, error);                                                                         \
        SQLITE_STEP_DONE(db, stmt, error);                                                                                 \
        sqlite3_finalize(stmt);
        /* Push one kara on the 'queue' */
    #define PUSH_QUEUE(name)                                                                                               \
        SQLITE_PREPARE(db, stmt, SQL_PUSH_ROW, error);                                                                     \
        SQLITE_BIND_INT(db, stmt, 1, kara_##name.position, error);                                                         \
        SQLITE_BIND_INT(db, stmt, 2, kara_##name.priority, error);                                                         \
        SQLITE_BIND_INT(db, stmt, 3, kara_##name.kara_id, error);                                                          \
        SQLITE_STEP_DONE(db, stmt, error);                                                                                 \
        sqlite3_finalize(stmt);
    
        SQLITE_EXEC(db, "BEGIN TRANSACTION;", error_no_rollback);
        POP_QUEUE(from);
        POP_QUEUE(to);
        tmp_kara_id       = kara_from.kara_id;
        kara_from.kara_id = kara_to.kara_id;
        kara_to.kara_id   = tmp_kara_id;
        PUSH_QUEUE(from);
        PUSH_QUEUE(to);
        SQLITE_EXEC(db, SQL_SLAP_MAX, error_no_stmt);
        SQLITE_EXEC(db, "END TRANSACTION;", error_no_stmt);
    
    #undef PUSH_QUEUE
    #undef POP_QUEUE
    
        sta = true;
        goto error_no_stmt;
    error:
        sqlite3_finalize(stmt);
    error_no_stmt:
        if (!sta)
            SQLITE_DO_ROLLBACK(db);
    error_no_rollback:
        return sta;
    }
    
    bool
    database_queue_move(lkt_db *db, int from, int to)
    {
        static const char *SQL_TEMPLATE_PART_1 =
            /* Clean queue_tmp */
            "DELETE FROM queue_tmp;"
            "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
            /* Construct queue */
            "INSERT INTO queue_tmp(kara_id,priority)"
            "WITH TEMP_TABLE AS (SELECT"
            "  position, kara_id, priority"
            "  FROM queue"
            "  ORDER BY position ASC, priority DESC)"
            "SELECT kara_id, priority"
            "  FROM TEMP_TABLE WHERE POSITION < %d AND POSITION != %d ORDER BY POSITION;"
            /* Construct queue */
            "INSERT INTO queue_tmp(kara_id,priority)"
            "  VALUES(("
            "    WITH TEMP_TABLE AS ("
            "      SELECT ROW_NUMBER() OVER(ORDER BY position ASC, priority DESC) AS POSITION,"
            "        kara_id, priority FROM queue)"
            "      SELECT kara_id FROM TEMP_TABLE WHERE POSITION = %d"
            "    ),("
            "      WITH TEMP_TABLE AS ("
            "      SELECT ROW_NUMBER() OVER(ORDER BY position ASC, priority DESC) AS POSITION,"
            "        kara_id, priority FROM queue)"
            "      SELECT priority FROM TEMP_TABLE WHERE POSITION = "
            "      CASE WHEN (SELECT COUNT(*) FROM TEMP_TABLE)>=%d THEN %d ELSE %d-1 END"
            "  ));";
        static const char *SQL_TEMPLATE_PART_2 =
            /* Construct queue */
            "INSERT INTO queue_tmp(kara_id,priority)"
            "WITH TEMP_TABLE AS ("
            "  SELECT ROW_NUMBER() OVER(ORDER BY position ASC, priority DESC) AS POSITION,"
            "    kara_id, priority FROM queue)"
            "SELECT kara_id, priority"
            "  FROM TEMP_TABLE WHERE POSITION >= %d AND POSITION != %d ORDER BY POSITION;"
            /* Clean queue and insert back */
            "UPDATE queue_state"
            "  SET current = CASE"
            "    WHEN current NOT NULL "
            "    THEN (SELECT position FROM queue_tmp "
            "       WHERE kara_id=(SELECT kara_id FROM queue WHERE position = current))"
            "    ELSE NULL END;"
            "DELETE FROM queue;"
            "DELETE FROM " LKT_PROTECTED_DATABASE ".sqlite_sequence WHERE name = 'queue';"
            "INSERT INTO queue(position, kara_id, priority)"
            "  SELECT position, kara_id, priority FROM queue_tmp ORDER BY priority, position;"
            /* Clean queue_tmp */
            "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
    
            "DELETE FROM queue_tmp;";
    
        char SQL[LKT_MAX_SQLITE_STATEMENT];
    
        SQLITE_EXEC(db, "BEGIN TRANSACTION;", error_no_rollback);
        safe_snprintf(SQL, LKT_MAX_SQLITE_STATEMENT, SQL_TEMPLATE_PART_1, to, from, from, to, to, to);
        SQLITE_EXEC(db, SQL, error);
    
        safe_snprintf(SQL, LKT_MAX_SQLITE_STATEMENT, SQL_TEMPLATE_PART_2, to, from);
        SQLITE_EXEC(db, SQL, error);
        SQLITE_EXEC(db, "COMMIT;", error);
        return true;
    error:
        SQLITE_DO_ROLLBACK(db);
    error_no_rollback:
        return false;
    }
    
    bool
    database_queue_play(lkt_db *db, int pos)
    {
        static const char *SQL_STMT = "UPDATE queue_state SET current = ?, paused = 0"
                                      "  WHERE current <= 0 OR current IS NULL";
        int code           = SQLITE_OK;
        bool ret           = false;
        sqlite3_stmt *stmt = NULL;
    
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
        SQLITE_BIND_INT(db, stmt, 1, pos, error);
    
        code = sqlite3_step(stmt);
    
        if (code != SQLITE_OK && code != SQLITE_DONE) {
            LOG_ERROR("DB", "Failed to update queue_state: %s", sqlite3_errmsg((sqlite3 *)db));
            goto error;
        }
    
        ret = true;
    error:
        sqlite3_finalize(stmt);
        return ret;
    }
    
    bool
    database_queue_set_current_index(lkt_db *db, int idx)
    {
        static const char *SQL_GET_TEMPLATE = "UPDATE queue_state SET current = %d;";
        char SQL_GET[LKT_MAX_SQLITE_STATEMENT];
    
        if (idx <= 0) {
            LOG_ERROR("DB", "An idx of %d is invalid, must be >= 0", idx);
            return false;
        }
    
        if (safe_snprintf(SQL_GET, LKT_MAX_SQLITE_STATEMENT, SQL_GET_TEMPLATE, idx) < 0)
            return false;
    
        SQLITE_EXEC(db, SQL_GET, error);
        return true;
    
    error:
        return false;
    }
    
    bool
    database_queue_get_current_file(lkt_db *db, char filepath[PATH_MAX])
    {
        static const char *SQL_STMT = "SELECT file_path"
                                      "  FROM kara"
                                      "  JOIN queue ON kara.id = queue.kara_id"
                                      "  JOIN queue_state ON queue.position = queue_state.current";
        bool status        = false;
        int code           = SQLITE_OK;
        sqlite3_stmt *stmt = NULL;
    
        if (filepath == NULL)
            goto error;
    
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
        code = sqlite3_step(stmt);
    
        if (code == SQLITE_ROW) {
            strncpy(filepath, sqlite3_column_chars(stmt, 0), PATH_MAX);
            filepath[PATH_MAX - 1] = '\0';
        }
    
        else {
            LOG_ERROR("DB", "Failed to fetch prev kara: %s", sqlite3_errmsg((sqlite3 *)db));
            goto error;
        }
    
        status = true;
    
    error:
        sqlite3_finalize(stmt);
        return status;
    }
    
    bool
    database_get_kara_path(lkt_db *db, int id, char filepath[PATH_MAX])
    {
        bool ret_code          = false;
        sqlite3_stmt *stmt     = NULL;
        static const char *SQL = "SELECT file_path FROM kara_cache WHERE kara_id = ?;";
        RETURN_UNLESS(id && db, "Invalid argument", false);
        SQLITE_PREPARE(db, stmt, SQL, error);
        SQLITE_BIND_INT(db, stmt, 1, id, error);
        SQLITE_STEP_ROW(db, stmt, error);
        if (filepath)
            safe_strncpy(filepath, sqlite3_column_chars(stmt, 0), PATH_MAX);
        ret_code = true;
    error:
        sqlite3_finalize(stmt);
        return ret_code;
    }
    
    bool
    database_get_kara_id(lkt_db *db, char filepath[PATH_MAX], int *id)
    {
        bool ret_code          = false;
        sqlite3_stmt *stmt     = NULL;
        static const char *SQL = "SELECT id FROM kara WHERE file_path = ? AND available = 1;";
        RETURN_UNLESS(id && db, "Invalid argument", false);
        SQLITE_PREPARE(db, stmt, SQL, error);
        SQLITE_BIND_TEXT(db, stmt, 1, filepath, error);
        SQLITE_STEP_ROW(db, stmt, error);
        if (id)
            *id = sqlite3_column_int(stmt, 0);
        ret_code = true;
    error:
        sqlite3_finalize(stmt);
        return ret_code;
    }
    
    bool
    database_get_kara_position(lkt_db *db, int id, int *pos)
    {
        bool ret_code          = false;
        sqlite3_stmt *stmt     = NULL;
        static const char *SQL = "WITH content AS ("
                                 " SELECT kara.id AS id, position"
                                 "  FROM queue"
                                 "  JOIN kara ON kara_id = kara.id"
                                 "  GROUP BY position ORDER BY position ASC, priority DESC)"
                                 "SELECT id, position FROM content WHERE id = ?;";
        RETURN_UNLESS(db && pos, "Invalid argument", false);
        SQLITE_PREPARE(db, stmt, SQL, error);
        SQLITE_BIND_INT(db, stmt, 1, id, error);
        SQLITE_STEP_ROW(db, stmt, error);
        *pos     = sqlite3_column_int(stmt, 0);
        ret_code = true;
    error:
        sqlite3_finalize(stmt);
        return ret_code;
    }
    
    int
    database_queue_probe_id(lkt_db *db, int id)
    {
        int position           = 0; /* The queue begins with the position 1 */
        sqlite3_stmt *stmt     = NULL;
        static const char *SQL = "SELECT position FROM queue WHERE kara_id = ?;";
        SQLITE_PREPARE(db, stmt, SQL, error);
        SQLITE_BIND_INT(db, stmt, 1, id, error);
        SQLITE_STEP_ROW(db, stmt, error);
        position = sqlite3_column_int(stmt, 0);
    error:
        sqlite3_finalize(stmt);
        return position;
    }
    
    bool
    database_queue_shuffle(lkt_db *db)
    {
        const char *SQL = "BEGIN TRANSACTION;"
                          /* Create temporary queue */
                          "DELETE FROM queue_tmp;"
                          "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
                          /* When current is NULL, that thing is also NULL, so no insertion is done */
                          "INSERT INTO queue_tmp (kara_id, priority)"
                          "  SELECT kara_id, 5"
                          "  FROM queue"
                          "  JOIN queue_state ON queue.position = queue_state.current;"
                          /* Insert the others karas */
                          "INSERT INTO queue_tmp (kara_id, priority)"
                          "  SELECT kara_id, priority"
                          "  FROM queue"
                          "  JOIN queue_state ON"
                          "    queue.position <> CASE"
                          "      WHEN queue_state.current IS NOT NULL THEN queue_state.current"
                          "      ELSE 0 END"
                          "  ORDER BY RANDOM();"
                          /* Do the insertion in the other way */
                          "DELETE FROM queue;"
                          "DELETE FROM " LKT_PROTECTED_DATABASE ".sqlite_sequence WHERE name = 'queue';"
                          "INSERT INTO queue (position, kara_id, priority)"
                          "  SELECT position, kara_id, priority"
                          "  FROM queue_tmp"
                          "  ORDER BY priority, position;"
                          /* Set the current to the right kara when needed */
                          "UPDATE queue_state"
                          "  SET current = CASE"
                          "    WHEN current NOT NULL THEN 1"
                          "    ELSE NULL END;"
                          /* Clear the TMP */
                          "DELETE FROM queue_tmp;"
                          "DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
                          "COMMIT;";
        database_queue_flat(db);
        SQLITE_EXEC(db, SQL, error);
        return true;
    error:
        SQLITE_DO_ROLLBACK(db);
        return false;
    }
    
    bool
    database_queue_list(lkt_db *db, size_t from, size_t to, struct lkt_callback *callback)
    {
        const char *SQL_STMT =
            "WITH content AS ("
            " SELECT kara.id AS id, string, LENGTH(CAST(kara.id AS TEXT)) AS len, position, LENGTH(CAST(position AS TEXT)) AS pos_len"
            "  FROM queue"
            "  JOIN kara ON kara_id = kara.id"
            "  WHERE position >= ? AND position <= ?"
            "  GROUP BY position ORDER BY position ASC, priority DESC)"
            "SELECT id, string, (SELECT MAX(len) FROM content), position, (SELECT MAX(pos_len) FROM content) FROM content;";
        int code, id, id_len, pos, pos_len;
        const char *row;
        bool ret = false;
        sqlite3_stmt *stmt;
    
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
        SQLITE_BIND_INT(db, stmt, 1, (int)from, error);
        SQLITE_BIND_INT(db, stmt, 2, (int)to, error);
    
        for (;;) {
            code = sqlite3_step(stmt);
    
            if (code == SQLITE_ROW) {
                id      = sqlite3_column_int(stmt, 0);
                row     = sqlite3_column_chars(stmt, 1);
                id_len  = sqlite3_column_int(stmt, 2);
                pos     = sqlite3_column_int(stmt, 3);
                pos_len = sqlite3_column_int(stmt, 4);
                ++(callback->iterations);
                if (callback->call(callback, pos_len, pos, id, id_len, row))
                    continue;
                else
                    break;
            }
    
            else if (code == SQLITE_OK || code == SQLITE_DONE)
                goto done;
    
            else {
                LOG_ERROR("DB", "Failed: %s", sqlite3_errmsg((sqlite3 *)db));
                break;
            }
        }
    
    done:
        ret = true;
    error:
        sqlite3_finalize(stmt);
        return ret;
    }
    
    bool
    database_queue_dump(lkt_db *db, const char *plt_name)
    {
        static const char *SQL = "WITH plt_id AS (SELECT playlist.id AS id FROM playlist"
                                 " WHERE name COLLATE nocase = ?) "
                                 "INSERT OR IGNORE INTO kara_playlist (kara_id, playlist_id)"
                                 " SELECT DISTINCT kara_id, plt_id.id"
                                 " FROM queue, plt_id"
                                 " ORDER BY RANDOM();";
        bool ret_code      = false;
        sqlite3_stmt *stmt = NULL;
        SQLITE_PREPARE(db, stmt, SQL, error);
        SQLITE_BIND_TEXT(db, stmt, 1, plt_name, error);
        SQLITE_STEP_DONE(db, stmt, error);
        ret_code = true;
    error:
        sqlite3_finalize(stmt);
        return ret_code;
    }
    
    bool
    database_queue_seekid(lkt_db *db, int id, int *out_pos)
    {
        static const char *SQL_STMT = "SELECT position FROM queue WHERE kara_id = ? LIMIT 1";
        int ret                     = 0;
        sqlite3_stmt *stmt;
    
        SQLITE_PREPARE(db, stmt, SQL_STMT, error);
        SQLITE_BIND_INT(db, stmt, 1, id, error);
        SQLITE_STEP_ROW(db, stmt, error);
        *out_pos = sqlite3_column_int(stmt, 0);
        ret      = true;
    error:
        sqlite3_finalize(stmt);
        return ret;
    }