int
lms_db_get_file_info(sqlite3_stmt *stmt, struct lms_file_info *finfo)
{
    int r, ret;

    ret = lms_db_bind_blob(stmt, 1, finfo->path, finfo->path_len);
    if (ret != 0)
        goto done;

    r = sqlite3_step(stmt);
    if (r == SQLITE_DONE) {
        ret = 1;
        finfo->id = -1;
        goto done;
    }

    if (r != SQLITE_ROW) {
        fprintf(stderr, "ERROR: could not get file info from table: %s\n",
                sqlite3_errmsg(sqlite3_db_handle(stmt)));
        ret = -2;
        goto done;
    }

    finfo->id = sqlite3_column_int64(stmt, 0);
    finfo->mtime = sqlite3_column_int(stmt, 1);
    finfo->dtime = sqlite3_column_int(stmt, 2);
    finfo->itime = sqlite3_column_int(stmt, 3);
    finfo->size = sqlite3_column_int(stmt, 4);
    ret = 0;

  done:
    lms_db_reset_stmt(stmt);

    return ret;
}
static int
_db_insert(lms_db_playlist_t *ldp, const struct lms_playlist_info *info)
{
    sqlite3_stmt *stmt;
    int r, ret;

    stmt = ldp->insert;

    ret = lms_db_bind_int64(stmt, 1, info->id);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_text(stmt, 2, info->title.str, info->title.len);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 3, info->n_entries);
    if (ret != 0)
        goto done;

    r = sqlite3_step(stmt);
    if (r != SQLITE_DONE) {
        fprintf(stderr, "ERROR: could not insert playlist info: %s\n",
                sqlite3_errmsg(ldp->db));
        ret = -4;
        goto done;
    }

    ret = 0;

  done:
    lms_db_reset_stmt(stmt);

    return ret;
}
int
lms_db_set_file_dtime(sqlite3_stmt *stmt, const struct lms_file_info *finfo)
{
    int r, ret;

    ret = lms_db_bind_int(stmt, 1, finfo->dtime);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 2, finfo->itime);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int64(stmt, 3, finfo->id);
    if (ret != 0)
        goto done;

    r = sqlite3_step(stmt);
    if (r != SQLITE_DONE) {
        fprintf(stderr, "ERROR: could not set file dtime: %s\n",
                sqlite3_errmsg(sqlite3_db_handle(stmt)));
        ret = -3;
        goto done;
    }

    ret = 0;

  done:
    lms_db_reset_stmt(stmt);

    return ret;
}
int
lms_db_table_version_set(sqlite3 *db, const char *table, unsigned int version)
{
    int r, ret;
    sqlite3_stmt *stmt;

    stmt = lms_db_compile_stmt(db,
        "INSERT OR REPLACE INTO lms_internal (tab, version) VALUES (?, ?)");
    if (!stmt)
        return -1;

    ret = lms_db_bind_text(stmt, 1, table, -1);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 2, version);
    if (ret != 0)
        goto done;

    r = sqlite3_step(stmt);
    if (r != SQLITE_DONE) {
        ret = -1;
        fprintf(stderr, "ERROR: could not set table '%s' version: %s\n",
                table, sqlite3_errmsg(db));
    }

  done:
    lms_db_reset_stmt(stmt);
    lms_db_finalize_stmt(stmt, "table_version_set");

    return ret;
}
int
lms_db_table_version_get(sqlite3 *db, const char *table)
{
    int r, version;
    sqlite3_stmt *stmt;

    stmt = lms_db_compile_stmt(db,
         "SELECT version FROM lms_internal WHERE tab = ?");
    if (!stmt)
        return -1;

    if (lms_db_bind_text(stmt, 1, table, -1) != 0) {
        version = -1;
        goto done;
    }

    r = sqlite3_step(stmt);
    if (r == SQLITE_DONE)
        version = 0;
    else if (r == SQLITE_ROW)
        version = sqlite3_column_int(stmt, 0);
    else {
        version = -1;
        fprintf(stderr, "ERROR: could not get table '%s' version: %s\n",
                table, sqlite3_errmsg(db));
    }

  done:
    lms_db_reset_stmt(stmt);
    lms_db_finalize_stmt(stmt, "table_version_get");

    return version;
}
static int
_check(struct pinfo *pinfo, int len, char *path)
{
    char query[PATH_SIZE + 3];
    struct master_db *db;
    int ret;

    db = _master_db_open(pinfo->common.lms->db_path);
    if (!db)
        return -1;

    if (_is_file(path))
        memcpy(query, path, len + 1);
    else {
        memcpy(query, path, len);
        memcpy(query + len, "/%", sizeof("/%"));
        len += sizeof("/%") - 1;
    }
    ret = lms_db_get_files(db->get_files, query, len);
    if (ret != 0)
        goto end;

    ret = lms_db_update_id_get(db->handle);
    if (ret < 0) {
        fprintf(stderr, "ERROR: could not get global update id.\n");
        goto end;
    }

    pinfo->common.update_id = ret + 1;

    if (lms_create_slave(pinfo, _slave_work) != 0) {
        ret = -2;
        goto end;
    }

    _init_sync_wait(pinfo, 1);

    ret = _db_files_loop(db, (struct cinfo *)pinfo, _check_row);

    _master_send_finish(&pinfo->master);
    _init_sync_wait(pinfo, 0);
    lms_finish_slave(pinfo, _master_dummy_send_finish);
  end:
    lms_db_reset_stmt(db->get_files);
    _master_db_close(db);

    return ret;
}
int
lms_db_insert_file_info(sqlite3_stmt *stmt, struct lms_file_info *finfo,
                        unsigned int update_id)
{
    int r, ret;

    ret = lms_db_bind_blob(stmt, 1, finfo->path, finfo->path_len);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 2, finfo->mtime);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 3, finfo->dtime);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 4, finfo->itime);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 5, finfo->size);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 6, update_id);
    if (ret != 0)
        goto done;

    r = sqlite3_step(stmt);
    if (r != SQLITE_DONE) {
        fprintf(stderr, "ERROR: could not insert file info: %s\n",
                sqlite3_errmsg(sqlite3_db_handle(stmt)));
        ret = -5;
        goto done;
    }

    finfo->id = sqlite3_last_insert_rowid(sqlite3_db_handle(stmt));
    ret = 0;

  done:
    lms_db_reset_stmt(stmt);

    return ret;
}
int
lms_db_update_file_info(sqlite3_stmt *stmt, const struct lms_file_info *finfo, unsigned int update_id)
{
    int r, ret;

    ret = lms_db_bind_int(stmt, 1, finfo->mtime);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 2, finfo->dtime);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 3, finfo->itime);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 4, finfo->size);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 5, update_id);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 6, finfo->id);
    if (ret != 0)
        goto done;

    r = sqlite3_step(stmt);
    if (r != SQLITE_DONE) {
        fprintf(stderr, "ERROR: could not update file info: %s\n",
                sqlite3_errmsg(sqlite3_db_handle(stmt)));
        ret = -5;
        goto done;
    }

    ret = 0;

  done:
    lms_db_reset_stmt(stmt);

    return ret;
}
int
lms_db_delete_file_info(sqlite3_stmt *stmt, const struct lms_file_info *finfo)
{
    int r, ret;

    ret = lms_db_bind_int64(stmt, 1, finfo->id);
    if (ret != 0)
        goto done;

    r = sqlite3_step(stmt);
    if (r != SQLITE_DONE) {
        fprintf(stderr, "ERROR: could not delete file info: %s\n",
                sqlite3_errmsg(sqlite3_db_handle(stmt)));
        ret = -2;
        goto done;
    }
    ret = 0;

  done:
    lms_db_reset_stmt(stmt);

    return ret;
}
static int
_check_single_process(struct sinfo *sinfo, int len, char *path)
{
    struct single_process_db *db;
    char query[PATH_SIZE + 2];
    void **parser_match = NULL;
    lms_t *lms;
    int ret;

    lms = sinfo->common.lms;
    db = _single_process_db_open(lms->db_path);
    if (!db)
        return -1;

    if (_is_file(path))
        memcpy(query, path, len + 1);
    else {
        memcpy(query, path, len);
        memcpy(query + len, "/%", sizeof("/%"));
        len += sizeof("/%") - 1;
    }
    ret = lms_db_get_files(db->get_files, query, len);
    if (ret != 0)
        goto end;

    if (lms_parsers_setup(lms, db->handle) != 0) {
        fprintf(stderr, "ERROR: could not setup parsers.\n");
        ret = -2;
        goto end;
    }

    if (lms_parsers_start(lms, db->handle) != 0) {
        fprintf(stderr, "ERROR: could not start parsers.\n");
        ret = -3;
        goto end;
    }

    if (lms->n_parsers < 1) {
        fprintf(stderr, "ERROR: no parser could be started, exit.\n");
        ret = -4;
        goto end;
    }

    parser_match = malloc(lms->n_parsers * sizeof(*parser_match));
    if (!parser_match) {
        perror("malloc");
        ret = -5;
        goto end;
    }

    ret = lms_db_update_id_get(db->handle);
    if (ret < 0) {
        fprintf(stderr, "ERROR: could not get global update id.\n");
        goto end;
    }

    sinfo->common.update_id = ret + 1;
    sinfo->parser_match = parser_match;

    lms_db_begin_transaction(db->transaction_begin);

    ret = _db_files_loop(db, (struct cinfo *)sinfo, _check_row_single_process);

    /* Check only if there are remaining commits to do */
    if (sinfo->commit_counter) {
        sinfo->total_committed += sinfo->commit_counter;
        lms_db_update_id_set(db->handle, sinfo->common.update_id);
    }

    lms_db_end_transaction(db->transaction_commit);

end:
    free(parser_match);
    lms_parsers_finish(lms, db->handle);
    lms_db_reset_stmt(db->get_files);
    _single_process_db_close(db);

    return ret;
}
static int
_db_insert(lms_db_image_t *ldi, const struct lms_image_info *info)
{
    sqlite3_stmt *stmt;
    int r, ret;

    stmt = ldi->insert;

    ret = lms_db_bind_int64(stmt, 1, info->id);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_text(stmt, 2, info->title.str, info->title.len);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_text(stmt, 3, info->artist.str, info->artist.len);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 4, info->date);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 5, info->width);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 6, info->height);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_int(stmt, 7, info->orientation);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_double(stmt, 8, info->gps.latitude);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_double(stmt, 9, info->gps.longitude);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_double(stmt, 10, info->gps.altitude);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_text(stmt, 11, info->dlna_profile.str,
                           info->dlna_profile.len);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_text(stmt, 12, info->dlna_mime.str, info->dlna_mime.len);
    if (ret != 0)
        goto done;

    ret = lms_db_bind_text(stmt, 13, info->container.str, info->container.len);
    if (ret != 0)
        goto done;

    r = sqlite3_step(stmt);
    if (r != SQLITE_DONE) {
        fprintf(stderr, "ERROR: could not insert image info: %s\n",
                sqlite3_errmsg(ldi->db));
        ret = -11;
        goto done;
    }

    ret = 0;

done:
    lms_db_reset_stmt(stmt);

    return ret;
}